{"diffoscope-json-version": 1, "source1": "/srv/reproducible-results/rbuild-debian/r-b-build.0XBMhEoX/b1/openlayers_2.13.1+ds2-11_amd64.changes", "source2": "/srv/reproducible-results/rbuild-debian/r-b-build.0XBMhEoX/b2/openlayers_2.13.1+ds2-11_amd64.changes", "unified_diff": null, "details": [{"source1": "Files", "source2": "Files", "unified_diff": "@@ -1,2 +1,2 @@\n \n- 12f05bd68b6a0ac46c6f21642c57b649 707036 javascript optional libjs-openlayers_2.13.1+ds2-11_all.deb\n+ 7f24c5f1887f3777877f076501d4536a 716092 javascript optional libjs-openlayers_2.13.1+ds2-11_all.deb\n"}, {"source1": "libjs-openlayers_2.13.1+ds2-11_all.deb", "source2": "libjs-openlayers_2.13.1+ds2-11_all.deb", "unified_diff": null, "details": [{"source1": "file list", "source2": "file list", "unified_diff": "@@ -1,3 +1,3 @@\n -rw-r--r-- 0 0 0 4 2025-03-06 18:35:30.000000 debian-binary\n--rw-r--r-- 0 0 0 3684 2025-03-06 18:35:30.000000 control.tar.xz\n--rw-r--r-- 0 0 0 703160 2025-03-06 18:35:30.000000 data.tar.xz\n+-rw-r--r-- 0 0 0 3680 2025-03-06 18:35:30.000000 control.tar.xz\n+-rw-r--r-- 0 0 0 712220 2025-03-06 18:35:30.000000 data.tar.xz\n"}, {"source1": "control.tar.xz", "source2": "control.tar.xz", "unified_diff": null, "details": [{"source1": "control.tar", "source2": "control.tar", "unified_diff": null, "details": [{"source1": "./md5sums", "source2": "./md5sums", "unified_diff": null, "details": [{"source1": "./md5sums", "source2": "./md5sums", "comments": ["Files differ"], "unified_diff": null}]}]}]}, {"source1": "data.tar.xz", "source2": "data.tar.xz", "unified_diff": null, "details": [{"source1": "data.tar", "source2": "data.tar", "unified_diff": null, "details": [{"source1": "./usr/share/javascript/openlayers/OpenLayers.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -263,1775 +263,519 @@\n source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n destination.toString = source.toString;\n }\n }\n return destination;\n };\n /* ======================================================================\n- OpenLayers/Console.js\n+ OpenLayers/Geometry.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Namespace: OpenLayers.Console\n- * The OpenLayers.Console namespace is used for debugging and error logging.\n- * If the Firebug Lite (../Firebug/firebug.js) is included before this script,\n- * calls to OpenLayers.Console methods will get redirected to window.console.\n- * This makes use of the Firebug extension where available and allows for\n- * cross-browser debugging Firebug style.\n+ * Class: OpenLayers.Geometry\n+ * A Geometry is a description of a geographic object. Create an instance of\n+ * this class with the <OpenLayers.Geometry> constructor. This is a base class,\n+ * typical geometry types are described by subclasses of this class.\n *\n- * Note:\n- * Note that behavior will differ with the Firebug extention and Firebug Lite.\n- * Most notably, the Firebug Lite console does not currently allow for\n- * hyperlinks to code or for clicking on object to explore their properties.\n- * \n+ * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must\n+ * explicitly include the OpenLayers.Format.WKT in your build.\n */\n-OpenLayers.Console = {\n- /**\n- * Create empty functions for all console methods. The real value of these\n- * properties will be set if Firebug Lite (../Firebug/firebug.js script) is\n- * included. We explicitly require the Firebug Lite script to trigger\n- * functionality of the OpenLayers.Console methods.\n- */\n-\n- /**\n- * APIFunction: log\n- * Log an object in the console. The Firebug Lite console logs string\n- * representation of objects. Given multiple arguments, they will\n- * be cast to strings and logged with a space delimiter. If the first\n- * argument is a string with printf-like formatting, subsequent arguments\n- * will be used in string substitution. Any additional arguments (beyond\n- * the number substituted in a format string) will be appended in a space-\n- * delimited line.\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- log: function() {},\n-\n- /**\n- * APIFunction: debug\n- * Writes a message to the console, including a hyperlink to the line\n- * where it was called.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- debug: function() {},\n-\n- /**\n- * APIFunction: info\n- * Writes a message to the console with the visual \"info\" icon and color\n- * coding and a hyperlink to the line where it was called.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- info: function() {},\n-\n- /**\n- * APIFunction: warn\n- * Writes a message to the console with the visual \"warning\" icon and\n- * color coding and a hyperlink to the line where it was called.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- warn: function() {},\n-\n- /**\n- * APIFunction: error\n- * Writes a message to the console with the visual \"error\" icon and color\n- * coding and a hyperlink to the line where it was called.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- error: function() {},\n-\n- /**\n- * APIFunction: userError\n- * A single interface for showing error messages to the user. The default\n- * behavior is a Javascript alert, though this can be overridden by\n- * reassigning OpenLayers.Console.userError to a different function.\n- *\n- * Expects a single error message\n- * \n- * Parameters:\n- * error - {Object}\n- */\n- userError: function(error) {\n- alert(error);\n- },\n-\n- /**\n- * APIFunction: assert\n- * Tests that an expression is true. If not, it will write a message to\n- * the console and throw an exception.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- assert: function() {},\n-\n- /**\n- * APIFunction: dir\n- * Prints an interactive listing of all properties of the object. This\n- * looks identical to the view that you would see in the DOM tab.\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- dir: function() {},\n-\n- /**\n- * APIFunction: dirxml\n- * Prints the XML source tree of an HTML or XML element. This looks\n- * identical to the view that you would see in the HTML tab. You can click\n- * on any node to inspect it in the HTML tab.\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- dirxml: function() {},\n-\n- /**\n- * APIFunction: trace\n- * Prints an interactive stack trace of JavaScript execution at the point\n- * where it is called. The stack trace details the functions on the stack,\n- * as well as the values that were passed as arguments to each function.\n- * You can click each function to take you to its source in the Script tab,\n- * and click each argument value to inspect it in the DOM or HTML tabs.\n- * \n- */\n- trace: function() {},\n-\n- /**\n- * APIFunction: group\n- * Writes a message to the console and opens a nested block to indent all\n- * future messages sent to the console. Call OpenLayers.Console.groupEnd()\n- * to close the block.\n- *\n- * May be called with multiple arguments as with OpenLayers.Console.log().\n- * \n- * Parameters:\n- * object - {Object}\n- */\n- group: function() {},\n-\n- /**\n- * APIFunction: groupEnd\n- * Closes the most recently opened block created by a call to\n- * OpenLayers.Console.group\n- */\n- groupEnd: function() {},\n-\n- /**\n- * APIFunction: time\n- * Creates a new timer under the given name. Call\n- * OpenLayers.Console.timeEnd(name)\n- * with the same name to stop the timer and print the time elapsed.\n- *\n- * Parameters:\n- * name - {String}\n- */\n- time: function() {},\n-\n- /**\n- * APIFunction: timeEnd\n- * Stops a timer created by a call to OpenLayers.Console.time(name) and\n- * writes the time elapsed.\n- *\n- * Parameters:\n- * name - {String}\n- */\n- timeEnd: function() {},\n-\n- /**\n- * APIFunction: profile\n- * Turns on the JavaScript profiler. The optional argument title would\n- * contain the text to be printed in the header of the profile report.\n- *\n- * This function is not currently implemented in Firebug Lite.\n- * \n- * Parameters:\n- * title - {String} Optional title for the profiler\n- */\n- profile: function() {},\n-\n- /**\n- * APIFunction: profileEnd\n- * Turns off the JavaScript profiler and prints its report.\n- * \n- * This function is not currently implemented in Firebug Lite.\n- */\n- profileEnd: function() {},\n-\n- /**\n- * APIFunction: count\n- * Writes the number of times that the line of code where count was called\n- * was executed. The optional argument title will print a message in\n- * addition to the number of the count.\n- *\n- * This function is not currently implemented in Firebug Lite.\n- *\n- * Parameters:\n- * title - {String} Optional title to be printed with count\n- */\n- count: function() {},\n-\n- CLASS_NAME: \"OpenLayers.Console\"\n-};\n+OpenLayers.Geometry = OpenLayers.Class({\n \n-/**\n- * Execute an anonymous function to extend the OpenLayers.Console namespace\n- * if the firebug.js script is included. This closure is used so that the\n- * \"scripts\" and \"i\" variables don't pollute the global namespace.\n- */\n-(function() {\n /**\n- * If Firebug Lite is included (before this script), re-route all\n- * OpenLayers.Console calls to the console object.\n- */\n- var scripts = document.getElementsByTagName(\"script\");\n- for (var i = 0, len = scripts.length; i < len; ++i) {\n- if (scripts[i].src.indexOf(\"firebug.js\") != -1) {\n- if (console) {\n- OpenLayers.Util.extend(OpenLayers.Console, console);\n- break;\n- }\n- }\n- }\n-})();\n-/* ======================================================================\n- OpenLayers/Popup.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-\n-/**\n- * Class: OpenLayers.Popup\n- * A popup is a small div that can opened and closed on the map.\n- * Typically opened in response to clicking on a marker. \n- * See <OpenLayers.Marker>. Popup's don't require their own\n- * layer and are added the the map using the <OpenLayers.Map.addPopup>\n- * method.\n- *\n- * Example:\n- * (code)\n- * popup = new OpenLayers.Popup(\"chicken\", \n- * new OpenLayers.LonLat(5,40),\n- * new OpenLayers.Size(200,200),\n- * \"example popup\",\n- * true);\n- * \n- * map.addPopup(popup);\n- * (end)\n- */\n-OpenLayers.Popup = OpenLayers.Class({\n-\n- /** \n- * Property: events \n- * {<OpenLayers.Events>} custom event manager \n- */\n- events: null,\n-\n- /** Property: id\n- * {String} the unique identifier assigned to this popup.\n- */\n- id: \"\",\n-\n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} the position of this popup on the map\n- */\n- lonlat: null,\n-\n- /** \n- * Property: div \n- * {DOMElement} the div that contains this popup.\n- */\n- div: null,\n-\n- /** \n- * Property: contentSize \n- * {<OpenLayers.Size>} the width and height of the content.\n- */\n- contentSize: null,\n-\n- /** \n- * Property: size \n- * {<OpenLayers.Size>} the width and height of the popup.\n- */\n- size: null,\n-\n- /** \n- * Property: contentHTML \n- * {String} An HTML string for this popup to display.\n- */\n- contentHTML: null,\n-\n- /** \n- * Property: backgroundColor \n- * {String} the background color used by the popup.\n- */\n- backgroundColor: \"\",\n-\n- /** \n- * Property: opacity \n- * {float} the opacity of this popup (between 0.0 and 1.0)\n- */\n- opacity: \"\",\n-\n- /** \n- * Property: border \n- * {String} the border size of the popup. (eg 2px)\n- */\n- border: \"\",\n-\n- /** \n- * Property: contentDiv \n- * {DOMElement} a reference to the element that holds the content of\n- * the div.\n- */\n- contentDiv: null,\n-\n- /** \n- * Property: groupDiv \n- * {DOMElement} First and only child of 'div'. The group Div contains the\n- * 'contentDiv' and the 'closeDiv'.\n- */\n- groupDiv: null,\n-\n- /** \n- * Property: closeDiv\n- * {DOMElement} the optional closer image\n- */\n- closeDiv: null,\n-\n- /** \n- * APIProperty: autoSize\n- * {Boolean} Resize the popup to auto-fit the contents.\n- * Default is false.\n+ * Property: id\n+ * {String} A unique identifier for this geometry.\n */\n- autoSize: false,\n+ id: null,\n \n /**\n- * APIProperty: minSize\n- * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.\n+ * Property: parent\n+ * {<OpenLayers.Geometry>}This is set when a Geometry is added as component\n+ * of another geometry\n */\n- minSize: null,\n+ parent: null,\n \n /**\n- * APIProperty: maxSize\n- * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.\n- */\n- maxSize: null,\n-\n- /** \n- * Property: displayClass\n- * {String} The CSS class of the popup.\n- */\n- displayClass: \"olPopup\",\n-\n- /** \n- * Property: contentDisplayClass\n- * {String} The CSS class of the popup content div.\n- */\n- contentDisplayClass: \"olPopupContent\",\n-\n- /** \n- * Property: padding \n- * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal \n- * padding of the content div inside the popup. This was originally\n- * confused with the css padding as specified in style.css's \n- * 'olPopupContent' class. We would like to get rid of this altogether,\n- * except that it does come in handy for the framed and anchoredbubble\n- * popups, who need to maintain yet another barrier between their \n- * content and the outer border of the popup itself. \n- * \n- * Note that in order to not break API, we must continue to support \n- * this property being set as an integer. Really, though, we'd like to \n- * have this specified as a Bounds object so that user can specify\n- * distinct left, top, right, bottom paddings. With the 3.0 release\n- * we can make this only a bounds.\n- */\n- padding: 0,\n-\n- /** \n- * Property: disableFirefoxOverflowHack\n- * {Boolean} The hack for overflow in Firefox causes all elements \n- * to be re-drawn, which causes Flash elements to be \n- * re-initialized, which is troublesome.\n- * With this property the hack can be disabled.\n+ * Property: bounds \n+ * {<OpenLayers.Bounds>} The bounds of this geometry\n */\n- disableFirefoxOverflowHack: false,\n+ bounds: null,\n \n /**\n- * Method: fixPadding\n- * To be removed in 3.0, this function merely helps us to deal with the \n- * case where the user may have set an integer value for padding, \n- * instead of an <OpenLayers.Bounds> object.\n+ * Constructor: OpenLayers.Geometry\n+ * Creates a geometry object. \n */\n- fixPadding: function() {\n- if (typeof this.padding == \"number\") {\n- this.padding = new OpenLayers.Bounds(\n- this.padding, this.padding, this.padding, this.padding\n- );\n- }\n+ initialize: function() {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n /**\n- * APIProperty: panMapIfOutOfView\n- * {Boolean} When drawn, pan map such that the entire popup is visible in\n- * the current viewport (if necessary).\n- * Default is false.\n- */\n- panMapIfOutOfView: false,\n-\n- /**\n- * APIProperty: keepInMap \n- * {Boolean} If panMapIfOutOfView is false, and this property is true, \n- * contrain the popup such that it always fits in the available map\n- * space. By default, this is not set on the base class. If you are\n- * creating popups that are near map edges and not allowing pannning,\n- * and especially if you have a popup which has a\n- * fixedRelativePosition, setting this to false may be a smart thing to\n- * do. Subclasses may want to override this setting.\n- * \n- * Default is false.\n- */\n- keepInMap: false,\n-\n- /**\n- * APIProperty: closeOnMove\n- * {Boolean} When map pans, close the popup.\n- * Default is false.\n- */\n- closeOnMove: false,\n-\n- /** \n- * Property: map \n- * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map\n- */\n- map: null,\n-\n- /** \n- * Constructor: OpenLayers.Popup\n- * Create a popup.\n- * \n- * Parameters: \n- * id - {String} a unqiue identifier for this popup. If null is passed\n- * an identifier will be automatically generated. \n- * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will\n- * be shown.\n- * contentSize - {<OpenLayers.Size>} The size of the content.\n- * contentHTML - {String} An HTML string to display inside the \n- * popup.\n- * closeBox - {Boolean} Whether to display a close box inside\n- * the popup.\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n- if (id == null) {\n- id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- }\n-\n- this.id = id;\n- this.lonlat = lonlat;\n-\n- this.contentSize = (contentSize != null) ? contentSize :\n- new OpenLayers.Size(\n- OpenLayers.Popup.WIDTH,\n- OpenLayers.Popup.HEIGHT);\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n- }\n- this.backgroundColor = OpenLayers.Popup.COLOR;\n- this.opacity = OpenLayers.Popup.OPACITY;\n- this.border = OpenLayers.Popup.BORDER;\n-\n- this.div = OpenLayers.Util.createDiv(this.id, null, null,\n- null, null, null, \"hidden\");\n- this.div.className = this.displayClass;\n-\n- var groupDivId = this.id + \"_GroupDiv\";\n- this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n- null, \"relative\", null,\n- \"hidden\");\n-\n- var id = this.div.id + \"_contentDiv\";\n- this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n- null, \"relative\");\n- this.contentDiv.className = this.contentDisplayClass;\n- this.groupDiv.appendChild(this.contentDiv);\n- this.div.appendChild(this.groupDiv);\n-\n- if (closeBox) {\n- this.addCloseBox(closeBoxCallback);\n- }\n-\n- this.registerEvents();\n- },\n-\n- /** \n * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n+ * Destroy this geometry.\n */\n destroy: function() {\n-\n this.id = null;\n- this.lonlat = null;\n- this.size = null;\n- this.contentHTML = null;\n-\n- this.backgroundColor = null;\n- this.opacity = null;\n- this.border = null;\n-\n- if (this.closeOnMove && this.map) {\n- this.map.events.unregister(\"movestart\", this, this.hide);\n- }\n-\n- this.events.destroy();\n- this.events = null;\n-\n- if (this.closeDiv) {\n- OpenLayers.Event.stopObservingElement(this.closeDiv);\n- this.groupDiv.removeChild(this.closeDiv);\n- }\n- this.closeDiv = null;\n-\n- this.div.removeChild(this.groupDiv);\n- this.groupDiv = null;\n-\n- if (this.map != null) {\n- this.map.removePopup(this);\n- }\n- this.map = null;\n- this.div = null;\n-\n- this.autoSize = null;\n- this.minSize = null;\n- this.maxSize = null;\n- this.padding = null;\n- this.panMapIfOutOfView = null;\n+ this.bounds = null;\n },\n \n- /** \n- * Method: draw\n- * Constructs the elements that make up the popup.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>} the position the popup in pixels.\n+ /**\n+ * APIMethod: clone\n+ * Create a clone of this geometry. Does not set any non-standard\n+ * properties of the cloned geometry.\n * \n * Returns:\n- * {DOMElement} Reference to a div that contains the drawn popup\n- */\n- draw: function(px) {\n- if (px == null) {\n- if ((this.lonlat != null) && (this.map != null)) {\n- px = this.map.getLayerPxFromLonLat(this.lonlat);\n- }\n- }\n-\n- // this assumes that this.map already exists, which is okay because \n- // this.draw is only called once the popup has been added to the map.\n- if (this.closeOnMove) {\n- this.map.events.register(\"movestart\", this, this.hide);\n- }\n-\n- //listen to movestart, moveend to disable overflow (FF bug)\n- if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n- this.map.events.register(\"movestart\", this, function() {\n- var style = document.defaultView.getComputedStyle(\n- this.contentDiv, null\n- );\n- var currentOverflow = style.getPropertyValue(\"overflow\");\n- if (currentOverflow != \"hidden\") {\n- this.contentDiv._oldOverflow = currentOverflow;\n- this.contentDiv.style.overflow = \"hidden\";\n- }\n- });\n- this.map.events.register(\"moveend\", this, function() {\n- var oldOverflow = this.contentDiv._oldOverflow;\n- if (oldOverflow) {\n- this.contentDiv.style.overflow = oldOverflow;\n- this.contentDiv._oldOverflow = null;\n- }\n- });\n- }\n-\n- this.moveTo(px);\n- if (!this.autoSize && !this.size) {\n- this.setSize(this.contentSize);\n- }\n- this.setBackgroundColor();\n- this.setOpacity();\n- this.setBorder();\n- this.setContentHTML();\n-\n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n- }\n-\n- return this.div;\n- },\n-\n- /** \n- * Method: updatePosition\n- * if the popup has a lonlat and its map members set, \n- * then have it move itself to its proper position\n+ * {<OpenLayers.Geometry>} An exact clone of this geometry.\n */\n- updatePosition: function() {\n- if ((this.lonlat) && (this.map)) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- if (px) {\n- this.moveTo(px);\n- }\n- }\n+ clone: function() {\n+ return new OpenLayers.Geometry();\n },\n \n /**\n- * Method: moveTo\n+ * Method: setBounds\n+ * Set the bounds for this Geometry.\n * \n * Parameters:\n- * px - {<OpenLayers.Pixel>} the top and left position of the popup div. \n+ * bounds - {<OpenLayers.Bounds>} \n */\n- moveTo: function(px) {\n- if ((px != null) && (this.div != null)) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\";\n+ setBounds: function(bounds) {\n+ if (bounds) {\n+ this.bounds = bounds.clone();\n }\n },\n \n /**\n- * Method: visible\n- *\n- * Returns: \n- * {Boolean} Boolean indicating whether or not the popup is visible\n+ * Method: clearBounds\n+ * Nullify this components bounds and that of its parent as well.\n */\n- visible: function() {\n- return OpenLayers.Element.visible(this.div);\n+ clearBounds: function() {\n+ this.bounds = null;\n+ if (this.parent) {\n+ this.parent.clearBounds();\n+ }\n },\n \n /**\n- * Method: toggle\n- * Toggles visibility of the popup.\n+ * Method: extendBounds\n+ * Extend the existing bounds to include the new bounds. \n+ * If geometry's bounds is not yet set, then set a new Bounds.\n+ * \n+ * Parameters:\n+ * newBounds - {<OpenLayers.Bounds>} \n */\n- toggle: function() {\n- if (this.visible()) {\n- this.hide();\n+ extendBounds: function(newBounds) {\n+ var bounds = this.getBounds();\n+ if (!bounds) {\n+ this.setBounds(newBounds);\n } else {\n- this.show();\n+ this.bounds.extend(newBounds);\n }\n },\n \n /**\n- * Method: show\n- * Makes the popup visible.\n+ * APIMethod: getBounds\n+ * Get the bounds for this Geometry. If bounds is not set, it \n+ * is calculated again, this makes queries faster.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>}\n */\n- show: function() {\n- this.div.style.display = '';\n-\n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n+ getBounds: function() {\n+ if (this.bounds == null) {\n+ this.calculateBounds();\n }\n+ return this.bounds;\n },\n \n- /**\n- * Method: hide\n- * Makes the popup invisible.\n+ /** \n+ * APIMethod: calculateBounds\n+ * Recalculate the bounds for the geometry. \n */\n- hide: function() {\n- this.div.style.display = 'none';\n+ calculateBounds: function() {\n+ //\n+ // This should be overridden by subclasses.\n+ //\n },\n \n /**\n- * Method: setSize\n- * Used to adjust the size of the popup. \n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n *\n * Parameters:\n- * contentSize - {<OpenLayers.Size>} the new size for the popup's \n- * contents div (in pixels).\n- */\n- setSize: function(contentSize) {\n- this.size = contentSize.clone();\n-\n- // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n- // must add that to the desired \"size\". \n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n-\n- // take into account the popup's 'padding' property\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n-\n- // make extra space for the close div\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right;\n- }\n-\n- //increase size of the main popup div to take into account the \n- // users's desired padding and close div. \n- this.size.w += wPadding;\n- this.size.h += hPadding;\n-\n- //now if our browser is IE, we need to actually make the contents \n- // div itself bigger to take its own padding into effect. this makes \n- // me want to shoot someone, but so it goes.\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.contentSize.w +=\n- contentDivPadding.left + contentDivPadding.right;\n- this.contentSize.h +=\n- contentDivPadding.bottom + contentDivPadding.top;\n- }\n-\n- if (this.div != null) {\n- this.div.style.width = this.size.w + \"px\";\n- this.div.style.height = this.size.h + \"px\";\n- }\n- if (this.contentDiv != null) {\n- this.contentDiv.style.width = contentSize.w + \"px\";\n- this.contentDiv.style.height = contentSize.h + \"px\";\n- }\n- },\n-\n- /**\n- * APIMethod: updateSize\n- * Auto size the popup so that it precisely fits its contents (as \n- * determined by this.contentDiv.innerHTML). Popup size will, of\n- * course, be limited by the available space on the current map\n- */\n- updateSize: function() {\n-\n- // determine actual render dimensions of the contents by putting its\n- // contents into a fake contentDiv (for the CSS) and then measuring it\n- var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" +\n- this.contentDiv.innerHTML +\n- \"</div>\";\n-\n- var containerElement = (this.map) ? this.map.div : document.body;\n- var realSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, null, {\n- displayClass: this.displayClass,\n- containerElement: containerElement\n- }\n- );\n-\n- // is the \"real\" size of the div is safe to display in our map?\n- var safeSize = this.getSafeContentSize(realSize);\n-\n- var newSize = null;\n- if (safeSize.equals(realSize)) {\n- //real size of content is small enough to fit on the map, \n- // so we use real size.\n- newSize = realSize;\n-\n- } else {\n-\n- // make a new 'size' object with the clipped dimensions \n- // set or null if not clipped.\n- var fixedSize = {\n- w: (safeSize.w < realSize.w) ? safeSize.w : null,\n- h: (safeSize.h < realSize.h) ? safeSize.h : null\n- };\n-\n- if (fixedSize.w && fixedSize.h) {\n- //content is too big in both directions, so we will use \n- // max popup size (safeSize), knowing well that it will \n- // overflow both ways. \n- newSize = safeSize;\n- } else {\n- //content is clipped in only one direction, so we need to \n- // run getRenderedDimensions() again with a fixed dimension\n- var clippedSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, fixedSize, {\n- displayClass: this.contentDisplayClass,\n- containerElement: containerElement\n- }\n- );\n-\n- //if the clipped size is still the same as the safeSize, \n- // that means that our content must be fixed in the \n- // offending direction. If overflow is 'auto', this means \n- // we are going to have a scrollbar for sure, so we must \n- // adjust for that.\n- //\n- var currentOverflow = OpenLayers.Element.getStyle(\n- this.contentDiv, \"overflow\"\n- );\n- if ((currentOverflow != \"hidden\") &&\n- (clippedSize.equals(safeSize))) {\n- var scrollBar = OpenLayers.Util.getScrollbarWidth();\n- if (fixedSize.w) {\n- clippedSize.h += scrollBar;\n- } else {\n- clippedSize.w += scrollBar;\n- }\n- }\n-\n- newSize = this.getSafeContentSize(clippedSize);\n- }\n- }\n- this.setSize(newSize);\n- },\n-\n- /**\n- * Method: setBackgroundColor\n- * Sets the background color of the popup.\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n *\n- * Parameters:\n- * color - {String} the background color. eg \"#FFBBBB\"\n- */\n- setBackgroundColor: function(color) {\n- if (color != undefined) {\n- this.backgroundColor = color;\n- }\n-\n- if (this.div != null) {\n- this.div.style.backgroundColor = this.backgroundColor;\n- }\n- },\n-\n- /**\n- * Method: setOpacity\n- * Sets the opacity of the popup.\n+ * Valid options depend on the specific geometry type.\n * \n- * Parameters:\n- * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ * Returns:\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n */\n- setOpacity: function(opacity) {\n- if (opacity != undefined) {\n- this.opacity = opacity;\n- }\n-\n- if (this.div != null) {\n- // for Mozilla and Safari\n- this.div.style.opacity = this.opacity;\n-\n- // for IE\n- this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n- }\n- },\n+ distanceTo: function(geometry, options) {},\n \n /**\n- * Method: setBorder\n- * Sets the border style of the popup.\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n *\n * Parameters:\n- * border - {String} The border style value. eg 2px \n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n+ *\n+ * Returns:\n+ * {Array} A list of all vertices in the geometry.\n */\n- setBorder: function(border) {\n- if (border != undefined) {\n- this.border = border;\n- }\n-\n- if (this.div != null) {\n- this.div.style.border = this.border;\n- }\n- },\n+ getVertices: function(nodes) {},\n \n /**\n- * Method: setContentHTML\n- * Allows the user to set the HTML content of the popup.\n- *\n+ * Method: atPoint\n+ * Note - This is only an approximation based on the bounds of the \n+ * geometry.\n+ * \n * Parameters:\n- * contentHTML - {String} HTML for the div.\n+ * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ * toleranceLon - {float} Optional tolerance in Geometric Coords\n+ * toleranceLat - {float} Optional tolerance in Geographic Coords\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the geometry is at the specified location\n */\n- setContentHTML: function(contentHTML) {\n-\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n- }\n-\n- if ((this.contentDiv != null) &&\n- (this.contentHTML != null) &&\n- (this.contentHTML != this.contentDiv.innerHTML)) {\n-\n- this.contentDiv.innerHTML = this.contentHTML;\n+ atPoint: function(lonlat, toleranceLon, toleranceLat) {\n+ var atPoint = false;\n+ var bounds = this.getBounds();\n+ if ((bounds != null) && (lonlat != null)) {\n \n- if (this.autoSize) {\n+ var dX = (toleranceLon != null) ? toleranceLon : 0;\n+ var dY = (toleranceLat != null) ? toleranceLat : 0;\n \n- //if popup has images, listen for when they finish\n- // loading and resize accordingly\n- this.registerImageListeners();\n+ var toleranceBounds =\n+ new OpenLayers.Bounds(this.bounds.left - dX,\n+ this.bounds.bottom - dY,\n+ this.bounds.right + dX,\n+ this.bounds.top + dY);\n \n- //auto size the popup to its current contents\n- this.updateSize();\n- }\n+ atPoint = toleranceBounds.containsLonLat(lonlat);\n }\n-\n+ return atPoint;\n },\n \n /**\n- * Method: registerImageListeners\n- * Called when an image contained by the popup loaded. this function\n- * updates the popup size, then unregisters the image load listener.\n+ * Method: getLength\n+ * Calculate the length of this geometry. This method is defined in\n+ * subclasses.\n+ * \n+ * Returns:\n+ * {Float} The length of the collection by summing its parts\n */\n- registerImageListeners: function() {\n-\n- // As the images load, this function will call updateSize() to \n- // resize the popup to fit the content div (which presumably is now\n- // bigger than when the image was not loaded).\n- // \n- // If the 'panMapIfOutOfView' property is set, we will pan the newly\n- // resized popup back into view.\n- // \n- // Note that this function, when called, will have 'popup' and \n- // 'img' properties in the context.\n+ getLength: function() {\n+ //to be overridden by geometries that actually have a length\n //\n- var onImgLoad = function() {\n- if (this.popup.id === null) { // this.popup has been destroyed!\n- return;\n- }\n- this.popup.updateSize();\n-\n- if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n- this.popup.panIntoView();\n- }\n-\n- OpenLayers.Event.stopObserving(\n- this.img, \"load\", this.img._onImgLoad\n- );\n-\n- };\n-\n- //cycle through the images and if their size is 0x0, that means that \n- // they haven't been loaded yet, so we attach the listener, which \n- // will fire when the images finish loading and will resize the \n- // popup accordingly to its new size.\n- var images = this.contentDiv.getElementsByTagName(\"img\");\n- for (var i = 0, len = images.length; i < len; i++) {\n- var img = images[i];\n- if (img.width == 0 || img.height == 0) {\n-\n- var context = {\n- 'popup': this,\n- 'img': img\n- };\n-\n- //expando this function to the image itself before registering\n- // it. This way we can easily and properly unregister it.\n- img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n-\n- OpenLayers.Event.observe(img, 'load', img._onImgLoad);\n- }\n- }\n+ return 0.0;\n },\n \n /**\n- * APIMethod: getSafeContentSize\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>} Desired size to make the popup.\n+ * Method: getArea\n+ * Calculate the area of this geometry. This method is defined in subclasses.\n * \n * Returns:\n- * {<OpenLayers.Size>} A size to make the popup which is neither smaller\n- * than the specified minimum size, nor bigger than the maximum \n- * size (which is calculated relative to the size of the viewport).\n+ * {Float} The area of the collection by summing its parts\n */\n- getSafeContentSize: function(size) {\n-\n- var safeContentSize = size.clone();\n-\n- // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n- // must add that to the desired \"size\". \n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n-\n- // take into account the popup's 'padding' property\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n-\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right;\n- }\n-\n- // prevent the popup from being smaller than a specified minimal size\n- if (this.minSize) {\n- safeContentSize.w = Math.max(safeContentSize.w,\n- (this.minSize.w - wPadding));\n- safeContentSize.h = Math.max(safeContentSize.h,\n- (this.minSize.h - hPadding));\n- }\n-\n- // prevent the popup from being bigger than a specified maximum size\n- if (this.maxSize) {\n- safeContentSize.w = Math.min(safeContentSize.w,\n- (this.maxSize.w - wPadding));\n- safeContentSize.h = Math.min(safeContentSize.h,\n- (this.maxSize.h - hPadding));\n- }\n-\n- //make sure the desired size to set doesn't result in a popup that \n- // is bigger than the map's viewport.\n+ getArea: function() {\n+ //to be overridden by geometries that actually have an area\n //\n- if (this.map && this.map.size) {\n-\n- var extraX = 0,\n- extraY = 0;\n- if (this.keepInMap && !this.panMapIfOutOfView) {\n- var px = this.map.getPixelFromLonLat(this.lonlat);\n- switch (this.relativePosition) {\n- case \"tr\":\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"tl\":\n- extraX = this.map.size.w - px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"bl\":\n- extraX = this.map.size.w - px.x;\n- extraY = px.y;\n- break;\n- case \"br\":\n- extraX = px.x;\n- extraY = px.y;\n- break;\n- default:\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- }\n- }\n-\n- var maxY = this.map.size.h -\n- this.map.paddingForPopups.top -\n- this.map.paddingForPopups.bottom -\n- hPadding - extraY;\n-\n- var maxX = this.map.size.w -\n- this.map.paddingForPopups.left -\n- this.map.paddingForPopups.right -\n- wPadding - extraX;\n-\n- safeContentSize.w = Math.min(safeContentSize.w, maxX);\n- safeContentSize.h = Math.min(safeContentSize.h, maxY);\n- }\n-\n- return safeContentSize;\n+ return 0.0;\n },\n \n /**\n- * Method: getContentDivPadding\n- * Glorious, oh glorious hack in order to determine the css 'padding' of \n- * the contentDiv. IE/Opera return null here unless we actually add the \n- * popup's main 'div' element (which contains contentDiv) to the DOM. \n- * So we make it invisible and then add it to the document temporarily. \n- *\n- * Once we've taken the padding readings we need, we then remove it \n- * from the DOM (it will actually get added to the DOM in \n- * Map.js's addPopup)\n+ * APIMethod: getCentroid\n+ * Calculate the centroid of this geometry. This method is defined in subclasses.\n *\n * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getContentDivPadding: function() {\n-\n- //use cached value if we have it\n- var contentDivPadding = this._contentDivPadding;\n- if (!contentDivPadding) {\n-\n- if (this.div.parentNode == null) {\n- //make the div invisible and add it to the page \n- this.div.style.display = \"none\";\n- document.body.appendChild(this.div);\n- }\n-\n- //read the padding settings from css, put them in an OL.Bounds \n- contentDivPadding = new OpenLayers.Bounds(\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"),\n- OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\")\n- );\n-\n- //cache the value\n- this._contentDivPadding = contentDivPadding;\n-\n- if (this.div.parentNode == document.body) {\n- //remove the div from the page and make it visible again\n- document.body.removeChild(this.div);\n- this.div.style.display = \"\";\n- }\n- }\n- return contentDivPadding;\n- },\n-\n- /**\n- * Method: addCloseBox\n- * \n- * Parameters:\n- * callback - {Function} The callback to be called when the close button\n- * is clicked.\n+ * {<OpenLayers.Geometry.Point>} The centroid of the collection\n */\n- addCloseBox: function(callback) {\n-\n- this.closeDiv = OpenLayers.Util.createDiv(\n- this.id + \"_close\", null, {\n- w: 17,\n- h: 17\n- }\n- );\n- this.closeDiv.className = \"olPopupCloseBox\";\n-\n- // use the content div's css padding to determine if we should\n- // padd the close div\n- var contentDivPadding = this.getContentDivPadding();\n-\n- this.closeDiv.style.right = contentDivPadding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top + \"px\";\n- this.groupDiv.appendChild(this.closeDiv);\n-\n- var closePopup = callback || function(e) {\n- this.hide();\n- OpenLayers.Event.stop(e);\n- };\n- OpenLayers.Event.observe(this.closeDiv, \"touchend\",\n- OpenLayers.Function.bindAsEventListener(closePopup, this));\n- OpenLayers.Event.observe(this.closeDiv, \"click\",\n- OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ getCentroid: function() {\n+ return null;\n },\n \n /**\n- * Method: panIntoView\n- * Pans the map such that the popup is totaly viewable (if necessary)\n- */\n- panIntoView: function() {\n-\n- var mapSize = this.map.getSize();\n-\n- //start with the top left corner of the popup, in px, \n- // relative to the viewport\n- var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(\n- parseInt(this.div.style.left),\n- parseInt(this.div.style.top)\n- ));\n- var newTL = origTL.clone();\n-\n- //new left (compare to margins, using this.size to calculate right)\n- if (origTL.x < this.map.paddingForPopups.left) {\n- newTL.x = this.map.paddingForPopups.left;\n- } else\n- if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {\n- newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;\n- }\n-\n- //new top (compare to margins, using this.size to calculate bottom)\n- if (origTL.y < this.map.paddingForPopups.top) {\n- newTL.y = this.map.paddingForPopups.top;\n- } else\n- if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {\n- newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;\n- }\n-\n- var dx = origTL.x - newTL.x;\n- var dy = origTL.y - newTL.y;\n-\n- this.map.pan(dx, dy);\n- },\n-\n- /** \n- * Method: registerEvents\n- * Registers events on the popup.\n+ * Method: toString\n+ * Returns a text representation of the geometry. If the WKT format is\n+ * included in a build, this will be the Well-Known Text \n+ * representation.\n *\n- * Do this in a separate function so that subclasses can \n- * choose to override it if they wish to deal differently\n- * with mouse events\n- * \n- * Note in the following handler functions that some special\n- * care is needed to deal correctly with mousing and popups. \n- * \n- * Because the user might select the zoom-rectangle option and\n- * then drag it over a popup, we need a safe way to allow the\n- * mousemove and mouseup events to pass through the popup when\n- * they are initiated from outside. The same procedure is needed for\n- * touchmove and touchend events.\n- * \n- * Otherwise, we want to essentially kill the event propagation\n- * for all other events, though we have to do so carefully, \n- * without disabling basic html functionality, like clicking on \n- * hyperlinks or drag-selecting text.\n- */\n- registerEvents: function() {\n- this.events = new OpenLayers.Events(this, this.div, null, true);\n-\n- function onTouchstart(evt) {\n- OpenLayers.Event.stop(evt, true);\n- }\n- this.events.on({\n- \"mousedown\": this.onmousedown,\n- \"mousemove\": this.onmousemove,\n- \"mouseup\": this.onmouseup,\n- \"click\": this.onclick,\n- \"mouseout\": this.onmouseout,\n- \"dblclick\": this.ondblclick,\n- \"touchstart\": onTouchstart,\n- scope: this\n- });\n-\n- },\n-\n- /** \n- * Method: onmousedown \n- * When mouse goes down within the popup, make a note of\n- * it locally, and then do not propagate the mousedown \n- * (but do so safely so that user can select text inside)\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onmousedown: function(evt) {\n- this.mousedown = true;\n- OpenLayers.Event.stop(evt, true);\n- },\n-\n- /** \n- * Method: onmousemove\n- * If the drag was started within the popup, then \n- * do not propagate the mousemove (but do so safely\n- * so that user can select text inside)\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onmousemove: function(evt) {\n- if (this.mousedown) {\n- OpenLayers.Event.stop(evt, true);\n- }\n- },\n-\n- /** \n- * Method: onmouseup\n- * When mouse comes up within the popup, after going down \n- * in it, reset the flag, and then (once again) do not \n- * propagate the event, but do so safely so that user can \n- * select text inside\n- * \n- * Parameters:\n- * evt - {Event} \n+ * Returns:\n+ * {String} String representation of this geometry.\n */\n- onmouseup: function(evt) {\n- if (this.mousedown) {\n- this.mousedown = false;\n- OpenLayers.Event.stop(evt, true);\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ string = OpenLayers.Format.WKT.prototype.write(\n+ new OpenLayers.Feature.Vector(this)\n+ );\n+ } else {\n+ string = Object.prototype.toString.call(this);\n }\n+ return string;\n },\n \n- /**\n- * Method: onclick\n- * Ignore clicks, but allowing default browser handling\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onclick: function(evt) {\n- OpenLayers.Event.stop(evt, true);\n- },\n-\n- /** \n- * Method: onmouseout\n- * When mouse goes out of the popup set the flag to false so that\n- * if they let go and then drag back in, we won't be confused.\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- onmouseout: function(evt) {\n- this.mousedown = false;\n- },\n-\n- /** \n- * Method: ondblclick\n- * Ignore double-clicks, but allowing default browser handling\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- ondblclick: function(evt) {\n- OpenLayers.Event.stop(evt, true);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Popup\"\n+ CLASS_NAME: \"OpenLayers.Geometry\"\n });\n \n-OpenLayers.Popup.WIDTH = 200;\n-OpenLayers.Popup.HEIGHT = 200;\n-OpenLayers.Popup.COLOR = \"white\";\n-OpenLayers.Popup.OPACITY = 1;\n-OpenLayers.Popup.BORDER = \"0px\";\n-/* ======================================================================\n- OpenLayers/Util/vendorPrefix.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n /**\n- * @requires OpenLayers/SingleFile.js\n+ * Function: OpenLayers.Geometry.fromWKT\n+ * Generate a geometry given a Well-Known Text string. For this method to\n+ * work, you must include the OpenLayers.Format.WKT in your build \n+ * explicitly.\n+ *\n+ * Parameters:\n+ * wkt - {String} A string representing the geometry in Well-Known Text.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry of the appropriate class.\n */\n+OpenLayers.Geometry.fromWKT = function(wkt) {\n+ var geom;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ var format = OpenLayers.Geometry.fromWKT.format;\n+ if (!format) {\n+ format = new OpenLayers.Format.WKT();\n+ OpenLayers.Geometry.fromWKT.format = format;\n+ }\n+ var result = format.read(wkt);\n+ if (result instanceof OpenLayers.Feature.Vector) {\n+ geom = result.geometry;\n+ } else if (OpenLayers.Util.isArray(result)) {\n+ var len = result.length;\n+ var components = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ components[i] = result[i].geometry;\n+ }\n+ geom = new OpenLayers.Geometry.Collection(components);\n+ }\n+ }\n+ return geom;\n+};\n \n-OpenLayers.Util = OpenLayers.Util || {};\n /**\n- * Namespace: OpenLayers.Util.vendorPrefix\n- * A collection of utility functions to detect vendor prefixed features\n+ * Method: OpenLayers.Geometry.segmentsIntersect\n+ * Determine whether two line segments intersect. Optionally calculates\n+ * and returns the intersection point. This function is optimized for\n+ * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those\n+ * obvious cases where there is no intersection, the function should\n+ * not be called.\n+ *\n+ * Parameters:\n+ * seg1 - {Object} Object representing a segment with properties x1, y1, x2,\n+ * and y2. The start point is represented by x1 and y1. The end point\n+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n+ * seg2 - {Object} Object representing a segment with properties x1, y1, x2,\n+ * and y2. The start point is represented by x1 and y1. The end point\n+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n+ * options - {Object} Optional properties for calculating the intersection.\n+ *\n+ * Valid options:\n+ * point - {Boolean} Return the intersection point. If false, the actual\n+ * intersection point will not be calculated. If true and the segments\n+ * intersect, the intersection point will be returned. If true and\n+ * the segments do not intersect, false will be returned. If true and\n+ * the segments are coincident, true will be returned.\n+ * tolerance - {Number} If a non-null value is provided, if the segments are\n+ * within the tolerance distance, this will be considered an intersection.\n+ * In addition, if the point option is true and the calculated intersection\n+ * is within the tolerance distance of an end point, the endpoint will be\n+ * returned instead of the calculated intersection. Further, if the\n+ * intersection is within the tolerance of endpoints on both segments, or\n+ * if two segment endpoints are within the tolerance distance of eachother\n+ * (but no intersection is otherwise calculated), an endpoint on the\n+ * first segment provided will be returned.\n+ *\n+ * Returns:\n+ * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.\n+ * If the point argument is true, the return will be the intersection\n+ * point or false if none exists. If point is true and the segments\n+ * are coincident, return will be true (and the instersection is equal\n+ * to the shorter segment).\n */\n-OpenLayers.Util.vendorPrefix = (function() {\n- \"use strict\";\n-\n- var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n- divStyle = document.createElement(\"div\").style,\n- cssCache = {},\n- jsCache = {};\n-\n-\n- /**\n- * Function: domToCss\n- * Converts a upper camel case DOM style property name to a CSS property\n- * i.e. transformOrigin -> transform-origin\n- * or WebkitTransformOrigin -> -webkit-transform-origin\n- *\n- * Parameters:\n- * prefixedDom - {String} The property to convert\n- *\n- * Returns:\n- * {String} The CSS property\n- */\n- function domToCss(prefixedDom) {\n- if (!prefixedDom) {\n- return null;\n+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n+ var point = options && options.point;\n+ var tolerance = options && options.tolerance;\n+ var intersection = false;\n+ var x11_21 = seg1.x1 - seg2.x1;\n+ var y11_21 = seg1.y1 - seg2.y1;\n+ var x12_11 = seg1.x2 - seg1.x1;\n+ var y12_11 = seg1.y2 - seg1.y1;\n+ var y22_21 = seg2.y2 - seg2.y1;\n+ var x22_21 = seg2.x2 - seg2.x1;\n+ var d = (y22_21 * x12_11) - (x22_21 * y12_11);\n+ var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);\n+ var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);\n+ if (d == 0) {\n+ // parallel\n+ if (n1 == 0 && n2 == 0) {\n+ // coincident\n+ intersection = true;\n }\n- return prefixedDom.\n- replace(/([A-Z])/g, function(c) {\n- return \"-\" + c.toLowerCase();\n- }).\n- replace(/^ms-/, \"-ms-\");\n- }\n-\n- /**\n- * APIMethod: css\n- * Detect which property is used for a CSS property\n- *\n- * Parameters:\n- * property - {String} The standard (unprefixed) CSS property name\n- *\n- * Returns:\n- * {String} The standard CSS property, prefixed property or null if not\n- * supported\n- */\n- function css(property) {\n- if (cssCache[property] === undefined) {\n- var domProperty = property.\n- replace(/(-[\\s\\S])/g, function(c) {\n- return c.charAt(1).toUpperCase();\n- });\n- var prefixedDom = style(domProperty);\n- cssCache[property] = domToCss(prefixedDom);\n+ } else {\n+ var along1 = n1 / d;\n+ var along2 = n2 / d;\n+ if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n+ // intersect\n+ if (!point) {\n+ intersection = true;\n+ } else {\n+ // calculate the intersection point\n+ var x = seg1.x1 + (along1 * x12_11);\n+ var y = seg1.y1 + (along1 * y12_11);\n+ intersection = new OpenLayers.Geometry.Point(x, y);\n+ }\n }\n- return cssCache[property];\n }\n-\n- /**\n- * APIMethod: js\n- * Detect which property is used for a JS property/method\n- *\n- * Parameters:\n- * obj - {Object} The object to test on\n- * property - {String} The standard (unprefixed) JS property name\n- *\n- * Returns:\n- * {String} The standard JS property, prefixed property or null if not\n- * supported\n- */\n- function js(obj, property) {\n- if (jsCache[property] === undefined) {\n- var tmpProp,\n- i = 0,\n- l = VENDOR_PREFIXES.length,\n- prefix,\n- isStyleObj = (typeof obj.cssText !== \"undefined\");\n-\n- jsCache[property] = null;\n- for (; i < l; i++) {\n- prefix = VENDOR_PREFIXES[i];\n- if (prefix) {\n- if (!isStyleObj) {\n- // js prefix should be lower-case, while style\n- // properties have upper case on first character\n- prefix = prefix.toLowerCase();\n+ if (tolerance) {\n+ var dist;\n+ if (intersection) {\n+ if (point) {\n+ var segs = [seg1, seg2];\n+ var seg, x, y;\n+ // check segment endpoints for proximity to intersection\n+ // set intersection to first endpoint within the tolerance\n+ outer: for (var i = 0; i < 2; ++i) {\n+ seg = segs[i];\n+ for (var j = 1; j < 3; ++j) {\n+ x = seg[\"x\" + j];\n+ y = seg[\"y\" + j];\n+ dist = Math.sqrt(\n+ Math.pow(x - intersection.x, 2) +\n+ Math.pow(y - intersection.y, 2)\n+ );\n+ if (dist < tolerance) {\n+ intersection.x = x;\n+ intersection.y = y;\n+ break outer;\n+ }\n }\n- tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);\n- } else {\n- tmpProp = property;\n }\n \n- if (obj[tmpProp] !== undefined) {\n- jsCache[property] = tmpProp;\n- break;\n+ }\n+ } else {\n+ // no calculated intersection, but segments could be within\n+ // the tolerance of one another\n+ var segs = [seg1, seg2];\n+ var source, target, x, y, p, result;\n+ // check segment endpoints for proximity to intersection\n+ // set intersection to first endpoint within the tolerance\n+ outer: for (var i = 0; i < 2; ++i) {\n+ source = segs[i];\n+ target = segs[(i + 1) % 2];\n+ for (var j = 1; j < 3; ++j) {\n+ p = {\n+ x: source[\"x\" + j],\n+ y: source[\"y\" + j]\n+ };\n+ result = OpenLayers.Geometry.distanceToSegment(p, target);\n+ if (result.distance < tolerance) {\n+ if (point) {\n+ intersection = new OpenLayers.Geometry.Point(p.x, p.y);\n+ } else {\n+ intersection = true;\n+ }\n+ break outer;\n+ }\n }\n }\n }\n- return jsCache[property];\n- }\n-\n- /**\n- * APIMethod: style\n- * Detect which property is used for a DOM style property\n- *\n- * Parameters:\n- * property - {String} The standard (unprefixed) style property name\n- *\n- * Returns:\n- * {String} The standard style property, prefixed property or null if not\n- * supported\n- */\n- function style(property) {\n- return js(divStyle, property);\n }\n-\n- return {\n- css: css,\n- js: js,\n- style: style,\n-\n- // used for testing\n- cssCache: cssCache,\n- jsCache: jsCache\n- };\n-}());\n-/* ======================================================================\n- OpenLayers/Animation.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ return intersection;\n+};\n \n /**\n- * @requires OpenLayers/SingleFile.js\n- * @requires OpenLayers/Util/vendorPrefix.js\n+ * Function: OpenLayers.Geometry.distanceToSegment\n+ *\n+ * Parameters:\n+ * point - {Object} An object with x and y properties representing the\n+ * point coordinates.\n+ * segment - {Object} An object with x1, y1, x2, and y2 properties\n+ * representing endpoint coordinates.\n+ *\n+ * Returns:\n+ * {Object} An object with distance, along, x, and y properties. The distance\n+ * will be the shortest distance between the input point and segment.\n+ * The x and y properties represent the coordinates along the segment\n+ * where the shortest distance meets the segment. The along attribute\n+ * describes how far between the two segment points the given point is.\n */\n+OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n+ var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n+ result.distance = Math.sqrt(result.distance);\n+ return result;\n+};\n \n /**\n- * Namespace: OpenLayers.Animation\n- * A collection of utility functions for executing methods that repaint a \n- * portion of the browser window. These methods take advantage of the\n- * browser's scheduled repaints where requestAnimationFrame is available.\n+ * Function: OpenLayers.Geometry.distanceSquaredToSegment\n+ *\n+ * Usually the distanceToSegment function should be used. This variant however\n+ * can be used for comparisons where the exact distance is not important.\n+ *\n+ * Parameters:\n+ * point - {Object} An object with x and y properties representing the\n+ * point coordinates.\n+ * segment - {Object} An object with x1, y1, x2, and y2 properties\n+ * representing endpoint coordinates.\n+ *\n+ * Returns:\n+ * {Object} An object with squared distance, along, x, and y properties.\n+ * The distance will be the shortest distance between the input point and\n+ * segment. The x and y properties represent the coordinates along the\n+ * segment where the shortest distance meets the segment. The along\n+ * attribute describes how far between the two segment points the given\n+ * point is.\n */\n-OpenLayers.Animation = (function(window) {\n-\n- /**\n- * Property: isNative\n- * {Boolean} true if a native requestAnimationFrame function is available\n- */\n- var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n- var isNative = !!(requestAnimationFrame);\n-\n- /**\n- * Function: requestFrame\n- * Schedule a function to be called at the next available animation frame.\n- * Uses the native method where available. Where requestAnimationFrame is\n- * not available, setTimeout will be called with a 16ms delay.\n- *\n- * Parameters:\n- * callback - {Function} The function to be called at the next animation frame.\n- * element - {DOMElement} Optional element that visually bounds the animation.\n- */\n- var requestFrame = (function() {\n- var request = window[requestAnimationFrame] ||\n- function(callback, element) {\n- window.setTimeout(callback, 16);\n- };\n- // bind to window to avoid illegal invocation of native function\n- return function(callback, element) {\n- request.apply(window, [callback, element]);\n- };\n- })();\n-\n- // private variables for animation loops\n- var counter = 0;\n- var loops = {};\n-\n- /**\n- * Function: start\n- * Executes a method with <requestFrame> in series for some \n- * duration.\n- *\n- * Parameters:\n- * callback - {Function} The function to be called at the next animation frame.\n- * duration - {Number} Optional duration for the loop. If not provided, the\n- * animation loop will execute indefinitely.\n- * element - {DOMElement} Optional element that visually bounds the animation.\n- *\n- * Returns:\n- * {Number} Identifier for the animation loop. Used to stop animations with\n- * <stop>.\n- */\n- function start(callback, duration, element) {\n- duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n- var id = ++counter;\n- var start = +new Date;\n- loops[id] = function() {\n- if (loops[id] && +new Date - start <= duration) {\n- callback();\n- if (loops[id]) {\n- requestFrame(loops[id], element);\n- }\n- } else {\n- delete loops[id];\n- }\n- };\n- requestFrame(loops[id], element);\n- return id;\n- }\n-\n- /**\n- * Function: stop\n- * Terminates an animation loop started with <start>.\n- *\n- * Parameters:\n- * id - {Number} Identifier returned from <start>.\n- */\n- function stop(id) {\n- delete loops[id];\n+OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n+ var x0 = point.x;\n+ var y0 = point.y;\n+ var x1 = segment.x1;\n+ var y1 = segment.y1;\n+ var x2 = segment.x2;\n+ var y2 = segment.y2;\n+ var dx = x2 - x1;\n+ var dy = y2 - y1;\n+ var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /\n+ (Math.pow(dx, 2) + Math.pow(dy, 2));\n+ var x, y;\n+ if (along <= 0.0) {\n+ x = x1;\n+ y = y1;\n+ } else if (along >= 1.0) {\n+ x = x2;\n+ y = y2;\n+ } else {\n+ x = x1 + along * dx;\n+ y = y1 + along * dy;\n }\n-\n return {\n- isNative: isNative,\n- requestFrame: requestFrame,\n- start: start,\n- stop: stop\n+ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n+ x: x,\n+ y: y,\n+ along: along\n };\n-\n-})(window);\n-/* ======================================================================\n- OpenLayers/Kinetic.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Animation.js\n- */\n-\n-OpenLayers.Kinetic = OpenLayers.Class({\n-\n- /**\n- * Property: threshold\n- * In most cases changing the threshold isn't needed.\n- * In px/ms, default to 0.\n- */\n- threshold: 0,\n-\n- /**\n- * Property: deceleration\n- * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n- */\n- deceleration: 0.0035,\n-\n- /**\n- * Property: nbPoints\n- * {Integer} the number of points we use to calculate the kinetic\n- * initial values.\n- */\n- nbPoints: 100,\n-\n- /**\n- * Property: delay\n- * {Float} time to consider to calculate the kinetic initial values.\n- * In ms, default to 200.\n- */\n- delay: 200,\n-\n- /**\n- * Property: points\n- * List of points use to calculate the kinetic initial values.\n- */\n- points: undefined,\n-\n- /**\n- * Property: timerId\n- * ID of the timer.\n- */\n- timerId: undefined,\n-\n- /**\n- * Constructor: OpenLayers.Kinetic\n- *\n- * Parameters:\n- * options - {Object}\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: begin\n- * Begins the dragging.\n- */\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = [];\n- },\n-\n- /**\n- * Method: update\n- * Updates during the dragging.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The new position.\n- */\n- update: function(xy) {\n- this.points.unshift({\n- xy: xy,\n- tick: new Date().getTime()\n- });\n- if (this.points.length > this.nbPoints) {\n- this.points.pop();\n- }\n- },\n-\n- /**\n- * Method: end\n- * Ends the dragging, start the kinetic.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The last position.\n- *\n- * Returns:\n- * {Object} An object with two properties: \"speed\", and \"theta\". The\n- * \"speed\" and \"theta\" values are to be passed to the move \n- * function when starting the animation.\n- */\n- end: function(xy) {\n- var last, now = new Date().getTime();\n- for (var i = 0, l = this.points.length, point; i < l; i++) {\n- point = this.points[i];\n- if (now - point.tick > this.delay) {\n- break;\n- }\n- last = point;\n- }\n- if (!last) {\n- return;\n- }\n- var time = new Date().getTime() - last.tick;\n- var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n- Math.pow(xy.y - last.xy.y, 2));\n- var speed = dist / time;\n- if (speed == 0 || speed < this.threshold) {\n- return;\n- }\n- var theta = Math.asin((xy.y - last.xy.y) / dist);\n- if (last.xy.x <= xy.x) {\n- theta = Math.PI - theta;\n- }\n- return {\n- speed: speed,\n- theta: theta\n- };\n- },\n-\n- /**\n- * Method: move\n- * Launch the kinetic move pan.\n- *\n- * Parameters:\n- * info - {Object} An object with two properties, \"speed\", and \"theta\".\n- * These values are those returned from the \"end\" call.\n- * callback - {Function} Function called on every step of the animation,\n- * receives x, y (values to pan), end (is the last point).\n- */\n- move: function(info, callback) {\n- var v0 = info.speed;\n- var fx = Math.cos(info.theta);\n- var fy = -Math.sin(info.theta);\n-\n- var initialTime = new Date().getTime();\n-\n- var lastX = 0;\n- var lastY = 0;\n-\n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return;\n- }\n-\n- var t = new Date().getTime() - initialTime;\n-\n- var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\n-\n- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n-\n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true;\n- }\n-\n- args.x = x - lastX;\n- args.y = y - lastY;\n- lastX = x;\n- lastY = y;\n- callback(args.x, args.y, args.end);\n- };\n-\n- this.timerId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(timerCallback, this)\n- );\n- },\n-\n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n+};\n /* ======================================================================\n OpenLayers/BaseTypes.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -4005,14 +2749,268 @@\n }\n return equals;\n },\n \n CLASS_NAME: \"OpenLayers.Size\"\n });\n /* ======================================================================\n+ OpenLayers/Console.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Console\n+ * The OpenLayers.Console namespace is used for debugging and error logging.\n+ * If the Firebug Lite (../Firebug/firebug.js) is included before this script,\n+ * calls to OpenLayers.Console methods will get redirected to window.console.\n+ * This makes use of the Firebug extension where available and allows for\n+ * cross-browser debugging Firebug style.\n+ *\n+ * Note:\n+ * Note that behavior will differ with the Firebug extention and Firebug Lite.\n+ * Most notably, the Firebug Lite console does not currently allow for\n+ * hyperlinks to code or for clicking on object to explore their properties.\n+ * \n+ */\n+OpenLayers.Console = {\n+ /**\n+ * Create empty functions for all console methods. The real value of these\n+ * properties will be set if Firebug Lite (../Firebug/firebug.js script) is\n+ * included. We explicitly require the Firebug Lite script to trigger\n+ * functionality of the OpenLayers.Console methods.\n+ */\n+\n+ /**\n+ * APIFunction: log\n+ * Log an object in the console. The Firebug Lite console logs string\n+ * representation of objects. Given multiple arguments, they will\n+ * be cast to strings and logged with a space delimiter. If the first\n+ * argument is a string with printf-like formatting, subsequent arguments\n+ * will be used in string substitution. Any additional arguments (beyond\n+ * the number substituted in a format string) will be appended in a space-\n+ * delimited line.\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ log: function() {},\n+\n+ /**\n+ * APIFunction: debug\n+ * Writes a message to the console, including a hyperlink to the line\n+ * where it was called.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ debug: function() {},\n+\n+ /**\n+ * APIFunction: info\n+ * Writes a message to the console with the visual \"info\" icon and color\n+ * coding and a hyperlink to the line where it was called.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ info: function() {},\n+\n+ /**\n+ * APIFunction: warn\n+ * Writes a message to the console with the visual \"warning\" icon and\n+ * color coding and a hyperlink to the line where it was called.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ warn: function() {},\n+\n+ /**\n+ * APIFunction: error\n+ * Writes a message to the console with the visual \"error\" icon and color\n+ * coding and a hyperlink to the line where it was called.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ error: function() {},\n+\n+ /**\n+ * APIFunction: userError\n+ * A single interface for showing error messages to the user. The default\n+ * behavior is a Javascript alert, though this can be overridden by\n+ * reassigning OpenLayers.Console.userError to a different function.\n+ *\n+ * Expects a single error message\n+ * \n+ * Parameters:\n+ * error - {Object}\n+ */\n+ userError: function(error) {\n+ alert(error);\n+ },\n+\n+ /**\n+ * APIFunction: assert\n+ * Tests that an expression is true. If not, it will write a message to\n+ * the console and throw an exception.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ assert: function() {},\n+\n+ /**\n+ * APIFunction: dir\n+ * Prints an interactive listing of all properties of the object. This\n+ * looks identical to the view that you would see in the DOM tab.\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ dir: function() {},\n+\n+ /**\n+ * APIFunction: dirxml\n+ * Prints the XML source tree of an HTML or XML element. This looks\n+ * identical to the view that you would see in the HTML tab. You can click\n+ * on any node to inspect it in the HTML tab.\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ dirxml: function() {},\n+\n+ /**\n+ * APIFunction: trace\n+ * Prints an interactive stack trace of JavaScript execution at the point\n+ * where it is called. The stack trace details the functions on the stack,\n+ * as well as the values that were passed as arguments to each function.\n+ * You can click each function to take you to its source in the Script tab,\n+ * and click each argument value to inspect it in the DOM or HTML tabs.\n+ * \n+ */\n+ trace: function() {},\n+\n+ /**\n+ * APIFunction: group\n+ * Writes a message to the console and opens a nested block to indent all\n+ * future messages sent to the console. Call OpenLayers.Console.groupEnd()\n+ * to close the block.\n+ *\n+ * May be called with multiple arguments as with OpenLayers.Console.log().\n+ * \n+ * Parameters:\n+ * object - {Object}\n+ */\n+ group: function() {},\n+\n+ /**\n+ * APIFunction: groupEnd\n+ * Closes the most recently opened block created by a call to\n+ * OpenLayers.Console.group\n+ */\n+ groupEnd: function() {},\n+\n+ /**\n+ * APIFunction: time\n+ * Creates a new timer under the given name. Call\n+ * OpenLayers.Console.timeEnd(name)\n+ * with the same name to stop the timer and print the time elapsed.\n+ *\n+ * Parameters:\n+ * name - {String}\n+ */\n+ time: function() {},\n+\n+ /**\n+ * APIFunction: timeEnd\n+ * Stops a timer created by a call to OpenLayers.Console.time(name) and\n+ * writes the time elapsed.\n+ *\n+ * Parameters:\n+ * name - {String}\n+ */\n+ timeEnd: function() {},\n+\n+ /**\n+ * APIFunction: profile\n+ * Turns on the JavaScript profiler. The optional argument title would\n+ * contain the text to be printed in the header of the profile report.\n+ *\n+ * This function is not currently implemented in Firebug Lite.\n+ * \n+ * Parameters:\n+ * title - {String} Optional title for the profiler\n+ */\n+ profile: function() {},\n+\n+ /**\n+ * APIFunction: profileEnd\n+ * Turns off the JavaScript profiler and prints its report.\n+ * \n+ * This function is not currently implemented in Firebug Lite.\n+ */\n+ profileEnd: function() {},\n+\n+ /**\n+ * APIFunction: count\n+ * Writes the number of times that the line of code where count was called\n+ * was executed. The optional argument title will print a message in\n+ * addition to the number of the count.\n+ *\n+ * This function is not currently implemented in Firebug Lite.\n+ *\n+ * Parameters:\n+ * title - {String} Optional title to be printed with count\n+ */\n+ count: function() {},\n+\n+ CLASS_NAME: \"OpenLayers.Console\"\n+};\n+\n+/**\n+ * Execute an anonymous function to extend the OpenLayers.Console namespace\n+ * if the firebug.js script is included. This closure is used so that the\n+ * \"scripts\" and \"i\" variables don't pollute the global namespace.\n+ */\n+(function() {\n+ /**\n+ * If Firebug Lite is included (before this script), re-route all\n+ * OpenLayers.Console calls to the console object.\n+ */\n+ var scripts = document.getElementsByTagName(\"script\");\n+ for (var i = 0, len = scripts.length; i < len; ++i) {\n+ if (scripts[i].src.indexOf(\"firebug.js\") != -1) {\n+ if (console) {\n+ OpenLayers.Util.extend(OpenLayers.Console, console);\n+ break;\n+ }\n+ }\n+ }\n+})();\n+/* ======================================================================\n OpenLayers/Lang.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -5934,13784 +4932,962 @@\n } else {\n str += coordinate < 0 ? OpenLayers.i18n(\"S\") : OpenLayers.i18n(\"N\");\n }\n return str;\n };\n \n /* ======================================================================\n- OpenLayers/Events.js\n+ OpenLayers/Feature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n+ * @requires OpenLayers/BaseTypes/Class.js\n * @requires OpenLayers/Util.js\n */\n \n /**\n- * Namespace: OpenLayers.Event\n- * Utility functions for event handling.\n+ * Class: OpenLayers.Feature\n+ * Features are combinations of geography and attributes. The OpenLayers.Feature\n+ * class specifically combines a marker and a lonlat.\n */\n-OpenLayers.Event = {\n-\n- /** \n- * Property: observers \n- * {Object} A hashtable cache of the event observers. Keyed by\n- * element._eventCacheID \n- */\n- observers: false,\n-\n- /**\n- * Constant: KEY_SPACE\n- * {int}\n- */\n- KEY_SPACE: 32,\n-\n- /** \n- * Constant: KEY_BACKSPACE \n- * {int} \n- */\n- KEY_BACKSPACE: 8,\n+OpenLayers.Feature = OpenLayers.Class({\n \n /** \n- * Constant: KEY_TAB \n- * {int} \n+ * Property: layer \n+ * {<OpenLayers.Layer>} \n */\n- KEY_TAB: 9,\n+ layer: null,\n \n /** \n- * Constant: KEY_RETURN \n- * {int} \n+ * Property: id \n+ * {String} \n */\n- KEY_RETURN: 13,\n+ id: null,\n \n /** \n- * Constant: KEY_ESC \n- * {int} \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} \n */\n- KEY_ESC: 27,\n+ lonlat: null,\n \n /** \n- * Constant: KEY_LEFT \n- * {int} \n+ * Property: data \n+ * {Object} \n */\n- KEY_LEFT: 37,\n+ data: null,\n \n /** \n- * Constant: KEY_UP \n- * {int} \n+ * Property: marker \n+ * {<OpenLayers.Marker>} \n */\n- KEY_UP: 38,\n+ marker: null,\n \n- /** \n- * Constant: KEY_RIGHT \n- * {int} \n+ /**\n+ * APIProperty: popupClass\n+ * {<OpenLayers.Class>} The class which will be used to instantiate\n+ * a new Popup. Default is <OpenLayers.Popup.Anchored>.\n */\n- KEY_RIGHT: 39,\n+ popupClass: null,\n \n /** \n- * Constant: KEY_DOWN \n- * {int} \n+ * Property: popup \n+ * {<OpenLayers.Popup>} \n */\n- KEY_DOWN: 40,\n+ popup: null,\n \n /** \n- * Constant: KEY_DELETE \n- * {int} \n- */\n- KEY_DELETE: 46,\n-\n-\n- /**\n- * Method: element\n- * Cross browser event element detection.\n- * \n+ * Constructor: OpenLayers.Feature\n+ * Constructor for features.\n+ *\n * Parameters:\n- * event - {Event} \n+ * layer - {<OpenLayers.Layer>} \n+ * lonlat - {<OpenLayers.LonLat>} \n+ * data - {Object} \n * \n * Returns:\n- * {DOMElement} The element that caused the event \n+ * {<OpenLayers.Feature>}\n */\n- element: function(event) {\n- return event.target || event.srcElement;\n+ initialize: function(layer, lonlat, data) {\n+ this.layer = layer;\n+ this.lonlat = lonlat;\n+ this.data = (data != null) ? data : {};\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n- /**\n- * Method: isSingleTouch\n- * Determine whether event was caused by a single touch\n- *\n- * Parameters:\n- * event - {Event}\n- *\n- * Returns:\n- * {Boolean}\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- isSingleTouch: function(event) {\n- return event.touches && event.touches.length == 1;\n- },\n+ destroy: function() {\n \n- /**\n- * Method: isMultiTouch\n- * Determine whether event was caused by a multi touch\n- *\n- * Parameters:\n- * event - {Event}\n- *\n- * Returns:\n- * {Boolean}\n- */\n- isMultiTouch: function(event) {\n- return event.touches && event.touches.length > 1;\n- },\n+ //remove the popup from the map\n+ if ((this.layer != null) && (this.layer.map != null)) {\n+ if (this.popup != null) {\n+ this.layer.map.removePopup(this.popup);\n+ }\n+ }\n+ // remove the marker from the layer\n+ if (this.layer != null && this.marker != null) {\n+ this.layer.removeMarker(this.marker);\n+ }\n \n- /**\n- * Method: isLeftClick\n- * Determine whether event was caused by a left click. \n- *\n- * Parameters:\n- * event - {Event} \n- * \n- * Returns:\n- * {Boolean}\n- */\n- isLeftClick: function(event) {\n- return (((event.which) && (event.which == 1)) ||\n- ((event.button) && (event.button == 1)));\n+ this.layer = null;\n+ this.id = null;\n+ this.lonlat = null;\n+ this.data = null;\n+ if (this.marker != null) {\n+ this.destroyMarker(this.marker);\n+ this.marker = null;\n+ }\n+ if (this.popup != null) {\n+ this.destroyPopup(this.popup);\n+ this.popup = null;\n+ }\n },\n \n /**\n- * Method: isRightClick\n- * Determine whether event was caused by a right mouse click. \n- *\n- * Parameters:\n- * event - {Event} \n+ * Method: onScreen\n * \n * Returns:\n- * {Boolean}\n+ * {Boolean} Whether or not the feature is currently visible on screen\n+ * (based on its 'lonlat' property)\n */\n- isRightClick: function(event) {\n- return (((event.which) && (event.which == 3)) ||\n- ((event.button) && (event.button == 2)));\n+ onScreen: function() {\n+\n+ var onScreen = false;\n+ if ((this.layer != null) && (this.layer.map != null)) {\n+ var screenBounds = this.layer.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat);\n+ }\n+ return onScreen;\n },\n \n+\n /**\n- * Method: stop\n- * Stops an event from propagating. \n+ * Method: createMarker\n+ * Based on the data associated with the Feature, create and return a marker object.\n *\n- * Parameters: \n- * event - {Event} \n- * allowDefault - {Boolean} If true, we stop the event chain but \n- * still allow the default browser behaviour (text selection,\n- * radio-button clicking, etc). Default is false.\n+ * Returns: \n+ * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties\n+ * set in this.data. If no 'lonlat' is set, returns null. If no\n+ * 'icon' is set, OpenLayers.Marker() will load the default image.\n+ * \n+ * Note - this.marker is set to return value\n+ * \n */\n- stop: function(event, allowDefault) {\n-\n- if (!allowDefault) {\n- OpenLayers.Event.preventDefault(event);\n- }\n+ createMarker: function() {\n \n- if (event.stopPropagation) {\n- event.stopPropagation();\n- } else {\n- event.cancelBubble = true;\n+ if (this.lonlat != null) {\n+ this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);\n }\n+ return this.marker;\n },\n \n /**\n- * Method: preventDefault\n- * Cancels the event if it is cancelable, without stopping further\n- * propagation of the event.\n- *\n- * Parameters:\n- * event - {Event}\n+ * Method: destroyMarker\n+ * Destroys marker.\n+ * If user overrides the createMarker() function, s/he should be able\n+ * to also specify an alternative function for destroying it\n */\n- preventDefault: function(event) {\n- if (event.preventDefault) {\n- event.preventDefault();\n- } else {\n- event.returnValue = false;\n- }\n+ destroyMarker: function() {\n+ this.marker.destroy();\n },\n \n- /** \n- * Method: findElement\n+ /**\n+ * Method: createPopup\n+ * Creates a popup object created from the 'lonlat', 'popupSize',\n+ * and 'popupContentHTML' properties set in this.data. It uses\n+ * this.marker.icon as default anchor. \n+ * \n+ * If no 'lonlat' is set, returns null. \n+ * If no this.marker has been created, no anchor is sent.\n+ *\n+ * Note - the returned popup object is 'owned' by the feature, so you\n+ * cannot use the popup's destroy method to discard the popup.\n+ * Instead, you must use the feature's destroyPopup\n * \n- * Parameters:\n- * event - {Event} \n- * tagName - {String} \n+ * Note - this.popup is set to return value\n+ * \n+ * Parameters: \n+ * closeBox - {Boolean} create popup with closebox or not\n * \n * Returns:\n- * {DOMElement} The first node with the given tagName, starting from the\n- * node the event was triggered on and traversing the DOM upwards\n- */\n- findElement: function(event, tagName) {\n- var element = OpenLayers.Event.element(event);\n- while (element.parentNode && (!element.tagName ||\n- (element.tagName.toUpperCase() != tagName.toUpperCase()))) {\n- element = element.parentNode;\n- }\n- return element;\n- },\n-\n- /** \n- * Method: observe\n+ * {<OpenLayers.Popup>} Returns the created popup, which is also set\n+ * as 'popup' property of this feature. Will be of whatever type\n+ * specified by this feature's 'popupClass' property, but must be\n+ * of type <OpenLayers.Popup>.\n * \n- * Parameters:\n- * elementParam - {DOMElement || String} \n- * name - {String} \n- * observer - {function} \n- * useCapture - {Boolean} \n */\n- observe: function(elementParam, name, observer, useCapture) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- useCapture = useCapture || false;\n-\n- if (name == 'keypress' &&\n- (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.attachEvent)) {\n- name = 'keydown';\n- }\n-\n- //if observers cache has not yet been created, create it\n- if (!this.observers) {\n- this.observers = {};\n- }\n+ createPopup: function(closeBox) {\n \n- //if not already assigned, make a new unique cache ID\n- if (!element._eventCacheID) {\n- var idPrefix = \"eventCacheID_\";\n- if (element.id) {\n- idPrefix = element.id + \"_\" + idPrefix;\n+ if (this.lonlat != null) {\n+ if (!this.popup) {\n+ var anchor = (this.marker) ? this.marker.icon : null;\n+ var popupClass = this.popupClass ?\n+ this.popupClass : OpenLayers.Popup.Anchored;\n+ this.popup = new popupClass(this.id + \"_popup\",\n+ this.lonlat,\n+ this.data.popupSize,\n+ this.data.popupContentHTML,\n+ anchor,\n+ closeBox);\n+ }\n+ if (this.data.overflow != null) {\n+ this.popup.contentDiv.style.overflow = this.data.overflow;\n }\n- element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);\n- }\n-\n- var cacheID = element._eventCacheID;\n-\n- //if there is not yet a hash entry for this element, add one\n- if (!this.observers[cacheID]) {\n- this.observers[cacheID] = [];\n- }\n-\n- //add a new observer to this element's list\n- this.observers[cacheID].push({\n- 'element': element,\n- 'name': name,\n- 'observer': observer,\n- 'useCapture': useCapture\n- });\n \n- //add the actual browser event listener\n- if (element.addEventListener) {\n- element.addEventListener(name, observer, useCapture);\n- } else if (element.attachEvent) {\n- element.attachEvent('on' + name, observer);\n+ this.popup.feature = this;\n }\n+ return this.popup;\n },\n \n- /** \n- * Method: stopObservingElement\n- * Given the id of an element to stop observing, cycle through the \n- * element's cached observers, calling stopObserving on each one, \n- * skipping those entries which can no longer be removed.\n- * \n- * parameters:\n- * elementParam - {DOMElement || String} \n- */\n- stopObservingElement: function(elementParam) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n-\n- this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n- },\n \n /**\n- * Method: _removeElementObservers\n+ * Method: destroyPopup\n+ * Destroys the popup created via createPopup.\n *\n- * Parameters:\n- * elementObservers - {Array(Object)} Array of (element, name, \n- * observer, usecapture) objects, \n- * taken directly from hashtable\n+ * As with the marker, if user overrides the createPopup() function, s/he \n+ * should also be able to override the destruction\n */\n- _removeElementObservers: function(elementObservers) {\n- if (elementObservers) {\n- for (var i = elementObservers.length - 1; i >= 0; i--) {\n- var entry = elementObservers[i];\n- OpenLayers.Event.stopObserving.apply(this, [\n- entry.element, entry.name, entry.observer, entry.useCapture\n- ]);\n- }\n+ destroyPopup: function() {\n+ if (this.popup) {\n+ this.popup.feature = null;\n+ this.popup.destroy();\n+ this.popup = null;\n }\n },\n \n- /**\n- * Method: stopObserving\n- * \n- * Parameters:\n- * elementParam - {DOMElement || String} \n- * name - {String} \n- * observer - {function} \n- * useCapture - {Boolean} \n- * \n- * Returns:\n- * {Boolean} Whether or not the event observer was removed\n- */\n- stopObserving: function(elementParam, name, observer, useCapture) {\n- useCapture = useCapture || false;\n-\n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n-\n- if (name == 'keypress') {\n- if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n- element.detachEvent) {\n- name = 'keydown';\n- }\n- }\n-\n- // find element's entry in this.observers cache and remove it\n- var foundEntry = false;\n- var elementObservers = OpenLayers.Event.observers[cacheID];\n- if (elementObservers) {\n-\n- // find the specific event type in the element's list\n- var i = 0;\n- while (!foundEntry && i < elementObservers.length) {\n- var cacheEntry = elementObservers[i];\n-\n- if ((cacheEntry.name == name) &&\n- (cacheEntry.observer == observer) &&\n- (cacheEntry.useCapture == useCapture)) {\n-\n- elementObservers.splice(i, 1);\n- if (elementObservers.length == 0) {\n- delete OpenLayers.Event.observers[cacheID];\n- }\n- foundEntry = true;\n- break;\n- }\n- i++;\n- }\n- }\n-\n- //actually remove the event listener from browser\n- if (foundEntry) {\n- if (element.removeEventListener) {\n- element.removeEventListener(name, observer, useCapture);\n- } else if (element && element.detachEvent) {\n- element.detachEvent('on' + name, observer);\n- }\n- }\n- return foundEntry;\n- },\n+ CLASS_NAME: \"OpenLayers.Feature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Feature/Vector.js\n+ ====================================================================== */\n \n- /** \n- * Method: unloadCache\n- * Cycle through all the element entries in the events cache and call\n- * stopObservingElement on each. \n- */\n- unloadCache: function() {\n- // check for OpenLayers.Event before checking for observers, because\n- // OpenLayers.Event may be undefined in IE if no map instance was\n- // created\n- if (OpenLayers.Event && OpenLayers.Event.observers) {\n- for (var cacheID in OpenLayers.Event.observers) {\n- var elementObservers = OpenLayers.Event.observers[cacheID];\n- OpenLayers.Event._removeElementObservers.apply(this,\n- [elementObservers]);\n- }\n- OpenLayers.Event.observers = false;\n- }\n- },\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- CLASS_NAME: \"OpenLayers.Event\"\n+// TRASH THIS\n+OpenLayers.State = {\n+ /** states */\n+ UNKNOWN: 'Unknown',\n+ INSERT: 'Insert',\n+ UPDATE: 'Update',\n+ DELETE: 'Delete'\n };\n \n-/* prevent memory leaks in IE */\n-OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n-\n /**\n- * Class: OpenLayers.Events\n+ * @requires OpenLayers/Feature.js\n+ * @requires OpenLayers/Util.js\n */\n-OpenLayers.Events = OpenLayers.Class({\n \n- /** \n- * Constant: BROWSER_EVENTS\n- * {Array(String)} supported events \n- */\n- BROWSER_EVENTS: [\n- \"mouseover\", \"mouseout\",\n- \"mousedown\", \"mouseup\", \"mousemove\",\n- \"click\", \"dblclick\", \"rightclick\", \"dblrightclick\",\n- \"resize\", \"focus\", \"blur\",\n- \"touchstart\", \"touchmove\", \"touchend\",\n- \"keydown\"\n- ],\n+/**\n+ * Class: OpenLayers.Feature.Vector\n+ * Vector features use the OpenLayers.Geometry classes as geometry description.\n+ * They have an 'attributes' property, which is the data object, and a 'style'\n+ * property, the default values of which are defined in the \n+ * <OpenLayers.Feature.Vector.style> objects.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Feature>\n+ */\n+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n \n /** \n- * Property: listeners \n- * {Object} Hashtable of Array(Function): events listener functions \n+ * Property: fid \n+ * {String} \n */\n- listeners: null,\n+ fid: null,\n \n /** \n- * Property: object \n- * {Object} the code object issuing application events \n+ * APIProperty: geometry \n+ * {<OpenLayers.Geometry>} \n */\n- object: null,\n+ geometry: null,\n \n /** \n- * Property: element \n- * {DOMElement} the DOM element receiving browser events \n+ * APIProperty: attributes \n+ * {Object} This object holds arbitrary, serializable properties that\n+ * describe the feature.\n */\n- element: null,\n+ attributes: null,\n \n- /** \n- * Property: eventHandler \n- * {Function} bound event handler attached to elements \n+ /**\n+ * Property: bounds\n+ * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that\n+ * property can be set by an <OpenLayers.Format> object when\n+ * deserializing the feature, so in most cases it represents an\n+ * information set by the server. \n */\n- eventHandler: null,\n+ bounds: null,\n \n /** \n- * APIProperty: fallThrough \n- * {Boolean} \n+ * Property: state \n+ * {String} \n */\n- fallThrough: null,\n+ state: null,\n \n /** \n- * APIProperty: includeXY\n- * {Boolean} Should the .xy property automatically be created for browser\n- * mouse events? In general, this should be false. If it is true, then\n- * mouse events will automatically generate a '.xy' property on the \n- * event object that is passed. (Prior to OpenLayers 2.7, this was true\n- * by default.) Otherwise, you can call the getMousePosition on the\n- * relevant events handler on the object available via the 'evt.object'\n- * property of the evt object. So, for most events, you can call:\n- * function named(evt) { \n- * this.xy = this.object.events.getMousePosition(evt) \n- * } \n- *\n- * This option typically defaults to false for performance reasons:\n- * when creating an events object whose primary purpose is to manage\n- * relatively positioned mouse events within a div, it may make\n- * sense to set it to true.\n- *\n- * This option is also used to control whether the events object caches\n- * offsets. If this is false, it will not: the reason for this is that\n- * it is only expected to be called many times if the includeXY property\n- * is set to true. If you set this to true, you are expected to clear \n- * the offset cache manually (using this.clearMouseCache()) if:\n- * the border of the element changes\n- * the location of the element in the page changes\n- */\n- includeXY: false,\n-\n- /**\n- * APIProperty: extensions\n- * {Object} Event extensions registered with this instance. Keys are\n- * event types, values are {OpenLayers.Events.*} extension instances or\n- * {Boolean} for events that an instantiated extension provides in\n- * addition to the one it was created for.\n- *\n- * Extensions create an event in addition to browser events, which usually\n- * fires when a sequence of browser events is completed. Extensions are\n- * automatically instantiated when a listener is registered for an event\n- * provided by an extension.\n- *\n- * Extensions are created in the <OpenLayers.Events> namespace using\n- * <OpenLayers.Class>, and named after the event they provide.\n- * The constructor receives the target <OpenLayers.Events> instance as\n- * argument. Extensions that need to capture browser events before they\n- * propagate can register their listeners events using <register>, with\n- * {extension: true} as 4th argument.\n- *\n- * If an extension creates more than one event, an alias for each event\n- * type should be created and reference the same class. The constructor\n- * should set a reference in the target's extensions registry to itself.\n- *\n- * Below is a minimal extension that provides the \"foostart\" and \"fooend\"\n- * event types, which replace the native \"click\" event type if clicked on\n- * an element with the css class \"foo\":\n- *\n- * (code)\n- * OpenLayers.Events.foostart = OpenLayers.Class({\n- * initialize: function(target) {\n- * this.target = target;\n- * this.target.register(\"click\", this, this.doStuff, {extension: true});\n- * // only required if extension provides more than one event type\n- * this.target.extensions[\"foostart\"] = true;\n- * this.target.extensions[\"fooend\"] = true;\n- * },\n- * destroy: function() {\n- * var target = this.target;\n- * target.unregister(\"click\", this, this.doStuff);\n- * delete this.target;\n- * // only required if extension provides more than one event type\n- * delete target.extensions[\"foostart\"];\n- * delete target.extensions[\"fooend\"];\n- * },\n- * doStuff: function(evt) {\n- * var propagate = true;\n- * if (OpenLayers.Event.element(evt).className === \"foo\") {\n- * propagate = false;\n- * var target = this.target;\n- * target.triggerEvent(\"foostart\");\n- * window.setTimeout(function() {\n- * target.triggerEvent(\"fooend\");\n- * }, 1000);\n- * }\n- * return propagate;\n- * }\n- * });\n- * // only required if extension provides more than one event type\n- * OpenLayers.Events.fooend = OpenLayers.Events.foostart;\n- * (end)\n- * \n- */\n- extensions: null,\n-\n- /**\n- * Property: extensionCount\n- * {Object} Keys are event types (like in <listeners>), values are the\n- * number of extension listeners for each event type.\n- */\n- extensionCount: null,\n-\n- /**\n- * Method: clearMouseListener\n- * A version of <clearMouseCache> that is bound to this instance so that\n- * it can be used with <OpenLayers.Event.observe> and\n- * <OpenLayers.Event.stopObserving>.\n- */\n- clearMouseListener: null,\n-\n- /**\n- * Constructor: OpenLayers.Events\n- * Construct an OpenLayers.Events object.\n- *\n- * Parameters:\n- * object - {Object} The js object to which this Events object is being added\n- * element - {DOMElement} A dom element to respond to browser events\n- * eventTypes - {Array(String)} Deprecated. Array of custom application\n- * events. A listener may be registered for any named event, regardless\n- * of the values provided here.\n- * fallThrough - {Boolean} Allow events to fall through after these have\n- * been handled?\n- * options - {Object} Options for the events object.\n- */\n- initialize: function(object, element, eventTypes, fallThrough, options) {\n- OpenLayers.Util.extend(this, options);\n- this.object = object;\n- this.fallThrough = fallThrough;\n- this.listeners = {};\n- this.extensions = {};\n- this.extensionCount = {};\n- this._msTouches = [];\n-\n- // if a dom element is specified, add a listeners list \n- // for browser events on the element and register them\n- if (element != null) {\n- this.attachToElement(element);\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n+ * APIProperty: style \n+ * {Object} \n */\n- destroy: function() {\n- for (var e in this.extensions) {\n- if (typeof this.extensions[e] !== \"boolean\") {\n- this.extensions[e].destroy();\n- }\n- }\n- this.extensions = null;\n- if (this.element) {\n- OpenLayers.Event.stopObservingElement(this.element);\n- if (this.element.hasScrollEvent) {\n- OpenLayers.Event.stopObserving(\n- window, \"scroll\", this.clearMouseListener\n- );\n- }\n- }\n- this.element = null;\n-\n- this.listeners = null;\n- this.object = null;\n- this.fallThrough = null;\n- this.eventHandler = null;\n- },\n+ style: null,\n \n /**\n- * APIMethod: addEventType\n- * Deprecated. Any event can be triggered without adding it first.\n- * \n- * Parameters:\n- * eventName - {String}\n+ * APIProperty: url\n+ * {String} If this property is set it will be taken into account by\n+ * {<OpenLayers.HTTP>} when upadting or deleting the feature.\n */\n- addEventType: function(eventName) {},\n+ url: null,\n \n /**\n- * Method: attachToElement\n- *\n- * Parameters:\n- * element - {HTMLDOMElement} a DOM element to attach browser events to\n+ * Property: renderIntent\n+ * {String} rendering intent currently being used\n */\n- attachToElement: function(element) {\n- if (this.element) {\n- OpenLayers.Event.stopObservingElement(this.element);\n- } else {\n- // keep a bound copy of handleBrowserEvent() so that we can\n- // pass the same function to both Event.observe() and .stopObserving()\n- this.eventHandler = OpenLayers.Function.bindAsEventListener(\n- this.handleBrowserEvent, this\n- );\n-\n- // to be used with observe and stopObserving\n- this.clearMouseListener = OpenLayers.Function.bind(\n- this.clearMouseCache, this\n- );\n- }\n- this.element = element;\n- var msTouch = !!window.navigator.msMaxTouchPoints;\n- var type;\n- for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {\n- type = this.BROWSER_EVENTS[i];\n- // register the event cross-browser\n- OpenLayers.Event.observe(element, type, this.eventHandler);\n- if (msTouch && type.indexOf('touch') === 0) {\n- this.addMsTouchListener(element, type, this.eventHandler);\n- }\n- }\n- // disable dragstart in IE so that mousedown/move/up works normally\n- OpenLayers.Event.observe(element, \"dragstart\", OpenLayers.Event.stop);\n- },\n+ renderIntent: \"default\",\n \n /**\n- * APIMethod: on\n- * Convenience method for registering listeners with a common scope.\n- * Internally, this method calls <register> as shown in the examples\n- * below.\n+ * APIProperty: modified\n+ * {Object} An object with the originals of the geometry and attributes of\n+ * the feature, if they were changed. Currently this property is only read\n+ * by <OpenLayers.Format.WFST.v1>, and written by\n+ * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.\n+ * Applications can set the originals of modified attributes in the\n+ * attributes property. Note that applications have to check if this\n+ * object and the attributes property is already created before using it.\n+ * After a change made with ModifyFeature, this object could look like\n *\n- * Example use:\n * (code)\n- * // register a single listener for the \"loadstart\" event\n- * events.on({\"loadstart\": loadStartListener});\n- *\n- * // this is equivalent to the following\n- * events.register(\"loadstart\", undefined, loadStartListener);\n+ * {\n+ * geometry: >Object\n+ * }\n+ * (end)\n *\n- * // register multiple listeners to be called with the same `this` object\n- * events.on({\n- * \"loadstart\": loadStartListener,\n- * \"loadend\": loadEndListener,\n- * scope: object\n- * });\n+ * When an application has made changes to feature attributes, it could\n+ * have set the attributes to something like this:\n *\n- * // this is equivalent to the following\n- * events.register(\"loadstart\", object, loadStartListener);\n- * events.register(\"loadend\", object, loadEndListener);\n+ * (code)\n+ * {\n+ * attributes: {\n+ * myAttribute: \"original\"\n+ * }\n+ * }\n * (end)\n *\n- * Parameters:\n- * object - {Object} \n+ * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in\n+ * *modified.geometry* and the attribute names in *modified.attributes*,\n+ * but it is recommended to set the original values (and not just true) as\n+ * attribute value, so applications could use this information to undo\n+ * changes.\n */\n- on: function(object) {\n- for (var type in object) {\n- if (type != \"scope\" && object.hasOwnProperty(type)) {\n- this.register(type, object.scope, object[type]);\n- }\n- }\n- },\n+ modified: null,\n \n- /**\n- * APIMethod: register\n- * Register an event on the events object.\n- *\n- * When the event is triggered, the 'func' function will be called, in the\n- * context of 'obj'. Imagine we were to register an event, specifying an \n- * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the \n- * context in the callback function will be our Bounds object. This means\n- * that within our callback function, we can access the properties and \n- * methods of the Bounds object through the \"this\" variable. So our \n- * callback could execute something like: \n- * : leftStr = \"Left: \" + this.left;\n- * \n- * or\n- * \n- * : centerStr = \"Center: \" + this.getCenterLonLat();\n- *\n+ /** \n+ * Constructor: OpenLayers.Feature.Vector\n+ * Create a vector feature. \n+ * \n * Parameters:\n- * type - {String} Name of the event to register\n- * obj - {Object} The object to bind the context to for the callback#.\n- * If no object is specified, default is the Events's 'object' property.\n- * func - {Function} The callback function. If no callback is \n- * specified, this function does nothing.\n- * priority - {Boolean|Object} If true, adds the new listener to the\n- * *front* of the events queue instead of to the end.\n- *\n- * Valid options for priority:\n- * extension - {Boolean} If true, then the event will be registered as\n- * extension event. Extension events are handled before all other\n- * events.\n+ * geometry - {<OpenLayers.Geometry>} The geometry that this feature\n+ * represents.\n+ * attributes - {Object} An optional object that will be mapped to the\n+ * <attributes> property. \n+ * style - {Object} An optional style object.\n */\n- register: function(type, obj, func, priority) {\n- if (type in OpenLayers.Events && !this.extensions[type]) {\n- this.extensions[type] = new OpenLayers.Events[type](this);\n- }\n- if (func != null) {\n- if (obj == null) {\n- obj = this.object;\n- }\n- var listeners = this.listeners[type];\n- if (!listeners) {\n- listeners = [];\n- this.listeners[type] = listeners;\n- this.extensionCount[type] = 0;\n- }\n- var listener = {\n- obj: obj,\n- func: func\n- };\n- if (priority) {\n- listeners.splice(this.extensionCount[type], 0, listener);\n- if (typeof priority === \"object\" && priority.extension) {\n- this.extensionCount[type]++;\n- }\n- } else {\n- listeners.push(listener);\n- }\n+ initialize: function(geometry, attributes, style) {\n+ OpenLayers.Feature.prototype.initialize.apply(this,\n+ [null, null, attributes]);\n+ this.lonlat = null;\n+ this.geometry = geometry ? geometry : null;\n+ this.state = null;\n+ this.attributes = {};\n+ if (attributes) {\n+ this.attributes = OpenLayers.Util.extend(this.attributes,\n+ attributes);\n }\n+ this.style = style ? style : null;\n },\n \n- /**\n- * APIMethod: registerPriority\n- * Same as register() but adds the new listener to the *front* of the\n- * events queue instead of to the end.\n- * \n- * TODO: get rid of this in 3.0 - Decide whether listeners should be \n- * called in the order they were registered or in reverse order.\n- *\n- *\n- * Parameters:\n- * type - {String} Name of the event to register\n- * obj - {Object} The object to bind the context to for the callback#.\n- * If no object is specified, default is the Events's \n- * 'object' property.\n- * func - {Function} The callback function. If no callback is \n- * specified, this function does nothing.\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- registerPriority: function(type, obj, func) {\n- this.register(type, obj, func, true);\n+ destroy: function() {\n+ if (this.layer) {\n+ this.layer.removeFeatures(this);\n+ this.layer = null;\n+ }\n+\n+ this.geometry = null;\n+ this.modified = null;\n+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * APIMethod: un\n- * Convenience method for unregistering listeners with a common scope.\n- * Internally, this method calls <unregister> as shown in the examples\n- * below.\n- *\n- * Example use:\n- * (code)\n- * // unregister a single listener for the \"loadstart\" event\n- * events.un({\"loadstart\": loadStartListener});\n- *\n- * // this is equivalent to the following\n- * events.unregister(\"loadstart\", undefined, loadStartListener);\n- *\n- * // unregister multiple listeners with the same `this` object\n- * events.un({\n- * \"loadstart\": loadStartListener,\n- * \"loadend\": loadEndListener,\n- * scope: object\n- * });\n+ * Method: clone\n+ * Create a clone of this vector feature. Does not set any non-standard\n+ * properties.\n *\n- * // this is equivalent to the following\n- * events.unregister(\"loadstart\", object, loadStartListener);\n- * events.unregister(\"loadend\", object, loadEndListener);\n- * (end)\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.\n */\n- un: function(object) {\n- for (var type in object) {\n- if (type != \"scope\" && object.hasOwnProperty(type)) {\n- this.unregister(type, object.scope, object[type]);\n- }\n- }\n+ clone: function() {\n+ return new OpenLayers.Feature.Vector(\n+ this.geometry ? this.geometry.clone() : null,\n+ this.attributes,\n+ this.style);\n },\n \n /**\n- * APIMethod: unregister\n+ * Method: onScreen\n+ * Determine whether the feature is within the map viewport. This method\n+ * tests for an intersection between the geometry and the viewport\n+ * bounds. If a more effecient but less precise geometry bounds\n+ * intersection is desired, call the method with the boundsOnly\n+ * parameter true.\n *\n * Parameters:\n- * type - {String} \n- * obj - {Object} If none specified, defaults to this.object\n- * func - {Function} \n+ * boundsOnly - {Boolean} Only test whether a feature's bounds intersects\n+ * the viewport bounds. Default is false. If false, the feature's\n+ * geometry must intersect the viewport for onScreen to return true.\n+ * \n+ * Returns:\n+ * {Boolean} The feature is currently visible on screen (optionally\n+ * based on its bounds if boundsOnly is true).\n */\n- unregister: function(type, obj, func) {\n- if (obj == null) {\n- obj = this.object;\n- }\n- var listeners = this.listeners[type];\n- if (listeners != null) {\n- for (var i = 0, len = listeners.length; i < len; i++) {\n- if (listeners[i].obj == obj && listeners[i].func == func) {\n- listeners.splice(i, 1);\n- break;\n- }\n+ onScreen: function(boundsOnly) {\n+ var onScreen = false;\n+ if (this.layer && this.layer.map) {\n+ var screenBounds = this.layer.map.getExtent();\n+ if (boundsOnly) {\n+ var featureBounds = this.geometry.getBounds();\n+ onScreen = screenBounds.intersectsBounds(featureBounds);\n+ } else {\n+ var screenPoly = screenBounds.toGeometry();\n+ onScreen = screenPoly.intersects(this.geometry);\n }\n }\n- },\n-\n- /** \n- * Method: remove\n- * Remove all listeners for a given event type. If type is not registered,\n- * does nothing.\n- *\n- * Parameters:\n- * type - {String} \n- */\n- remove: function(type) {\n- if (this.listeners[type] != null) {\n- this.listeners[type] = [];\n- }\n+ return onScreen;\n },\n \n /**\n- * APIMethod: triggerEvent\n- * Trigger a specified registered event. \n+ * Method: getVisibility\n+ * Determine whether the feature is displayed or not. It may not displayed\n+ * because:\n+ * - its style display property is set to 'none',\n+ * - it doesn't belong to any layer,\n+ * - the styleMap creates a symbolizer with display property set to 'none'\n+ * for it,\n+ * - the layer which it belongs to is not visible.\n * \n- * Parameters:\n- * type - {String} \n- * evt - {Event || Object} will be passed to the listeners.\n- *\n * Returns:\n- * {Boolean} The last listener return. If a listener returns false, the\n- * chain of listeners will stop getting called.\n+ * {Boolean} The feature is currently displayed.\n */\n- triggerEvent: function(type, evt) {\n- var listeners = this.listeners[type];\n-\n- // fast path\n- if (!listeners || listeners.length == 0) {\n- return undefined;\n- }\n-\n- // prep evt object with object & div references\n- if (evt == null) {\n- evt = {};\n- }\n- evt.object = this.object;\n- evt.element = this.element;\n- if (!evt.type) {\n- evt.type = type;\n- }\n-\n- // execute all callbacks registered for specified type\n- // get a clone of the listeners array to\n- // allow for splicing during callbacks\n- listeners = listeners.slice();\n- var continueChain;\n- for (var i = 0, len = listeners.length; i < len; i++) {\n- var callback = listeners[i];\n- // bind the context to callback.obj\n- continueChain = callback.func.apply(callback.obj, [evt]);\n-\n- if ((continueChain != undefined) && (continueChain == false)) {\n- // if callback returns false, execute no more callbacks.\n- break;\n- }\n- }\n- // don't fall through to other DOM elements\n- if (!this.fallThrough) {\n- OpenLayers.Event.stop(evt, true);\n- }\n- return continueChain;\n+ getVisibility: function() {\n+ return !(this.style && this.style.display == 'none' ||\n+ !this.layer ||\n+ this.layer && this.layer.styleMap &&\n+ this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||\n+ this.layer && !this.layer.getVisibility());\n },\n \n /**\n- * Method: handleBrowserEvent\n- * Basically just a wrapper to the triggerEvent() function, but takes \n- * care to set a property 'xy' on the event with the current mouse \n- * position.\n- *\n- * Parameters:\n- * evt - {Event} \n+ * Method: createMarker\n+ * HACK - we need to decide if all vector features should be able to\n+ * create markers\n+ * \n+ * Returns:\n+ * {<OpenLayers.Marker>} For now just returns null\n */\n- handleBrowserEvent: function(evt) {\n- var type = evt.type,\n- listeners = this.listeners[type];\n- if (!listeners || listeners.length == 0) {\n- // noone's listening, bail out\n- return;\n- }\n- // add clientX & clientY to all events - corresponds to average x, y\n- var touches = evt.touches;\n- if (touches && touches[0]) {\n- var x = 0;\n- var y = 0;\n- var num = touches.length;\n- var touch;\n- for (var i = 0; i < num; ++i) {\n- touch = this.getTouchClientXY(touches[i]);\n- x += touch.clientX;\n- y += touch.clientY;\n- }\n- evt.clientX = x / num;\n- evt.clientY = y / num;\n- }\n- if (this.includeXY) {\n- evt.xy = this.getMousePosition(evt);\n- }\n- this.triggerEvent(type, evt);\n+ createMarker: function() {\n+ return null;\n },\n \n /**\n- * Method: getTouchClientXY\n- * WebKit has a few bugs for clientX/clientY. This method detects them\n- * and calculate the correct values.\n- *\n- * Parameters:\n- * evt - {Touch} a Touch object from a TouchEvent\n+ * Method: destroyMarker\n+ * HACK - we need to decide if all vector features should be able to\n+ * delete markers\n * \n- * Returns:\n- * {Object} An object with only clientX and clientY properties with the\n- * calculated values.\n+ * If user overrides the createMarker() function, s/he should be able\n+ * to also specify an alternative function for destroying it\n */\n- getTouchClientXY: function(evt) {\n- // olMochWin is to override window, used for testing\n- var win = window.olMockWin || window,\n- winPageX = win.pageXOffset,\n- winPageY = win.pageYOffset,\n- x = evt.clientX,\n- y = evt.clientY;\n-\n- if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||\n- evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {\n- // iOS4 include scroll offset in clientX/Y\n- x = x - winPageX;\n- y = y - winPageY;\n- } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX)) {\n- // Some Android browsers have totally bogus values for clientX/Y\n- // when scrolling/zooming a page\n- x = evt.pageX - winPageX;\n- y = evt.pageY - winPageY;\n- }\n-\n- evt.olClientX = x;\n- evt.olClientY = y;\n-\n- return {\n- clientX: x,\n- clientY: y\n- };\n+ destroyMarker: function() {\n+ // pass\n },\n \n /**\n- * APIMethod: clearMouseCache\n- * Clear cached data about the mouse position. This should be called any \n- * time the element that events are registered on changes position \n- * within the page.\n+ * Method: createPopup\n+ * HACK - we need to decide if all vector features should be able to\n+ * create popups\n+ * \n+ * Returns:\n+ * {<OpenLayers.Popup>} For now just returns null\n */\n- clearMouseCache: function() {\n- this.element.scrolls = null;\n- this.element.lefttop = null;\n- this.element.offsets = null;\n+ createPopup: function() {\n+ return null;\n },\n \n /**\n- * Method: getMousePosition\n+ * Method: atPoint\n+ * Determins whether the feature intersects with the specified location.\n * \n- * Parameters:\n- * evt - {Event} \n+ * Parameters: \n+ * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ * toleranceLon - {float} Optional tolerance in Geometric Coords\n+ * toleranceLat - {float} Optional tolerance in Geographic Coords\n * \n * Returns:\n- * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted\n- * for offsets\n+ * {Boolean} Whether or not the feature is at the specified location\n */\n- getMousePosition: function(evt) {\n- if (!this.includeXY) {\n- this.clearMouseCache();\n- } else if (!this.element.hasScrollEvent) {\n- OpenLayers.Event.observe(window, \"scroll\", this.clearMouseListener);\n- this.element.hasScrollEvent = true;\n- }\n-\n- if (!this.element.scrolls) {\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- this.element.scrolls = [\n- window.pageXOffset || viewportElement.scrollLeft,\n- window.pageYOffset || viewportElement.scrollTop\n- ];\n- }\n-\n- if (!this.element.lefttop) {\n- this.element.lefttop = [\n- (document.documentElement.clientLeft || 0),\n- (document.documentElement.clientTop || 0)\n- ];\n- }\n-\n- if (!this.element.offsets) {\n- this.element.offsets = OpenLayers.Util.pagePosition(this.element);\n+ atPoint: function(lonlat, toleranceLon, toleranceLat) {\n+ var atPoint = false;\n+ if (this.geometry) {\n+ atPoint = this.geometry.atPoint(lonlat, toleranceLon,\n+ toleranceLat);\n }\n-\n- return new OpenLayers.Pixel(\n- (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0] -\n- this.element.lefttop[0],\n- (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1] -\n- this.element.lefttop[1]\n- );\n+ return atPoint;\n },\n \n /**\n- * Method: addMsTouchListener\n- *\n- * Parameters:\n- * element - {DOMElement} The DOM element to register the listener on\n- * type - {String} The event type\n- * handler - {Function} the handler\n+ * Method: destroyPopup\n+ * HACK - we need to decide if all vector features should be able to\n+ * delete popups\n */\n- addMsTouchListener: function(element, type, handler) {\n- var eventHandler = this.eventHandler;\n- var touches = this._msTouches;\n-\n- function msHandler(evt) {\n- handler(OpenLayers.Util.applyDefaults({\n- stopPropagation: function() {\n- for (var i = touches.length - 1; i >= 0; --i) {\n- touches[i].stopPropagation();\n- }\n- },\n- preventDefault: function() {\n- for (var i = touches.length - 1; i >= 0; --i) {\n- touches[i].preventDefault();\n- }\n- },\n- type: type\n- }, evt));\n- }\n-\n- switch (type) {\n- case 'touchstart':\n- return this.addMsTouchListenerStart(element, type, msHandler);\n- case 'touchend':\n- return this.addMsTouchListenerEnd(element, type, msHandler);\n- case 'touchmove':\n- return this.addMsTouchListenerMove(element, type, msHandler);\n- default:\n- throw 'Unknown touch event type';\n- }\n+ destroyPopup: function() {\n+ // pass\n },\n \n /**\n- * Method: addMsTouchListenerStart\n+ * Method: move\n+ * Moves the feature and redraws it at its new location\n *\n * Parameters:\n- * element - {DOMElement} The DOM element to register the listener on\n- * type - {String} The event type\n- * handler - {Function} the handler\n+ * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the\n+ * location to which to move the feature.\n */\n- addMsTouchListenerStart: function(element, type, handler) {\n- var touches = this._msTouches;\n-\n- var cb = function(e) {\n-\n- var alreadyInArray = false;\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- alreadyInArray = true;\n- break;\n- }\n- }\n- if (!alreadyInArray) {\n- touches.push(e);\n- }\n+ move: function(location) {\n \n- e.touches = touches.slice();\n- handler(e);\n- };\n+ if (!this.layer || !this.geometry.move) {\n+ //do nothing if no layer or immoveable geometry\n+ return undefined;\n+ }\n \n- OpenLayers.Event.observe(element, 'MSPointerDown', cb);\n+ var pixel;\n+ if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n+ pixel = this.layer.getViewPortPxFromLonLat(location);\n+ } else {\n+ pixel = location;\n+ }\n \n- // Need to also listen for end events to keep the _msTouches list\n- // accurate\n- var internalCb = function(e) {\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches.splice(i, 1);\n- break;\n- }\n- }\n- };\n- OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);\n+ var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n+ var res = this.layer.map.getResolution();\n+ this.geometry.move(res * (pixel.x - lastPixel.x),\n+ res * (lastPixel.y - pixel.y));\n+ this.layer.drawFeature(this);\n+ return lastPixel;\n },\n \n /**\n- * Method: addMsTouchListenerMove\n+ * Method: toState\n+ * Sets the new state\n *\n * Parameters:\n- * element - {DOMElement} The DOM element to register the listener on\n- * type - {String} The event type\n- * handler - {Function} the handler\n+ * state - {String} \n */\n- addMsTouchListenerMove: function(element, type, handler) {\n- var touches = this._msTouches;\n- var cb = function(e) {\n-\n- //Don't fire touch moves when mouse isn't down\n- if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {\n- return;\n+ toState: function(state) {\n+ if (state == OpenLayers.State.UPDATE) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.DELETE:\n+ this.state = state;\n+ break;\n+ case OpenLayers.State.UPDATE:\n+ case OpenLayers.State.INSERT:\n+ break;\n }\n-\n- if (touches.length == 1 && touches[0].pageX == e.pageX &&\n- touches[0].pageY == e.pageY) {\n- // don't trigger event when pointer has not moved\n- return;\n+ } else if (state == OpenLayers.State.INSERT) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ break;\n+ default:\n+ this.state = state;\n+ break;\n }\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches[i] = e;\n+ } else if (state == OpenLayers.State.DELETE) {\n+ switch (this.state) {\n+ case OpenLayers.State.INSERT:\n+ // the feature should be destroyed\n+ break;\n+ case OpenLayers.State.DELETE:\n+ break;\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.UPDATE:\n+ this.state = state;\n break;\n- }\n }\n-\n- e.touches = touches.slice();\n- handler(e);\n- };\n-\n- OpenLayers.Event.observe(element, 'MSPointerMove', cb);\n+ } else if (state == OpenLayers.State.UNKNOWN) {\n+ this.state = state;\n+ }\n },\n \n- /**\n- * Method: addMsTouchListenerEnd\n- *\n- * Parameters:\n- * element - {DOMElement} The DOM element to register the listener on\n- * type - {String} The event type\n- * handler - {Function} the handler\n- */\n- addMsTouchListenerEnd: function(element, type, handler) {\n- var touches = this._msTouches;\n-\n- var cb = function(e) {\n+ CLASS_NAME: \"OpenLayers.Feature.Vector\"\n+});\n \n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches.splice(i, 1);\n- break;\n- }\n- }\n \n- e.touches = touches.slice();\n- handler(e);\n- };\n+/**\n+ * Constant: OpenLayers.Feature.Vector.style\n+ * OpenLayers features can have a number of style attributes. The 'default' \n+ * style will typically be used if no other style is specified. These\n+ * styles correspond for the most part, to the styling properties defined\n+ * by the SVG standard. \n+ * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties\n+ * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties\n+ *\n+ * Symbolizer properties:\n+ * fill - {Boolean} Set to false if no fill is desired.\n+ * fillColor - {String} Hex fill color. Default is \"#ee9900\".\n+ * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 \n+ * stroke - {Boolean} Set to false if no stroke is desired.\n+ * strokeColor - {String} Hex stroke color. Default is \"#ee9900\".\n+ * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.\n+ * strokeWidth - {Number} Pixel stroke width. Default is 1.\n+ * strokeLinecap - {String} Stroke cap type. Default is \"round\". [butt | round | square]\n+ * strokeDashstyle - {String} Stroke dash style. Default is \"solid\". [dot | dash | dashdot | longdash | longdashdot | solid]\n+ * graphic - {Boolean} Set to false if no graphic is desired.\n+ * pointRadius - {Number} Pixel point radius. Default is 6.\n+ * pointerEvents - {String} Default is \"visiblePainted\".\n+ * cursor - {String} Default is \"\".\n+ * externalGraphic - {String} Url to an external graphic that will be used for rendering points.\n+ * graphicWidth - {Number} Pixel width for sizing an external graphic.\n+ * graphicHeight - {Number} Pixel height for sizing an external graphic.\n+ * graphicOpacity - {Number} Opacity (0-1) for an external graphic.\n+ * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.\n+ * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.\n+ * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).\n+ * graphicZIndex - {Number} The integer z-index value to use in rendering.\n+ * graphicName - {String} Named graphic to use when rendering points. Supported values include \"circle\" (default),\n+ * \"square\", \"star\", \"x\", \"cross\", \"triangle\".\n+ * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead\n+ * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.\n+ * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.\n+ * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.\n+ * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.\n+ * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.\n+ * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.\n+ * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.\n+ * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either\n+ * fillText or mozDrawText to be available.\n+ * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string\n+ * composed of two characters. The first character is for the horizontal alignment, the second for the vertical\n+ * alignment. Valid values for horizontal alignment: \"l\"=left, \"c\"=center, \"r\"=right. Valid values for vertical\n+ * alignment: \"t\"=top, \"m\"=middle, \"b\"=bottom. Example values: \"lt\", \"cm\", \"rb\". Default is \"cm\".\n+ * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.\n+ * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.\n+ * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.\n+ * Default is false.\n+ * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.\n+ * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.\n+ * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.\n+ * fontColor - {String} The font color for the label, to be provided like CSS.\n+ * fontOpacity - {Number} Opacity (0-1) for the label\n+ * fontFamily - {String} The font family for the label, to be provided like in CSS.\n+ * fontSize - {String} The font size for the label, to be provided like in CSS.\n+ * fontStyle - {String} The font style for the label, to be provided like in CSS.\n+ * fontWeight - {String} The font weight for the label, to be provided like in CSS.\n+ * display - {String} Symbolizers will have no effect if display is set to \"none\". All other values have no effect.\n+ */\n+OpenLayers.Feature.Vector.style = {\n+ 'default': {\n+ fillColor: \"#ee9900\",\n+ fillOpacity: 0.4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"#ee9900\",\n+ strokeOpacity: 1,\n+ strokeWidth: 1,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+ },\n+ 'select': {\n+ fillColor: \"blue\",\n+ fillOpacity: 0.4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"blue\",\n+ strokeOpacity: 1,\n+ strokeWidth: 2,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"pointer\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n \n- OpenLayers.Event.observe(element, 'MSPointerUp', cb);\n },\n+ 'temporary': {\n+ fillColor: \"#66cccc\",\n+ fillOpacity: 0.2,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"#66cccc\",\n+ strokeOpacity: 1,\n+ strokeLinecap: \"round\",\n+ strokeWidth: 2,\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n \n- CLASS_NAME: \"OpenLayers.Events\"\n-});\n+ },\n+ 'delete': {\n+ display: \"none\"\n+ }\n+};\n /* ======================================================================\n- OpenLayers/Tween.js\n+ OpenLayers/Format.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Animation.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Namespace: OpenLayers.Tween\n+ * Class: OpenLayers.Format\n+ * Base class for format reading/writing a variety of formats. Subclasses\n+ * of OpenLayers.Format are expected to have read and write methods.\n */\n-OpenLayers.Tween = OpenLayers.Class({\n-\n- /**\n- * APIProperty: easing\n- * {<OpenLayers.Easing>(Function)} Easing equation used for the animation\n- * Defaultly set to OpenLayers.Easing.Expo.easeOut\n- */\n- easing: null,\n-\n- /**\n- * APIProperty: begin\n- * {Object} Values to start the animation with\n- */\n- begin: null,\n-\n- /**\n- * APIProperty: finish\n- * {Object} Values to finish the animation with\n- */\n- finish: null,\n-\n- /**\n- * APIProperty: duration\n- * {int} duration of the tween (number of steps)\n- */\n- duration: null,\n+OpenLayers.Format = OpenLayers.Class({\n \n /**\n- * APIProperty: callbacks\n- * {Object} An object with start, eachStep and done properties whose values\n- * are functions to be call during the animation. They are passed the\n- * current computed value as argument.\n+ * Property: options\n+ * {Object} A reference to options passed to the constructor.\n */\n- callbacks: null,\n+ options: null,\n \n /**\n- * Property: time\n- * {int} Step counter\n+ * APIProperty: externalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The externalProjection is the projection used by\n+ * the content which is passed into read or which comes out of write.\n+ * In order to reproject, a projection transformation function for the\n+ * specified projections must be available. This support may be \n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- time: null,\n+ externalProjection: null,\n \n /**\n- * APIProperty: minFrameRate\n- * {Number} The minimum framerate for animations in frames per second. After\n- * each step, the time spent in the animation is compared to the calculated\n- * time at this frame rate. If the animation runs longer than the calculated\n- * time, the next step is skipped. Default is 30.\n+ * APIProperty: internalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The internalProjection is the projection used by\n+ * the geometries which are returned by read or which are passed into\n+ * write. In order to reproject, a projection transformation function\n+ * for the specified projections must be available. This support may be\n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- minFrameRate: null,\n+ internalProjection: null,\n \n /**\n- * Property: startTime\n- * {Number} The timestamp of the first execution step. Used for skipping\n- * frames\n+ * APIProperty: data\n+ * {Object} When <keepData> is true, this is the parsed string sent to\n+ * <read>.\n */\n- startTime: null,\n+ data: null,\n \n /**\n- * Property: animationId\n- * {int} Loop id returned by OpenLayers.Animation.start\n+ * APIProperty: keepData\n+ * {Object} Maintain a reference (<data>) to the most recently read data.\n+ * Default is false.\n */\n- animationId: null,\n+ keepData: false,\n \n /**\n- * Property: playing\n- * {Boolean} Tells if the easing is currently playing\n- */\n- playing: false,\n-\n- /** \n- * Constructor: OpenLayers.Tween\n- * Creates a Tween.\n+ * Constructor: OpenLayers.Format\n+ * Instances of this class are not useful. See one of the subclasses.\n *\n * Parameters:\n- * easing - {<OpenLayers.Easing>(Function)} easing function method to use\n+ * options - {Object} An optional object with properties to set on the\n+ * format\n+ *\n+ * Valid options:\n+ * keepData - {Boolean} If true, upon <read>, the data property will be\n+ * set to the parsed object (e.g. the json or xml object).\n+ *\n+ * Returns:\n+ * An instance of OpenLayers.Format\n */\n- initialize: function(easing) {\n- this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n },\n \n /**\n- * APIMethod: start\n- * Plays the Tween, and calls the callback method on each step\n- * \n- * Parameters:\n- * begin - {Object} values to start the animation with\n- * finish - {Object} values to finish the animation with\n- * duration - {int} duration of the tween (number of steps)\n- * options - {Object} hash of options (callbacks (start, eachStep, done),\n- * minFrameRate)\n+ * APIMethod: destroy\n+ * Clean up.\n */\n- start: function(begin, finish, duration, options) {\n- this.playing = true;\n- this.begin = begin;\n- this.finish = finish;\n- this.duration = duration;\n- this.callbacks = options.callbacks;\n- this.minFrameRate = options.minFrameRate || 30;\n- this.time = 0;\n- this.startTime = new Date().getTime();\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- if (this.callbacks && this.callbacks.start) {\n- this.callbacks.start.call(this, this.begin);\n- }\n- this.animationId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(this.play, this)\n- );\n- },\n+ destroy: function() {},\n \n /**\n- * APIMethod: stop\n- * Stops the Tween, and calls the done callback\n- * Doesn't do anything if animation is already finished\n+ * Method: read\n+ * Read data from a string, and return an object whose type depends on the\n+ * subclass. \n+ * \n+ * Parameters:\n+ * data - {string} Data to read/parse.\n+ *\n+ * Returns:\n+ * Depends on the subclass\n */\n- stop: function() {\n- if (!this.playing) {\n- return;\n- }\n-\n- if (this.callbacks && this.callbacks.done) {\n- this.callbacks.done.call(this, this.finish);\n- }\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- this.playing = false;\n+ read: function(data) {\n+ throw new Error('Read not implemented.');\n },\n \n /**\n- * Method: play\n- * Calls the appropriate easing method\n+ * Method: write\n+ * Accept an object, and return a string. \n+ *\n+ * Parameters:\n+ * object - {Object} Object to be serialized\n+ *\n+ * Returns:\n+ * {String} A string representation of the object.\n */\n- play: function() {\n- var value = {};\n- for (var i in this.begin) {\n- var b = this.begin[i];\n- var f = this.finish[i];\n- if (b == null || f == null || isNaN(b) || isNaN(f)) {\n- throw new TypeError('invalid value for Tween');\n- }\n-\n- var c = f - b;\n- value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);\n- }\n- this.time++;\n-\n- if (this.callbacks && this.callbacks.eachStep) {\n- // skip frames if frame rate drops below threshold\n- if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {\n- this.callbacks.eachStep.call(this, value);\n- }\n- }\n-\n- if (this.time > this.duration) {\n- this.stop();\n- }\n+ write: function(object) {\n+ throw new Error('Write not implemented.');\n },\n \n- /**\n- * Create empty functions for all easing methods.\n- */\n- CLASS_NAME: \"OpenLayers.Tween\"\n+ CLASS_NAME: \"OpenLayers.Format\"\n });\n+/* ======================================================================\n+ OpenLayers/Geometry/Point.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n /**\n- * Namespace: OpenLayers.Easing\n- * \n- * Credits:\n- * Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>\n+ * @requires OpenLayers/Geometry.js\n */\n-OpenLayers.Easing = {\n- /**\n- * Create empty functions for all easing methods.\n- */\n- CLASS_NAME: \"OpenLayers.Easing\"\n-};\n \n /**\n- * Namespace: OpenLayers.Easing.Linear\n+ * Class: OpenLayers.Geometry.Point\n+ * Point geometry class. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Geometry> \n */\n-OpenLayers.Easing.Linear = {\n+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n \n- /**\n- * Function: easeIn\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n+ /** \n+ * APIProperty: x \n+ * {float} \n */\n- easeIn: function(t, b, c, d) {\n- return c * t / d + b;\n- },\n+ x: null,\n+\n+ /** \n+ * APIProperty: y \n+ * {float} \n+ */\n+ y: null,\n \n /**\n- * Function: easeOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n+ * Constructor: OpenLayers.Geometry.Point\n+ * Construct a point geometry.\n *\n- * Returns:\n- * {Float}\n+ * Parameters:\n+ * x - {float} \n+ * y - {float}\n+ * \n */\n- easeOut: function(t, b, c, d) {\n- return c * t / d + b;\n+ initialize: function(x, y) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+\n+ this.x = parseFloat(x);\n+ this.y = parseFloat(y);\n },\n \n /**\n- * Function: easeInOut\n+ * APIMethod: clone\n * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n * Returns:\n- * {Float}\n+ * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point\n */\n- easeInOut: function(t, b, c, d) {\n- return c * t / d + b;\n- },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Geometry.Point(this.x, this.y);\n+ }\n \n- CLASS_NAME: \"OpenLayers.Easing.Linear\"\n-};\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n-/**\n- * Namespace: OpenLayers.Easing.Expo\n- */\n-OpenLayers.Easing.Expo = {\n+ return obj;\n+ },\n+\n+ /** \n+ * Method: calculateBounds\n+ * Create a new Bounds based on the lon/lat\n+ */\n+ calculateBounds: function() {\n+ this.bounds = new OpenLayers.Bounds(this.x, this.y,\n+ this.x, this.y);\n+ },\n \n /**\n- * Function: easeIn\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeIn: function(t, b, c, d) {\n- return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;\n- },\n-\n- /**\n- * Function: easeOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeOut: function(t, b, c, d) {\n- return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;\n- },\n-\n- /**\n- * Function: easeInOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeInOut: function(t, b, c, d) {\n- if (t == 0) return b;\n- if (t == d) return b + c;\n- if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;\n- return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Easing.Expo\"\n-};\n-\n-/**\n- * Namespace: OpenLayers.Easing.Quad\n- */\n-OpenLayers.Easing.Quad = {\n-\n- /**\n- * Function: easeIn\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeIn: function(t, b, c, d) {\n- return c * (t /= d) * t + b;\n- },\n-\n- /**\n- * Function: easeOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeOut: function(t, b, c, d) {\n- return -c * (t /= d) * (t - 2) + b;\n- },\n-\n- /**\n- * Function: easeInOut\n- * \n- * Parameters:\n- * t - {Float} time\n- * b - {Float} beginning position\n- * c - {Float} total change\n- * d - {Float} duration of the transition\n- *\n- * Returns:\n- * {Float}\n- */\n- easeInOut: function(t, b, c, d) {\n- if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n- return -c / 2 * ((--t) * (t - 2) - 1) + b;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Easing.Quad\"\n-};\n-/* ======================================================================\n- OpenLayers/Projection.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Namespace: OpenLayers.Projection\n- * Methods for coordinate transforms between coordinate systems. By default,\n- * OpenLayers ships with the ability to transform coordinates between\n- * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)\n- * coordinate reference systems. See the <transform> method for details\n- * on usage.\n- *\n- * Additional transforms may be added by using the <proj4js at http://proj4js.org/>\n- * library. If the proj4js library is included, the <transform> method \n- * will work between any two coordinate reference systems with proj4js \n- * definitions.\n- *\n- * If the proj4js library is not included, or if you wish to allow transforms\n- * between arbitrary coordinate reference systems, use the <addTransform>\n- * method to register a custom transform method.\n- */\n-OpenLayers.Projection = OpenLayers.Class({\n-\n- /**\n- * Property: proj\n- * {Object} Proj4js.Proj instance.\n- */\n- proj: null,\n-\n- /**\n- * Property: projCode\n- * {String}\n- */\n- projCode: null,\n-\n- /**\n- * Property: titleRegEx\n- * {RegExp} regular expression to strip the title from a proj4js definition\n- */\n- titleRegEx: /\\+title=[^\\+]*/,\n-\n- /**\n- * Constructor: OpenLayers.Projection\n- * This class offers several methods for interacting with a wrapped \n- * pro4js projection object. \n- *\n- * Parameters:\n- * projCode - {String} A string identifying the Well Known Identifier for\n- * the projection.\n- * options - {Object} An optional object to set additional properties\n- * on the projection.\n- *\n- * Returns:\n- * {<OpenLayers.Projection>} A projection object.\n- */\n- initialize: function(projCode, options) {\n- OpenLayers.Util.extend(this, options);\n- this.projCode = projCode;\n- if (typeof Proj4js == \"object\") {\n- this.proj = new Proj4js.Proj(projCode);\n- }\n- },\n-\n- /**\n- * APIMethod: getCode\n- * Get the string SRS code.\n- *\n- * Returns:\n- * {String} The SRS code.\n- */\n- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode;\n- },\n-\n- /**\n- * APIMethod: getUnits\n- * Get the units string for the projection -- returns null if \n- * proj4js is not available.\n- *\n- * Returns:\n- * {String} The units abbreviation.\n- */\n- getUnits: function() {\n- return this.proj ? this.proj.units : null;\n- },\n-\n- /**\n- * Method: toString\n- * Convert projection to string (getCode wrapper).\n- *\n- * Returns:\n- * {String} The projection code.\n- */\n- toString: function() {\n- return this.getCode();\n- },\n-\n- /**\n- * Method: equals\n- * Test equality of two projection instances. Determines equality based\n- * soley on the projection code.\n- *\n- * Returns:\n- * {Boolean} The two projections are equivalent.\n- */\n- equals: function(projection) {\n- var p = projection,\n- equals = false;\n- if (p) {\n- if (!(p instanceof OpenLayers.Projection)) {\n- p = new OpenLayers.Projection(p);\n- }\n- if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n- equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n- p.proj.defData.replace(this.titleRegEx, \"\");\n- } else if (p.getCode) {\n- var source = this.getCode(),\n- target = p.getCode();\n- equals = source == target ||\n- !!OpenLayers.Projection.transforms[source] &&\n- OpenLayers.Projection.transforms[source][target] ===\n- OpenLayers.Projection.nullTransform;\n- }\n- }\n- return equals;\n- },\n-\n- /* Method: destroy\n- * Destroy projection object.\n- */\n- destroy: function() {\n- delete this.proj;\n- delete this.projCode;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Projection\"\n-});\n-\n-/**\n- * Property: transforms\n- * {Object} Transforms is an object, with from properties, each of which may\n- * have a to property. This allows you to define projections without \n- * requiring support for proj4js to be included.\n- *\n- * This object has keys which correspond to a 'source' projection object. The\n- * keys should be strings, corresponding to the projection.getCode() value.\n- * Each source projection object should have a set of destination projection\n- * keys included in the object. \n- * \n- * Each value in the destination object should be a transformation function,\n- * where the function is expected to be passed an object with a .x and a .y\n- * property. The function should return the object, with the .x and .y\n- * transformed according to the transformation function.\n- *\n- * Note - Properties on this object should not be set directly. To add a\n- * transform method to this object, use the <addTransform> method. For an\n- * example of usage, see the OpenLayers.Layer.SphericalMercator file.\n- */\n-OpenLayers.Projection.transforms = {};\n-\n-/**\n- * APIProperty: defaults\n- * {Object} Defaults for the SRS codes known to OpenLayers (currently\n- * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,\n- * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,\n- * maxExtent (the validity extent for the SRS) and yx (true if this SRS is\n- * known to have a reverse axis order).\n- */\n-OpenLayers.Projection.defaults = {\n- \"EPSG:4326\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90],\n- yx: true\n- },\n- \"CRS:84\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90]\n- },\n- \"EPSG:900913\": {\n- units: \"m\",\n- maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n- }\n-};\n-\n-/**\n- * APIMethod: addTransform\n- * Set a custom transform method between two projections. Use this method in\n- * cases where the proj4js lib is not available or where custom projections\n- * need to be handled.\n- *\n- * Parameters:\n- * from - {String} The code for the source projection\n- * to - {String} the code for the destination projection\n- * method - {Function} A function that takes a point as an argument and\n- * transforms that point from the source to the destination projection\n- * in place. The original point should be modified.\n- */\n-OpenLayers.Projection.addTransform = function(from, to, method) {\n- if (method === OpenLayers.Projection.nullTransform) {\n- var defaults = OpenLayers.Projection.defaults[from];\n- if (defaults && !OpenLayers.Projection.defaults[to]) {\n- OpenLayers.Projection.defaults[to] = defaults;\n- }\n- }\n- if (!OpenLayers.Projection.transforms[from]) {\n- OpenLayers.Projection.transforms[from] = {};\n- }\n- OpenLayers.Projection.transforms[from][to] = method;\n-};\n-\n-/**\n- * APIMethod: transform\n- * Transform a point coordinate from one projection to another. Note that\n- * the input point is transformed in place.\n- * \n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y\n- * properties representing coordinates in those dimensions.\n- * source - {OpenLayers.Projection} Source map coordinate system\n- * dest - {OpenLayers.Projection} Destination map coordinate system\n- *\n- * Returns:\n- * point - {object} A transformed coordinate. The original point is modified.\n- */\n-OpenLayers.Projection.transform = function(point, source, dest) {\n- if (source && dest) {\n- if (!(source instanceof OpenLayers.Projection)) {\n- source = new OpenLayers.Projection(source);\n- }\n- if (!(dest instanceof OpenLayers.Projection)) {\n- dest = new OpenLayers.Projection(dest);\n- }\n- if (source.proj && dest.proj) {\n- point = Proj4js.transform(source.proj, dest.proj, point);\n- } else {\n- var sourceCode = source.getCode();\n- var destCode = dest.getCode();\n- var transforms = OpenLayers.Projection.transforms;\n- if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n- transforms[sourceCode][destCode](point);\n- }\n- }\n- }\n- return point;\n-};\n-\n-/**\n- * APIFunction: nullTransform\n- * A null transformation - useful for defining projection aliases when\n- * proj4js is not available:\n- *\n- * (code)\n- * OpenLayers.Projection.addTransform(\"EPSG:3857\", \"EPSG:900913\",\n- * OpenLayers.Projection.nullTransform);\n- * OpenLayers.Projection.addTransform(\"EPSG:900913\", \"EPSG:3857\",\n- * OpenLayers.Projection.nullTransform);\n- * (end)\n- */\n-OpenLayers.Projection.nullTransform = function(point) {\n- return point;\n-};\n-\n-/**\n- * Note: Transforms for web mercator <-> geographic\n- * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.\n- * OpenLayers originally started referring to EPSG:900913 as web mercator.\n- * The EPSG has declared EPSG:3857 to be web mercator.\n- * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as\n- * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.\n- * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and\n- * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis\n- * order for EPSG:4326. \n- */\n-(function() {\n-\n- var pole = 20037508.34;\n-\n- function inverseMercator(xy) {\n- xy.x = 180 * xy.x / pole;\n- xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);\n- return xy;\n- }\n-\n- function forwardMercator(xy) {\n- xy.x = xy.x * pole / 180;\n- var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n- xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n- return xy;\n- }\n-\n- function map(base, codes) {\n- var add = OpenLayers.Projection.addTransform;\n- var same = OpenLayers.Projection.nullTransform;\n- var i, len, code, other, j;\n- for (i = 0, len = codes.length; i < len; ++i) {\n- code = codes[i];\n- add(base, code, forwardMercator);\n- add(code, base, inverseMercator);\n- for (j = i + 1; j < len; ++j) {\n- other = codes[j];\n- add(code, other, same);\n- add(other, code, same);\n- }\n- }\n- }\n-\n- // list of equivalent codes for web mercator\n- var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n- geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n- i;\n- for (i = mercator.length - 1; i >= 0; --i) {\n- map(mercator[i], geographic);\n- }\n- for (i = geographic.length - 1; i >= 0; --i) {\n- map(geographic[i], mercator);\n- }\n-\n-})();\n-/* ======================================================================\n- OpenLayers/Map.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Util/vendorPrefix.js\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Tween.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Map\n- * Instances of OpenLayers.Map are interactive maps embedded in a web page.\n- * Create a new map with the <OpenLayers.Map> constructor.\n- * \n- * On their own maps do not provide much functionality. To extend a map\n- * it's necessary to add controls (<OpenLayers.Control>) and \n- * layers (<OpenLayers.Layer>) to the map. \n- */\n-OpenLayers.Map = OpenLayers.Class({\n-\n- /**\n- * Constant: Z_INDEX_BASE\n- * {Object} Base z-indexes for different classes of thing \n- */\n- Z_INDEX_BASE: {\n- BaseLayer: 100,\n- Overlay: 325,\n- Feature: 725,\n- Popup: 750,\n- Control: 1000\n- },\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>}\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * map.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to map.events.object.\n- * element - {DOMElement} A reference to map.events.element.\n- *\n- * Browser events have the following additional properties:\n- * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative\n- * to the the map viewport).\n- *\n- * Supported map event types:\n- * preaddlayer - triggered before a layer has been added. The event\n- * object will include a *layer* property that references the layer \n- * to be added. When a listener returns \"false\" the adding will be \n- * aborted.\n- * addlayer - triggered after a layer has been added. The event object\n- * will include a *layer* property that references the added layer.\n- * preremovelayer - triggered before a layer has been removed. The event\n- * object will include a *layer* property that references the layer \n- * to be removed. When a listener returns \"false\" the removal will be \n- * aborted.\n- * removelayer - triggered after a layer has been removed. The event\n- * object will include a *layer* property that references the removed\n- * layer.\n- * changelayer - triggered after a layer name change, order change,\n- * opacity change, params change, visibility change (actual visibility,\n- * not the layer's visibility property) or attribution change (due to\n- * extent change). Listeners will receive an event object with *layer*\n- * and *property* properties. The *layer* property will be a reference\n- * to the changed layer. The *property* property will be a key to the\n- * changed property (name, order, opacity, params, visibility or\n- * attribution).\n- * movestart - triggered after the start of a drag, pan, or zoom. The event\n- * object may include a *zoomChanged* property that tells whether the\n- * zoom has changed.\n- * move - triggered after each drag, pan, or zoom\n- * moveend - triggered after a drag, pan, or zoom completes\n- * zoomend - triggered after a zoom completes\n- * mouseover - triggered after mouseover the map\n- * mouseout - triggered after mouseout the map\n- * mousemove - triggered after mousemove the map\n- * changebaselayer - triggered after the base layer changes\n- * updatesize - triggered after the <updateSize> method was executed\n- */\n-\n- /**\n- * Property: id\n- * {String} Unique identifier for the map\n- */\n- id: null,\n-\n- /**\n- * Property: fractionalZoom\n- * {Boolean} For a base layer that supports it, allow the map resolution\n- * to be set to a value between one of the values in the resolutions\n- * array. Default is false.\n- *\n- * When fractionalZoom is set to true, it is possible to zoom to\n- * an arbitrary extent. This requires a base layer from a source\n- * that supports requests for arbitrary extents (i.e. not cached\n- * tiles on a regular lattice). This means that fractionalZoom\n- * will not work with commercial layers (Google, Yahoo, VE), layers\n- * using TileCache, or any other pre-cached data sources.\n- *\n- * If you are using fractionalZoom, then you should also use\n- * <getResolutionForZoom> instead of layer.resolutions[zoom] as the\n- * former works for non-integer zoom levels.\n- */\n- fractionalZoom: false,\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the map\n- */\n- events: null,\n-\n- /**\n- * APIProperty: allOverlays\n- * {Boolean} Allow the map to function with \"overlays\" only. Defaults to\n- * false. If true, the lowest layer in the draw order will act as\n- * the base layer. In addition, if set to true, all layers will\n- * have isBaseLayer set to false when they are added to the map.\n- *\n- * Note:\n- * If you set map.allOverlays to true, then you *cannot* use\n- * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,\n- * the lowest layer in the draw layer is the base layer. So, to change\n- * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer\n- * index to 0.\n- */\n- allOverlays: false,\n-\n- /**\n- * APIProperty: div\n- * {DOMElement|String} The element that contains the map (or an id for\n- * that element). If the <OpenLayers.Map> constructor is called\n- * with two arguments, this should be provided as the first argument.\n- * Alternatively, the map constructor can be called with the options\n- * object as the only argument. In this case (one argument), a\n- * div property may or may not be provided. If the div property\n- * is not provided, the map can be rendered to a container later\n- * using the <render> method.\n- * \n- * Note:\n- * If you are calling <render> after map construction, do not use\n- * <maxResolution> auto. Instead, divide your <maxExtent> by your\n- * maximum expected dimension.\n- */\n- div: null,\n-\n- /**\n- * Property: dragging\n- * {Boolean} The map is currently being dragged.\n- */\n- dragging: false,\n-\n- /**\n- * Property: size\n- * {<OpenLayers.Size>} Size of the main div (this.div)\n- */\n- size: null,\n-\n- /**\n- * Property: viewPortDiv\n- * {HTMLDivElement} The element that represents the map viewport\n- */\n- viewPortDiv: null,\n-\n- /**\n- * Property: layerContainerOrigin\n- * {<OpenLayers.LonLat>} The lonlat at which the later container was\n- * re-initialized (on-zoom)\n- */\n- layerContainerOrigin: null,\n-\n- /**\n- * Property: layerContainerDiv\n- * {HTMLDivElement} The element that contains the layers.\n- */\n- layerContainerDiv: null,\n-\n- /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map\n- */\n- layers: null,\n-\n- /**\n- * APIProperty: controls\n- * {Array(<OpenLayers.Control>)} List of controls associated with the map.\n- *\n- * If not provided in the map options at construction, the map will\n- * by default be given the following controls if present in the build:\n- * - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>\n- * - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>\n- * - <OpenLayers.Control.ArgParser>\n- * - <OpenLayers.Control.Attribution>\n- */\n- controls: null,\n-\n- /**\n- * Property: popups\n- * {Array(<OpenLayers.Popup>)} List of popups associated with the map\n- */\n- popups: null,\n-\n- /**\n- * APIProperty: baseLayer\n- * {<OpenLayers.Layer>} The currently selected base layer. This determines\n- * min/max zoom level, projection, etc.\n- */\n- baseLayer: null,\n-\n- /**\n- * Property: center\n- * {<OpenLayers.LonLat>} The current center of the map\n- */\n- center: null,\n-\n- /**\n- * Property: resolution\n- * {Float} The resolution of the map.\n- */\n- resolution: null,\n-\n- /**\n- * Property: zoom\n- * {Integer} The current zoom level of the map\n- */\n- zoom: 0,\n-\n- /**\n- * Property: panRatio\n- * {Float} The ratio of the current extent within\n- * which panning will tween.\n- */\n- panRatio: 1.5,\n-\n- /**\n- * APIProperty: options\n- * {Object} The options object passed to the class constructor. Read-only.\n- */\n- options: null,\n-\n- // Options\n-\n- /**\n- * APIProperty: tileSize\n- * {<OpenLayers.Size>} Set in the map options to override the default tile\n- * size for this map.\n- */\n- tileSize: null,\n-\n- /**\n- * APIProperty: projection\n- * {String} Set in the map options to specify the default projection \n- * for layers added to this map. When using a projection other than EPSG:4326\n- * (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),\n- * also set maxExtent, maxResolution or resolutions. Default is \"EPSG:4326\".\n- * Note that the projection of the map is usually determined\n- * by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).\n- */\n- projection: \"EPSG:4326\",\n-\n- /**\n- * APIProperty: units\n- * {String} The map units. Possible values are 'degrees' (or 'dd'), 'm', \n- * 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.\n- * Only required if both map and layers do not define a projection,\n- * or if they define a projection which does not define units\n- */\n- units: null,\n-\n- /**\n- * APIProperty: resolutions\n- * {Array(Float)} A list of map resolutions (map units per pixel) in \n- * descending order. If this is not set in the layer constructor, it \n- * will be set based on other resolution related properties \n- * (maxExtent, maxResolution, maxScale, etc.).\n- */\n- resolutions: null,\n-\n- /**\n- * APIProperty: maxResolution\n- * {Float} Required if you are not displaying the whole world on a tile\n- * with the size specified in <tileSize>.\n- */\n- maxResolution: null,\n-\n- /**\n- * APIProperty: minResolution\n- * {Float}\n- */\n- minResolution: null,\n-\n- /**\n- * APIProperty: maxScale\n- * {Float}\n- */\n- maxScale: null,\n-\n- /**\n- * APIProperty: minScale\n- * {Float}\n- */\n- minScale: null,\n-\n- /**\n- * APIProperty: maxExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The maximum extent for the map.\n- * Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults\n- * (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;\n- * else, defaults to null.\n- * To restrict user panning and zooming of the map, use <restrictedExtent> instead.\n- * The value for <maxExtent> will change calculations for tile URLs.\n- */\n- maxExtent: null,\n-\n- /**\n- * APIProperty: minExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The minimum extent for the map. Defaults to null.\n- */\n- minExtent: null,\n-\n- /**\n- * APIProperty: restrictedExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * Limit map navigation to this extent where possible.\n- * If a non-null restrictedExtent is set, panning will be restricted\n- * to the given bounds. In addition, zooming to a resolution that\n- * displays more than the restricted extent will center the map\n- * on the restricted extent. If you wish to limit the zoom level\n- * or resolution, use maxResolution.\n- */\n- restrictedExtent: null,\n-\n- /**\n- * APIProperty: numZoomLevels\n- * {Integer} Number of zoom levels for the map. Defaults to 16. Set a\n- * different value in the map options if needed.\n- */\n- numZoomLevels: 16,\n-\n- /**\n- * APIProperty: theme\n- * {String} Relative path to a CSS file from which to load theme styles.\n- * Specify null in the map options (e.g. {theme: null}) if you \n- * want to get cascading style declarations - by putting links to \n- * stylesheets or style declarations directly in your page.\n- */\n- theme: null,\n-\n- /** \n- * APIProperty: displayProjection\n- * {<OpenLayers.Projection>} Requires proj4js support for projections other\n- * than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by\n- * several controls to display data to user. If this property is set,\n- * it will be set on any control which has a null displayProjection\n- * property at the time the control is added to the map. \n- */\n- displayProjection: null,\n-\n- /**\n- * APIProperty: tileManager\n- * {<OpenLayers.TileManager>|Object} By default, and if the build contains\n- * TileManager.js, the map will use the TileManager to queue image requests\n- * and to cache tile image elements. To create a map without a TileManager\n- * configure the map with tileManager: null. To create a TileManager with\n- * non-default options, supply the options instead or alternatively supply\n- * an instance of {<OpenLayers.TileManager>}.\n- */\n-\n- /**\n- * APIProperty: fallThrough\n- * {Boolean} Should OpenLayers allow events on the map to fall through to\n- * other elements on the page, or should it swallow them? (#457)\n- * Default is to swallow.\n- */\n- fallThrough: false,\n-\n- /**\n- * APIProperty: autoUpdateSize\n- * {Boolean} Should OpenLayers automatically update the size of the map\n- * when the resize event is fired. Default is true.\n- */\n- autoUpdateSize: true,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- */\n- eventListeners: null,\n-\n- /**\n- * Property: panTween\n- * {<OpenLayers.Tween>} Animated panning tween object, see panTo()\n- */\n- panTween: null,\n-\n- /**\n- * APIProperty: panMethod\n- * {Function} The Easing function to be used for tweening. Default is\n- * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off\n- * animated panning.\n- */\n- panMethod: OpenLayers.Easing.Expo.easeOut,\n-\n- /**\n- * Property: panDuration\n- * {Integer} The number of steps to be passed to the\n- * OpenLayers.Tween.start() method when the map is\n- * panned.\n- * Default is 50.\n- */\n- panDuration: 50,\n-\n- /**\n- * Property: zoomTween\n- * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()\n- */\n- zoomTween: null,\n-\n- /**\n- * APIProperty: zoomMethod\n- * {Function} The Easing function to be used for tweening. Default is\n- * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off\n- * animated zooming.\n- */\n- zoomMethod: OpenLayers.Easing.Quad.easeOut,\n-\n- /**\n- * Property: zoomDuration\n- * {Integer} The number of steps to be passed to the\n- * OpenLayers.Tween.start() method when the map is zoomed.\n- * Default is 20.\n- */\n- zoomDuration: 20,\n-\n- /**\n- * Property: paddingForPopups\n- * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent \n- * the popup from getting too close to the map border.\n- */\n- paddingForPopups: null,\n-\n- /**\n- * Property: layerContainerOriginPx\n- * {Object} Cached object representing the layer container origin (in pixels).\n- */\n- layerContainerOriginPx: null,\n-\n- /**\n- * Property: minPx\n- * {Object} An object with a 'x' and 'y' values that is the lower\n- * left of maxExtent in viewport pixel space.\n- * Used to verify in moveByPx that the new location we're moving to\n- * is valid. It is also used in the getLonLatFromViewPortPx function\n- * of Layer.\n- */\n- minPx: null,\n-\n- /**\n- * Property: maxPx\n- * {Object} An object with a 'x' and 'y' values that is the top\n- * right of maxExtent in viewport pixel space.\n- * Used to verify in moveByPx that the new location we're moving to\n- * is valid.\n- */\n- maxPx: null,\n-\n- /**\n- * Constructor: OpenLayers.Map\n- * Constructor for a new OpenLayers.Map instance. There are two possible\n- * ways to call the map constructor. See the examples below.\n- *\n- * Parameters:\n- * div - {DOMElement|String} The element or id of an element in your page\n- * that will contain the map. May be omitted if the <div> option is\n- * provided or if you intend to call the <render> method later.\n- * options - {Object} Optional object with properties to tag onto the map.\n- *\n- * Valid options (in addition to the listed API properties):\n- * center - {<OpenLayers.LonLat>|Array} The default initial center of the map.\n- * If provided as array, the first value is the x coordinate,\n- * and the 2nd value is the y coordinate.\n- * Only specify if <layers> is provided.\n- * Note that if an ArgParser/Permalink control is present,\n- * and the querystring contains coordinates, center will be set\n- * by that, and this option will be ignored.\n- * zoom - {Number} The initial zoom level for the map. Only specify if\n- * <layers> is provided.\n- * Note that if an ArgParser/Permalink control is present,\n- * and the querystring contains a zoom level, zoom will be set\n- * by that, and this option will be ignored.\n- * extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.\n- * If provided as an array, the array should consist of\n- * four values (left, bottom, right, top).\n- * Only specify if <center> and <zoom> are not provided.\n- * \n- * Examples:\n- * (code)\n- * // create a map with default options in an element with the id \"map1\"\n- * var map = new OpenLayers.Map(\"map1\");\n- *\n- * // create a map with non-default options in an element with id \"map2\"\n- * var options = {\n- * projection: \"EPSG:3857\",\n- * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),\n- * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)\n- * };\n- * var map = new OpenLayers.Map(\"map2\", options);\n- *\n- * // map with non-default options - same as above but with a single argument,\n- * // a restricted extent, and using arrays for bounds and center\n- * var map = new OpenLayers.Map({\n- * div: \"map_id\",\n- * projection: \"EPSG:3857\",\n- * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],\n- * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],\n- * center: [-12356463.476333, 5621521.4854095]\n- * });\n- *\n- * // create a map without a reference to a container - call render later\n- * var map = new OpenLayers.Map({\n- * projection: \"EPSG:3857\",\n- * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)\n- * });\n- * (end)\n- */\n- initialize: function(div, options) {\n-\n- // If only one argument is provided, check if it is an object.\n- if (arguments.length === 1 && typeof div === \"object\") {\n- options = div;\n- div = options && options.div;\n- }\n-\n- // Simple-type defaults are set in class definition. \n- // Now set complex-type defaults \n- this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,\n- OpenLayers.Map.TILE_HEIGHT);\n-\n- this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n-\n- this.theme = OpenLayers._getScriptLocation() +\n- 'theme/default/style.css';\n-\n- // backup original options\n- this.options = OpenLayers.Util.extend({}, options);\n-\n- // now override default options \n- OpenLayers.Util.extend(this, options);\n-\n- var projCode = this.projection instanceof OpenLayers.Projection ?\n- this.projection.projCode : this.projection;\n- OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n-\n- // allow extents and center to be arrays\n- if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n- this.maxExtent = new OpenLayers.Bounds(this.maxExtent);\n- }\n- if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n- this.minExtent = new OpenLayers.Bounds(this.minExtent);\n- }\n- if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n- this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);\n- }\n- if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n- this.center = new OpenLayers.LonLat(this.center);\n- }\n-\n- // initialize layers array\n- this.layers = [];\n-\n- this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n-\n- this.div = OpenLayers.Util.getElement(div);\n- if (!this.div) {\n- this.div = document.createElement(\"div\");\n- this.div.style.height = \"1px\";\n- this.div.style.width = \"1px\";\n- }\n-\n- OpenLayers.Element.addClass(this.div, 'olMap');\n-\n- // the viewPortDiv is the outermost div we modify\n- var id = this.id + \"_OpenLayers_ViewPort\";\n- this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,\n- \"relative\", null,\n- \"hidden\");\n- this.viewPortDiv.style.width = \"100%\";\n- this.viewPortDiv.style.height = \"100%\";\n- this.viewPortDiv.className = \"olMapViewport\";\n- this.div.appendChild(this.viewPortDiv);\n-\n- this.events = new OpenLayers.Events(\n- this, this.viewPortDiv, null, this.fallThrough, {\n- includeXY: true\n- }\n- );\n-\n- if (OpenLayers.TileManager && this.tileManager !== null) {\n- if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n- this.tileManager = new OpenLayers.TileManager(this.tileManager);\n- }\n- this.tileManager.addMap(this);\n- }\n-\n- // the layerContainerDiv is the one that holds all the layers\n- id = this.id + \"_OpenLayers_Container\";\n- this.layerContainerDiv = OpenLayers.Util.createDiv(id);\n- this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1;\n- this.layerContainerOriginPx = {\n- x: 0,\n- y: 0\n- };\n- this.applyTransform();\n-\n- this.viewPortDiv.appendChild(this.layerContainerDiv);\n-\n- this.updateSize();\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n-\n- if (this.autoUpdateSize === true) {\n- // updateSize on catching the window's resize\n- // Note that this is ok, as updateSize() does nothing if the \n- // map's size has not actually changed.\n- this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,\n- this);\n- OpenLayers.Event.observe(window, 'resize',\n- this.updateSizeDestroy);\n- }\n-\n- // only append link stylesheet if the theme property is set\n- if (this.theme) {\n- // check existing links for equivalent url\n- var addNode = true;\n- var nodes = document.getElementsByTagName('link');\n- for (var i = 0, len = nodes.length; i < len; ++i) {\n- if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,\n- this.theme)) {\n- addNode = false;\n- break;\n- }\n- }\n- // only add a new node if one with an equivalent url hasn't already\n- // been added\n- if (addNode) {\n- var cssNode = document.createElement('link');\n- cssNode.setAttribute('rel', 'stylesheet');\n- cssNode.setAttribute('type', 'text/css');\n- cssNode.setAttribute('href', this.theme);\n- document.getElementsByTagName('head')[0].appendChild(cssNode);\n- }\n- }\n-\n- if (this.controls == null) { // default controls\n- this.controls = [];\n- if (OpenLayers.Control != null) { // running full or lite?\n- // Navigation or TouchNavigation depending on what is in build\n- if (OpenLayers.Control.Navigation) {\n- this.controls.push(new OpenLayers.Control.Navigation());\n- } else if (OpenLayers.Control.TouchNavigation) {\n- this.controls.push(new OpenLayers.Control.TouchNavigation());\n- }\n- if (OpenLayers.Control.Zoom) {\n- this.controls.push(new OpenLayers.Control.Zoom());\n- } else if (OpenLayers.Control.PanZoom) {\n- this.controls.push(new OpenLayers.Control.PanZoom());\n- }\n-\n- if (OpenLayers.Control.ArgParser) {\n- this.controls.push(new OpenLayers.Control.ArgParser());\n- }\n- if (OpenLayers.Control.Attribution) {\n- this.controls.push(new OpenLayers.Control.Attribution());\n- }\n- }\n- }\n-\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.addControlToMap(this.controls[i]);\n- }\n-\n- this.popups = [];\n-\n- this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);\n-\n-\n- // always call map.destroy()\n- OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);\n-\n- // add any initial layers\n- if (options && options.layers) {\n- /** \n- * If you have set options.center, the map center property will be\n- * set at this point. However, since setCenter has not been called,\n- * addLayers gets confused. So we delete the map center in this \n- * case. Because the check below uses options.center, it will\n- * be properly set below.\n- */\n- delete this.center;\n- delete this.zoom;\n- this.addLayers(options.layers);\n- // set center (and optionally zoom)\n- if (options.center && !this.getCenter()) {\n- // zoom can be undefined here\n- this.setCenter(options.center, options.zoom);\n- }\n- }\n-\n- if (this.panMethod) {\n- this.panTween = new OpenLayers.Tween(this.panMethod);\n- }\n- if (this.zoomMethod && this.applyTransform.transform) {\n- this.zoomTween = new OpenLayers.Tween(this.zoomMethod);\n- }\n- },\n-\n- /** \n- * APIMethod: getViewport\n- * Get the DOMElement representing the view port.\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- getViewport: function() {\n- return this.viewPortDiv;\n- },\n-\n- /**\n- * APIMethod: render\n- * Render the map to a specified container.\n- * \n- * Parameters:\n- * div - {String|DOMElement} The container that the map should be rendered\n- * to. If different than the current container, the map viewport\n- * will be moved from the current to the new container.\n- */\n- render: function(div) {\n- this.div = OpenLayers.Util.getElement(div);\n- OpenLayers.Element.addClass(this.div, 'olMap');\n- this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n- this.div.appendChild(this.viewPortDiv);\n- this.updateSize();\n- },\n-\n- /**\n- * Method: unloadDestroy\n- * Function that is called to destroy the map on page unload. stored here\n- * so that if map is manually destroyed, we can unregister this.\n- */\n- unloadDestroy: null,\n-\n- /**\n- * Method: updateSizeDestroy\n- * When the map is destroyed, we need to stop listening to updateSize\n- * events: this method stores the function we need to unregister in \n- * non-IE browsers.\n- */\n- updateSizeDestroy: null,\n-\n- /**\n- * APIMethod: destroy\n- * Destroy this map.\n- * Note that if you are using an application which removes a container\n- * of the map from the DOM, you need to ensure that you destroy the\n- * map *before* this happens; otherwise, the page unload handler\n- * will fail because the DOM elements that map.destroy() wants\n- * to clean up will be gone. (See \n- * http://trac.osgeo.org/openlayers/ticket/2277 for more information).\n- * This will apply to GeoExt and also to other applications which\n- * modify the DOM of the container of the OpenLayers Map.\n- */\n- destroy: function() {\n- // if unloadDestroy is null, we've already been destroyed\n- if (!this.unloadDestroy) {\n- return false;\n- }\n-\n- // make sure panning doesn't continue after destruction\n- if (this.panTween) {\n- this.panTween.stop();\n- this.panTween = null;\n- }\n- // make sure zooming doesn't continue after destruction\n- if (this.zoomTween) {\n- this.zoomTween.stop();\n- this.zoomTween = null;\n- }\n-\n- // map has been destroyed. dont do it again!\n- OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);\n- this.unloadDestroy = null;\n-\n- if (this.updateSizeDestroy) {\n- OpenLayers.Event.stopObserving(window, 'resize',\n- this.updateSizeDestroy);\n- }\n-\n- this.paddingForPopups = null;\n-\n- if (this.controls != null) {\n- for (var i = this.controls.length - 1; i >= 0; --i) {\n- this.controls[i].destroy();\n- }\n- this.controls = null;\n- }\n- if (this.layers != null) {\n- for (var i = this.layers.length - 1; i >= 0; --i) {\n- //pass 'false' to destroy so that map wont try to set a new \n- // baselayer after each baselayer is removed\n- this.layers[i].destroy(false);\n- }\n- this.layers = null;\n- }\n- if (this.viewPortDiv && this.viewPortDiv.parentNode) {\n- this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n- }\n- this.viewPortDiv = null;\n-\n- if (this.tileManager) {\n- this.tileManager.removeMap(this);\n- this.tileManager = null;\n- }\n-\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- this.eventListeners = null;\n- }\n- this.events.destroy();\n- this.events = null;\n-\n- this.options = null;\n- },\n-\n- /**\n- * APIMethod: setOptions\n- * Change the map options\n- *\n- * Parameters:\n- * options - {Object} Hashtable of options to tag to the map\n- */\n- setOptions: function(options) {\n- var updatePxExtent = this.minPx &&\n- options.restrictedExtent != this.restrictedExtent;\n- OpenLayers.Util.extend(this, options);\n- // force recalculation of minPx and maxPx\n- updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {\n- forceZoomChange: true\n- });\n- },\n-\n- /**\n- * APIMethod: getTileSize\n- * Get the tile size for the map\n- *\n- * Returns:\n- * {<OpenLayers.Size>}\n- */\n- getTileSize: function() {\n- return this.tileSize;\n- },\n-\n-\n- /**\n- * APIMethod: getBy\n- * Get a list of objects given a property and a match item.\n- *\n- * Parameters:\n- * array - {String} A property on the map whose value is an array.\n- * property - {String} A property on each item of the given array.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(map[array][i][property]) evaluates to true, the item will\n- * be included in the array returned. If no items are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array} An array of items where the given property matches the given\n- * criteria.\n- */\n- getBy: function(array, property, match) {\n- var test = (typeof match.test == \"function\");\n- var found = OpenLayers.Array.filter(this[array], function(item) {\n- return item[property] == match || (test && match.test(item[property]));\n- });\n- return found;\n- },\n-\n- /**\n- * APIMethod: getLayersBy\n- * Get a list of layers with properties matching the given criteria.\n- *\n- * Parameters:\n- * property - {String} A layer property to be matched.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(layer[property]) evaluates to true, the layer will be\n- * included in the array returned. If no layers are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.\n- * An empty array is returned if no matches are found.\n- */\n- getLayersBy: function(property, match) {\n- return this.getBy(\"layers\", property, match);\n- },\n-\n- /**\n- * APIMethod: getLayersByName\n- * Get a list of layers with names matching the given name.\n- *\n- * Parameters:\n- * match - {String | Object} A layer name. The name can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * name.test(layer.name) evaluates to true, the layer will be included\n- * in the list of layers returned. If no layers are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.\n- * An empty array is returned if no matches are found.\n- */\n- getLayersByName: function(match) {\n- return this.getLayersBy(\"name\", match);\n- },\n-\n- /**\n- * APIMethod: getLayersByClass\n- * Get a list of layers of a given class (CLASS_NAME).\n- *\n- * Parameters:\n- * match - {String | Object} A layer class name. The match can also be a\n- * regular expression literal or object. In addition, it can be any\n- * object with a method named test. For reqular expressions or other,\n- * if type.test(layer.CLASS_NAME) evaluates to true, the layer will\n- * be included in the list of layers returned. If no layers are\n- * found, an empty array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.\n- * An empty array is returned if no matches are found.\n- */\n- getLayersByClass: function(match) {\n- return this.getLayersBy(\"CLASS_NAME\", match);\n- },\n-\n- /**\n- * APIMethod: getControlsBy\n- * Get a list of controls with properties matching the given criteria.\n- *\n- * Parameters:\n- * property - {String} A control property to be matched.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(layer[property]) evaluates to true, the layer will be\n- * included in the array returned. If no layers are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given\n- * criteria. An empty array is returned if no matches are found.\n- */\n- getControlsBy: function(property, match) {\n- return this.getBy(\"controls\", property, match);\n- },\n-\n- /**\n- * APIMethod: getControlsByClass\n- * Get a list of controls of a given class (CLASS_NAME).\n- *\n- * Parameters:\n- * match - {String | Object} A control class name. The match can also be a\n- * regular expression literal or object. In addition, it can be any\n- * object with a method named test. For reqular expressions or other,\n- * if type.test(control.CLASS_NAME) evaluates to true, the control will\n- * be included in the list of controls returned. If no controls are\n- * found, an empty array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given class.\n- * An empty array is returned if no matches are found.\n- */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\n- },\n-\n- /********************************************************/\n- /* */\n- /* Layer Functions */\n- /* */\n- /* The following functions deal with adding and */\n- /* removing Layers to and from the Map */\n- /* */\n- /********************************************************/\n-\n- /**\n- * APIMethod: getLayer\n- * Get a layer based on its id\n- *\n- * Parameters:\n- * id - {String} A layer id\n- *\n- * Returns:\n- * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's \n- * layer collection, or null if not found.\n- */\n- getLayer: function(id) {\n- var foundLayer = null;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer.id == id) {\n- foundLayer = layer;\n- break;\n- }\n- }\n- return foundLayer;\n- },\n-\n- /**\n- * Method: setLayerZIndex\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- * zIdx - {int} \n- */\n- setLayerZIndex: function(layer, zIdx) {\n- layer.setZIndex(\n- this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] +\n- zIdx * 5);\n- },\n-\n- /**\n- * Method: resetLayersZIndex\n- * Reset each layer's z-index based on layer's array index\n- */\n- resetLayersZIndex: function() {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- this.setLayerZIndex(layer, i);\n- }\n- },\n-\n- /**\n- * APIMethod: addLayer\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- *\n- * Returns:\n- * {Boolean} True if the layer has been added to the map.\n- */\n- addLayer: function(layer) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- if (this.layers[i] == layer) {\n- return false;\n- }\n- }\n- if (this.events.triggerEvent(\"preaddlayer\", {\n- layer: layer\n- }) === false) {\n- return false;\n- }\n- if (this.allOverlays) {\n- layer.isBaseLayer = false;\n- }\n-\n- layer.div.className = \"olLayerDiv\";\n- layer.div.style.overflow = \"\";\n- this.setLayerZIndex(layer, this.layers.length);\n-\n- if (layer.isFixed) {\n- this.viewPortDiv.appendChild(layer.div);\n- } else {\n- this.layerContainerDiv.appendChild(layer.div);\n- }\n- this.layers.push(layer);\n- layer.setMap(this);\n-\n- if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {\n- if (this.baseLayer == null) {\n- // set the first baselaye we add as the baselayer\n- this.setBaseLayer(layer);\n- } else {\n- layer.setVisibility(false);\n- }\n- } else {\n- layer.redraw();\n- }\n-\n- this.events.triggerEvent(\"addlayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"added\", {\n- map: this,\n- layer: layer\n- });\n- layer.afterAdd();\n-\n- return true;\n- },\n-\n- /**\n- * APIMethod: addLayers \n- *\n- * Parameters:\n- * layers - {Array(<OpenLayers.Layer>)} \n- */\n- addLayers: function(layers) {\n- for (var i = 0, len = layers.length; i < len; i++) {\n- this.addLayer(layers[i]);\n- }\n- },\n-\n- /** \n- * APIMethod: removeLayer\n- * Removes a layer from the map by removing its visual element (the \n- * layer.div property), then removing it from the map's internal list \n- * of layers, setting the layer's map property to null. \n- * \n- * a \"removelayer\" event is triggered.\n- * \n- * very worthy of mention is that simply removing a layer from a map\n- * will not cause the removal of any popups which may have been created\n- * by the layer. this is due to the fact that it was decided at some\n- * point that popups would not belong to layers. thus there is no way \n- * for us to know here to which layer the popup belongs.\n- * \n- * A simple solution to this is simply to call destroy() on the layer.\n- * the default OpenLayers.Layer class's destroy() function\n- * automatically takes care to remove itself from whatever map it has\n- * been attached to. \n- * \n- * The correct solution is for the layer itself to register an \n- * event-handler on \"removelayer\" and when it is called, if it \n- * recognizes itself as the layer being removed, then it cycles through\n- * its own personal list of popups, removing them from the map.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- * setNewBaseLayer - {Boolean} Default is true\n- */\n- removeLayer: function(layer, setNewBaseLayer) {\n- if (this.events.triggerEvent(\"preremovelayer\", {\n- layer: layer\n- }) === false) {\n- return;\n- }\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true;\n- }\n-\n- if (layer.isFixed) {\n- this.viewPortDiv.removeChild(layer.div);\n- } else {\n- this.layerContainerDiv.removeChild(layer.div);\n- }\n- OpenLayers.Util.removeItem(this.layers, layer);\n- layer.removeMap(this);\n- layer.map = null;\n-\n- // if we removed the base layer, need to set a new one\n- if (this.baseLayer == layer) {\n- this.baseLayer = null;\n- if (setNewBaseLayer) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var iLayer = this.layers[i];\n- if (iLayer.isBaseLayer || this.allOverlays) {\n- this.setBaseLayer(iLayer);\n- break;\n- }\n- }\n- }\n- }\n-\n- this.resetLayersZIndex();\n-\n- this.events.triggerEvent(\"removelayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"removed\", {\n- map: this,\n- layer: layer\n- });\n- },\n-\n- /**\n- * APIMethod: getNumLayers\n- * \n- * Returns:\n- * {Int} The number of layers attached to the map.\n- */\n- getNumLayers: function() {\n- return this.layers.length;\n- },\n-\n- /** \n- * APIMethod: getLayerIndex\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer>}\n- *\n- * Returns:\n- * {Integer} The current (zero-based) index of the given layer in the map's\n- * layer stack. Returns -1 if the layer isn't on the map.\n- */\n- getLayerIndex: function(layer) {\n- return OpenLayers.Util.indexOf(this.layers, layer);\n- },\n-\n- /** \n- * APIMethod: setLayerIndex\n- * Move the given layer to the specified (zero-based) index in the layer\n- * list, changing its z-index in the map display. Use\n- * map.getLayerIndex() to find out the current index of a layer. Note\n- * that this cannot (or at least should not) be effectively used to\n- * raise base layers above overlays.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- * idx - {int} \n- */\n- setLayerIndex: function(layer, idx) {\n- var base = this.getLayerIndex(layer);\n- if (idx < 0) {\n- idx = 0;\n- } else if (idx > this.layers.length) {\n- idx = this.layers.length;\n- }\n- if (base != idx) {\n- this.layers.splice(base, 1);\n- this.layers.splice(idx, 0, layer);\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- this.setLayerZIndex(this.layers[i], i);\n- }\n- this.events.triggerEvent(\"changelayer\", {\n- layer: layer,\n- property: \"order\"\n- });\n- if (this.allOverlays) {\n- if (idx === 0) {\n- this.setBaseLayer(layer);\n- } else if (this.baseLayer !== this.layers[0]) {\n- this.setBaseLayer(this.layers[0]);\n- }\n- }\n- }\n- },\n-\n- /** \n- * APIMethod: raiseLayer\n- * Change the index of the given layer by delta. If delta is positive, \n- * the layer is moved up the map's layer stack; if delta is negative,\n- * the layer is moved down. Again, note that this cannot (or at least\n- * should not) be effectively used to raise base layers above overlays.\n- *\n- * Paremeters:\n- * layer - {<OpenLayers.Layer>} \n- * delta - {int} \n- */\n- raiseLayer: function(layer, delta) {\n- var idx = this.getLayerIndex(layer) + delta;\n- this.setLayerIndex(layer, idx);\n- },\n-\n- /** \n- * APIMethod: setBaseLayer\n- * Allows user to specify one of the currently-loaded layers as the Map's\n- * new base layer.\n- * \n- * Parameters:\n- * newBaseLayer - {<OpenLayers.Layer>}\n- */\n- setBaseLayer: function(newBaseLayer) {\n-\n- if (newBaseLayer != this.baseLayer) {\n-\n- // ensure newBaseLayer is already loaded\n- if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n-\n- // preserve center and scale when changing base layers\n- var center = this.getCachedCenter();\n- var newResolution = OpenLayers.Util.getResolutionFromScale(\n- this.getScale(), newBaseLayer.units\n- );\n-\n- // make the old base layer invisible \n- if (this.baseLayer != null && !this.allOverlays) {\n- this.baseLayer.setVisibility(false);\n- }\n-\n- // set new baselayer\n- this.baseLayer = newBaseLayer;\n-\n- if (!this.allOverlays || this.baseLayer.visibility) {\n- this.baseLayer.setVisibility(true);\n- // Layer may previously have been visible but not in range.\n- // In this case we need to redraw it to make it visible.\n- if (this.baseLayer.inRange === false) {\n- this.baseLayer.redraw();\n- }\n- }\n-\n- // recenter the map\n- if (center != null) {\n- // new zoom level derived from old scale\n- var newZoom = this.getZoomForResolution(\n- newResolution || this.resolution, true\n- );\n- // zoom and force zoom change\n- this.setCenter(center, newZoom, false, true);\n- }\n-\n- this.events.triggerEvent(\"changebaselayer\", {\n- layer: this.baseLayer\n- });\n- }\n- }\n- },\n-\n-\n- /********************************************************/\n- /* */\n- /* Control Functions */\n- /* */\n- /* The following functions deal with adding and */\n- /* removing Controls to and from the Map */\n- /* */\n- /********************************************************/\n-\n- /**\n- * APIMethod: addControl\n- * Add the passed over control to the map. Optionally \n- * position the control at the given pixel.\n- * \n- * Parameters:\n- * control - {<OpenLayers.Control>}\n- * px - {<OpenLayers.Pixel>}\n- */\n- addControl: function(control, px) {\n- this.controls.push(control);\n- this.addControlToMap(control, px);\n- },\n-\n- /**\n- * APIMethod: addControls\n- * Add all of the passed over controls to the map. \n- * You can pass over an optional second array\n- * with pixel-objects to position the controls.\n- * The indices of the two arrays should match and\n- * you can add null as pixel for those controls \n- * you want to be autopositioned. \n- * \n- * Parameters:\n- * controls - {Array(<OpenLayers.Control>)}\n- * pixels - {Array(<OpenLayers.Pixel>)}\n- */\n- addControls: function(controls, pixels) {\n- var pxs = (arguments.length === 1) ? [] : pixels;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var ctrl = controls[i];\n- var px = (pxs[i]) ? pxs[i] : null;\n- this.addControl(ctrl, px);\n- }\n- },\n-\n- /**\n- * Method: addControlToMap\n- * \n- * Parameters:\n- * \n- * control - {<OpenLayers.Control>}\n- * px - {<OpenLayers.Pixel>}\n- */\n- addControlToMap: function(control, px) {\n- // If a control doesn't have a div at this point, it belongs in the\n- // viewport.\n- control.outsideViewport = (control.div != null);\n-\n- // If the map has a displayProjection, and the control doesn't, set \n- // the display projection.\n- if (this.displayProjection && !control.displayProjection) {\n- control.displayProjection = this.displayProjection;\n- }\n-\n- control.setMap(this);\n- var div = control.draw(px);\n- if (div) {\n- if (!control.outsideViewport) {\n- div.style.zIndex = this.Z_INDEX_BASE['Control'] +\n- this.controls.length;\n- this.viewPortDiv.appendChild(div);\n- }\n- }\n- if (control.autoActivate) {\n- control.activate();\n- }\n- },\n-\n- /**\n- * APIMethod: getControl\n- * \n- * Parameters:\n- * id - {String} ID of the control to return.\n- * \n- * Returns:\n- * {<OpenLayers.Control>} The control from the map's list of controls \n- * which has a matching 'id'. If none found, \n- * returns null.\n- */\n- getControl: function(id) {\n- var returnControl = null;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- var control = this.controls[i];\n- if (control.id == id) {\n- returnControl = control;\n- break;\n- }\n- }\n- return returnControl;\n- },\n-\n- /** \n- * APIMethod: removeControl\n- * Remove a control from the map. Removes the control both from the map \n- * object's internal array of controls, as well as from the map's \n- * viewPort (assuming the control was not added outsideViewport)\n- * \n- * Parameters:\n- * control - {<OpenLayers.Control>} The control to remove.\n- */\n- removeControl: function(control) {\n- //make sure control is non-null and actually part of our map\n- if ((control) && (control == this.getControl(control.id))) {\n- if (control.div && (control.div.parentNode == this.viewPortDiv)) {\n- this.viewPortDiv.removeChild(control.div);\n- }\n- OpenLayers.Util.removeItem(this.controls, control);\n- }\n- },\n-\n- /********************************************************/\n- /* */\n- /* Popup Functions */\n- /* */\n- /* The following functions deal with adding and */\n- /* removing Popups to and from the Map */\n- /* */\n- /********************************************************/\n-\n- /** \n- * APIMethod: addPopup\n- * \n- * Parameters:\n- * popup - {<OpenLayers.Popup>}\n- * exclusive - {Boolean} If true, closes all other popups first\n- */\n- addPopup: function(popup, exclusive) {\n-\n- if (exclusive) {\n- //remove all other popups from screen\n- for (var i = this.popups.length - 1; i >= 0; --i) {\n- this.removePopup(this.popups[i]);\n- }\n- }\n-\n- popup.map = this;\n- this.popups.push(popup);\n- var popupDiv = popup.draw();\n- if (popupDiv) {\n- popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +\n- this.popups.length;\n- this.layerContainerDiv.appendChild(popupDiv);\n- }\n- },\n-\n- /** \n- * APIMethod: removePopup\n- * \n- * Parameters:\n- * popup - {<OpenLayers.Popup>}\n- */\n- removePopup: function(popup) {\n- OpenLayers.Util.removeItem(this.popups, popup);\n- if (popup.div) {\n- try {\n- this.layerContainerDiv.removeChild(popup.div);\n- } catch (e) {} // Popups sometimes apparently get disconnected\n- // from the layerContainerDiv, and cause complaints.\n- }\n- popup.map = null;\n- },\n-\n- /********************************************************/\n- /* */\n- /* Container Div Functions */\n- /* */\n- /* The following functions deal with the access to */\n- /* and maintenance of the size of the container div */\n- /* */\n- /********************************************************/\n-\n- /**\n- * APIMethod: getSize\n- * \n- * Returns:\n- * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the \n- * size, in pixels, of the div into which OpenLayers \n- * has been loaded. \n- * Note - A clone() of this locally cached variable is\n- * returned, so as not to allow users to modify it.\n- */\n- getSize: function() {\n- var size = null;\n- if (this.size != null) {\n- size = this.size.clone();\n- }\n- return size;\n- },\n-\n- /**\n- * APIMethod: updateSize\n- * This function should be called by any external code which dynamically\n- * changes the size of the map div (because mozilla wont let us catch \n- * the \"onresize\" for an element)\n- */\n- updateSize: function() {\n- // the div might have moved on the page, also\n- var newSize = this.getCurrentSize();\n- if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {\n- this.events.clearMouseCache();\n- var oldSize = this.getSize();\n- if (oldSize == null) {\n- this.size = oldSize = newSize;\n- }\n- if (!newSize.equals(oldSize)) {\n-\n- // store the new size\n- this.size = newSize;\n-\n- //notify layers of mapresize\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- this.layers[i].onMapResize();\n- }\n-\n- var center = this.getCachedCenter();\n-\n- if (this.baseLayer != null && center != null) {\n- var zoom = this.getZoom();\n- this.zoom = null;\n- this.setCenter(center, zoom);\n- }\n-\n- }\n- }\n- this.events.triggerEvent(\"updatesize\");\n- },\n-\n- /**\n- * Method: getCurrentSize\n- * \n- * Returns:\n- * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions \n- * of the map div\n- */\n- getCurrentSize: function() {\n-\n- var size = new OpenLayers.Size(this.div.clientWidth,\n- this.div.clientHeight);\n-\n- if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n- size.w = this.div.offsetWidth;\n- size.h = this.div.offsetHeight;\n- }\n- if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n- size.w = parseInt(this.div.style.width);\n- size.h = parseInt(this.div.style.height);\n- }\n- return size;\n- },\n-\n- /** \n- * Method: calculateBounds\n- * \n- * Parameters:\n- * center - {<OpenLayers.LonLat>} Default is this.getCenter()\n- * resolution - {float} Default is this.getResolution() \n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} A bounds based on resolution, center, and \n- * current mapsize.\n- */\n- calculateBounds: function(center, resolution) {\n-\n- var extent = null;\n-\n- if (center == null) {\n- center = this.getCachedCenter();\n- }\n- if (resolution == null) {\n- resolution = this.getResolution();\n- }\n-\n- if ((center != null) && (resolution != null)) {\n- var halfWDeg = (this.size.w * resolution) / 2;\n- var halfHDeg = (this.size.h * resolution) / 2;\n-\n- extent = new OpenLayers.Bounds(center.lon - halfWDeg,\n- center.lat - halfHDeg,\n- center.lon + halfWDeg,\n- center.lat + halfHDeg);\n- }\n-\n- return extent;\n- },\n-\n-\n- /********************************************************/\n- /* */\n- /* Zoom, Center, Pan Functions */\n- /* */\n- /* The following functions handle the validation, */\n- /* getting and setting of the Zoom Level and Center */\n- /* as well as the panning of the Map */\n- /* */\n- /********************************************************/\n- /**\n- * APIMethod: getCenter\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>}\n- */\n- getCenter: function() {\n- var center = null;\n- var cachedCenter = this.getCachedCenter();\n- if (cachedCenter) {\n- center = cachedCenter.clone();\n- }\n- return center;\n- },\n-\n- /**\n- * Method: getCachedCenter\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>}\n- */\n- getCachedCenter: function() {\n- if (!this.center && this.size) {\n- this.center = this.getLonLatFromViewPortPx({\n- x: this.size.w / 2,\n- y: this.size.h / 2\n- });\n- }\n- return this.center;\n- },\n-\n- /**\n- * APIMethod: getZoom\n- * \n- * Returns:\n- * {Integer}\n- */\n- getZoom: function() {\n- return this.zoom;\n- },\n-\n- /** \n- * APIMethod: pan\n- * Allows user to pan by a value of screen pixels\n- * \n- * Parameters:\n- * dx - {Integer}\n- * dy - {Integer}\n- * options - {Object} Options to configure panning:\n- * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.\n- * - *dragging* {Boolean} Call setCenter with dragging true. Default is\n- * false.\n- */\n- pan: function(dx, dy, options) {\n- options = OpenLayers.Util.applyDefaults(options, {\n- animate: true,\n- dragging: false\n- });\n- if (options.dragging) {\n- if (dx != 0 || dy != 0) {\n- this.moveByPx(dx, dy);\n- }\n- } else {\n- // getCenter\n- var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());\n-\n- // adjust\n- var newCenterPx = centerPx.add(dx, dy);\n-\n- if (this.dragging || !newCenterPx.equals(centerPx)) {\n- var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);\n- if (options.animate) {\n- this.panTo(newCenterLonLat);\n- } else {\n- this.moveTo(newCenterLonLat);\n- if (this.dragging) {\n- this.dragging = false;\n- this.events.triggerEvent(\"moveend\");\n- }\n- }\n- }\n- }\n-\n- },\n-\n- /** \n- * APIMethod: panTo\n- * Allows user to pan to a new lonlat\n- * If the new lonlat is in the current extent the map will slide smoothly\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- */\n- panTo: function(lonlat) {\n- if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {\n- var center = this.getCachedCenter();\n-\n- // center will not change, don't do nothing\n- if (lonlat.equals(center)) {\n- return;\n- }\n-\n- var from = this.getPixelFromLonLat(center);\n- var to = this.getPixelFromLonLat(lonlat);\n- var vector = {\n- x: to.x - from.x,\n- y: to.y - from.y\n- };\n- var last = {\n- x: 0,\n- y: 0\n- };\n-\n- this.panTween.start({\n- x: 0,\n- y: 0\n- }, vector, this.panDuration, {\n- callbacks: {\n- eachStep: OpenLayers.Function.bind(function(px) {\n- var x = px.x - last.x,\n- y = px.y - last.y;\n- this.moveByPx(x, y);\n- last.x = Math.round(px.x);\n- last.y = Math.round(px.y);\n- }, this),\n- done: OpenLayers.Function.bind(function(px) {\n- this.moveTo(lonlat);\n- this.dragging = false;\n- this.events.triggerEvent(\"moveend\");\n- }, this)\n- }\n- });\n- } else {\n- this.setCenter(lonlat);\n- }\n- },\n-\n- /**\n- * APIMethod: setCenter\n- * Set the map center (and optionally, the zoom level).\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Array} The new center location.\n- * If provided as array, the first value is the x coordinate,\n- * and the 2nd value is the y coordinate.\n- * zoom - {Integer} Optional zoom level.\n- * dragging - {Boolean} Specifies whether or not to trigger \n- * movestart/end events\n- * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom \n- * change events (needed on baseLayer change)\n- *\n- * TBD: reconsider forceZoomChange in 3.0\n- */\n- setCenter: function(lonlat, zoom, dragging, forceZoomChange) {\n- if (this.panTween) {\n- this.panTween.stop();\n- }\n- if (this.zoomTween) {\n- this.zoomTween.stop();\n- }\n- this.moveTo(lonlat, zoom, {\n- 'dragging': dragging,\n- 'forceZoomChange': forceZoomChange\n- });\n- },\n-\n- /** \n- * Method: moveByPx\n- * Drag the map by pixels.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n- */\n- moveByPx: function(dx, dy) {\n- var hw = this.size.w / 2;\n- var hh = this.size.h / 2;\n- var x = hw + dx;\n- var y = hh + dy;\n- var wrapDateLine = this.baseLayer.wrapDateLine;\n- var xRestriction = 0;\n- var yRestriction = 0;\n- if (this.restrictedExtent) {\n- xRestriction = hw;\n- yRestriction = hh;\n- // wrapping the date line makes no sense for restricted extents\n- wrapDateLine = false;\n- }\n- dx = wrapDateLine ||\n- x <= this.maxPx.x - xRestriction &&\n- x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;\n- dy = y <= this.maxPx.y - yRestriction &&\n- y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;\n- if (dx || dy) {\n- if (!this.dragging) {\n- this.dragging = true;\n- this.events.triggerEvent(\"movestart\");\n- }\n- this.center = null;\n- if (dx) {\n- this.layerContainerOriginPx.x -= dx;\n- this.minPx.x -= dx;\n- this.maxPx.x -= dx;\n- }\n- if (dy) {\n- this.layerContainerOriginPx.y -= dy;\n- this.minPx.y -= dy;\n- this.maxPx.y -= dy;\n- }\n- this.applyTransform();\n- var layer, i, len;\n- for (i = 0, len = this.layers.length; i < len; ++i) {\n- layer = this.layers[i];\n- if (layer.visibility &&\n- (layer === this.baseLayer || layer.inRange)) {\n- layer.moveByPx(dx, dy);\n- layer.events.triggerEvent(\"move\");\n- }\n- }\n- this.events.triggerEvent(\"move\");\n- }\n- },\n-\n- /**\n- * Method: adjustZoom\n- *\n- * Parameters:\n- * zoom - {Number} The zoom level to adjust\n- *\n- * Returns:\n- * {Integer} Adjusted zoom level that shows a map not wider than its\n- * <baseLayer>'s maxExtent.\n- */\n- adjustZoom: function(zoom) {\n- if (this.baseLayer && this.baseLayer.wrapDateLine) {\n- var resolution, resolutions = this.baseLayer.resolutions,\n- maxResolution = this.getMaxExtent().getWidth() / this.size.w;\n- if (this.getResolutionForZoom(zoom) > maxResolution) {\n- if (this.fractionalZoom) {\n- zoom = this.getZoomForResolution(maxResolution);\n- } else {\n- for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) {\n- if (resolutions[i] <= maxResolution) {\n- zoom = i;\n- break;\n- }\n- }\n- }\n- }\n- }\n- return zoom;\n- },\n-\n- /**\n- * APIMethod: getMinZoom\n- * Returns the minimum zoom level for the current map view. If the base\n- * layer is configured with <wrapDateLine> set to true, this will be the\n- * first zoom level that shows no more than one world width in the current\n- * map viewport. Components that rely on this value (e.g. zoom sliders)\n- * should also listen to the map's \"updatesize\" event and call this method\n- * in the \"updatesize\" listener.\n- *\n- * Returns:\n- * {Number} Minimum zoom level that shows a map not wider than its\n- * <baseLayer>'s maxExtent. This is an Integer value, unless the map is\n- * configured with <fractionalZoom> set to true.\n- */\n- getMinZoom: function() {\n- return this.adjustZoom(0);\n- },\n-\n- /**\n- * Method: moveTo\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- * zoom - {Integer}\n- * options - {Object}\n- */\n- moveTo: function(lonlat, zoom, options) {\n- if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {\n- lonlat = new OpenLayers.LonLat(lonlat);\n- }\n- if (!options) {\n- options = {};\n- }\n- if (zoom != null) {\n- zoom = parseFloat(zoom);\n- if (!this.fractionalZoom) {\n- zoom = Math.round(zoom);\n- }\n- }\n- var requestedZoom = zoom;\n- zoom = this.adjustZoom(zoom);\n- if (zoom !== requestedZoom) {\n- // zoom was adjusted, so keep old lonlat to avoid panning\n- lonlat = this.getCenter();\n- }\n- // dragging is false by default\n- var dragging = options.dragging || this.dragging;\n- // forceZoomChange is false by default\n- var forceZoomChange = options.forceZoomChange;\n-\n- if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {\n- lonlat = this.maxExtent.getCenterLonLat();\n- this.center = lonlat.clone();\n- }\n-\n- if (this.restrictedExtent != null) {\n- // In 3.0, decide if we want to change interpretation of maxExtent.\n- if (lonlat == null) {\n- lonlat = this.center;\n- }\n- if (zoom == null) {\n- zoom = this.getZoom();\n- }\n- var resolution = this.getResolutionForZoom(zoom);\n- var extent = this.calculateBounds(lonlat, resolution);\n- if (!this.restrictedExtent.containsBounds(extent)) {\n- var maxCenter = this.restrictedExtent.getCenterLonLat();\n- if (extent.getWidth() > this.restrictedExtent.getWidth()) {\n- lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);\n- } else if (extent.left < this.restrictedExtent.left) {\n- lonlat = lonlat.add(this.restrictedExtent.left -\n- extent.left, 0);\n- } else if (extent.right > this.restrictedExtent.right) {\n- lonlat = lonlat.add(this.restrictedExtent.right -\n- extent.right, 0);\n- }\n- if (extent.getHeight() > this.restrictedExtent.getHeight()) {\n- lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);\n- } else if (extent.bottom < this.restrictedExtent.bottom) {\n- lonlat = lonlat.add(0, this.restrictedExtent.bottom -\n- extent.bottom);\n- } else if (extent.top > this.restrictedExtent.top) {\n- lonlat = lonlat.add(0, this.restrictedExtent.top -\n- extent.top);\n- }\n- }\n- }\n-\n- var zoomChanged = forceZoomChange || (\n- (this.isValidZoomLevel(zoom)) &&\n- (zoom != this.getZoom()));\n-\n- var centerChanged = (this.isValidLonLat(lonlat)) &&\n- (!lonlat.equals(this.center));\n-\n- // if neither center nor zoom will change, no need to do anything\n- if (zoomChanged || centerChanged || dragging) {\n- dragging || this.events.triggerEvent(\"movestart\", {\n- zoomChanged: zoomChanged\n- });\n-\n- if (centerChanged) {\n- if (!zoomChanged && this.center) {\n- // if zoom hasnt changed, just slide layerContainer\n- // (must be done before setting this.center to new value)\n- this.centerLayerContainer(lonlat);\n- }\n- this.center = lonlat.clone();\n- }\n-\n- var res = zoomChanged ?\n- this.getResolutionForZoom(zoom) : this.getResolution();\n- // (re)set the layerContainerDiv's location\n- if (zoomChanged || this.layerContainerOrigin == null) {\n- this.layerContainerOrigin = this.getCachedCenter();\n- this.layerContainerOriginPx.x = 0;\n- this.layerContainerOriginPx.y = 0;\n- this.applyTransform();\n- var maxExtent = this.getMaxExtent({\n- restricted: true\n- });\n- var maxExtentCenter = maxExtent.getCenterLonLat();\n- var lonDelta = this.center.lon - maxExtentCenter.lon;\n- var latDelta = maxExtentCenter.lat - this.center.lat;\n- var extentWidth = Math.round(maxExtent.getWidth() / res);\n- var extentHeight = Math.round(maxExtent.getHeight() / res);\n- this.minPx = {\n- x: (this.size.w - extentWidth) / 2 - lonDelta / res,\n- y: (this.size.h - extentHeight) / 2 - latDelta / res\n- };\n- this.maxPx = {\n- x: this.minPx.x + Math.round(maxExtent.getWidth() / res),\n- y: this.minPx.y + Math.round(maxExtent.getHeight() / res)\n- };\n- }\n-\n- if (zoomChanged) {\n- this.zoom = zoom;\n- this.resolution = res;\n- }\n-\n- var bounds = this.getExtent();\n-\n- //send the move call to the baselayer and all the overlays \n-\n- if (this.baseLayer.visibility) {\n- this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);\n- options.dragging || this.baseLayer.events.triggerEvent(\n- \"moveend\", {\n- zoomChanged: zoomChanged\n- }\n- );\n- }\n-\n- bounds = this.baseLayer.getExtent();\n-\n- for (var i = this.layers.length - 1; i >= 0; --i) {\n- var layer = this.layers[i];\n- if (layer !== this.baseLayer && !layer.isBaseLayer) {\n- var inRange = layer.calculateInRange();\n- if (layer.inRange != inRange) {\n- // the inRange property has changed. If the layer is\n- // no longer in range, we turn it off right away. If\n- // the layer is no longer out of range, the moveTo\n- // call below will turn on the layer.\n- layer.inRange = inRange;\n- if (!inRange) {\n- layer.display(false);\n- }\n- this.events.triggerEvent(\"changelayer\", {\n- layer: layer,\n- property: \"visibility\"\n- });\n- }\n- if (inRange && layer.visibility) {\n- layer.moveTo(bounds, zoomChanged, options.dragging);\n- options.dragging || layer.events.triggerEvent(\n- \"moveend\", {\n- zoomChanged: zoomChanged\n- }\n- );\n- }\n- }\n- }\n-\n- this.events.triggerEvent(\"move\");\n- dragging || this.events.triggerEvent(\"moveend\");\n-\n- if (zoomChanged) {\n- //redraw popups\n- for (var i = 0, len = this.popups.length; i < len; i++) {\n- this.popups[i].updatePosition();\n- }\n- this.events.triggerEvent(\"zoomend\");\n- }\n- }\n- },\n-\n- /** \n- * Method: centerLayerContainer\n- * This function takes care to recenter the layerContainerDiv.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- */\n- centerLayerContainer: function(lonlat) {\n- var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);\n- var newPx = this.getViewPortPxFromLonLat(lonlat);\n-\n- if ((originPx != null) && (newPx != null)) {\n- var oldLeft = this.layerContainerOriginPx.x;\n- var oldTop = this.layerContainerOriginPx.y;\n- var newLeft = Math.round(originPx.x - newPx.x);\n- var newTop = Math.round(originPx.y - newPx.y);\n- this.applyTransform(\n- (this.layerContainerOriginPx.x = newLeft),\n- (this.layerContainerOriginPx.y = newTop));\n- var dx = oldLeft - newLeft;\n- var dy = oldTop - newTop;\n- this.minPx.x -= dx;\n- this.maxPx.x -= dx;\n- this.minPx.y -= dy;\n- this.maxPx.y -= dy;\n- }\n- },\n-\n- /**\n- * Method: isValidZoomLevel\n- * \n- * Parameters:\n- * zoomLevel - {Integer}\n- * \n- * Returns:\n- * {Boolean} Whether or not the zoom level passed in is non-null and \n- * within the min/max range of zoom levels.\n- */\n- isValidZoomLevel: function(zoomLevel) {\n- return ((zoomLevel != null) &&\n- (zoomLevel >= 0) &&\n- (zoomLevel < this.getNumZoomLevels()));\n- },\n-\n- /**\n- * Method: isValidLonLat\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- * \n- * Returns:\n- * {Boolean} Whether or not the lonlat passed in is non-null and within\n- * the maxExtent bounds\n- */\n- isValidLonLat: function(lonlat) {\n- var valid = false;\n- if (lonlat != null) {\n- var maxExtent = this.getMaxExtent();\n- var worldBounds = this.baseLayer.wrapDateLine && maxExtent;\n- valid = maxExtent.containsLonLat(lonlat, {\n- worldBounds: worldBounds\n- });\n- }\n- return valid;\n- },\n-\n- /********************************************************/\n- /* */\n- /* Layer Options */\n- /* */\n- /* Accessor functions to Layer Options parameters */\n- /* */\n- /********************************************************/\n-\n- /**\n- * APIMethod: getProjection\n- * This method returns a string representing the projection. In \n- * the case of projection support, this will be the srsCode which\n- * is loaded -- otherwise it will simply be the string value that\n- * was passed to the projection at startup.\n- *\n- * FIXME: In 3.0, we will remove getProjectionObject, and instead\n- * return a Projection object from this function. \n- * \n- * Returns:\n- * {String} The Projection string from the base layer or null. \n- */\n- getProjection: function() {\n- var projection = this.getProjectionObject();\n- return projection ? projection.getCode() : null;\n- },\n-\n- /**\n- * APIMethod: getProjectionObject\n- * Returns the projection obect from the baselayer.\n- *\n- * Returns:\n- * {<OpenLayers.Projection>} The Projection of the base layer.\n- */\n- getProjectionObject: function() {\n- var projection = null;\n- if (this.baseLayer != null) {\n- projection = this.baseLayer.projection;\n- }\n- return projection;\n- },\n-\n- /**\n- * APIMethod: getMaxResolution\n- * \n- * Returns:\n- * {String} The Map's Maximum Resolution\n- */\n- getMaxResolution: function() {\n- var maxResolution = null;\n- if (this.baseLayer != null) {\n- maxResolution = this.baseLayer.maxResolution;\n- }\n- return maxResolution;\n- },\n-\n- /**\n- * APIMethod: getMaxExtent\n- *\n- * Parameters:\n- * options - {Object} \n- * \n- * Allowed Options:\n- * restricted - {Boolean} If true, returns restricted extent (if it is \n- * available.)\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} The maxExtent property as set on the current \n- * baselayer, unless the 'restricted' option is set, in which case\n- * the 'restrictedExtent' option from the map is returned (if it\n- * is set).\n- */\n- getMaxExtent: function(options) {\n- var maxExtent = null;\n- if (options && options.restricted && this.restrictedExtent) {\n- maxExtent = this.restrictedExtent;\n- } else if (this.baseLayer != null) {\n- maxExtent = this.baseLayer.maxExtent;\n- }\n- return maxExtent;\n- },\n-\n- /**\n- * APIMethod: getNumZoomLevels\n- * \n- * Returns:\n- * {Integer} The total number of zoom levels that can be displayed by the \n- * current baseLayer.\n- */\n- getNumZoomLevels: function() {\n- var numZoomLevels = null;\n- if (this.baseLayer != null) {\n- numZoomLevels = this.baseLayer.numZoomLevels;\n- }\n- return numZoomLevels;\n- },\n-\n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /* The following functions, all publicly exposed */\n- /* in the API?, are all merely wrappers to the */\n- /* the same calls on whatever layer is set as */\n- /* the current base layer */\n- /* */\n- /********************************************************/\n-\n- /**\n- * APIMethod: getExtent\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n- * bounds of the current viewPort. \n- * If no baselayer is set, returns null.\n- */\n- getExtent: function() {\n- var extent = null;\n- if (this.baseLayer != null) {\n- extent = this.baseLayer.getExtent();\n- }\n- return extent;\n- },\n-\n- /**\n- * APIMethod: getResolution\n- * \n- * Returns:\n- * {Float} The current resolution of the map. \n- * If no baselayer is set, returns null.\n- */\n- getResolution: function() {\n- var resolution = null;\n- if (this.baseLayer != null) {\n- resolution = this.baseLayer.getResolution();\n- } else if (this.allOverlays === true && this.layers.length > 0) {\n- // while adding the 1st layer to the map in allOverlays mode,\n- // this.baseLayer is not set yet when we need the resolution\n- // for calculateInRange.\n- resolution = this.layers[0].getResolution();\n- }\n- return resolution;\n- },\n-\n- /**\n- * APIMethod: getUnits\n- * \n- * Returns:\n- * {Float} The current units of the map. \n- * If no baselayer is set, returns null.\n- */\n- getUnits: function() {\n- var units = null;\n- if (this.baseLayer != null) {\n- units = this.baseLayer.units;\n- }\n- return units;\n- },\n-\n- /**\n- * APIMethod: getScale\n- * \n- * Returns:\n- * {Float} The current scale denominator of the map. \n- * If no baselayer is set, returns null.\n- */\n- getScale: function() {\n- var scale = null;\n- if (this.baseLayer != null) {\n- var res = this.getResolution();\n- var units = this.baseLayer.units;\n- scale = OpenLayers.Util.getScaleFromResolution(res, units);\n- }\n- return scale;\n- },\n-\n-\n- /**\n- * APIMethod: getZoomForExtent\n- * \n- * Parameters: \n- * bounds - {<OpenLayers.Bounds>}\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified bounds. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- * \n- * Returns:\n- * {Integer} A suitable zoom level for the specified bounds.\n- * If no baselayer is set, returns null.\n- */\n- getZoomForExtent: function(bounds, closest) {\n- var zoom = null;\n- if (this.baseLayer != null) {\n- zoom = this.baseLayer.getZoomForExtent(bounds, closest);\n- }\n- return zoom;\n- },\n-\n- /**\n- * APIMethod: getResolutionForZoom\n- * \n- * Parameters:\n- * zoom - {Float}\n- * \n- * Returns:\n- * {Float} A suitable resolution for the specified zoom. If no baselayer\n- * is set, returns null.\n- */\n- getResolutionForZoom: function(zoom) {\n- var resolution = null;\n- if (this.baseLayer) {\n- resolution = this.baseLayer.getResolutionForZoom(zoom);\n- }\n- return resolution;\n- },\n-\n- /**\n- * APIMethod: getZoomForResolution\n- * \n- * Parameters:\n- * resolution - {Float}\n- * closest - {Boolean} Find the zoom level that corresponds to the absolute \n- * closest resolution, which may result in a zoom whose corresponding\n- * resolution is actually smaller than we would have desired (if this\n- * is being called from a getZoomForExtent() call, then this means that\n- * the returned zoom index might not actually contain the entire \n- * extent specified... but it'll be close).\n- * Default is false.\n- * \n- * Returns:\n- * {Integer} A suitable zoom level for the specified resolution.\n- * If no baselayer is set, returns null.\n- */\n- getZoomForResolution: function(resolution, closest) {\n- var zoom = null;\n- if (this.baseLayer != null) {\n- zoom = this.baseLayer.getZoomForResolution(resolution, closest);\n- }\n- return zoom;\n- },\n-\n- /********************************************************/\n- /* */\n- /* Zooming Functions */\n- /* */\n- /* The following functions, all publicly exposed */\n- /* in the API, are all merely wrappers to the */\n- /* the setCenter() function */\n- /* */\n- /********************************************************/\n-\n- /** \n- * APIMethod: zoomTo\n- * Zoom to a specific zoom level. Zooming will be animated unless the map\n- * is configured with {zoomMethod: null}. To zoom without animation, use\n- * <setCenter> without a lonlat argument.\n- * \n- * Parameters:\n- * zoom - {Integer}\n- */\n- zoomTo: function(zoom, xy) {\n- // non-API arguments:\n- // xy - {<OpenLayers.Pixel>} optional zoom origin\n-\n- var map = this;\n- if (map.isValidZoomLevel(zoom)) {\n- if (map.baseLayer.wrapDateLine) {\n- zoom = map.adjustZoom(zoom);\n- }\n- if (map.zoomTween) {\n- var currentRes = map.getResolution(),\n- targetRes = map.getResolutionForZoom(zoom),\n- start = {\n- scale: 1\n- },\n- end = {\n- scale: currentRes / targetRes\n- };\n- if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {\n- // update the end scale, and reuse the running zoomTween\n- map.zoomTween.finish = {\n- scale: map.zoomTween.finish.scale * end.scale\n- };\n- } else {\n- if (!xy) {\n- var size = map.getSize();\n- xy = {\n- x: size.w / 2,\n- y: size.h / 2\n- };\n- }\n- map.zoomTween.start(start, end, map.zoomDuration, {\n- minFrameRate: 50, // don't spend much time zooming\n- callbacks: {\n- eachStep: function(data) {\n- var containerOrigin = map.layerContainerOriginPx,\n- scale = data.scale,\n- dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,\n- dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;\n- map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);\n- },\n- done: function(data) {\n- map.applyTransform();\n- var resolution = map.getResolution() / data.scale,\n- zoom = map.getZoomForResolution(resolution, true)\n- map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);\n- }\n- }\n- });\n- }\n- } else {\n- var center = xy ?\n- map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :\n- null;\n- map.setCenter(center, zoom);\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: zoomIn\n- * \n- */\n- zoomIn: function() {\n- this.zoomTo(this.getZoom() + 1);\n- },\n-\n- /**\n- * APIMethod: zoomOut\n- * \n- */\n- zoomOut: function() {\n- this.zoomTo(this.getZoom() - 1);\n- },\n-\n- /**\n- * APIMethod: zoomToExtent\n- * Zoom to the passed in bounds, recenter\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified bounds. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- * \n- */\n- zoomToExtent: function(bounds, closest) {\n- if (!(bounds instanceof OpenLayers.Bounds)) {\n- bounds = new OpenLayers.Bounds(bounds);\n- }\n- var center = bounds.getCenterLonLat();\n- if (this.baseLayer.wrapDateLine) {\n- var maxExtent = this.getMaxExtent();\n-\n- //fix straddling bounds (in the case of a bbox that straddles the \n- // dateline, it's left and right boundaries will appear backwards. \n- // we fix this by allowing a right value that is greater than the\n- // max value at the dateline -- this allows us to pass a valid \n- // bounds to calculate zoom)\n- //\n- bounds = bounds.clone();\n- while (bounds.right < bounds.left) {\n- bounds.right += maxExtent.getWidth();\n- }\n- //if the bounds was straddling (see above), then the center point \n- // we got from it was wrong. So we take our new bounds and ask it\n- // for the center.\n- //\n- center = bounds.getCenterLonLat().wrapDateLine(maxExtent);\n- }\n- this.setCenter(center, this.getZoomForExtent(bounds, closest));\n- },\n-\n- /** \n- * APIMethod: zoomToMaxExtent\n- * Zoom to the full extent and recenter.\n- *\n- * Parameters:\n- * options - {Object}\n- * \n- * Allowed Options:\n- * restricted - {Boolean} True to zoom to restricted extent if it is \n- * set. Defaults to true.\n- */\n- zoomToMaxExtent: function(options) {\n- //restricted is true by default\n- var restricted = (options) ? options.restricted : true;\n-\n- var maxExtent = this.getMaxExtent({\n- 'restricted': restricted\n- });\n- this.zoomToExtent(maxExtent);\n- },\n-\n- /** \n- * APIMethod: zoomToScale\n- * Zoom to a specified scale \n- * \n- * Parameters:\n- * scale - {float}\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified scale. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- * \n- */\n- zoomToScale: function(scale, closest) {\n- var res = OpenLayers.Util.getResolutionFromScale(scale,\n- this.baseLayer.units);\n-\n- var halfWDeg = (this.size.w * res) / 2;\n- var halfHDeg = (this.size.h * res) / 2;\n- var center = this.getCachedCenter();\n-\n- var extent = new OpenLayers.Bounds(center.lon - halfWDeg,\n- center.lat - halfHDeg,\n- center.lon + halfWDeg,\n- center.lat + halfHDeg);\n- this.zoomToExtent(extent, closest);\n- },\n-\n- /********************************************************/\n- /* */\n- /* Translation Functions */\n- /* */\n- /* The following functions translate between */\n- /* LonLat, LayerPx, and ViewPortPx */\n- /* */\n- /********************************************************/\n-\n- //\n- // TRANSLATION: LonLat <-> ViewPortPx\n- //\n-\n- /**\n- * Method: getLonLatFromViewPortPx\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view \n- * port <OpenLayers.Pixel>, translated into lon/lat\n- * by the current base layer.\n- */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if (this.baseLayer != null) {\n- lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);\n- }\n- return lonlat;\n- },\n-\n- /**\n- * APIMethod: getViewPortPxFromLonLat\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- * \n- * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in \n- * <OpenLayers.LonLat>, translated into view port \n- * pixels by the current base layer.\n- */\n- getViewPortPxFromLonLat: function(lonlat) {\n- var px = null;\n- if (this.baseLayer != null) {\n- px = this.baseLayer.getViewPortPxFromLonLat(lonlat);\n- }\n- return px;\n- },\n-\n- /**\n- * Method: getZoomTargetCenter\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen\n- * resolution - {Float} The resolution we want to get the center for\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} The location of the map center after the\n- * transformation described by the origin xy and the target resolution.\n- */\n- getZoomTargetCenter: function(xy, resolution) {\n- var lonlat = null,\n- size = this.getSize(),\n- deltaX = size.w / 2 - xy.x,\n- deltaY = xy.y - size.h / 2,\n- zoomPoint = this.getLonLatFromPixel(xy);\n- if (zoomPoint) {\n- lonlat = new OpenLayers.LonLat(\n- zoomPoint.lon + deltaX * resolution,\n- zoomPoint.lat + deltaY * resolution\n- );\n- }\n- return lonlat;\n- },\n-\n- //\n- // CONVENIENCE TRANSLATION FUNCTIONS FOR API\n- //\n-\n- /**\n- * APIMethod: getLonLatFromPixel\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with\n- * a 'x' and 'y' properties.\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given\n- * OpenLayers.Pixel, translated into lon/lat by the \n- * current base layer\n- */\n- getLonLatFromPixel: function(px) {\n- return this.getLonLatFromViewPortPx(px);\n- },\n-\n- /**\n- * APIMethod: getPixelFromLonLat\n- * Returns a pixel location given a map location. The map location is\n- * translated to an integer pixel location (in viewport pixel\n- * coordinates) by the current base layer.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>} A map location.\n- * \n- * Returns: \n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the \n- * <OpenLayers.LonLat> translated into view port pixels by the current\n- * base layer.\n- */\n- getPixelFromLonLat: function(lonlat) {\n- var px = this.getViewPortPxFromLonLat(lonlat);\n- px.x = Math.round(px.x);\n- px.y = Math.round(px.y);\n- return px;\n- },\n-\n- /**\n- * Method: getGeodesicPixelSize\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If\n- * not provided, the center pixel of the map viewport will be used.\n- * \n- * Returns:\n- * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.\n- */\n- getGeodesicPixelSize: function(px) {\n- var lonlat = px ? this.getLonLatFromPixel(px) : (\n- this.getCachedCenter() || new OpenLayers.LonLat(0, 0));\n- var res = this.getResolution();\n- var left = lonlat.add(-res / 2, 0);\n- var right = lonlat.add(res / 2, 0);\n- var bottom = lonlat.add(0, -res / 2);\n- var top = lonlat.add(0, res / 2);\n- var dest = new OpenLayers.Projection(\"EPSG:4326\");\n- var source = this.getProjectionObject() || dest;\n- if (!source.equals(dest)) {\n- left.transform(source, dest);\n- right.transform(source, dest);\n- bottom.transform(source, dest);\n- top.transform(source, dest);\n- }\n-\n- return new OpenLayers.Size(\n- OpenLayers.Util.distVincenty(left, right),\n- OpenLayers.Util.distVincenty(bottom, top)\n- );\n- },\n-\n-\n-\n- //\n- // TRANSLATION: ViewPortPx <-> LayerPx\n- //\n-\n- /**\n- * APIMethod: getViewPortPxFromLayerPx\n- * \n- * Parameters:\n- * layerPx - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel \n- * coordinates\n- */\n- getViewPortPxFromLayerPx: function(layerPx) {\n- var viewPortPx = null;\n- if (layerPx != null) {\n- var dX = this.layerContainerOriginPx.x;\n- var dY = this.layerContainerOriginPx.y;\n- viewPortPx = layerPx.add(dX, dY);\n- }\n- return viewPortPx;\n- },\n-\n- /**\n- * APIMethod: getLayerPxFromViewPortPx\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel \n- * coordinates\n- */\n- getLayerPxFromViewPortPx: function(viewPortPx) {\n- var layerPx = null;\n- if (viewPortPx != null) {\n- var dX = -this.layerContainerOriginPx.x;\n- var dY = -this.layerContainerOriginPx.y;\n- layerPx = viewPortPx.add(dX, dY);\n- if (isNaN(layerPx.x) || isNaN(layerPx.y)) {\n- layerPx = null;\n- }\n- }\n- return layerPx;\n- },\n-\n- //\n- // TRANSLATION: LonLat <-> LayerPx\n- //\n-\n- /**\n- * Method: getLonLatFromLayerPx\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>}\n- */\n- getLonLatFromLayerPx: function(px) {\n- //adjust for displacement of layerContainerDiv\n- px = this.getViewPortPxFromLayerPx(px);\n- return this.getLonLatFromViewPortPx(px);\n- },\n-\n- /**\n- * APIMethod: getLayerPxFromLonLat\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>} lonlat\n- *\n- * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in \n- * <OpenLayers.LonLat>, translated into layer pixels \n- * by the current base layer\n- */\n- getLayerPxFromLonLat: function(lonlat) {\n- //adjust for displacement of layerContainerDiv\n- var px = this.getPixelFromLonLat(lonlat);\n- return this.getLayerPxFromViewPortPx(px);\n- },\n-\n- /**\n- * Method: applyTransform\n- * Applies the given transform to the <layerContainerDiv>. This method has\n- * a 2-stage fallback from translate3d/scale3d via translate/scale to plain\n- * style.left/style.top, in which case no scaling is supported.\n- *\n- * Parameters:\n- * x - {Number} x parameter for the translation. Defaults to the x value of\n- * the map's <layerContainerOriginPx>\n- * y - {Number} y parameter for the translation. Defaults to the y value of\n- * the map's <layerContainerOriginPx>\n- * scale - {Number} scale. Defaults to 1 if not provided.\n- */\n- applyTransform: function(x, y, scale) {\n- scale = scale || 1;\n- var origin = this.layerContainerOriginPx,\n- needTransform = scale !== 1;\n- x = x || origin.x;\n- y = y || origin.y;\n-\n- var style = this.layerContainerDiv.style,\n- transform = this.applyTransform.transform,\n- template = this.applyTransform.template;\n-\n- if (transform === undefined) {\n- transform = OpenLayers.Util.vendorPrefix.style('transform');\n- this.applyTransform.transform = transform;\n- if (transform) {\n- // Try translate3d, but only if the viewPortDiv has a transform\n- // defined in a stylesheet\n- var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,\n- OpenLayers.Util.vendorPrefix.css('transform'));\n- if (!computedStyle || computedStyle !== 'none') {\n- template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];\n- style[transform] = [template[0], '0,0', template[1]].join('');\n- }\n- // If no transform is defined in the stylesheet or translate3d\n- // does not stick, use translate and scale\n- if (!template || !~style[transform].indexOf(template[0])) {\n- template = ['translate(', ') ', 'scale(', ')'];\n- }\n- this.applyTransform.template = template;\n- }\n- }\n-\n- // If we do 3d transforms, we always want to use them. If we do 2d\n- // transforms, we only use them when we need to.\n- if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {\n- // Our 2d transforms are combined with style.left and style.top, so\n- // adjust x and y values and set the origin as left and top\n- if (needTransform === true && template[0] === 'translate(') {\n- x -= origin.x;\n- y -= origin.y;\n- style.left = origin.x + 'px';\n- style.top = origin.y + 'px';\n- }\n- style[transform] = [\n- template[0], x, 'px,', y, 'px', template[1],\n- template[2], scale, ',', scale, template[3]\n- ].join('');\n- } else {\n- style.left = x + 'px';\n- style.top = y + 'px';\n- // We previously might have had needTransform, so remove transform\n- if (transform !== null) {\n- style[transform] = '';\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Map\"\n-});\n-\n-/**\n- * Constant: TILE_WIDTH\n- * {Integer} 256 Default tile width (unless otherwise specified)\n- */\n-OpenLayers.Map.TILE_WIDTH = 256;\n-/**\n- * Constant: TILE_HEIGHT\n- * {Integer} 256 Default tile height (unless otherwise specified)\n- */\n-OpenLayers.Map.TILE_HEIGHT = 256;\n-/* ======================================================================\n- OpenLayers/Layer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Map.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer\n- */\n-OpenLayers.Layer = OpenLayers.Class({\n-\n- /**\n- * APIProperty: id\n- * {String}\n- */\n- id: null,\n-\n- /** \n- * APIProperty: name\n- * {String}\n- */\n- name: null,\n-\n- /** \n- * APIProperty: div\n- * {DOMElement}\n- */\n- div: null,\n-\n- /**\n- * APIProperty: opacity\n- * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n- * is 1.\n- */\n- opacity: 1,\n-\n- /**\n- * APIProperty: alwaysInRange\n- * {Boolean} If a layer's display should not be scale-based, this should \n- * be set to true. This will cause the layer, as an overlay, to always \n- * be 'active', by always returning true from the calculateInRange() \n- * function. \n- * \n- * If not explicitly specified for a layer, its value will be \n- * determined on startup in initResolutions() based on whether or not \n- * any scale-specific properties have been set as options on the \n- * layer. If no scale-specific options have been set on the layer, we \n- * assume that it should always be in range.\n- * \n- * See #987 for more info.\n- */\n- alwaysInRange: null,\n-\n- /**\n- * Constant: RESOLUTION_PROPERTIES\n- * {Array} The properties that are used for calculating resolutions\n- * information.\n- */\n- RESOLUTION_PROPERTIES: [\n- 'scales', 'resolutions',\n- 'maxScale', 'minScale',\n- 'maxResolution', 'minResolution',\n- 'numZoomLevels', 'maxZoomLevel'\n- ],\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>}\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n- *\n- * Supported map event types:\n- * loadstart - Triggered when layer loading starts. When using a Vector \n- * layer with a Fixed or BBOX strategy, the event object includes \n- * a *filter* property holding the OpenLayers.Filter used when \n- * calling read on the protocol.\n- * loadend - Triggered when layer loading ends. When using a Vector layer\n- * with a Fixed or BBOX strategy, the event object includes a \n- * *response* property holding an OpenLayers.Protocol.Response object.\n- * visibilitychanged - Triggered when the layer's visibility property is\n- * changed, e.g. by turning the layer on or off in the layer switcher.\n- * Note that the actual visibility of the layer can also change if it\n- * gets out of range (see <calculateInRange>). If you also want to catch\n- * these cases, register for the map's 'changelayer' event instead.\n- * move - Triggered when layer moves (triggered with every mousemove\n- * during a drag).\n- * moveend - Triggered when layer is done moving, object passed as\n- * argument has a zoomChanged boolean property which tells that the\n- * zoom has changed.\n- * added - Triggered after the layer is added to a map. Listeners will\n- * receive an object with a *map* property referencing the map and a\n- * *layer* property referencing the layer.\n- * removed - Triggered after the layer is removed from the map. Listeners\n- * will receive an object with a *map* property referencing the map and\n- * a *layer* property referencing the layer.\n- */\n- events: null,\n-\n- /**\n- * APIProperty: map\n- * {<OpenLayers.Map>} This variable is set when the layer is added to \n- * the map, via the accessor function setMap().\n- */\n- map: null,\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Whether or not the layer is a base layer. This should be set \n- * individually by all subclasses. Default is false\n- */\n- isBaseLayer: false,\n-\n- /**\n- * Property: alpha\n- * {Boolean} The layer's images have an alpha channel. Default is false.\n- */\n- alpha: false,\n-\n- /** \n- * APIProperty: displayInLayerSwitcher\n- * {Boolean} Display the layer's name in the layer switcher. Default is\n- * true.\n- */\n- displayInLayerSwitcher: true,\n-\n- /**\n- * APIProperty: visibility\n- * {Boolean} The layer should be displayed in the map. Default is true.\n- */\n- visibility: true,\n-\n- /**\n- * APIProperty: attribution\n- * {String} Attribution string, displayed when an \n- * <OpenLayers.Control.Attribution> has been added to the map.\n- */\n- attribution: null,\n-\n- /** \n- * Property: inRange\n- * {Boolean} The current map resolution is within the layer's min/max \n- * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom \n- * changes.\n- */\n- inRange: false,\n-\n- /**\n- * Propery: imageSize\n- * {<OpenLayers.Size>} For layers with a gutter, the image is larger than \n- * the tile by twice the gutter in each dimension.\n- */\n- imageSize: null,\n-\n- // OPTIONS\n-\n- /** \n- * Property: options\n- * {Object} An optional object whose properties will be set on the layer.\n- * Any of the layer properties can be set as a property of the options\n- * object and sent to the constructor when the layer is created.\n- */\n- options: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- */\n- eventListeners: null,\n-\n- /**\n- * APIProperty: gutter\n- * {Integer} Determines the width (in pixels) of the gutter around image\n- * tiles to ignore. By setting this property to a non-zero value,\n- * images will be requested that are wider and taller than the tile\n- * size by a value of 2 x gutter. This allows artifacts of rendering\n- * at tile edges to be ignored. Set a gutter value that is equal to\n- * half the size of the widest symbol that needs to be displayed.\n- * Defaults to zero. Non-tiled layers always have zero gutter.\n- */\n- gutter: 0,\n-\n- /**\n- * APIProperty: projection\n- * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.\n- * Can be set in the layer options. If not specified in the layer options,\n- * it is set to the default projection specified in the map,\n- * when the layer is added to the map.\n- * Projection along with default maxExtent and resolutions\n- * are set automatically with commercial baselayers in EPSG:3857,\n- * such as Google, Bing and OpenStreetMap, and do not need to be specified.\n- * Otherwise, if specifying projection, also set maxExtent,\n- * maxResolution or resolutions as appropriate.\n- * When using vector layers with strategies, layer projection should be set\n- * to the projection of the source data if that is different from the map default.\n- * \n- * Can be either a string or an <OpenLayers.Projection> object;\n- * if a string is passed, will be converted to an object when\n- * the layer is added to the map.\n- * \n- */\n- projection: null,\n-\n- /**\n- * APIProperty: units\n- * {String} The layer map units. Defaults to null. Possible values\n- * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.\n- * Normally taken from the projection.\n- * Only required if both map and layers do not define a projection,\n- * or if they define a projection which does not define units.\n- */\n- units: null,\n-\n- /**\n- * APIProperty: scales\n- * {Array} An array of map scales in descending order. The values in the\n- * array correspond to the map scale denominator. Note that these\n- * values only make sense if the display (monitor) resolution of the\n- * client is correctly guessed by whomever is configuring the\n- * application. In addition, the units property must also be set.\n- * Use <resolutions> instead wherever possible.\n- */\n- scales: null,\n-\n- /**\n- * APIProperty: resolutions\n- * {Array} A list of map resolutions (map units per pixel) in descending\n- * order. If this is not set in the layer constructor, it will be set\n- * based on other resolution related properties (maxExtent,\n- * maxResolution, maxScale, etc.).\n- */\n- resolutions: null,\n-\n- /**\n- * APIProperty: maxExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The maximum extent for the layer. Defaults to null.\n- * \n- * The center of these bounds will not stray outside\n- * of the viewport extent during panning. In addition, if\n- * <displayOutsideMaxExtent> is set to false, data will not be\n- * requested that falls completely outside of these bounds.\n- */\n- maxExtent: null,\n-\n- /**\n- * APIProperty: minExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The minimum extent for the layer. Defaults to null.\n- */\n- minExtent: null,\n-\n- /**\n- * APIProperty: maxResolution\n- * {Float} Default max is 360 deg / 256 px, which corresponds to\n- * zoom level 0 on gmaps. Specify a different value in the layer \n- * options if you are not using the default <OpenLayers.Map.tileSize>\n- * and displaying the whole world.\n- */\n- maxResolution: null,\n-\n- /**\n- * APIProperty: minResolution\n- * {Float}\n- */\n- minResolution: null,\n-\n- /**\n- * APIProperty: numZoomLevels\n- * {Integer}\n- */\n- numZoomLevels: null,\n-\n- /**\n- * APIProperty: minScale\n- * {Float}\n- */\n- minScale: null,\n-\n- /**\n- * APIProperty: maxScale\n- * {Float}\n- */\n- maxScale: null,\n-\n- /**\n- * APIProperty: displayOutsideMaxExtent\n- * {Boolean} Request map tiles that are completely outside of the max \n- * extent for this layer. Defaults to false.\n- */\n- displayOutsideMaxExtent: false,\n-\n- /**\n- * APIProperty: wrapDateLine\n- * {Boolean} Wraps the world at the international dateline, so the map can\n- * be panned infinitely in longitudinal direction. Only use this on the\n- * base layer, and only if the layer's maxExtent equals the world bounds.\n- * #487 for more info. \n- */\n- wrapDateLine: false,\n-\n- /**\n- * Property: metadata\n- * {Object} This object can be used to store additional information on a\n- * layer object.\n- */\n- metadata: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer\n- *\n- * Parameters:\n- * name - {String} The layer name\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, options) {\n-\n- this.metadata = {};\n-\n- options = OpenLayers.Util.extend({}, options);\n- // make sure we respect alwaysInRange if set on the prototype\n- if (this.alwaysInRange != null) {\n- options.alwaysInRange = this.alwaysInRange;\n- }\n- this.addOptions(options);\n-\n- this.name = name;\n-\n- if (this.id == null) {\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n-\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.style.width = \"100%\";\n- this.div.style.height = \"100%\";\n- this.div.dir = \"ltr\";\n-\n- this.events = new OpenLayers.Events(this, this.div);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n-\n- }\n- },\n-\n- /**\n- * Method: destroy\n- * Destroy is a destructor: this is to alleviate cyclic references which\n- * the Javascript garbage cleaner can not take care of on its own.\n- *\n- * Parameters:\n- * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n- * been destroyed. Default is true.\n- */\n- destroy: function(setNewBaseLayer) {\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true;\n- }\n- if (this.map != null) {\n- this.map.removeLayer(this, setNewBaseLayer);\n- }\n- this.projection = null;\n- this.map = null;\n- this.name = null;\n- this.div = null;\n- this.options = null;\n-\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- }\n- this.eventListeners = null;\n- this.events = null;\n- },\n-\n- /**\n- * Method: clone\n- *\n- * Parameters:\n- * obj - {<OpenLayers.Layer>} The layer to be cloned\n- *\n- * Returns:\n- * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer(this.name, this.getOptions());\n- }\n-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- // a cloned layer should never have its map property set\n- // because it has not been added to a map yet. \n- obj.map = null;\n-\n- return obj;\n- },\n-\n- /**\n- * Method: getOptions\n- * Extracts an object from the layer with the properties that were set as\n- * options, but updates them with the values currently set on the\n- * instance.\n- * \n- * Returns:\n- * {Object} the <options> of the layer, representing the current state.\n- */\n- getOptions: function() {\n- var options = {};\n- for (var o in this.options) {\n- options[o] = this[o];\n- }\n- return options;\n- },\n-\n- /** \n- * APIMethod: setName\n- * Sets the new layer name for this layer. Can trigger a changelayer event\n- * on the map.\n- *\n- * Parameters:\n- * newName - {String} The new name.\n- */\n- setName: function(newName) {\n- if (newName != this.name) {\n- this.name = newName;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"name\"\n- });\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: addOptions\n- * \n- * Parameters:\n- * newOptions - {Object}\n- * reinitialize - {Boolean} If set to true, and if resolution options of the\n- * current baseLayer were changed, the map will be recentered to make\n- * sure that it is displayed with a valid resolution, and a\n- * changebaselayer event will be triggered.\n- */\n- addOptions: function(newOptions, reinitialize) {\n-\n- if (this.options == null) {\n- this.options = {};\n- }\n-\n- if (newOptions) {\n- // make sure this.projection references a projection object\n- if (typeof newOptions.projection == \"string\") {\n- newOptions.projection = new OpenLayers.Projection(newOptions.projection);\n- }\n- if (newOptions.projection) {\n- // get maxResolution, units and maxExtent from projection defaults if\n- // they are not defined already\n- OpenLayers.Util.applyDefaults(newOptions,\n- OpenLayers.Projection.defaults[newOptions.projection.getCode()]);\n- }\n- // allow array for extents\n- if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n- newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);\n- }\n- if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n- newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);\n- }\n- }\n-\n- // update our copy for clone\n- OpenLayers.Util.extend(this.options, newOptions);\n-\n- // add new options to this\n- OpenLayers.Util.extend(this, newOptions);\n-\n- // get the units from the projection, if we have a projection\n- // and it it has units\n- if (this.projection && this.projection.getUnits()) {\n- this.units = this.projection.getUnits();\n- }\n-\n- // re-initialize resolutions if necessary, i.e. if any of the\n- // properties of the \"properties\" array defined below is set\n- // in the new options\n- if (this.map) {\n- // store current resolution so we can try to restore it later\n- var resolution = this.map.getResolution();\n- var properties = this.RESOLUTION_PROPERTIES.concat(\n- [\"projection\", \"units\", \"minExtent\", \"maxExtent\"]\n- );\n- for (var o in newOptions) {\n- if (newOptions.hasOwnProperty(o) &&\n- OpenLayers.Util.indexOf(properties, o) >= 0) {\n-\n- this.initResolutions();\n- if (reinitialize && this.map.baseLayer === this) {\n- // update map position, and restore previous resolution\n- this.map.setCenter(this.map.getCenter(),\n- this.map.getZoomForResolution(resolution),\n- false, true\n- );\n- // trigger a changebaselayer event to make sure that\n- // all controls (especially\n- // OpenLayers.Control.PanZoomBar) get notified of the\n- // new options\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- });\n- }\n- break;\n- }\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: onMapResize\n- * This function can be implemented by subclasses\n- */\n- onMapResize: function() {\n- //this function can be implemented by subclasses \n- },\n-\n- /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n- *\n- * Returns:\n- * {Boolean} The layer was redrawn.\n- */\n- redraw: function() {\n- var redrawn = false;\n- if (this.map) {\n-\n- // min/max Range may have changed\n- this.inRange = this.calculateInRange();\n-\n- // map's center might not yet be set\n- var extent = this.getExtent();\n-\n- if (extent && this.inRange && this.visibility) {\n- var zoomChanged = true;\n- this.moveTo(extent, zoomChanged, false);\n- this.events.triggerEvent(\"moveend\", {\n- \"zoomChanged\": zoomChanged\n- });\n- redrawn = true;\n- }\n- }\n- return redrawn;\n- },\n-\n- /**\n- * Method: moveTo\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- var display = this.visibility;\n- if (!this.isBaseLayer) {\n- display = display && this.inRange;\n- }\n- this.display(display);\n- },\n-\n- /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector. To be implemented by subclasses.\n- *\n- * Parameters:\n- * dx - {Number} The x coord of the displacement vector.\n- * dy - {Number} The y coord of the displacement vector.\n- */\n- moveByPx: function(dx, dy) {},\n-\n- /**\n- * Method: setMap\n- * Set the map property for the layer. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- * \n- * Here we take care to bring over any of the necessary default \n- * properties from the map. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- setMap: function(map) {\n- if (this.map == null) {\n-\n- this.map = map;\n-\n- // grab some essential layer data from the map if it hasn't already\n- // been set\n- this.maxExtent = this.maxExtent || this.map.maxExtent;\n- this.minExtent = this.minExtent || this.map.minExtent;\n-\n- this.projection = this.projection || this.map.projection;\n- if (typeof this.projection == \"string\") {\n- this.projection = new OpenLayers.Projection(this.projection);\n- }\n-\n- // Check the projection to see if we can get units -- if not, refer\n- // to properties.\n- this.units = this.projection.getUnits() ||\n- this.units || this.map.units;\n-\n- this.initResolutions();\n-\n- if (!this.isBaseLayer) {\n- this.inRange = this.calculateInRange();\n- var show = ((this.visibility) && (this.inRange));\n- this.div.style.display = show ? \"\" : \"none\";\n- }\n-\n- // deal with gutters\n- this.setTileSize();\n- }\n- },\n-\n- /**\n- * Method: afterAdd\n- * Called at the end of the map.addLayer sequence. At this point, the map\n- * will have a base layer. To be overridden by subclasses.\n- */\n- afterAdd: function() {},\n-\n- /**\n- * APIMethod: removeMap\n- * Just as setMap() allows each layer the possibility to take a \n- * personalized action on being added to the map, removeMap() allows\n- * each layer to take a personalized action on being removed from it. \n- * For now, this will be mostly unused, except for the EventPane layer,\n- * which needs this hook so that it can remove the special invisible\n- * pane. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- //to be overridden by subclasses\n- },\n-\n- /**\n- * APIMethod: getImageSize\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used\n- * by subclasses that have to deal with different tile sizes at the\n- * layer extent edges (e.g. Zoomify)\n- * \n- * Returns:\n- * {<OpenLayers.Size>} The size that the image should be, taking into \n- * account gutters.\n- */\n- getImageSize: function(bounds) {\n- return (this.imageSize || this.tileSize);\n- },\n-\n- /**\n- * APIMethod: setTileSize\n- * Set the tile size based on the map size. This also sets layer.imageSize\n- * or use by Tile.Image.\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>}\n- */\n- setTileSize: function(size) {\n- var tileSize = (size) ? size :\n- ((this.tileSize) ? this.tileSize :\n- this.map.getTileSize());\n- this.tileSize = tileSize;\n- if (this.gutter) {\n- // layers with gutters need non-null tile sizes\n- //if(tileSize == null) {\n- // OpenLayers.console.error(\"Error in layer.setMap() for \" +\n- // this.name + \": layers with \" +\n- // \"gutters need non-null tile sizes\");\n- //}\n- this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter),\n- tileSize.h + (2 * this.gutter));\n- }\n- },\n-\n- /**\n- * APIMethod: getVisibility\n- * \n- * Returns:\n- * {Boolean} The layer should be displayed (if in range).\n- */\n- getVisibility: function() {\n- return this.visibility;\n- },\n-\n- /** \n- * APIMethod: setVisibility\n- * Set the visibility flag for the layer and hide/show & redraw \n- * accordingly. Fire event unless otherwise specified\n- * \n- * Note that visibility is no longer simply whether or not the layer's\n- * style.display is set to \"block\". Now we store a 'visibility' state \n- * property on the layer class, this allows us to remember whether or \n- * not we *desire* for a layer to be visible. In the case where the \n- * map's resolution is out of the layer's range, this desire may be \n- * subverted.\n- * \n- * Parameters:\n- * visibility - {Boolean} Whether or not to display the layer (if in range)\n- */\n- setVisibility: function(visibility) {\n- if (visibility != this.visibility) {\n- this.visibility = visibility;\n- this.display(visibility);\n- this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"visibility\"\n- });\n- }\n- this.events.triggerEvent(\"visibilitychanged\");\n- }\n- },\n-\n- /** \n- * APIMethod: display\n- * Hide or show the Layer. This is designed to be used internally, and \n- * is not generally the way to enable or disable the layer. For that,\n- * use the setVisibility function instead..\n- * \n- * Parameters:\n- * display - {Boolean}\n- */\n- display: function(display) {\n- if (display != (this.div.style.display != \"none\")) {\n- this.div.style.display = (display && this.calculateInRange()) ? \"block\" : \"none\";\n- }\n- },\n-\n- /**\n- * APIMethod: calculateInRange\n- * \n- * Returns:\n- * {Boolean} The layer is displayable at the current map's current\n- * resolution. Note that if 'alwaysInRange' is true for the layer, \n- * this function will always return true.\n- */\n- calculateInRange: function() {\n- var inRange = false;\n-\n- if (this.alwaysInRange) {\n- inRange = true;\n- } else {\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- inRange = ((resolution >= this.minResolution) &&\n- (resolution <= this.maxResolution));\n- }\n- }\n- return inRange;\n- },\n-\n- /** \n- * APIMethod: setIsBaseLayer\n- * \n- * Parameters:\n- * isBaseLayer - {Boolean}\n- */\n- setIsBaseLayer: function(isBaseLayer) {\n- if (isBaseLayer != this.isBaseLayer) {\n- this.isBaseLayer = isBaseLayer;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- });\n- }\n- }\n- },\n-\n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /********************************************************/\n-\n- /** \n- * Method: initResolutions\n- * This method's responsibility is to set up the 'resolutions' array \n- * for the layer -- this array is what the layer will use to interface\n- * between the zoom levels of the map and the resolution display \n- * of the layer.\n- * \n- * The user has several options that determine how the array is set up.\n- * \n- * For a detailed explanation, see the following wiki from the \n- * openlayers.org homepage:\n- * http://trac.openlayers.org/wiki/SettingZoomLevels\n- */\n- initResolutions: function() {\n-\n- // ok we want resolutions, here's our strategy:\n- //\n- // 1. if resolutions are defined in the layer config, use them\n- // 2. else, if scales are defined in the layer config then derive\n- // resolutions from these scales\n- // 3. else, attempt to calculate resolutions from maxResolution,\n- // minResolution, numZoomLevels, maxZoomLevel set in the\n- // layer config\n- // 4. if we still don't have resolutions, and if resolutions\n- // are defined in the same, use them\n- // 5. else, if scales are defined in the map then derive\n- // resolutions from these scales\n- // 6. else, attempt to calculate resolutions from maxResolution,\n- // minResolution, numZoomLevels, maxZoomLevel set in the\n- // map\n- // 7. hope for the best!\n-\n- var i, len, p;\n- var props = {},\n- alwaysInRange = true;\n-\n- // get resolution data from layer config\n- // (we also set alwaysInRange in the layer as appropriate)\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p];\n- if (alwaysInRange && this.options[p]) {\n- alwaysInRange = false;\n- }\n- }\n- if (this.options.alwaysInRange == null) {\n- this.alwaysInRange = alwaysInRange;\n- }\n-\n- // if we don't have resolutions then attempt to derive them from scales\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales);\n- }\n-\n- // if we still don't have resolutions then attempt to calculate them\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props);\n- }\n-\n- // if we couldn't calculate resolutions then we look at we have\n- // in the map\n- if (props.resolutions == null) {\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p] != null ?\n- this.options[p] : this.map[p];\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales);\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props);\n- }\n- }\n-\n- // ok, we new need to set properties in the instance\n-\n- // get maxResolution from the config if it's defined there\n- var maxResolution;\n- if (this.options.maxResolution &&\n- this.options.maxResolution !== \"auto\") {\n- maxResolution = this.options.maxResolution;\n- }\n- if (this.options.minScale) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(\n- this.options.minScale, this.units);\n- }\n-\n- // get minResolution from the config if it's defined there\n- var minResolution;\n- if (this.options.minResolution &&\n- this.options.minResolution !== \"auto\") {\n- minResolution = this.options.minResolution;\n- }\n- if (this.options.maxScale) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(\n- this.options.maxScale, this.units);\n- }\n-\n- if (props.resolutions) {\n-\n- //sort resolutions array descendingly\n- props.resolutions.sort(function(a, b) {\n- return (b - a);\n- });\n-\n- // if we still don't have a maxResolution get it from the\n- // resolutions array\n- if (!maxResolution) {\n- maxResolution = props.resolutions[0];\n- }\n-\n- // if we still don't have a minResolution get it from the\n- // resolutions array\n- if (!minResolution) {\n- var lastIdx = props.resolutions.length - 1;\n- minResolution = props.resolutions[lastIdx];\n- }\n- }\n-\n- this.resolutions = props.resolutions;\n- if (this.resolutions) {\n- len = this.resolutions.length;\n- this.scales = new Array(len);\n- for (i = 0; i < len; i++) {\n- this.scales[i] = OpenLayers.Util.getScaleFromResolution(\n- this.resolutions[i], this.units);\n- }\n- this.numZoomLevels = len;\n- }\n- this.minResolution = minResolution;\n- if (minResolution) {\n- this.maxScale = OpenLayers.Util.getScaleFromResolution(\n- minResolution, this.units);\n- }\n- this.maxResolution = maxResolution;\n- if (maxResolution) {\n- this.minScale = OpenLayers.Util.getScaleFromResolution(\n- maxResolution, this.units);\n- }\n- },\n-\n- /**\n- * Method: resolutionsFromScales\n- * Derive resolutions from scales.\n- *\n- * Parameters:\n- * scales - {Array(Number)} Scales\n- *\n- * Returns\n- * {Array(Number)} Resolutions\n- */\n- resolutionsFromScales: function(scales) {\n- if (scales == null) {\n- return;\n- }\n- var resolutions, i, len;\n- len = scales.length;\n- resolutions = new Array(len);\n- for (i = 0; i < len; i++) {\n- resolutions[i] = OpenLayers.Util.getResolutionFromScale(\n- scales[i], this.units);\n- }\n- return resolutions;\n- },\n-\n- /**\n- * Method: calculateResolutions\n- * Calculate resolutions based on the provided properties.\n- *\n- * Parameters:\n- * props - {Object} Properties\n- *\n- * Returns:\n- * {Array({Number})} Array of resolutions.\n- */\n- calculateResolutions: function(props) {\n-\n- var viewSize, wRes, hRes;\n-\n- // determine maxResolution\n- var maxResolution = props.maxResolution;\n- if (props.minScale != null) {\n- maxResolution =\n- OpenLayers.Util.getResolutionFromScale(props.minScale,\n- this.units);\n- } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.maxExtent.getWidth() / viewSize.w;\n- hRes = this.maxExtent.getHeight() / viewSize.h;\n- maxResolution = Math.max(wRes, hRes);\n- }\n-\n- // determine minResolution\n- var minResolution = props.minResolution;\n- if (props.maxScale != null) {\n- minResolution =\n- OpenLayers.Util.getResolutionFromScale(props.maxScale,\n- this.units);\n- } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.minExtent.getWidth() / viewSize.w;\n- hRes = this.minExtent.getHeight() / viewSize.h;\n- minResolution = Math.max(wRes, hRes);\n- }\n-\n- if (typeof maxResolution !== \"number\" &&\n- typeof minResolution !== \"number\" &&\n- this.maxExtent != null) {\n- // maxResolution for default grid sets assumes that at zoom\n- // level zero, the whole world fits on one tile.\n- var tileSize = this.map.getTileSize();\n- maxResolution = Math.max(\n- this.maxExtent.getWidth() / tileSize.w,\n- this.maxExtent.getHeight() / tileSize.h\n- );\n- }\n-\n- // determine numZoomLevels\n- var maxZoomLevel = props.maxZoomLevel;\n- var numZoomLevels = props.numZoomLevels;\n- if (typeof minResolution === \"number\" &&\n- typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n- var ratio = maxResolution / minResolution;\n- numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;\n- } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n- numZoomLevels = maxZoomLevel + 1;\n- }\n-\n- // are we able to calculate resolutions?\n- if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 ||\n- (typeof maxResolution !== \"number\" &&\n- typeof minResolution !== \"number\")) {\n- return;\n- }\n-\n- // now we have numZoomLevels and at least one of maxResolution\n- // or minResolution, we can populate the resolutions array\n-\n- var resolutions = new Array(numZoomLevels);\n- var base = 2;\n- if (typeof minResolution == \"number\" &&\n- typeof maxResolution == \"number\") {\n- // if maxResolution and minResolution are set, we calculate\n- // the base for exponential scaling that starts at\n- // maxResolution and ends at minResolution in numZoomLevels\n- // steps.\n- base = Math.pow(\n- (maxResolution / minResolution),\n- (1 / (numZoomLevels - 1))\n- );\n- }\n-\n- var i;\n- if (typeof maxResolution === \"number\") {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[i] = maxResolution / Math.pow(base, i);\n- }\n- } else {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[numZoomLevels - 1 - i] =\n- minResolution * Math.pow(base, i);\n- }\n- }\n-\n- return resolutions;\n- },\n-\n- /**\n- * APIMethod: getResolution\n- * \n- * Returns:\n- * {Float} The currently selected resolution of the map, taken from the\n- * resolutions array, indexed by current zoom level.\n- */\n- getResolution: function() {\n- var zoom = this.map.getZoom();\n- return this.getResolutionForZoom(zoom);\n- },\n-\n- /** \n- * APIMethod: getExtent\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n- * bounds of the current viewPort.\n- */\n- getExtent: function() {\n- // just use stock map calculateBounds function -- passing no arguments\n- // means it will user map's current center & resolution\n- //\n- return this.map.calculateBounds();\n- },\n-\n- /**\n- * APIMethod: getZoomForExtent\n- * \n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified bounds. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- *\n- * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * for the passed-in extent. We do this by calculating the ideal \n- * resolution for the given extent (based on the map size) and then \n- * calling getZoomForResolution(), passing along the 'closest'\n- * parameter.\n- */\n- getZoomForExtent: function(extent, closest) {\n- var viewSize = this.map.getSize();\n- var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n- extent.getHeight() / viewSize.h);\n-\n- return this.getZoomForResolution(idealResolution, closest);\n- },\n-\n- /** \n- * Method: getDataExtent\n- * Calculates the max extent which includes all of the data for the layer.\n- * This function is to be implemented by subclasses.\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getDataExtent: function() {\n- //to be implemented by subclasses\n- },\n-\n- /**\n- * APIMethod: getResolutionForZoom\n- * \n- * Parameters:\n- * zoom - {Float}\n- * \n- * Returns:\n- * {Float} A suitable resolution for the specified zoom.\n- */\n- getResolutionForZoom: function(zoom) {\n- zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n- var resolution;\n- if (this.map.fractionalZoom) {\n- var low = Math.floor(zoom);\n- var high = Math.ceil(zoom);\n- resolution = this.resolutions[low] -\n- ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n- } else {\n- resolution = this.resolutions[Math.round(zoom)];\n- }\n- return resolution;\n- },\n-\n- /**\n- * APIMethod: getZoomForResolution\n- * \n- * Parameters:\n- * resolution - {Float}\n- * closest - {Boolean} Find the zoom level that corresponds to the absolute \n- * closest resolution, which may result in a zoom whose corresponding\n- * resolution is actually smaller than we would have desired (if this\n- * is being called from a getZoomForExtent() call, then this means that\n- * the returned zoom index might not actually contain the entire \n- * extent specified... but it'll be close).\n- * Default is false.\n- * \n- * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * that corresponds to the best fit resolution given the passed in \n- * value and the 'closest' specification.\n- */\n- getZoomForResolution: function(resolution, closest) {\n- var zoom, i, len;\n- if (this.map.fractionalZoom) {\n- var lowZoom = 0;\n- var highZoom = this.resolutions.length - 1;\n- var highRes = this.resolutions[lowZoom];\n- var lowRes = this.resolutions[highZoom];\n- var res;\n- for (i = 0, len = this.resolutions.length; i < len; ++i) {\n- res = this.resolutions[i];\n- if (res >= resolution) {\n- highRes = res;\n- lowZoom = i;\n- }\n- if (res <= resolution) {\n- lowRes = res;\n- highZoom = i;\n- break;\n- }\n- }\n- var dRes = highRes - lowRes;\n- if (dRes > 0) {\n- zoom = lowZoom + ((highRes - resolution) / dRes);\n- } else {\n- zoom = lowZoom;\n- }\n- } else {\n- var diff;\n- var minDiff = Number.POSITIVE_INFINITY;\n- for (i = 0, len = this.resolutions.length; i < len; i++) {\n- if (closest) {\n- diff = Math.abs(this.resolutions[i] - resolution);\n- if (diff > minDiff) {\n- break;\n- }\n- minDiff = diff;\n- } else {\n- if (this.resolutions[i] < resolution) {\n- break;\n- }\n- }\n- }\n- zoom = Math.max(0, i - 1);\n- }\n- return zoom;\n- },\n-\n- /**\n- * APIMethod: getLonLatFromViewPortPx\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in \n- * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.\n- */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- var map = this.map;\n- if (viewPortPx != null && map.minPx) {\n- var res = map.getResolution();\n- var maxExtent = map.getMaxExtent({\n- restricted: true\n- });\n- var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n- var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n- lonlat = new OpenLayers.LonLat(lon, lat);\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n- }\n- }\n- return lonlat;\n- },\n-\n- /**\n- * APIMethod: getViewPortPxFromLonLat\n- * Returns a pixel location given a map location. This method will return\n- * fractional pixel values.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or\n- * an object with a 'lon'\n- * and 'lat' properties.\n- *\n- * Returns: \n- * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in \n- * lonlat translated into view port pixels.\n- */\n- getViewPortPxFromLonLat: function(lonlat, resolution) {\n- var px = null;\n- if (lonlat != null) {\n- resolution = resolution || this.map.getResolution();\n- var extent = this.map.calculateBounds(null, resolution);\n- px = new OpenLayers.Pixel(\n- (1 / resolution * (lonlat.lon - extent.left)),\n- (1 / resolution * (extent.top - lonlat.lat))\n- );\n- }\n- return px;\n- },\n-\n- /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n- * \n- * Parameters:\n- * opacity - {Float}\n- */\n- setOpacity: function(opacity) {\n- if (opacity != this.opacity) {\n- this.opacity = opacity;\n- var childNodes = this.div.childNodes;\n- for (var i = 0, len = childNodes.length; i < len; ++i) {\n- var element = childNodes[i].firstChild || childNodes[i];\n- var lastChild = childNodes[i].lastChild;\n- //TODO de-uglify this\n- if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n- element = lastChild.parentNode;\n- }\n- OpenLayers.Util.modifyDOMElement(element, null, null, null,\n- null, null, null, opacity);\n- }\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- });\n- }\n- }\n- },\n-\n- /**\n- * Method: getZIndex\n- * \n- * Returns: \n- * {Integer} the z-index of this layer\n- */\n- getZIndex: function() {\n- return this.div.style.zIndex;\n- },\n-\n- /**\n- * Method: setZIndex\n- * \n- * Parameters: \n- * zIndex - {Integer}\n- */\n- setZIndex: function(zIndex) {\n- this.div.style.zIndex = zIndex;\n- },\n-\n- /**\n- * Method: adjustBounds\n- * This function will take a bounds, and if wrapDateLine option is set\n- * on the layer, it will return a bounds which is wrapped around the \n- * world. We do not wrap for bounds which *cross* the \n- * maxExtent.left/right, only bounds which are entirely to the left \n- * or entirely to the right.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- adjustBounds: function(bounds) {\n-\n- if (this.gutter) {\n- // Adjust the extent of a bounds in map units by the \n- // layer's gutter in pixels.\n- var mapGutter = this.gutter * this.map.getResolution();\n- bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n- bounds.bottom - mapGutter,\n- bounds.right + mapGutter,\n- bounds.top + mapGutter);\n- }\n-\n- if (this.wrapDateLine) {\n- // wrap around the date line, within the limits of rounding error\n- var wrappingOptions = {\n- 'rightTolerance': this.getResolution(),\n- 'leftTolerance': this.getResolution()\n- };\n- bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n-\n- }\n- return bounds;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer\"\n-});\n-/* ======================================================================\n- OpenLayers/Request/XMLHttpRequest.js\n- ====================================================================== */\n-\n-// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-// http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-/**\n- * @requires OpenLayers/Request.js\n- */\n-\n-(function() {\n-\n- // Save reference to earlier defined object implementation (if any)\n- var oXMLHttpRequest = window.XMLHttpRequest;\n-\n- // Define on browser type\n- var bGecko = !!window.controllers,\n- bIE = window.document.all && !window.opera,\n- bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n-\n- // Enables \"XMLHttpRequest()\" call next to \"new XMLHttpReques()\"\n- function fXMLHttpRequest() {\n- this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n- this._listeners = [];\n- };\n-\n- // Constructor\n- function cXMLHttpRequest() {\n- return new fXMLHttpRequest;\n- };\n- cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n-\n- // BUGFIX: Firefox with Firebug installed would break pages if not executed\n- if (bGecko && oXMLHttpRequest.wrapped)\n- cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n-\n- // Constants\n- cXMLHttpRequest.UNSENT = 0;\n- cXMLHttpRequest.OPENED = 1;\n- cXMLHttpRequest.HEADERS_RECEIVED = 2;\n- cXMLHttpRequest.LOADING = 3;\n- cXMLHttpRequest.DONE = 4;\n-\n- // Public Properties\n- cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n- cXMLHttpRequest.prototype.responseText = '';\n- cXMLHttpRequest.prototype.responseXML = null;\n- cXMLHttpRequest.prototype.status = 0;\n- cXMLHttpRequest.prototype.statusText = '';\n-\n- // Priority proposal\n- cXMLHttpRequest.prototype.priority = \"NORMAL\";\n-\n- // Instance-level Events Handlers\n- cXMLHttpRequest.prototype.onreadystatechange = null;\n-\n- // Class-level Events Handlers\n- cXMLHttpRequest.onreadystatechange = null;\n- cXMLHttpRequest.onopen = null;\n- cXMLHttpRequest.onsend = null;\n- cXMLHttpRequest.onabort = null;\n-\n- // Public Methods\n- cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n- // Delete headers, required when object is reused\n- delete this._headers;\n-\n- // When bAsync parameter value is omitted, use true as default\n- if (arguments.length < 3)\n- bAsync = true;\n-\n- // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests\n- this._async = bAsync;\n-\n- // Set the onreadystatechange handler\n- var oRequest = this,\n- nState = this.readyState,\n- fOnUnload;\n-\n- // BUGFIX: IE - memory leak on page unload (inter-page leak)\n- if (bIE && bAsync) {\n- fOnUnload = function() {\n- if (nState != cXMLHttpRequest.DONE) {\n- fCleanTransport(oRequest);\n- // Safe to abort here since onreadystatechange handler removed\n- oRequest.abort();\n- }\n- };\n- window.attachEvent(\"onunload\", fOnUnload);\n- }\n-\n- // Add method sniffer\n- if (cXMLHttpRequest.onopen)\n- cXMLHttpRequest.onopen.apply(this, arguments);\n-\n- if (arguments.length > 4)\n- this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else\n- if (arguments.length > 3)\n- this._object.open(sMethod, sUrl, bAsync, sUser);\n- else\n- this._object.open(sMethod, sUrl, bAsync);\n-\n- this.readyState = cXMLHttpRequest.OPENED;\n- fReadyStateChange(this);\n-\n- this._object.onreadystatechange = function() {\n- if (bGecko && !bAsync)\n- return;\n-\n- // Synchronize state\n- oRequest.readyState = oRequest._object.readyState;\n-\n- //\n- fSynchronizeValues(oRequest);\n-\n- // BUGFIX: Firefox fires unnecessary DONE when aborting\n- if (oRequest._aborted) {\n- // Reset readyState to UNSENT\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n-\n- // Return now\n- return;\n- }\n-\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- // Free up queue\n- delete oRequest._data;\n- /* if (bAsync)\n- fQueue_remove(oRequest);*/\n- //\n- fCleanTransport(oRequest);\n- // Uncomment this block if you need a fix for IE cache\n- /*\n- // BUGFIX: IE - cache issue\n- if (!oRequest._object.getResponseHeader(\"Date\")) {\n- // Save object to cache\n- oRequest._cached = oRequest._object;\n-\n- // Instantiate a new transport object\n- cXMLHttpRequest.call(oRequest);\n-\n- // Re-send request\n- if (sUser) {\n- if (sPassword)\n- oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else\n- oRequest._object.open(sMethod, sUrl, bAsync, sUser);\n- }\n- else\n- oRequest._object.open(sMethod, sUrl, bAsync);\n- oRequest._object.setRequestHeader(\"If-Modified-Since\", oRequest._cached.getResponseHeader(\"Last-Modified\") || new window.Date(0));\n- // Copy headers set\n- if (oRequest._headers)\n- for (var sHeader in oRequest._headers)\n- if (typeof oRequest._headers[sHeader] == \"string\") // Some frameworks prototype objects with functions\n- oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);\n-\n- oRequest._object.onreadystatechange = function() {\n- // Synchronize state\n- oRequest.readyState = oRequest._object.readyState;\n-\n- if (oRequest._aborted) {\n- //\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n-\n- // Return\n- return;\n- }\n-\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- // Clean Object\n- fCleanTransport(oRequest);\n-\n- // get cached request\n- if (oRequest.status == 304)\n- oRequest._object = oRequest._cached;\n-\n- //\n- delete oRequest._cached;\n-\n- //\n- fSynchronizeValues(oRequest);\n-\n- //\n- fReadyStateChange(oRequest);\n-\n- // BUGFIX: IE - memory leak in interrupted\n- if (bIE && bAsync)\n- window.detachEvent(\"onunload\", fOnUnload);\n- }\n- };\n- oRequest._object.send(null);\n-\n- // Return now - wait until re-sent request is finished\n- return;\n- };\n- */\n- // BUGFIX: IE - memory leak in interrupted\n- if (bIE && bAsync)\n- window.detachEvent(\"onunload\", fOnUnload);\n- }\n-\n- // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice\n- if (nState != oRequest.readyState)\n- fReadyStateChange(oRequest);\n-\n- nState = oRequest.readyState;\n- }\n- };\n-\n- function fXMLHttpRequest_send(oRequest) {\n- oRequest._object.send(oRequest._data);\n-\n- // BUGFIX: Gecko - missing readystatechange calls in synchronous requests\n- if (bGecko && !oRequest._async) {\n- oRequest.readyState = cXMLHttpRequest.OPENED;\n-\n- // Synchronize state\n- fSynchronizeValues(oRequest);\n-\n- // Simulate missing states\n- while (oRequest.readyState < cXMLHttpRequest.DONE) {\n- oRequest.readyState++;\n- fReadyStateChange(oRequest);\n- // Check if we are aborted\n- if (oRequest._aborted)\n- return;\n- }\n- }\n- };\n- cXMLHttpRequest.prototype.send = function(vData) {\n- // Add method sniffer\n- if (cXMLHttpRequest.onsend)\n- cXMLHttpRequest.onsend.apply(this, arguments);\n-\n- if (!arguments.length)\n- vData = null;\n-\n- // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required\n- // BUGFIX: IE - rewrites any custom mime-type to \"text/xml\" in case an XMLNode is sent\n- // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)\n- if (vData && vData.nodeType) {\n- vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;\n- if (!this._headers[\"Content-Type\"])\n- this._object.setRequestHeader(\"Content-Type\", \"application/xml\");\n- }\n-\n- this._data = vData;\n- /*\n- // Add to queue\n- if (this._async)\n- fQueue_add(this);\n- else*/\n- fXMLHttpRequest_send(this);\n- };\n- cXMLHttpRequest.prototype.abort = function() {\n- // Add method sniffer\n- if (cXMLHttpRequest.onabort)\n- cXMLHttpRequest.onabort.apply(this, arguments);\n-\n- // BUGFIX: Gecko - unnecessary DONE when aborting\n- if (this.readyState > cXMLHttpRequest.UNSENT)\n- this._aborted = true;\n-\n- this._object.abort();\n-\n- // BUGFIX: IE - memory leak\n- fCleanTransport(this);\n-\n- this.readyState = cXMLHttpRequest.UNSENT;\n-\n- delete this._data;\n- /* if (this._async)\n- fQueue_remove(this);*/\n- };\n- cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n- return this._object.getAllResponseHeaders();\n- };\n- cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n- return this._object.getResponseHeader(sName);\n- };\n- cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n- // BUGFIX: IE - cache issue\n- if (!this._headers)\n- this._headers = {};\n- this._headers[sName] = sValue;\n-\n- return this._object.setRequestHeader(sName, sValue);\n- };\n-\n- // EventTarget interface implementation\n- cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n- return;\n- // Add listener\n- this._listeners.push([sName, fHandler, bUseCapture]);\n- };\n-\n- cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n- break;\n- // Remove listener\n- if (oListener)\n- this._listeners.splice(nIndex, 1);\n- };\n-\n- cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n- var oEventPseudo = {\n- 'type': oEvent.type,\n- 'target': this,\n- 'currentTarget': this,\n- 'eventPhase': 2,\n- 'bubbles': oEvent.bubbles,\n- 'cancelable': oEvent.cancelable,\n- 'timeStamp': oEvent.timeStamp,\n- 'stopPropagation': function() {}, // There is no flow\n- 'preventDefault': function() {}, // There is no default action\n- 'initEvent': function() {} // Original event object should be initialized\n- };\n-\n- // Execute onreadystatechange\n- if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)\n- (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n-\n- // Execute listeners\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == oEventPseudo.type && !oListener[2])\n- (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);\n- };\n-\n- //\n- cXMLHttpRequest.prototype.toString = function() {\n- return '[' + \"object\" + ' ' + \"XMLHttpRequest\" + ']';\n- };\n-\n- cXMLHttpRequest.toString = function() {\n- return '[' + \"XMLHttpRequest\" + ']';\n- };\n-\n- // Helper function\n- function fReadyStateChange(oRequest) {\n- // Sniffing code\n- if (cXMLHttpRequest.onreadystatechange)\n- cXMLHttpRequest.onreadystatechange.apply(oRequest);\n-\n- // Fake event\n- oRequest.dispatchEvent({\n- 'type': \"readystatechange\",\n- 'bubbles': false,\n- 'cancelable': false,\n- 'timeStamp': new Date + 0\n- });\n- };\n-\n- function fGetDocument(oRequest) {\n- var oDocument = oRequest.responseXML,\n- sResponse = oRequest.responseText;\n- // Try parsing responseText\n- if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n- oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n- oDocument.async = false;\n- oDocument.validateOnParse = false;\n- oDocument.loadXML(sResponse);\n- }\n- // Check if there is no error in document\n- if (oDocument)\n- if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\"))\n- return null;\n- return oDocument;\n- };\n-\n- function fSynchronizeValues(oRequest) {\n- try {\n- oRequest.responseText = oRequest._object.responseText;\n- } catch (e) {}\n- try {\n- oRequest.responseXML = fGetDocument(oRequest._object);\n- } catch (e) {}\n- try {\n- oRequest.status = oRequest._object.status;\n- } catch (e) {}\n- try {\n- oRequest.statusText = oRequest._object.statusText;\n- } catch (e) {}\n- };\n-\n- function fCleanTransport(oRequest) {\n- // BUGFIX: IE - memory leak (on-page leak)\n- oRequest._object.onreadystatechange = new window.Function;\n- };\n- /*\n- // Queue manager\n- var oQueuePending = {\"CRITICAL\":[],\"HIGH\":[],\"NORMAL\":[],\"LOW\":[],\"LOWEST\":[]},\n- aQueueRunning = [];\n- function fQueue_add(oRequest) {\n- oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : \"NORMAL\"].push(oRequest);\n- //\n- setTimeout(fQueue_process);\n- };\n-\n- function fQueue_remove(oRequest) {\n- for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)\n- if (bFound)\n- aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];\n- else\n- if (aQueueRunning[nIndex] == oRequest)\n- bFound = true;\n- if (bFound)\n- aQueueRunning.length--;\n- //\n- setTimeout(fQueue_process);\n- };\n-\n- function fQueue_process() {\n- if (aQueueRunning.length < 6) {\n- for (var sPriority in oQueuePending) {\n- if (oQueuePending[sPriority].length) {\n- var oRequest = oQueuePending[sPriority][0];\n- oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);\n- //\n- aQueueRunning.push(oRequest);\n- // Send request\n- fXMLHttpRequest_send(oRequest);\n- break;\n- }\n- }\n- }\n- };\n- */\n- // Internet Explorer 5.0 (missing apply)\n- if (!window.Function.prototype.apply) {\n- window.Function.prototype.apply = function(oRequest, oArguments) {\n- if (!oArguments)\n- oArguments = [];\n- oRequest.__func = this;\n- oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n- delete oRequest.__func;\n- };\n- };\n-\n- // Register new object with window\n- /**\n- * Class: OpenLayers.Request.XMLHttpRequest\n- * Standard-compliant (W3C) cross-browser implementation of the\n- * XMLHttpRequest object. From\n- * http://code.google.com/p/xmlhttprequest/.\n- */\n- if (!OpenLayers.Request) {\n- /**\n- * This allows for OpenLayers/Request.js to be included\n- * before or after this script.\n- */\n- OpenLayers.Request = {};\n- }\n- OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;\n-})();\n-/* ======================================================================\n- OpenLayers/Request.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * TODO: deprecate me\n- * Use OpenLayers.Request.proxy instead.\n- */\n-OpenLayers.ProxyHost = \"\";\n-\n-/**\n- * Namespace: OpenLayers.Request\n- * The OpenLayers.Request namespace contains convenience methods for working\n- * with XMLHttpRequests. These methods work with a cross-browser\n- * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.\n- */\n-if (!OpenLayers.Request) {\n- /**\n- * This allows for OpenLayers/Request/XMLHttpRequest.js to be included\n- * before or after this script.\n- */\n- OpenLayers.Request = {};\n-}\n-OpenLayers.Util.extend(OpenLayers.Request, {\n-\n- /**\n- * Constant: DEFAULT_CONFIG\n- * {Object} Default configuration for all requests.\n- */\n- DEFAULT_CONFIG: {\n- method: \"GET\",\n- url: window.location.href,\n- async: true,\n- user: undefined,\n- password: undefined,\n- params: null,\n- proxy: OpenLayers.ProxyHost,\n- headers: {},\n- data: null,\n- callback: function() {},\n- success: null,\n- failure: null,\n- scope: null\n- },\n-\n- /**\n- * Constant: URL_SPLIT_REGEX\n- */\n- URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the {<OpenLayers.Request>} object.\n- *\n- * All event listeners will receive an event object with three properties:\n- * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.\n- * config - {Object} The config object sent to the specific request method.\n- * requestUrl - {String} The request url.\n- * \n- * Supported event types:\n- * complete - Triggered when we have a response from the request, if a\n- * listener returns false, no further response processing will take\n- * place.\n- * success - Triggered when the HTTP response has a success code (200-299).\n- * failure - Triggered when the HTTP response does not have a success code.\n- */\n- events: new OpenLayers.Events(this),\n-\n- /**\n- * Method: makeSameOrigin\n- * Using the specified proxy, returns a same origin url of the provided url.\n- *\n- * Parameters:\n- * url - {String} An arbitrary url\n- * proxy {String|Function} The proxy to use to make the provided url a\n- * same origin url.\n- *\n- * Returns\n- * {String} the same origin url. If no proxy is provided, the returned url\n- * will be the same as the provided url.\n- */\n- makeSameOrigin: function(url, proxy) {\n- var sameOrigin = url.indexOf(\"http\") !== 0;\n- var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n- if (urlParts) {\n- var location = window.location;\n- sameOrigin =\n- urlParts[1] == location.protocol &&\n- urlParts[3] == location.hostname;\n- var uPort = urlParts[4],\n- lPort = location.port;\n- if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n- sameOrigin = sameOrigin && uPort == lPort;\n- }\n- }\n- if (!sameOrigin) {\n- if (proxy) {\n- if (typeof proxy == \"function\") {\n- url = proxy(url);\n- } else {\n- url = proxy + encodeURIComponent(url);\n- }\n- }\n- }\n- return url;\n- },\n-\n- /**\n- * APIMethod: issue\n- * Create a new XMLHttpRequest object, open it, set any headers, bind\n- * a callback to done state, and send any data. It is recommended that\n- * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.\n- * This method is only documented to provide detail on the configuration\n- * options available to all request methods.\n- *\n- * Parameters:\n- * config - {Object} Object containing properties for configuring the\n- * request. Allowed configuration properties are described below.\n- * This object is modified and should not be reused.\n- *\n- * Allowed config properties:\n- * method - {String} One of GET, POST, PUT, DELETE, HEAD, or\n- * OPTIONS. Default is GET.\n- * url - {String} URL for the request.\n- * async - {Boolean} Open an asynchronous request. Default is true.\n- * user - {String} User for relevant authentication scheme. Set\n- * to null to clear current user.\n- * password - {String} Password for relevant authentication scheme.\n- * Set to null to clear current password.\n- * proxy - {String} Optional proxy. Defaults to\n- * <OpenLayers.ProxyHost>.\n- * params - {Object} Any key:value pairs to be appended to the\n- * url as a query string. Assumes url doesn't already include a query\n- * string or hash. Typically, this is only appropriate for <GET>\n- * requests where the query string will be appended to the url.\n- * Parameter values that are arrays will be\n- * concatenated with a comma (note that this goes against form-encoding)\n- * as is done with <OpenLayers.Util.getParameterString>.\n- * headers - {Object} Object with header:value pairs to be set on\n- * the request.\n- * data - {String | Document} Optional data to send with the request.\n- * Typically, this is only used with <POST> and <PUT> requests.\n- * Make sure to provide the appropriate \"Content-Type\" header for your\n- * data. For <POST> and <PUT> requests, the content type defaults to\n- * \"application-xml\". If your data is a different content type, or\n- * if you are using a different HTTP method, set the \"Content-Type\"\n- * header to match your data type.\n- * callback - {Function} Function to call when request is done.\n- * To determine if the request failed, check request.status (200\n- * indicates success).\n- * success - {Function} Optional function to call if request status is in\n- * the 200s. This will be called in addition to callback above and\n- * would typically only be used as an alternative.\n- * failure - {Function} Optional function to call if request status is not\n- * in the 200s. This will be called in addition to callback above and\n- * would typically only be used as an alternative.\n- * scope - {Object} If callback is a public method on some object,\n- * set the scope to that object.\n- *\n- * Returns:\n- * {XMLHttpRequest} Request object. To abort the request before a response\n- * is received, call abort() on the request object.\n- */\n- issue: function(config) {\n- // apply default config - proxy host may have changed\n- var defaultConfig = OpenLayers.Util.extend(\n- this.DEFAULT_CONFIG, {\n- proxy: OpenLayers.ProxyHost\n- }\n- );\n- config = config || {};\n- config.headers = config.headers || {};\n- config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n- config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n- // Always set the \"X-Requested-With\" header to signal that this request\n- // was issued through the XHR-object. Since header keys are case \n- // insensitive and we want to allow overriding of the \"X-Requested-With\"\n- // header through the user we cannot use applyDefaults, but have to \n- // check manually whether we were called with a \"X-Requested-With\"\n- // header.\n- var customRequestedWithHeader = false,\n- headerKey;\n- for (headerKey in config.headers) {\n- if (config.headers.hasOwnProperty(headerKey)) {\n- if (headerKey.toLowerCase() === 'x-requested-with') {\n- customRequestedWithHeader = true;\n- }\n- }\n- }\n- if (customRequestedWithHeader === false) {\n- // we did not have a custom \"X-Requested-With\" header\n- config.headers['X-Requested-With'] = 'XMLHttpRequest';\n- }\n-\n- // create request, open, and set headers\n- var request = new OpenLayers.Request.XMLHttpRequest();\n- var url = OpenLayers.Util.urlAppend(config.url,\n- OpenLayers.Util.getParameterString(config.params || {}));\n- url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n- request.open(\n- config.method, url, config.async, config.user, config.password\n- );\n- for (var header in config.headers) {\n- request.setRequestHeader(header, config.headers[header]);\n- }\n-\n- var events = this.events;\n-\n- // we want to execute runCallbacks with \"this\" as the\n- // execution scope\n- var self = this;\n-\n- request.onreadystatechange = function() {\n- if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n- var proceed = events.triggerEvent(\n- \"complete\", {\n- request: request,\n- config: config,\n- requestUrl: url\n- }\n- );\n- if (proceed !== false) {\n- self.runCallbacks({\n- request: request,\n- config: config,\n- requestUrl: url\n- });\n- }\n- }\n- };\n-\n- // send request (optionally with data) and return\n- // call in a timeout for asynchronous requests so the return is\n- // available before readyState == 4 for cached docs\n- if (config.async === false) {\n- request.send(config.data);\n- } else {\n- window.setTimeout(function() {\n- if (request.readyState !== 0) { // W3C: 0-UNSENT\n- request.send(config.data);\n- }\n- }, 0);\n- }\n- return request;\n- },\n-\n- /**\n- * Method: runCallbacks\n- * Calls the complete, success and failure callbacks. Application\n- * can listen to the \"complete\" event, have the listener \n- * display a confirm window and always return false, and\n- * execute OpenLayers.Request.runCallbacks if the user\n- * hits \"yes\" in the confirm window.\n- *\n- * Parameters:\n- * options - {Object} Hash containing request, config and requestUrl keys\n- */\n- runCallbacks: function(options) {\n- var request = options.request;\n- var config = options.config;\n-\n- // bind callbacks to readyState 4 (done)\n- var complete = (config.scope) ?\n- OpenLayers.Function.bind(config.callback, config.scope) :\n- config.callback;\n-\n- // optional success callback\n- var success;\n- if (config.success) {\n- success = (config.scope) ?\n- OpenLayers.Function.bind(config.success, config.scope) :\n- config.success;\n- }\n-\n- // optional failure callback\n- var failure;\n- if (config.failure) {\n- failure = (config.scope) ?\n- OpenLayers.Function.bind(config.failure, config.scope) :\n- config.failure;\n- }\n-\n- if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" &&\n- request.responseText) {\n- request.status = 200;\n- }\n- complete(request);\n-\n- if (!request.status || (request.status >= 200 && request.status < 300)) {\n- this.events.triggerEvent(\"success\", options);\n- if (success) {\n- success(request);\n- }\n- }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"failure\", options);\n- if (failure) {\n- failure(request);\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: GET\n- * Send an HTTP GET request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to GET.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- GET: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"GET\"\n- });\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: POST\n- * Send a POST request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to POST and \"Content-Type\" header set to \"application/xml\".\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties. The\n- * default \"Content-Type\" header will be set to \"application-xml\" if\n- * none is provided. This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- POST: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"POST\"\n- });\n- // set content type to application/xml if it isn't already set\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\";\n- }\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: PUT\n- * Send an HTTP PUT request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to PUT and \"Content-Type\" header set to \"application/xml\".\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties. The\n- * default \"Content-Type\" header will be set to \"application-xml\" if\n- * none is provided. This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- PUT: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"PUT\"\n- });\n- // set content type to application/xml if it isn't already set\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\";\n- }\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: DELETE\n- * Send an HTTP DELETE request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to DELETE.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- DELETE: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"DELETE\"\n- });\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: HEAD\n- * Send an HTTP HEAD request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to HEAD.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- HEAD: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"HEAD\"\n- });\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: OPTIONS\n- * Send an HTTP OPTIONS request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to OPTIONS.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- OPTIONS: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"OPTIONS\"\n- });\n- return OpenLayers.Request.issue(config);\n- }\n-\n-});\n-/* ======================================================================\n- OpenLayers/Icon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Icon\n- * \n- * The icon represents a graphical icon on the screen. Typically used in\n- * conjunction with a <OpenLayers.Marker> to represent markers on a screen.\n- *\n- * An icon has a url, size and position. It also contains an offset which \n- * allows the center point to be represented correctly. This can be\n- * provided either as a fixed offset or a function provided to calculate\n- * the desired offset. \n- * \n- */\n-OpenLayers.Icon = OpenLayers.Class({\n-\n- /** \n- * Property: url \n- * {String} image url\n- */\n- url: null,\n-\n- /** \n- * Property: size \n- * {<OpenLayers.Size>|Object} An OpenLayers.Size or\n- * an object with a 'w' and 'h' properties.\n- */\n- size: null,\n-\n- /** \n- * Property: offset \n- * {<OpenLayers.Pixel>|Object} distance in pixels to offset the\n- * image when being rendered. An OpenLayers.Pixel or an object\n- * with a 'x' and 'y' properties.\n- */\n- offset: null,\n-\n- /** \n- * Property: calculateOffset \n- * {Function} Function to calculate the offset (based on the size)\n- */\n- calculateOffset: null,\n-\n- /** \n- * Property: imageDiv \n- * {DOMElement} \n- */\n- imageDiv: null,\n-\n- /** \n- * Property: px \n- * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object\n- * with a 'x' and 'y' properties.\n- */\n- px: null,\n-\n- /** \n- * Constructor: OpenLayers.Icon\n- * Creates an icon, which is an image tag in a div. \n- *\n- * url - {String} \n- * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an\n- * object with a 'w' and 'h'\n- * properties.\n- * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n- * object with a 'x' and 'y'\n- * properties.\n- * calculateOffset - {Function} \n- */\n- initialize: function(url, size, offset, calculateOffset) {\n- this.url = url;\n- this.size = size || {\n- w: 20,\n- h: 20\n- };\n- this.offset = offset || {\n- x: -(this.size.w / 2),\n- y: -(this.size.h / 2)\n- };\n- this.calculateOffset = calculateOffset;\n-\n- var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n- this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);\n- },\n-\n- /** \n- * Method: destroy\n- * Nullify references and remove event listeners to prevent circular \n- * references and memory leaks\n- */\n- destroy: function() {\n- // erase any drawn elements\n- this.erase();\n-\n- OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n- this.imageDiv.innerHTML = \"\";\n- this.imageDiv = null;\n- },\n-\n- /** \n- * Method: clone\n- * \n- * Returns:\n- * {<OpenLayers.Icon>} A fresh copy of the icon.\n- */\n- clone: function() {\n- return new OpenLayers.Icon(this.url,\n- this.size,\n- this.offset,\n- this.calculateOffset);\n- },\n-\n- /**\n- * Method: setSize\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or\n- * an object with a 'w' and 'h' properties.\n- */\n- setSize: function(size) {\n- if (size != null) {\n- this.size = size;\n- }\n- this.draw();\n- },\n-\n- /**\n- * Method: setUrl\n- * \n- * Parameters:\n- * url - {String} \n- */\n- setUrl: function(url) {\n- if (url != null) {\n- this.url = url;\n- }\n- this.draw();\n- },\n-\n- /** \n- * Method: draw\n- * Move the div to the given pixel.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n- * object with a 'x' and 'y' properties.\n- * \n- * Returns:\n- * {DOMElement} A new DOM Image of this icon set at the location passed-in\n- */\n- draw: function(px) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,\n- null,\n- null,\n- this.size,\n- this.url,\n- \"absolute\");\n- this.moveTo(px);\n- return this.imageDiv;\n- },\n-\n- /** \n- * Method: erase\n- * Erase the underlying image element.\n- */\n- erase: function() {\n- if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n- OpenLayers.Element.remove(this.imageDiv);\n- }\n- },\n-\n- /** \n- * Method: setOpacity\n- * Change the icon's opacity\n- *\n- * Parameters:\n- * opacity - {float} \n- */\n- setOpacity: function(opacity) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,\n- null, null, null, null, opacity);\n-\n- },\n-\n- /**\n- * Method: moveTo\n- * move icon to passed in px.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n- * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n- */\n- moveTo: function(px) {\n- //if no px passed in, use stored location\n- if (px != null) {\n- this.px = px;\n- }\n-\n- if (this.imageDiv != null) {\n- if (this.px == null) {\n- this.display(false);\n- } else {\n- if (this.calculateOffset) {\n- this.offset = this.calculateOffset(this.size);\n- }\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n- x: this.px.x + this.offset.x,\n- y: this.px.y + this.offset.y\n- });\n- }\n- }\n- },\n-\n- /** \n- * Method: display\n- * Hide or show the icon\n- *\n- * Parameters:\n- * display - {Boolean} \n- */\n- display: function(display) {\n- this.imageDiv.style.display = (display) ? \"\" : \"none\";\n- },\n-\n-\n- /**\n- * APIMethod: isDrawn\n- * \n- * Returns:\n- * {Boolean} Whether or not the icon is drawn.\n- */\n- isDrawn: function() {\n- // nodeType 11 for ie, whose nodes *always* have a parentNode\n- // (of type document fragment)\n- var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&\n- (this.imageDiv.parentNode.nodeType != 11));\n-\n- return isDrawn;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Icon\"\n-});\n-/* ======================================================================\n- OpenLayers/Marker.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Icon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Marker\n- * Instances of OpenLayers.Marker are a combination of a \n- * <OpenLayers.LonLat> and an <OpenLayers.Icon>. \n- *\n- * Markers are generally added to a special layer called\n- * <OpenLayers.Layer.Markers>.\n- *\n- * Example:\n- * (code)\n- * var markers = new OpenLayers.Layer.Markers( \"Markers\" );\n- * map.addLayer(markers);\n- *\n- * var size = new OpenLayers.Size(21,25);\n- * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);\n- * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);\n- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));\n- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));\n- *\n- * (end)\n- *\n- * Note that if you pass an icon into the Marker constructor, it will take\n- * that icon and use it. This means that you should not share icons between\n- * markers -- you use them once, but you should clone() for any additional\n- * markers using that same icon.\n- */\n-OpenLayers.Marker = OpenLayers.Class({\n-\n- /** \n- * Property: icon \n- * {<OpenLayers.Icon>} The icon used by this marker.\n- */\n- icon: null,\n-\n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} location of object\n- */\n- lonlat: null,\n-\n- /** \n- * Property: events \n- * {<OpenLayers.Events>} the event handler.\n- */\n- events: null,\n-\n- /** \n- * Property: map \n- * {<OpenLayers.Map>} the map this marker is attached to\n- */\n- map: null,\n-\n- /** \n- * Constructor: OpenLayers.Marker\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>} the position of this marker\n- * icon - {<OpenLayers.Icon>} the icon for this marker\n- */\n- initialize: function(lonlat, icon) {\n- this.lonlat = lonlat;\n-\n- var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();\n- if (this.icon == null) {\n- this.icon = newIcon;\n- } else {\n- this.icon.url = newIcon.url;\n- this.icon.size = newIcon.size;\n- this.icon.offset = newIcon.offset;\n- this.icon.calculateOffset = newIcon.calculateOffset;\n- }\n- this.events = new OpenLayers.Events(this, this.icon.imageDiv);\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Destroy the marker. You must first remove the marker from any \n- * layer which it has been added to, or you will get buggy behavior.\n- * (This can not be done within the marker since the marker does not\n- * know which layer it is attached to.)\n- */\n- destroy: function() {\n- // erase any drawn features\n- this.erase();\n-\n- this.map = null;\n-\n- this.events.destroy();\n- this.events = null;\n-\n- if (this.icon != null) {\n- this.icon.destroy();\n- this.icon = null;\n- }\n- },\n-\n- /** \n- * Method: draw\n- * Calls draw on the icon, and returns that output.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {DOMElement} A new DOM Image with this marker's icon set at the \n- * location passed-in\n- */\n- draw: function(px) {\n- return this.icon.draw(px);\n- },\n-\n- /** \n- * Method: erase\n- * Erases any drawn elements for this marker.\n- */\n- erase: function() {\n- if (this.icon != null) {\n- this.icon.erase();\n- }\n- },\n-\n- /**\n- * Method: moveTo\n- * Move the marker to the new location.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n- * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n- */\n- moveTo: function(px) {\n- if ((px != null) && (this.icon != null)) {\n- this.icon.moveTo(px);\n- }\n- this.lonlat = this.map.getLonLatFromLayerPx(px);\n- },\n-\n- /**\n- * APIMethod: isDrawn\n- * \n- * Returns:\n- * {Boolean} Whether or not the marker is drawn.\n- */\n- isDrawn: function() {\n- var isDrawn = (this.icon && this.icon.isDrawn());\n- return isDrawn;\n- },\n-\n- /**\n- * Method: onScreen\n- *\n- * Returns:\n- * {Boolean} Whether or not the marker is currently visible on screen.\n- */\n- onScreen: function() {\n-\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat);\n- }\n- return onScreen;\n- },\n-\n- /**\n- * Method: inflate\n- * Englarges the markers icon by the specified ratio.\n- *\n- * Parameters:\n- * inflate - {float} the ratio to enlarge the marker by (passing 2\n- * will double the size).\n- */\n- inflate: function(inflate) {\n- if (this.icon) {\n- this.icon.setSize({\n- w: this.icon.size.w * inflate,\n- h: this.icon.size.h * inflate\n- });\n- }\n- },\n-\n- /** \n- * Method: setOpacity\n- * Change the opacity of the marker by changin the opacity of \n- * its icon\n- * \n- * Parameters:\n- * opacity - {float} Specified as fraction (0.4, etc)\n- */\n- setOpacity: function(opacity) {\n- this.icon.setOpacity(opacity);\n- },\n-\n- /**\n- * Method: setUrl\n- * Change URL of the Icon Image.\n- * \n- * url - {String} \n- */\n- setUrl: function(url) {\n- this.icon.setUrl(url);\n- },\n-\n- /** \n- * Method: display\n- * Hide or show the icon\n- * \n- * display - {Boolean} \n- */\n- display: function(display) {\n- this.icon.display(display);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Marker\"\n-});\n-\n-\n-/**\n- * Function: defaultIcon\n- * Creates a default <OpenLayers.Icon>.\n- * \n- * Returns:\n- * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker\n- */\n-OpenLayers.Marker.defaultIcon = function() {\n- return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n- w: 21,\n- h: 25\n- }, {\n- x: -10.5,\n- y: -25\n- });\n-};\n-\n-\n-/* ======================================================================\n- OpenLayers/Handler.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- */\n-\n-/**\n- * Class: OpenLayers.Handler\n- * Base class to construct a higher-level handler for event sequences. All\n- * handlers have activate and deactivate methods. In addition, they have\n- * methods named like browser events. When a handler is activated, any\n- * additional methods named like a browser event is registered as a\n- * listener for the corresponding event. When a handler is deactivated,\n- * those same methods are unregistered as event listeners.\n- *\n- * Handlers also typically have a callbacks object with keys named like\n- * the abstracted events or event sequences that they are in charge of\n- * handling. The controls that wrap handlers define the methods that\n- * correspond to these abstract events - so instead of listening for\n- * individual browser events, they only listen for the abstract events\n- * defined by the handler.\n- * \n- * Handlers are created by controls, which ultimately have the responsibility\n- * of making changes to the the state of the application. Handlers\n- * themselves may make temporary changes, but in general are expected to\n- * return the application in the same state that they found it.\n- */\n-OpenLayers.Handler = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String}\n- */\n- id: null,\n-\n- /**\n- * APIProperty: control\n- * {<OpenLayers.Control>}. The control that initialized this handler. The\n- * control is assumed to have a valid map property - that map is used\n- * in the handler's own setMap method.\n- */\n- control: null,\n-\n- /**\n- * Property: map\n- * {<OpenLayers.Map>}\n- */\n- map: null,\n-\n- /**\n- * APIProperty: keyMask\n- * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n- * constants to construct a keyMask. The keyMask is used by\n- * <checkModifiers>. If the keyMask matches the combination of keys\n- * down on an event, checkModifiers returns true.\n- *\n- * Example:\n- * (code)\n- * // handler only responds if the Shift key is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n- *\n- * // handler only responds if Ctrl-Shift is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n- * OpenLayers.Handler.MOD_CTRL;\n- * (end)\n- */\n- keyMask: null,\n-\n- /**\n- * Property: active\n- * {Boolean}\n- */\n- active: false,\n-\n- /**\n- * Property: evt\n- * {Event} This property references the last event handled by the handler.\n- * Note that this property is not part of the stable API. Use of the\n- * evt property should be restricted to controls in the library\n- * or other applications that are willing to update with changes to\n- * the OpenLayers code.\n- */\n- evt: null,\n-\n- /**\n- * Property: touch\n- * {Boolean} Indicates the support of touch events. When touch events are \n- * started touch will be true and all mouse related listeners will do \n- * nothing.\n- */\n- touch: false,\n-\n- /**\n- * Constructor: OpenLayers.Handler\n- * Construct a handler.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that initialized this\n- * handler. The control is assumed to have a valid map property; that\n- * map is used in the handler's own setMap method. If a map property\n- * is present in the options argument it will be used instead.\n- * callbacks - {Object} An object whose properties correspond to abstracted\n- * events or sequences of browser events. The values for these\n- * properties are functions defined by the control that get called by\n- * the handler.\n- * options - {Object} An optional object whose properties will be set on\n- * the handler.\n- */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Util.extend(this, options);\n- this.control = control;\n- this.callbacks = callbacks;\n-\n- var map = this.map || control.map;\n- if (map) {\n- this.setMap(map);\n- }\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /**\n- * Method: setMap\n- */\n- setMap: function(map) {\n- this.map = map;\n- },\n-\n- /**\n- * Method: checkModifiers\n- * Check the keyMask on the handler. If no <keyMask> is set, this always\n- * returns true. If a <keyMask> is set and it matches the combination\n- * of keys down on an event, this returns true.\n- *\n- * Returns:\n- * {Boolean} The keyMask matches the keys down on an event.\n- */\n- checkModifiers: function(evt) {\n- if (this.keyMask == null) {\n- return true;\n- }\n- /* calculate the keyboard modifier mask for this event */\n- var keyModifiers =\n- (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n- (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n- (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n- (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n-\n- /* if it differs from the handler object's key mask,\n- bail out of the event handler */\n- return (keyModifiers == this.keyMask);\n- },\n-\n- /**\n- * APIMethod: activate\n- * Turn on the handler. Returns false if the handler was already active.\n- * \n- * Returns: \n- * {Boolean} The handler was activated.\n- */\n- activate: function() {\n- if (this.active) {\n- return false;\n- }\n- // register for event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.register(events[i], this[events[i]]);\n- }\n- }\n- this.active = true;\n- return true;\n- },\n-\n- /**\n- * APIMethod: deactivate\n- * Turn off the handler. Returns false if the handler was already inactive.\n- * \n- * Returns:\n- * {Boolean} The handler was deactivated.\n- */\n- deactivate: function() {\n- if (!this.active) {\n- return false;\n- }\n- // unregister event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n- }\n- }\n- this.touch = false;\n- this.active = false;\n- return true;\n- },\n-\n- /**\n- * Method: startTouch\n- * Start touch events, this method must be called by subclasses in \n- * \"touchstart\" method. When touch events are started <touch> will be\n- * true and all mouse related listeners will do nothing.\n- */\n- startTouch: function() {\n- if (!this.touch) {\n- this.touch = true;\n- var events = [\n- \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n- \"mouseout\"\n- ];\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: callback\n- * Trigger the control's named callback with the given arguments\n- *\n- * Parameters:\n- * name - {String} The key for the callback that is one of the properties\n- * of the handler's callbacks object.\n- * args - {Array(*)} An array of arguments (any type) with which to call \n- * the callback (defined by the control).\n- */\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args);\n- }\n- },\n-\n- /**\n- * Method: register\n- * register an event on the map\n- */\n- register: function(name, method) {\n- // TODO: deal with registerPriority in 3.0\n- this.map.events.registerPriority(name, this, method);\n- this.map.events.registerPriority(name, this, this.setEvent);\n- },\n-\n- /**\n- * Method: unregister\n- * unregister an event from the map\n- */\n- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent);\n- },\n-\n- /**\n- * Method: setEvent\n- * With each registered browser event, the handler sets its own evt\n- * property. This property can be accessed by controls if needed\n- * to get more information about the event that the handler is\n- * processing.\n- *\n- * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n- * and meta cannot be checked with the keyboard handler). For a\n- * control to determine which modifier keys are associated with the\n- * event that a handler is currently processing, it should access\n- * (code)handler.evt.altKey || handler.evt.shiftKey ||\n- * handler.evt.ctrlKey || handler.evt.metaKey(end).\n- *\n- * Parameters:\n- * evt - {Event} The browser event.\n- */\n- setEvent: function(evt) {\n- this.evt = evt;\n- return true;\n- },\n-\n- /**\n- * Method: destroy\n- * Deconstruct the handler.\n- */\n- destroy: function() {\n- // unregister event listeners\n- this.deactivate();\n- // eliminate circular references\n- this.control = this.map = null;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Handler\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_NONE\n- * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n- */\n-OpenLayers.Handler.MOD_NONE = 0;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_SHIFT\n- * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n- */\n-OpenLayers.Handler.MOD_SHIFT = 1;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_CTRL\n- * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n- */\n-OpenLayers.Handler.MOD_CTRL = 2;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_ALT\n- * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n- */\n-OpenLayers.Handler.MOD_ALT = 4;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_META\n- * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n- */\n-OpenLayers.Handler.MOD_META = 8;\n-\n-\n-/* ======================================================================\n- OpenLayers/Feature.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Feature\n- * Features are combinations of geography and attributes. The OpenLayers.Feature\n- * class specifically combines a marker and a lonlat.\n- */\n-OpenLayers.Feature = OpenLayers.Class({\n-\n- /** \n- * Property: layer \n- * {<OpenLayers.Layer>} \n- */\n- layer: null,\n-\n- /** \n- * Property: id \n- * {String} \n- */\n- id: null,\n-\n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} \n- */\n- lonlat: null,\n-\n- /** \n- * Property: data \n- * {Object} \n- */\n- data: null,\n-\n- /** \n- * Property: marker \n- * {<OpenLayers.Marker>} \n- */\n- marker: null,\n-\n- /**\n- * APIProperty: popupClass\n- * {<OpenLayers.Class>} The class which will be used to instantiate\n- * a new Popup. Default is <OpenLayers.Popup.Anchored>.\n- */\n- popupClass: null,\n-\n- /** \n- * Property: popup \n- * {<OpenLayers.Popup>} \n- */\n- popup: null,\n-\n- /** \n- * Constructor: OpenLayers.Feature\n- * Constructor for features.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- * lonlat - {<OpenLayers.LonLat>} \n- * data - {Object} \n- * \n- * Returns:\n- * {<OpenLayers.Feature>}\n- */\n- initialize: function(layer, lonlat, data) {\n- this.layer = layer;\n- this.lonlat = lonlat;\n- this.data = (data != null) ? data : {};\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n-\n- //remove the popup from the map\n- if ((this.layer != null) && (this.layer.map != null)) {\n- if (this.popup != null) {\n- this.layer.map.removePopup(this.popup);\n- }\n- }\n- // remove the marker from the layer\n- if (this.layer != null && this.marker != null) {\n- this.layer.removeMarker(this.marker);\n- }\n-\n- this.layer = null;\n- this.id = null;\n- this.lonlat = null;\n- this.data = null;\n- if (this.marker != null) {\n- this.destroyMarker(this.marker);\n- this.marker = null;\n- }\n- if (this.popup != null) {\n- this.destroyPopup(this.popup);\n- this.popup = null;\n- }\n- },\n-\n- /**\n- * Method: onScreen\n- * \n- * Returns:\n- * {Boolean} Whether or not the feature is currently visible on screen\n- * (based on its 'lonlat' property)\n- */\n- onScreen: function() {\n-\n- var onScreen = false;\n- if ((this.layer != null) && (this.layer.map != null)) {\n- var screenBounds = this.layer.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat);\n- }\n- return onScreen;\n- },\n-\n-\n- /**\n- * Method: createMarker\n- * Based on the data associated with the Feature, create and return a marker object.\n- *\n- * Returns: \n- * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties\n- * set in this.data. If no 'lonlat' is set, returns null. If no\n- * 'icon' is set, OpenLayers.Marker() will load the default image.\n- * \n- * Note - this.marker is set to return value\n- * \n- */\n- createMarker: function() {\n-\n- if (this.lonlat != null) {\n- this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);\n- }\n- return this.marker;\n- },\n-\n- /**\n- * Method: destroyMarker\n- * Destroys marker.\n- * If user overrides the createMarker() function, s/he should be able\n- * to also specify an alternative function for destroying it\n- */\n- destroyMarker: function() {\n- this.marker.destroy();\n- },\n-\n- /**\n- * Method: createPopup\n- * Creates a popup object created from the 'lonlat', 'popupSize',\n- * and 'popupContentHTML' properties set in this.data. It uses\n- * this.marker.icon as default anchor. \n- * \n- * If no 'lonlat' is set, returns null. \n- * If no this.marker has been created, no anchor is sent.\n- *\n- * Note - the returned popup object is 'owned' by the feature, so you\n- * cannot use the popup's destroy method to discard the popup.\n- * Instead, you must use the feature's destroyPopup\n- * \n- * Note - this.popup is set to return value\n- * \n- * Parameters: \n- * closeBox - {Boolean} create popup with closebox or not\n- * \n- * Returns:\n- * {<OpenLayers.Popup>} Returns the created popup, which is also set\n- * as 'popup' property of this feature. Will be of whatever type\n- * specified by this feature's 'popupClass' property, but must be\n- * of type <OpenLayers.Popup>.\n- * \n- */\n- createPopup: function(closeBox) {\n-\n- if (this.lonlat != null) {\n- if (!this.popup) {\n- var anchor = (this.marker) ? this.marker.icon : null;\n- var popupClass = this.popupClass ?\n- this.popupClass : OpenLayers.Popup.Anchored;\n- this.popup = new popupClass(this.id + \"_popup\",\n- this.lonlat,\n- this.data.popupSize,\n- this.data.popupContentHTML,\n- anchor,\n- closeBox);\n- }\n- if (this.data.overflow != null) {\n- this.popup.contentDiv.style.overflow = this.data.overflow;\n- }\n-\n- this.popup.feature = this;\n- }\n- return this.popup;\n- },\n-\n-\n- /**\n- * Method: destroyPopup\n- * Destroys the popup created via createPopup.\n- *\n- * As with the marker, if user overrides the createPopup() function, s/he \n- * should also be able to override the destruction\n- */\n- destroyPopup: function() {\n- if (this.popup) {\n- this.popup.feature = null;\n- this.popup.destroy();\n- this.popup = null;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Feature\"\n-});\n-/* ======================================================================\n- OpenLayers/Feature/Vector.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-// TRASH THIS\n-OpenLayers.State = {\n- /** states */\n- UNKNOWN: 'Unknown',\n- INSERT: 'Insert',\n- UPDATE: 'Update',\n- DELETE: 'Delete'\n-};\n-\n-/**\n- * @requires OpenLayers/Feature.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Feature.Vector\n- * Vector features use the OpenLayers.Geometry classes as geometry description.\n- * They have an 'attributes' property, which is the data object, and a 'style'\n- * property, the default values of which are defined in the \n- * <OpenLayers.Feature.Vector.style> objects.\n- * \n- * Inherits from:\n- * - <OpenLayers.Feature>\n- */\n-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n-\n- /** \n- * Property: fid \n- * {String} \n- */\n- fid: null,\n-\n- /** \n- * APIProperty: geometry \n- * {<OpenLayers.Geometry>} \n- */\n- geometry: null,\n-\n- /** \n- * APIProperty: attributes \n- * {Object} This object holds arbitrary, serializable properties that\n- * describe the feature.\n- */\n- attributes: null,\n-\n- /**\n- * Property: bounds\n- * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that\n- * property can be set by an <OpenLayers.Format> object when\n- * deserializing the feature, so in most cases it represents an\n- * information set by the server. \n- */\n- bounds: null,\n-\n- /** \n- * Property: state \n- * {String} \n- */\n- state: null,\n-\n- /** \n- * APIProperty: style \n- * {Object} \n- */\n- style: null,\n-\n- /**\n- * APIProperty: url\n- * {String} If this property is set it will be taken into account by\n- * {<OpenLayers.HTTP>} when upadting or deleting the feature.\n- */\n- url: null,\n-\n- /**\n- * Property: renderIntent\n- * {String} rendering intent currently being used\n- */\n- renderIntent: \"default\",\n-\n- /**\n- * APIProperty: modified\n- * {Object} An object with the originals of the geometry and attributes of\n- * the feature, if they were changed. Currently this property is only read\n- * by <OpenLayers.Format.WFST.v1>, and written by\n- * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.\n- * Applications can set the originals of modified attributes in the\n- * attributes property. Note that applications have to check if this\n- * object and the attributes property is already created before using it.\n- * After a change made with ModifyFeature, this object could look like\n- *\n- * (code)\n- * {\n- * geometry: >Object\n- * }\n- * (end)\n- *\n- * When an application has made changes to feature attributes, it could\n- * have set the attributes to something like this:\n- *\n- * (code)\n- * {\n- * attributes: {\n- * myAttribute: \"original\"\n- * }\n- * }\n- * (end)\n- *\n- * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in\n- * *modified.geometry* and the attribute names in *modified.attributes*,\n- * but it is recommended to set the original values (and not just true) as\n- * attribute value, so applications could use this information to undo\n- * changes.\n- */\n- modified: null,\n-\n- /** \n- * Constructor: OpenLayers.Feature.Vector\n- * Create a vector feature. \n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The geometry that this feature\n- * represents.\n- * attributes - {Object} An optional object that will be mapped to the\n- * <attributes> property. \n- * style - {Object} An optional style object.\n- */\n- initialize: function(geometry, attributes, style) {\n- OpenLayers.Feature.prototype.initialize.apply(this,\n- [null, null, attributes]);\n- this.lonlat = null;\n- this.geometry = geometry ? geometry : null;\n- this.state = null;\n- this.attributes = {};\n- if (attributes) {\n- this.attributes = OpenLayers.Util.extend(this.attributes,\n- attributes);\n- }\n- this.style = style ? style : null;\n- },\n-\n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- if (this.layer) {\n- this.layer.removeFeatures(this);\n- this.layer = null;\n- }\n-\n- this.geometry = null;\n- this.modified = null;\n- OpenLayers.Feature.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this vector feature. Does not set any non-standard\n- * properties.\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.\n- */\n- clone: function() {\n- return new OpenLayers.Feature.Vector(\n- this.geometry ? this.geometry.clone() : null,\n- this.attributes,\n- this.style);\n- },\n-\n- /**\n- * Method: onScreen\n- * Determine whether the feature is within the map viewport. This method\n- * tests for an intersection between the geometry and the viewport\n- * bounds. If a more effecient but less precise geometry bounds\n- * intersection is desired, call the method with the boundsOnly\n- * parameter true.\n- *\n- * Parameters:\n- * boundsOnly - {Boolean} Only test whether a feature's bounds intersects\n- * the viewport bounds. Default is false. If false, the feature's\n- * geometry must intersect the viewport for onScreen to return true.\n- * \n- * Returns:\n- * {Boolean} The feature is currently visible on screen (optionally\n- * based on its bounds if boundsOnly is true).\n- */\n- onScreen: function(boundsOnly) {\n- var onScreen = false;\n- if (this.layer && this.layer.map) {\n- var screenBounds = this.layer.map.getExtent();\n- if (boundsOnly) {\n- var featureBounds = this.geometry.getBounds();\n- onScreen = screenBounds.intersectsBounds(featureBounds);\n- } else {\n- var screenPoly = screenBounds.toGeometry();\n- onScreen = screenPoly.intersects(this.geometry);\n- }\n- }\n- return onScreen;\n- },\n-\n- /**\n- * Method: getVisibility\n- * Determine whether the feature is displayed or not. It may not displayed\n- * because:\n- * - its style display property is set to 'none',\n- * - it doesn't belong to any layer,\n- * - the styleMap creates a symbolizer with display property set to 'none'\n- * for it,\n- * - the layer which it belongs to is not visible.\n- * \n- * Returns:\n- * {Boolean} The feature is currently displayed.\n- */\n- getVisibility: function() {\n- return !(this.style && this.style.display == 'none' ||\n- !this.layer ||\n- this.layer && this.layer.styleMap &&\n- this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||\n- this.layer && !this.layer.getVisibility());\n- },\n-\n- /**\n- * Method: createMarker\n- * HACK - we need to decide if all vector features should be able to\n- * create markers\n- * \n- * Returns:\n- * {<OpenLayers.Marker>} For now just returns null\n- */\n- createMarker: function() {\n- return null;\n- },\n-\n- /**\n- * Method: destroyMarker\n- * HACK - we need to decide if all vector features should be able to\n- * delete markers\n- * \n- * If user overrides the createMarker() function, s/he should be able\n- * to also specify an alternative function for destroying it\n- */\n- destroyMarker: function() {\n- // pass\n- },\n-\n- /**\n- * Method: createPopup\n- * HACK - we need to decide if all vector features should be able to\n- * create popups\n- * \n- * Returns:\n- * {<OpenLayers.Popup>} For now just returns null\n- */\n- createPopup: function() {\n- return null;\n- },\n-\n- /**\n- * Method: atPoint\n- * Determins whether the feature intersects with the specified location.\n- * \n- * Parameters: \n- * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * toleranceLon - {float} Optional tolerance in Geometric Coords\n- * toleranceLat - {float} Optional tolerance in Geographic Coords\n- * \n- * Returns:\n- * {Boolean} Whether or not the feature is at the specified location\n- */\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- if (this.geometry) {\n- atPoint = this.geometry.atPoint(lonlat, toleranceLon,\n- toleranceLat);\n- }\n- return atPoint;\n- },\n-\n- /**\n- * Method: destroyPopup\n- * HACK - we need to decide if all vector features should be able to\n- * delete popups\n- */\n- destroyPopup: function() {\n- // pass\n- },\n-\n- /**\n- * Method: move\n- * Moves the feature and redraws it at its new location\n- *\n- * Parameters:\n- * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the\n- * location to which to move the feature.\n- */\n- move: function(location) {\n-\n- if (!this.layer || !this.geometry.move) {\n- //do nothing if no layer or immoveable geometry\n- return undefined;\n- }\n-\n- var pixel;\n- if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n- pixel = this.layer.getViewPortPxFromLonLat(location);\n- } else {\n- pixel = location;\n- }\n-\n- var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n- var res = this.layer.map.getResolution();\n- this.geometry.move(res * (pixel.x - lastPixel.x),\n- res * (lastPixel.y - pixel.y));\n- this.layer.drawFeature(this);\n- return lastPixel;\n- },\n-\n- /**\n- * Method: toState\n- * Sets the new state\n- *\n- * Parameters:\n- * state - {String} \n- */\n- toState: function(state) {\n- if (state == OpenLayers.State.UPDATE) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.DELETE:\n- this.state = state;\n- break;\n- case OpenLayers.State.UPDATE:\n- case OpenLayers.State.INSERT:\n- break;\n- }\n- } else if (state == OpenLayers.State.INSERT) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- break;\n- default:\n- this.state = state;\n- break;\n- }\n- } else if (state == OpenLayers.State.DELETE) {\n- switch (this.state) {\n- case OpenLayers.State.INSERT:\n- // the feature should be destroyed\n- break;\n- case OpenLayers.State.DELETE:\n- break;\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.UPDATE:\n- this.state = state;\n- break;\n- }\n- } else if (state == OpenLayers.State.UNKNOWN) {\n- this.state = state;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Feature.Vector\"\n-});\n-\n-\n-/**\n- * Constant: OpenLayers.Feature.Vector.style\n- * OpenLayers features can have a number of style attributes. The 'default' \n- * style will typically be used if no other style is specified. These\n- * styles correspond for the most part, to the styling properties defined\n- * by the SVG standard. \n- * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties\n- * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties\n- *\n- * Symbolizer properties:\n- * fill - {Boolean} Set to false if no fill is desired.\n- * fillColor - {String} Hex fill color. Default is \"#ee9900\".\n- * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 \n- * stroke - {Boolean} Set to false if no stroke is desired.\n- * strokeColor - {String} Hex stroke color. Default is \"#ee9900\".\n- * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.\n- * strokeWidth - {Number} Pixel stroke width. Default is 1.\n- * strokeLinecap - {String} Stroke cap type. Default is \"round\". [butt | round | square]\n- * strokeDashstyle - {String} Stroke dash style. Default is \"solid\". [dot | dash | dashdot | longdash | longdashdot | solid]\n- * graphic - {Boolean} Set to false if no graphic is desired.\n- * pointRadius - {Number} Pixel point radius. Default is 6.\n- * pointerEvents - {String} Default is \"visiblePainted\".\n- * cursor - {String} Default is \"\".\n- * externalGraphic - {String} Url to an external graphic that will be used for rendering points.\n- * graphicWidth - {Number} Pixel width for sizing an external graphic.\n- * graphicHeight - {Number} Pixel height for sizing an external graphic.\n- * graphicOpacity - {Number} Opacity (0-1) for an external graphic.\n- * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.\n- * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.\n- * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).\n- * graphicZIndex - {Number} The integer z-index value to use in rendering.\n- * graphicName - {String} Named graphic to use when rendering points. Supported values include \"circle\" (default),\n- * \"square\", \"star\", \"x\", \"cross\", \"triangle\".\n- * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead\n- * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.\n- * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.\n- * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.\n- * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.\n- * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.\n- * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.\n- * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.\n- * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either\n- * fillText or mozDrawText to be available.\n- * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string\n- * composed of two characters. The first character is for the horizontal alignment, the second for the vertical\n- * alignment. Valid values for horizontal alignment: \"l\"=left, \"c\"=center, \"r\"=right. Valid values for vertical\n- * alignment: \"t\"=top, \"m\"=middle, \"b\"=bottom. Example values: \"lt\", \"cm\", \"rb\". Default is \"cm\".\n- * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.\n- * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.\n- * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.\n- * Default is false.\n- * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.\n- * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.\n- * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.\n- * fontColor - {String} The font color for the label, to be provided like CSS.\n- * fontOpacity - {Number} Opacity (0-1) for the label\n- * fontFamily - {String} The font family for the label, to be provided like in CSS.\n- * fontSize - {String} The font size for the label, to be provided like in CSS.\n- * fontStyle - {String} The font style for the label, to be provided like in CSS.\n- * fontWeight - {String} The font weight for the label, to be provided like in CSS.\n- * display - {String} Symbolizers will have no effect if display is set to \"none\". All other values have no effect.\n- */\n-OpenLayers.Feature.Vector.style = {\n- 'default': {\n- fillColor: \"#ee9900\",\n- fillOpacity: 0.4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"#ee9900\",\n- strokeOpacity: 1,\n- strokeWidth: 1,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n- },\n- 'select': {\n- fillColor: \"blue\",\n- fillOpacity: 0.4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"blue\",\n- strokeOpacity: 1,\n- strokeWidth: 2,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"pointer\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n-\n- },\n- 'temporary': {\n- fillColor: \"#66cccc\",\n- fillOpacity: 0.2,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"#66cccc\",\n- strokeOpacity: 1,\n- strokeLinecap: \"round\",\n- strokeWidth: 2,\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n-\n- },\n- 'delete': {\n- display: \"none\"\n- }\n-};\n-/* ======================================================================\n- OpenLayers/Style.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Style\n- * This class represents a UserStyle obtained\n- * from a SLD, containing styling rules.\n- */\n-OpenLayers.Style = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n-\n- /**\n- * APIProperty: name\n- * {String}\n- */\n- name: null,\n-\n- /**\n- * Property: title\n- * {String} Title of this style (set if included in SLD)\n- */\n- title: null,\n-\n- /**\n- * Property: description\n- * {String} Description of this style (set if abstract is included in SLD)\n- */\n- description: null,\n-\n- /**\n- * APIProperty: layerName\n- * {<String>} name of the layer that this style belongs to, usually\n- * according to the NamedLayer attribute of an SLD document.\n- */\n- layerName: null,\n-\n- /**\n- * APIProperty: isDefault\n- * {Boolean}\n- */\n- isDefault: false,\n-\n- /** \n- * Property: rules \n- * {Array(<OpenLayers.Rule>)}\n- */\n- rules: null,\n-\n- /**\n- * APIProperty: context\n- * {Object} An optional object with properties that symbolizers' property\n- * values should be evaluated against. If no context is specified,\n- * feature.attributes will be used\n- */\n- context: null,\n-\n- /**\n- * Property: defaultStyle\n- * {Object} hash of style properties to use as default for merging\n- * rule-based style symbolizers onto. If no rules are defined,\n- * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n- * true, the defaultStyle will only be taken into account if there are\n- * rules defined.\n- */\n- defaultStyle: null,\n-\n- /**\n- * Property: defaultsPerSymbolizer\n- * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n- * of every rule. Properties of the <defaultStyle> will also be used to set\n- * missing symbolizer properties if the symbolizer has stroke, fill or\n- * graphic set to true. Default is false.\n- */\n- defaultsPerSymbolizer: false,\n-\n- /**\n- * Property: propertyStyles\n- * {Hash of Boolean} cache of style properties that need to be parsed for\n- * propertyNames. Property names are keys, values won't be used.\n- */\n- propertyStyles: null,\n-\n-\n- /** \n- * Constructor: OpenLayers.Style\n- * Creates a UserStyle.\n- *\n- * Parameters:\n- * style - {Object} Optional hash of style properties that will be\n- * used as default style for this style object. This style\n- * applies if no rules are specified. Symbolizers defined in\n- * rules will extend this default style.\n- * options - {Object} An optional object with properties to set on the\n- * style.\n- *\n- * Valid options:\n- * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n- * style.\n- * \n- * Returns:\n- * {<OpenLayers.Style>}\n- */\n- initialize: function(style, options) {\n-\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules);\n- }\n-\n- // use the default style from OpenLayers.Feature.Vector if no style\n- // was given in the constructor\n- this.setDefaultStyle(style ||\n- OpenLayers.Feature.Vector.style[\"default\"]);\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null;\n- }\n- this.rules = null;\n- this.defaultStyle = null;\n- },\n-\n- /**\n- * Method: createSymbolizer\n- * creates a style by applying all feature-dependent rules to the base\n- * style.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n- * \n- * Returns:\n- * {Object} symbolizer hash\n- */\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n- OpenLayers.Util.extend({}, this.defaultStyle), feature);\n-\n- var rules = this.rules;\n-\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- // does the rule apply?\n- var applies = rule.evaluate(feature);\n-\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule);\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature);\n- }\n- }\n- }\n-\n- // if no other rules apply, apply the rules with else filters\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature);\n- }\n- }\n-\n- // don't display if there were rules but none applied\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\";\n- }\n-\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label);\n- }\n-\n- return style;\n- },\n-\n- /**\n- * Method: applySymbolizer\n- *\n- * Parameters:\n- * rule - {<OpenLayers.Rule>}\n- * style - {Object}\n- * feature - {<OpenLayer.Feature.Vector>}\n- *\n- * Returns:\n- * {Object} A style with new symbolizer applied.\n- */\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ?\n- this.getSymbolizerPrefix(feature.geometry) :\n- OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n-\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n-\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- });\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- });\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- });\n- }\n- }\n-\n- // merge the style with the current style\n- return this.createLiterals(\n- OpenLayers.Util.extend(style, symbolizer), feature);\n- },\n-\n- /**\n- * Method: createLiterals\n- * creates literals for all style properties that have an entry in\n- * <this.propertyStyles>.\n- * \n- * Parameters:\n- * style - {Object} style to create literals for. Will be modified\n- * inline.\n- * feature - {Object}\n- * \n- * Returns:\n- * {Object} the modified style\n- */\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n-\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n- }\n- return style;\n- },\n-\n- /**\n- * Method: findPropertyStyles\n- * Looks into all rules for this style and the defaultStyle to collect\n- * all the style hash property names containing ${...} strings that have\n- * to be replaced using the createLiteral method before returning them.\n- * \n- * Returns:\n- * {Object} hash of property names that need createLiteral parsing. The\n- * name of the property is the key, and the value is true;\n- */\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n-\n- // check the default style\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n-\n- // walk through all rules to check for properties in their symbolizer\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n- this.addPropertyStyles(propertyStyles, value);\n- } else {\n- // symbolizer is a hash of style properties\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break;\n- }\n- }\n- }\n- return propertyStyles;\n- },\n-\n- /**\n- * Method: addPropertyStyles\n- * \n- * Parameters:\n- * propertyStyles - {Object} hash to add new property styles to. Will be\n- * modified inline\n- * symbolizer - {Object} search this symbolizer for property styles\n- * \n- * Returns:\n- * {Object} propertyStyles hash\n- */\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" &&\n- property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true;\n- }\n- }\n- return propertyStyles;\n- },\n-\n- /**\n- * APIMethod: addRules\n- * Adds rules to this style.\n- * \n- * Parameters:\n- * rules - {Array(<OpenLayers.Rule>)}\n- */\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles();\n- },\n-\n- /**\n- * APIMethod: setDefaultStyle\n- * Sets the default style for this style object.\n- * \n- * Parameters:\n- * style - {Object} Hash of style properties\n- */\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles();\n- },\n-\n- /**\n- * Method: getSymbolizerPrefix\n- * Returns the correct symbolizer prefix according to the\n- * geometry type of the passed geometry\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {String} key of the according symbolizer\n- */\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i];\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this style.\n- * \n- * Returns:\n- * {<OpenLayers.Style>} Clone of this style.\n- */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- // clone rules\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone());\n- }\n- }\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- //clone default style\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Style\"\n-});\n-\n-\n-/**\n- * Function: createLiteral\n- * converts a style value holding a combination of PropertyName and Literal\n- * into a Literal, taking the property values from the passed features.\n- * \n- * Parameters:\n- * value - {String} value to parse. If this string contains a construct like\n- * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n- * will be replaced by the value of the \"bar\" attribute of the passed\n- * feature.\n- * context - {Object} context to take attribute values from\n- * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n- * <OpenLayers.String.format> for evaluating functions in the\n- * context.\n- * property - {String} optional, name of the property for which the literal is\n- * being created for evaluating functions in the context.\n- * \n- * Returns:\n- * {String} the parsed value. In the example of the value parameter above, the\n- * result would be \"foo valueOfBar\", assuming that the passed feature has an\n- * attribute named \"bar\" with the value \"valueOfBar\".\n- */\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = (isNaN(value) || !value) ? value : parseFloat(value);\n- }\n- return value;\n-};\n-\n-/**\n- * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n- * {Array} prefixes of the sld symbolizers. These are the\n- * same as the main geometry types\n- */\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n- 'Raster'\n-];\n-/* ======================================================================\n- OpenLayers/Filter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Filter\n- * This class represents an OGC Filter.\n- */\n-OpenLayers.Filter = OpenLayers.Class({\n-\n- /** \n- * Constructor: OpenLayers.Filter\n- * This class represents a generic filter.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- * \n- * Returns:\n- * {<OpenLayers.Filter>}\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /** \n- * APIMethod: destroy\n- * Remove reference to anything added.\n- */\n- destroy: function() {},\n-\n- /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context. Instances or subclasses\n- * are supposed to override this method.\n- * \n- * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n- * \n- * Returns:\n- * {Boolean} The filter applies.\n- */\n- evaluate: function(context) {\n- return true;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this filter. Should be implemented by subclasses.\n- * \n- * Returns:\n- * {<OpenLayers.Filter>} Clone of this filter.\n- */\n- clone: function() {\n- return null;\n- },\n-\n- /**\n- * APIMethod: toString\n- *\n- * Returns:\n- * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n- * representation of the filter returned. Otherwise \"[Object object]\"\n- * will be returned.\n- */\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this);\n- } else {\n- string = Object.prototype.toString.call(this);\n- }\n- return string;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Filter\"\n-});\n-/* ======================================================================\n- OpenLayers/Format.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format\n- * Base class for format reading/writing a variety of formats. Subclasses\n- * of OpenLayers.Format are expected to have read and write methods.\n- */\n-OpenLayers.Format = OpenLayers.Class({\n-\n- /**\n- * Property: options\n- * {Object} A reference to options passed to the constructor.\n- */\n- options: null,\n-\n- /**\n- * APIProperty: externalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The externalProjection is the projection used by\n- * the content which is passed into read or which comes out of write.\n- * In order to reproject, a projection transformation function for the\n- * specified projections must be available. This support may be \n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- externalProjection: null,\n-\n- /**\n- * APIProperty: internalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The internalProjection is the projection used by\n- * the geometries which are returned by read or which are passed into\n- * write. In order to reproject, a projection transformation function\n- * for the specified projections must be available. This support may be\n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- internalProjection: null,\n-\n- /**\n- * APIProperty: data\n- * {Object} When <keepData> is true, this is the parsed string sent to\n- * <read>.\n- */\n- data: null,\n-\n- /**\n- * APIProperty: keepData\n- * {Object} Maintain a reference (<data>) to the most recently read data.\n- * Default is false.\n- */\n- keepData: false,\n-\n- /**\n- * Constructor: OpenLayers.Format\n- * Instances of this class are not useful. See one of the subclasses.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * format\n- *\n- * Valid options:\n- * keepData - {Boolean} If true, upon <read>, the data property will be\n- * set to the parsed object (e.g. the json or xml object).\n- *\n- * Returns:\n- * An instance of OpenLayers.Format\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up.\n- */\n- destroy: function() {},\n-\n- /**\n- * Method: read\n- * Read data from a string, and return an object whose type depends on the\n- * subclass. \n- * \n- * Parameters:\n- * data - {string} Data to read/parse.\n- *\n- * Returns:\n- * Depends on the subclass\n- */\n- read: function(data) {\n- throw new Error('Read not implemented.');\n- },\n-\n- /**\n- * Method: write\n- * Accept an object, and return a string. \n- *\n- * Parameters:\n- * object - {Object} Object to be serialized\n- *\n- * Returns:\n- * {String} A string representation of the object.\n- */\n- write: function(object) {\n- throw new Error('Write not implemented.');\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/HTTPRequest.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.HTTPRequest\n- * \n- * Inherits from: \n- * - <OpenLayers.Layer>\n- */\n-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /** \n- * Constant: URL_HASH_FACTOR\n- * {Float} Used to hash URL param strings for multi-WMS server selection.\n- * Set to the Golden Ratio per Knuth's recommendation.\n- */\n- URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n-\n- /** \n- * Property: url\n- * {Array(String) or String} This is either an array of url strings or \n- * a single url string. \n- */\n- url: null,\n-\n- /** \n- * Property: params\n- * {Object} Hashtable of key/value parameters\n- */\n- params: null,\n-\n- /** \n- * APIProperty: reproject\n- * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n- * for information on the replacement for this functionality. \n- * {Boolean} Whether layer should reproject itself based on base layer \n- * locations. This allows reprojection onto commercial layers. \n- * Default is false: Most layers can't reproject, but layers \n- * which can create non-square geographic pixels can, like WMS.\n- * \n- */\n- reproject: false,\n-\n- /**\n- * Constructor: OpenLayers.Layer.HTTPRequest\n- * \n- * Parameters:\n- * name - {String}\n- * url - {Array(String) or String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.url = url;\n- if (!this.params) {\n- this.params = OpenLayers.Util.extend({}, params);\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.url = null;\n- this.params = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this \n- * <OpenLayers.Layer.HTTPRequest>\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.HTTPRequest(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n- /** \n- * APIMethod: setUrl\n- * \n- * Parameters:\n- * newUrl - {String}\n- */\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * \n- * Parameters:\n- * newParams - {Object}\n- *\n- * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n- */\n- mergeNewParams: function(newParams) {\n- this.params = OpenLayers.Util.extend(this.params, newParams);\n- var ret = this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"params\"\n- });\n- }\n- return ret;\n- },\n-\n- /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n- *\n- * Parameters:\n- * force - {Boolean} Force redraw by adding random parameter.\n- *\n- * Returns:\n- * {Boolean} The layer was redrawn.\n- */\n- redraw: function(force) {\n- if (force) {\n- return this.mergeNewParams({\n- \"_olSalt\": Math.random()\n- });\n- } else {\n- return OpenLayers.Layer.prototype.redraw.apply(this, []);\n- }\n- },\n-\n- /**\n- * Method: selectUrl\n- * selectUrl() implements the standard floating-point multiplicative\n- * hash function described by Knuth, and hashes the contents of the \n- * given param string into a float between 0 and 1. This float is then\n- * scaled to the size of the provided urls array, and used to select\n- * a URL.\n- *\n- * Parameters:\n- * paramString - {String}\n- * urls - {Array(String)}\n- * \n- * Returns:\n- * {String} An entry from the urls array, deterministically selected based\n- * on the paramString.\n- */\n- selectUrl: function(paramString, urls) {\n- var product = 1;\n- for (var i = 0, len = paramString.length; i < len; i++) {\n- product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n- product -= Math.floor(product);\n- }\n- return urls[Math.floor(product * urls.length)];\n- },\n-\n- /** \n- * Method: getFullRequestString\n- * Combine url with layer's params and these newParams. \n- * \n- * does checking on the serverPath variable, allowing for cases when it \n- * is supplied with trailing ? or &, as well as cases where not. \n- *\n- * return in formatted string like this:\n- * \"server?key1=value1&key2=value2&key3=value3\"\n- * \n- * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n- *\n- * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n- * Returns: \n- * {String}\n- */\n- getFullRequestString: function(newParams, altUrl) {\n-\n- // if not altUrl passed in, use layer's url\n- var url = altUrl || this.url;\n-\n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will deterministically select one of them in \n- // order to evenly distribute requests to different urls.\n- //\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url);\n- }\n-\n- // ignore parameters that are already in the url search string\n- var urlParams =\n- OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n- }\n- }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n-\n- return OpenLayers.Util.urlAppend(url, paramsString);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n-});\n-/* ======================================================================\n- OpenLayers/Tile.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Tile \n- * This is a class designed to designate a single tile, however\n- * it is explicitly designed to do relatively little. Tiles store \n- * information about themselves -- such as the URL that they are related\n- * to, and their size - but do not add themselves to the layer div \n- * automatically, for example. Create a new tile with the \n- * <OpenLayers.Tile> constructor, or a subclass. \n- * \n- * TBD 3.0 - remove reference to url in above paragraph\n- * \n- */\n-OpenLayers.Tile = OpenLayers.Class({\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types:\n- * beforedraw - Triggered before the tile is drawn. Used to defer\n- * drawing to an animation queue. To defer drawing, listeners need\n- * to return false, which will abort drawing. The queue handler needs\n- * to call <draw>(true) to actually draw the tile.\n- * loadstart - Triggered when tile loading starts.\n- * loadend - Triggered when tile loading ends.\n- * loaderror - Triggered before the loadend event (i.e. when the tile is\n- * still hidden) if the tile could not be loaded.\n- * reload - Triggered when an already loading tile is reloaded.\n- * unload - Triggered before a tile is unloaded.\n- */\n- events: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- *\n- * This options can be set in the ``tileOptions`` option from\n- * <OpenLayers.Layer.Grid>. For example, to be notified of the\n- * ``loadend`` event of each tiles:\n- * (code)\n- * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n- * tileOptions: {\n- * eventListeners: {\n- * 'loadend': function(evt) {\n- * // do something on loadend\n- * }\n- * }\n- * }\n- * });\n- * (end)\n- */\n- eventListeners: null,\n-\n- /**\n- * Property: id \n- * {String} null\n- */\n- id: null,\n-\n- /** \n- * Property: layer \n- * {<OpenLayers.Layer>} layer the tile is attached to \n- */\n- layer: null,\n-\n- /**\n- * Property: url\n- * {String} url of the request.\n- *\n- * TBD 3.0 \n- * Deprecated. The base tile class does not need an url. This should be \n- * handled in subclasses. Does not belong here.\n- */\n- url: null,\n-\n- /** \n- * APIProperty: bounds \n- * {<OpenLayers.Bounds>} null\n- */\n- bounds: null,\n-\n- /** \n- * Property: size \n- * {<OpenLayers.Size>} null\n- */\n- size: null,\n-\n- /** \n- * Property: position \n- * {<OpenLayers.Pixel>} Top Left pixel of the tile\n- */\n- position: null,\n-\n- /**\n- * Property: isLoading\n- * {Boolean} Is the tile loading?\n- */\n- isLoading: false,\n-\n- /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n- * there is no need for the base tile class to have a url.\n- */\n-\n- /** \n- * Constructor: OpenLayers.Tile\n- * Constructor for a new <OpenLayers.Tile> instance.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>}\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n- */\n- initialize: function(layer, position, bounds, url, size, options) {\n- this.layer = layer;\n- this.position = position.clone();\n- this.setBounds(bounds);\n- this.url = url;\n- if (size) {\n- this.size = size.clone();\n- }\n-\n- //give the tile a unique id based on its BBOX.\n- this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n-\n- OpenLayers.Util.extend(this, options);\n-\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n- },\n-\n- /**\n- * Method: unload\n- * Call immediately before destroying if you are listening to tile\n- * events, so that counters are properly handled if tile is still\n- * loading at destroy-time. Will only fire an event if the tile is\n- * still loading.\n- */\n- unload: function() {\n- if (this.isLoading) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"unload\");\n- }\n- },\n-\n- /** \n- * APIMethod: destroy\n- * Nullify references to prevent circular references and memory leaks.\n- */\n- destroy: function() {\n- this.layer = null;\n- this.bounds = null;\n- this.size = null;\n- this.position = null;\n-\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- this.eventListeners = null;\n- this.events = null;\n- },\n-\n- /**\n- * Method: draw\n- * Clear whatever is currently in the tile, then return whether or not \n- * it should actually be re-drawn. This is an example implementation\n- * that can be overridden by subclasses. The minimum thing to do here\n- * is to call <clear> and return the result from <shouldDraw>.\n- *\n- * Parameters:\n- * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n- * event will be fired. This is used for drawing tiles asynchronously\n- * after drawing has been cancelled by returning false from a beforedraw\n- * listener.\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn. Returns null\n- * if a beforedraw listener returned false.\n- */\n- draw: function(force) {\n- if (!force) {\n- //clear tile's contents and mark as not drawn\n- this.clear();\n- }\n- var draw = this.shouldDraw();\n- if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n- draw = null;\n- }\n- return draw;\n- },\n-\n- /**\n- * Method: shouldDraw\n- * Return whether or not the tile should actually be (re-)drawn. The only\n- * case where we *wouldn't* want to draw the tile is if the tile is outside\n- * its layer's maxExtent\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn.\n- */\n- shouldDraw: function() {\n- var withinMaxExtent = false,\n- maxExtent = this.layer.maxExtent;\n- if (maxExtent) {\n- var map = this.layer.map;\n- var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n- if (this.bounds.intersectsBounds(maxExtent, {\n- inclusive: false,\n- worldBounds: worldBounds\n- })) {\n- withinMaxExtent = true;\n- }\n- }\n-\n- return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n- },\n-\n- /**\n- * Method: setBounds\n- * Sets the bounds on this instance\n- *\n- * Parameters:\n- * bounds {<OpenLayers.Bounds>}\n- */\n- setBounds: function(bounds) {\n- bounds = bounds.clone();\n- if (this.layer.map.baseLayer.wrapDateLine) {\n- var worldExtent = this.layer.map.getMaxExtent(),\n- tolerance = this.layer.map.getResolution();\n- bounds = bounds.wrapDateLine(worldExtent, {\n- leftTolerance: tolerance,\n- rightTolerance: tolerance\n- });\n- }\n- this.bounds = bounds;\n- },\n-\n- /** \n- * Method: moveTo\n- * Reposition the tile.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * position - {<OpenLayers.Pixel>}\n- * redraw - {Boolean} Call draw method on tile after moving.\n- * Default is true\n- */\n- moveTo: function(bounds, position, redraw) {\n- if (redraw == null) {\n- redraw = true;\n- }\n-\n- this.setBounds(bounds);\n- this.position = position.clone();\n- if (redraw) {\n- this.draw();\n- }\n- },\n-\n- /** \n- * Method: clear\n- * Clear the tile of any bounds/position-related data so that it can \n- * be reused in a new location.\n- */\n- clear: function(draw) {\n- // to be extended by subclasses\n- },\n-\n- CLASS_NAME: \"OpenLayers.Tile\"\n-});\n-/* ======================================================================\n- OpenLayers/Tile/Image.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Animation.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Tile.Image\n- * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n- * used by various layers. Create a new image tile with the\n- * <OpenLayers.Tile.Image> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Tile>\n- */\n-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to the <OpenLayers.Tile> events):\n- * beforeload - Triggered before an image is prepared for loading, when the\n- * url for the image is known already. Listeners may call <setImage> on\n- * the tile instance. If they do so, that image will be used and no new\n- * one will be created.\n- */\n-\n- /** \n- * APIProperty: url\n- * {String} The URL of the image being requested. No default. Filled in by\n- * layer.getURL() function. May be modified by loadstart listeners.\n- */\n- url: null,\n-\n- /** \n- * Property: imgDiv\n- * {HTMLImageElement} The image for this tile.\n- */\n- imgDiv: null,\n-\n- /**\n- * Property: frame\n- * {DOMElement} The image element is appended to the frame. Any gutter on\n- * the image will be hidden behind the frame. If no gutter is set,\n- * this will be null.\n- */\n- frame: null,\n-\n- /** \n- * Property: imageReloadAttempts\n- * {Integer} Attempts to load the image.\n- */\n- imageReloadAttempts: null,\n-\n- /**\n- * Property: layerAlphaHack\n- * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n- */\n- layerAlphaHack: null,\n-\n- /**\n- * Property: asyncRequestId\n- * {Integer} ID of an request to see if request is still valid. This is a\n- * number which increments by 1 for each asynchronous request.\n- */\n- asyncRequestId: null,\n-\n- /**\n- * APIProperty: maxGetUrlLength\n- * {Number} If set, requests that would result in GET urls with more\n- * characters than the number provided will be made using form-encoded\n- * HTTP POST. It is good practice to avoid urls that are longer than 2048\n- * characters.\n- *\n- * Caution:\n- * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n- * Opera versions do not fully support this option. On all browsers,\n- * transition effects are not supported if POST requests are used.\n- */\n- maxGetUrlLength: null,\n-\n- /**\n- * Property: canvasContext\n- * {CanvasRenderingContext2D} A canvas context associated with\n- * the tile image.\n- */\n- canvasContext: null,\n-\n- /**\n- * APIProperty: crossOriginKeyword\n- * The value of the crossorigin keyword to use when loading images. This is\n- * only relevant when using <getCanvasContext> for tiles from remote\n- * origins and should be set to either 'anonymous' or 'use-credentials'\n- * for servers that send Access-Control-Allow-Origin headers with their\n- * tiles.\n- */\n- crossOriginKeyword: null,\n-\n- /** TBD 3.0 - reorder the parameters to the init function to remove \n- * URL. the getUrl() function on the layer gets called on \n- * each draw(), so no need to specify it here.\n- */\n-\n- /** \n- * Constructor: OpenLayers.Tile.Image\n- * Constructor for a new <OpenLayers.Tile.Image> instance.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>} Deprecated. Remove me in 3.0.\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n- */\n- initialize: function(layer, position, bounds, url, size, options) {\n- OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n-\n- this.url = url; //deprecated remove me\n-\n- this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n-\n- if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n- // only create frame if it's needed\n- this.frame = document.createElement(\"div\");\n- this.frame.style.position = \"absolute\";\n- this.frame.style.overflow = \"hidden\";\n- }\n- if (this.maxGetUrlLength != null) {\n- OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n- }\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- if (this.imgDiv) {\n- this.clear();\n- this.imgDiv = null;\n- this.frame = null;\n- }\n- // don't handle async requests any more\n- this.asyncRequestId = null;\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * \n- * Returns:\n- * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n- * false.\n- */\n- draw: function() {\n- var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (shouldDraw) {\n- // The layer's reproject option is deprecated.\n- if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n- // getBoundsFromBaseLayer is defined in deprecated.js.\n- this.bounds = this.getBoundsFromBaseLayer(this.position);\n- }\n- if (this.isLoading) {\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this._loadEvent = \"reload\";\n- } else {\n- this.isLoading = true;\n- this._loadEvent = \"loadstart\";\n- }\n- this.renderTile();\n- this.positionTile();\n- } else if (shouldDraw === false) {\n- this.unload();\n- }\n- return shouldDraw;\n- },\n-\n- /**\n- * Method: renderTile\n- * Internal function to actually initialize the image tile,\n- * position it correctly, and set its url.\n- */\n- renderTile: function() {\n- if (this.layer.async) {\n- // Asynchronous image requests call the asynchronous getURL method\n- // on the layer to fetch an image that covers 'this.bounds'.\n- var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n- this.layer.getURLasync(this.bounds, function(url) {\n- if (id == this.asyncRequestId) {\n- this.url = url;\n- this.initImage();\n- }\n- }, this);\n- } else {\n- // synchronous image requests get the url immediately.\n- this.url = this.layer.getURL(this.bounds);\n- this.initImage();\n- }\n- },\n-\n- /**\n- * Method: positionTile\n- * Using the properties currenty set on the layer, position the tile correctly.\n- * This method is used both by the async and non-async versions of the Tile.Image\n- * code.\n- */\n- positionTile: function() {\n- var style = this.getTile().style,\n- size = this.frame ? this.size :\n- this.layer.getImageSize(this.bounds),\n- ratio = 1;\n- if (this.layer instanceof OpenLayers.Layer.Grid) {\n- ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n- }\n- style.left = this.position.x + \"px\";\n- style.top = this.position.y + \"px\";\n- style.width = Math.round(ratio * size.w) + \"px\";\n- style.height = Math.round(ratio * size.h) + \"px\";\n- },\n-\n- /** \n- * Method: clear\n- * Remove the tile from the DOM, clear it of any image related data so that\n- * it can be reused in a new location.\n- */\n- clear: function() {\n- OpenLayers.Tile.prototype.clear.apply(this, arguments);\n- var img = this.imgDiv;\n- if (img) {\n- var tile = this.getTile();\n- if (tile.parentNode === this.layer.div) {\n- this.layer.div.removeChild(tile);\n- }\n- this.setImgSrc();\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"\";\n- }\n- OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n- }\n- this.canvasContext = null;\n- },\n-\n- /**\n- * Method: getImage\n- * Returns or creates and returns the tile image.\n- */\n- getImage: function() {\n- if (!this.imgDiv) {\n- this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n-\n- var style = this.imgDiv.style;\n- if (this.frame) {\n- var left = 0,\n- top = 0;\n- if (this.layer.gutter) {\n- left = this.layer.gutter / this.layer.tileSize.w * 100;\n- top = this.layer.gutter / this.layer.tileSize.h * 100;\n- }\n- style.left = -left + \"%\";\n- style.top = -top + \"%\";\n- style.width = (2 * left + 100) + \"%\";\n- style.height = (2 * top + 100) + \"%\";\n- }\n- style.visibility = \"hidden\";\n- style.opacity = 0;\n- if (this.layer.opacity < 1) {\n- style.filter = 'alpha(opacity=' +\n- (this.layer.opacity * 100) +\n- ')';\n- }\n- style.position = \"absolute\";\n- if (this.layerAlphaHack) {\n- // move the image out of sight\n- style.paddingTop = style.height;\n- style.height = \"0\";\n- style.width = \"100%\";\n- }\n- if (this.frame) {\n- this.frame.appendChild(this.imgDiv);\n- }\n- }\n-\n- return this.imgDiv;\n- },\n-\n- /**\n- * APIMethod: setImage\n- * Sets the image element for this tile. This method should only be called\n- * from beforeload listeners.\n- *\n- * Parameters\n- * img - {HTMLImageElement} The image to use for this tile.\n- */\n- setImage: function(img) {\n- this.imgDiv = img;\n- },\n-\n- /**\n- * Method: initImage\n- * Creates the content for the frame on the tile.\n- */\n- initImage: function() {\n- if (!this.url && !this.imgDiv) {\n- // fast path out - if there is no tile url and no previous image\n- this.isLoading = false;\n- return;\n- }\n- this.events.triggerEvent('beforeload');\n- this.layer.div.appendChild(this.getTile());\n- this.events.triggerEvent(this._loadEvent);\n- var img = this.getImage();\n- var src = img.getAttribute('src') || '';\n- if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n- this._loadTimeout = window.setTimeout(\n- OpenLayers.Function.bind(this.onImageLoad, this), 0\n- );\n- } else {\n- this.stopLoading();\n- if (this.crossOriginKeyword) {\n- img.removeAttribute(\"crossorigin\");\n- }\n- OpenLayers.Event.observe(img, \"load\",\n- OpenLayers.Function.bind(this.onImageLoad, this)\n- );\n- OpenLayers.Event.observe(img, \"error\",\n- OpenLayers.Function.bind(this.onImageError, this)\n- );\n- this.imageReloadAttempts = 0;\n- this.setImgSrc(this.url);\n- }\n- },\n-\n- /**\n- * Method: setImgSrc\n- * Sets the source for the tile image\n- *\n- * Parameters:\n- * url - {String} or undefined to hide the image\n- */\n- setImgSrc: function(url) {\n- var img = this.imgDiv;\n- if (url) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- // don't set crossOrigin if the url is a data URL\n- if (this.crossOriginKeyword) {\n- if (url.substr(0, 5) !== 'data:') {\n- img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n- } else {\n- img.removeAttribute(\"crossorigin\");\n- }\n- }\n- img.src = url;\n- } else {\n- // Remove reference to the image, and leave it to the browser's\n- // caching and garbage collection.\n- this.stopLoading();\n- this.imgDiv = null;\n- if (img.parentNode) {\n- img.parentNode.removeChild(img);\n- }\n- }\n- },\n-\n- /**\n- * Method: getTile\n- * Get the tile's markup.\n- *\n- * Returns:\n- * {DOMElement} The tile's markup\n- */\n- getTile: function() {\n- return this.frame ? this.frame : this.getImage();\n- },\n-\n- /**\n- * Method: createBackBuffer\n- * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n- * of the tile's markup, because we want to avoid the reloading of the\n- * image. So we clone the frame, and steal the image from the tile.\n- *\n- * Returns:\n- * {DOMElement} The markup, or undefined if the tile has no image\n- * or if it's currently loading.\n- */\n- createBackBuffer: function() {\n- if (!this.imgDiv || this.isLoading) {\n- return;\n- }\n- var backBuffer;\n- if (this.frame) {\n- backBuffer = this.frame.cloneNode(false);\n- backBuffer.appendChild(this.imgDiv);\n- } else {\n- backBuffer = this.imgDiv;\n- }\n- this.imgDiv = null;\n- return backBuffer;\n- },\n-\n- /**\n- * Method: onImageLoad\n- * Handler for the image onload event\n- */\n- onImageLoad: function() {\n- var img = this.imgDiv;\n- this.stopLoading();\n- img.style.visibility = 'inherit';\n- img.style.opacity = this.layer.opacity;\n- this.isLoading = false;\n- this.canvasContext = null;\n- this.events.triggerEvent(\"loadend\");\n-\n- if (this.layerAlphaHack === true) {\n- img.style.filter =\n- \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n- img.src + \"', sizingMethod='scale')\";\n- }\n- },\n-\n- /**\n- * Method: onImageError\n- * Handler for the image onerror event\n- */\n- onImageError: function() {\n- var img = this.imgDiv;\n- if (img.src != null) {\n- this.imageReloadAttempts++;\n- if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n- this.setImgSrc(this.layer.getURL(this.bounds));\n- } else {\n- OpenLayers.Element.addClass(img, \"olImageLoadError\");\n- this.events.triggerEvent(\"loaderror\");\n- this.onImageLoad();\n- }\n- }\n- },\n-\n- /**\n- * Method: stopLoading\n- * Stops a loading sequence so <onImageLoad> won't be executed.\n- */\n- stopLoading: function() {\n- OpenLayers.Event.stopObservingElement(this.imgDiv);\n- window.clearTimeout(this._loadTimeout);\n- delete this._loadTimeout;\n- },\n-\n- /**\n- * APIMethod: getCanvasContext\n- * Returns a canvas context associated with the tile image (with\n- * the image drawn on it).\n- * Returns undefined if the browser does not support canvas, if\n- * the tile has no image or if it's currently loading.\n- *\n- * The function returns a canvas context instance but the\n- * underlying canvas is still available in the 'canvas' property:\n- * (code)\n- * var context = tile.getCanvasContext();\n- * if (context) {\n- * var data = context.canvas.toDataURL('image/jpeg');\n- * }\n- * (end)\n- *\n- * Returns:\n- * {Boolean}\n- */\n- getCanvasContext: function() {\n- if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n- if (!this.canvasContext) {\n- var canvas = document.createElement(\"canvas\");\n- canvas.width = this.size.w;\n- canvas.height = this.size.h;\n- this.canvasContext = canvas.getContext(\"2d\");\n- this.canvasContext.drawImage(this.imgDiv, 0, 0);\n- }\n- return this.canvasContext;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Tile.Image\"\n-\n-});\n-\n-/** \n- * Constant: OpenLayers.Tile.Image.IMAGE\n- * {HTMLImageElement} The image for a tile.\n- */\n-OpenLayers.Tile.Image.IMAGE = (function() {\n- var img = new Image();\n- img.className = \"olTileImage\";\n- // avoid image gallery menu in IE6\n- img.galleryImg = \"no\";\n- return img;\n-}());\n-\n-/* ======================================================================\n- OpenLayers/Layer/Grid.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/HTTPRequest.js\n- * @requires OpenLayers/Tile/Image.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Grid\n- * Base class for layers that use a lattice of tiles. Create a new grid\n- * layer with the <OpenLayers.Layer.Grid> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.HTTPRequest>\n- */\n-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n-\n- /**\n- * APIProperty: tileSize\n- * {<OpenLayers.Size>}\n- */\n- tileSize: null,\n-\n- /**\n- * Property: tileOriginCorner\n- * {String} If the <tileOrigin> property is not provided, the tile origin \n- * will be derived from the layer's <maxExtent>. The corner of the \n- * <maxExtent> used is determined by this property. Acceptable values\n- * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n- * (bottom right). Default is \"bl\".\n- */\n- tileOriginCorner: \"bl\",\n-\n- /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n- * If provided, requests for tiles at all resolutions will be aligned\n- * with this location (no tiles shall overlap this location). If\n- * not provided, the grid of tiles will be aligned with the layer's\n- * <maxExtent>. Default is ``null``.\n- */\n- tileOrigin: null,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer, if supported by the tile class.\n- */\n- tileOptions: null,\n-\n- /**\n- * APIProperty: tileClass\n- * {<OpenLayers.Tile>} The tile class to use for this layer.\n- * Defaults is OpenLayers.Tile.Image.\n- */\n- tileClass: OpenLayers.Tile.Image,\n-\n- /**\n- * Property: grid\n- * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is \n- * an array of tiles.\n- */\n- grid: null,\n-\n- /**\n- * APIProperty: singleTile\n- * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n- * will be loaded. The tile's size will be determined by the 'ratio'\n- * property. When the tile is dragged such that it does not cover the \n- * entire viewport, it is reloaded.\n- */\n- singleTile: false,\n-\n- /** APIProperty: ratio\n- * {Float} Used only when in single-tile mode, this specifies the \n- * ratio of the size of the single tile to the size of the map.\n- * Default value is 1.5.\n- */\n- ratio: 1.5,\n-\n- /**\n- * APIProperty: buffer\n- * {Integer} Used only when in gridded mode, this specifies the number of \n- * extra rows and colums of tiles on each side which will\n- * surround the minimum grid tiles to cover the map.\n- * For very slow loading layers, a larger value may increase\n- * performance somewhat when dragging, but will increase bandwidth\n- * use significantly. \n- */\n- buffer: 0,\n-\n- /**\n- * APIProperty: transitionEffect\n- * {String} The transition effect to use when the map is zoomed.\n- * Two posible values:\n- *\n- * \"resize\" - Existing tiles are resized on zoom to provide a visual\n- * effect of the zoom having taken place immediately. As the\n- * new tiles become available, they are drawn on top of the\n- * resized tiles (this is the default setting).\n- * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n- * base layer. New tiles for the base layer will cover existing tiles.\n- * This setting is recommended when having an overlay duplicated during\n- * the transition is undesirable (e.g. street labels or big transparent\n- * fills). \n- * null - No transition effect.\n- *\n- * Using \"resize\" on non-opaque layers can cause undesired visual\n- * effects. Set transitionEffect to null in this case.\n- */\n- transitionEffect: \"resize\",\n-\n- /**\n- * APIProperty: numLoadingTiles\n- * {Integer} How many tiles are still loading?\n- */\n- numLoadingTiles: 0,\n-\n- /**\n- * Property: serverResolutions\n- * {Array(Number}} This property is documented in subclasses as\n- * an API property.\n- */\n- serverResolutions: null,\n-\n- /**\n- * Property: loading\n- * {Boolean} Indicates if tiles are being loaded.\n- */\n- loading: false,\n-\n- /**\n- * Property: backBuffer\n- * {DOMElement} The back buffer.\n- */\n- backBuffer: null,\n-\n- /**\n- * Property: gridResolution\n- * {Number} The resolution of the current grid. Used for backbuffer and\n- * client zoom. This property is updated every time the grid is\n- * initialized.\n- */\n- gridResolution: null,\n-\n- /**\n- * Property: backBufferResolution\n- * {Number} The resolution of the current back buffer. This property is\n- * updated each time a back buffer is created.\n- */\n- backBufferResolution: null,\n-\n- /**\n- * Property: backBufferLonLat\n- * {Object} The top-left corner of the current back buffer. Includes lon\n- * and lat properties. This object is updated each time a back buffer\n- * is created.\n- */\n- backBufferLonLat: null,\n-\n- /**\n- * Property: backBufferTimerId\n- * {Number} The id of the back buffer timer. This timer is used to\n- * delay the removal of the back buffer, thereby preventing\n- * flash effects caused by tile animation.\n- */\n- backBufferTimerId: null,\n-\n- /**\n- * APIProperty: removeBackBufferDelay\n- * {Number} Delay for removing the backbuffer when all tiles have finished\n- * loading. Can be set to 0 when no css opacity transitions for the\n- * olTileImage class are used. Default is 0 for <singleTile> layers,\n- * 2500 for tiled layers. See <className> for more information on\n- * tile animation.\n- */\n- removeBackBufferDelay: null,\n-\n- /**\n- * APIProperty: className\n- * {String} Name of the class added to the layer div. If not set in the\n- * options passed to the constructor then className defaults to\n- * \"olLayerGridSingleTile\" for single tile layers (see <singleTile>),\n- * and \"olLayerGrid\" for non single tile layers.\n- *\n- * Note:\n- *\n- * The displaying of tiles is not animated by default for single tile\n- * layers - OpenLayers' default theme (style.css) includes this:\n- * (code)\n- * .olLayerGrid .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * To animate tile displaying for any grid layer the following\n- * CSS rule can be used:\n- * (code)\n- * .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * In that case, to avoid flash effects, <removeBackBufferDelay>\n- * should not be zero.\n- */\n- className: null,\n-\n- /**\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n- *\n- * Supported event types:\n- * addtile - Triggered when a tile is added to this layer. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that has been added.\n- * tileloadstart - Triggered when a tile starts loading. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that starts loading.\n- * tileloaded - Triggered when each new tile is\n- * loaded, as a means of progress update to listeners.\n- * listeners can access 'numLoadingTiles' if they wish to keep\n- * track of the loading progress. Listeners are called with an object\n- * with a 'tile' property as first argument, making the loaded tile\n- * available to the listener, and an 'aborted' property, which will be\n- * true when loading was aborted and no tile data is available.\n- * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n- * still hidden) if a tile failed to load. Listeners receive an object\n- * as first argument, which has a tile property that references the\n- * tile that could not be loaded.\n- * retile - Triggered when the layer recreates its tile grid.\n- */\n-\n- /**\n- * Property: gridLayout\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n- */\n- gridLayout: null,\n-\n- /**\n- * Property: rowSign\n- * {Number} 1 for grids starting at the top, -1 for grids starting at the\n- * bottom. This is used for several grid index and offset calculations.\n- */\n- rowSign: null,\n-\n- /**\n- * Property: transitionendEvents\n- * {Array} Event names for transitionend\n- */\n- transitionendEvents: [\n- 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n- 'oTransitionEnd'\n- ],\n-\n- /**\n- * Constructor: OpenLayers.Layer.Grid\n- * Create a new grid layer\n- *\n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n- arguments);\n- this.grid = [];\n- this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n-\n- this.initProperties();\n-\n- this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n- },\n-\n- /**\n- * Method: initProperties\n- * Set any properties that depend on the value of singleTile.\n- * Currently sets removeBackBufferDelay and className\n- */\n- initProperties: function() {\n- if (this.options.removeBackBufferDelay === undefined) {\n- this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n- }\n-\n- if (this.options.className === undefined) {\n- this.className = this.singleTile ? 'olLayerGridSingleTile' :\n- 'olLayerGrid';\n- }\n- },\n-\n- /**\n- * Method: setMap\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} The map.\n- */\n- setMap: function(map) {\n- OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n- OpenLayers.Element.addClass(this.div, this.className);\n- },\n-\n- /**\n- * Method: removeMap\n- * Called when the layer is removed from the map.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} The map.\n- */\n- removeMap: function(map) {\n- this.removeBackBuffer();\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Deconstruct the layer and clear the grid.\n- */\n- destroy: function() {\n- this.removeBackBuffer();\n- this.clearGrid();\n-\n- this.grid = null;\n- this.tileSize = null;\n- OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * Refetches tiles with new params merged, keeping a backbuffer. Each\n- * loading new tile will have a css class of '.olTileReplacing'. If a\n- * stylesheet applies a 'display: none' style to that class, any fade-in\n- * transition will not apply, and backbuffers for each tile will be removed\n- * as soon as the tile is loaded.\n- * \n- * Parameters:\n- * newParams - {Object}\n- *\n- * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n- */\n-\n- /**\n- * Method: clearGrid\n- * Go through and remove all tiles from the grid, calling\n- * destroy() on each of them to kill circular references\n- */\n- clearGrid: function() {\n- if (this.grid) {\n- for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n- var row = this.grid[iRow];\n- for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n- var tile = row[iCol];\n- this.destroyTile(tile);\n- }\n- }\n- this.grid = [];\n- this.gridResolution = null;\n- this.gridLayout = null;\n- }\n- },\n-\n- /**\n- * APIMethod: addOptions\n- * \n- * Parameters:\n- * newOptions - {Object}\n- * reinitialize - {Boolean} If set to true, and if resolution options of the\n- * current baseLayer were changed, the map will be recentered to make\n- * sure that it is displayed with a valid resolution, and a\n- * changebaselayer event will be triggered.\n- */\n- addOptions: function(newOptions, reinitialize) {\n- var singleTileChanged = newOptions.singleTile !== undefined &&\n- newOptions.singleTile !== this.singleTile;\n- OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n- if (this.map && singleTileChanged) {\n- this.initProperties();\n- this.clearGrid();\n- this.tileSize = this.options.tileSize;\n- this.setTileSize();\n- this.moveTo(null, true);\n- }\n- },\n-\n- /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n- * Returns:\n- * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Grid(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone();\n- }\n-\n- // we do not want to copy reference to grid, so we make a new array\n- obj.grid = [];\n- obj.gridResolution = null;\n- // same for backbuffer\n- obj.backBuffer = null;\n- obj.backBufferTimerId = null;\n- obj.loading = false;\n- obj.numLoadingTiles = 0;\n-\n- return obj;\n- },\n-\n- /**\n- * Method: moveTo\n- * This function is called whenever the map is moved. All the moving\n- * of actual 'tiles' is done by the map, but moveTo's role is to accept\n- * a bounds and make sure the data that that bounds requires is pre-loaded.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n-\n- OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n-\n- bounds = bounds || this.map.getExtent();\n-\n- if (bounds != null) {\n-\n- // if grid is empty or zoom has changed, we *must* re-tile\n- var forceReTile = !this.grid.length || zoomChanged;\n-\n- // total bounds of the tiles\n- var tilesBounds = this.getTilesBounds();\n-\n- // the new map resolution\n- var resolution = this.map.getResolution();\n-\n- // the server-supported resolution for the new map resolution\n- var serverResolution = this.getServerResolution(resolution);\n-\n- if (this.singleTile) {\n-\n- // We want to redraw whenever even the slightest part of the \n- // current bounds is not contained by our tile.\n- // (thus, we do not specify partial -- its default is false)\n-\n- if (forceReTile ||\n- (!dragging && !tilesBounds.containsBounds(bounds))) {\n-\n- // In single tile mode with no transition effect, we insert\n- // a non-scaled backbuffer when the layer is moved. But if\n- // a zoom occurs right after a move, i.e. before the new\n- // image is received, we need to remove the backbuffer, or\n- // an ill-positioned image will be visible during the zoom\n- // transition.\n-\n- if (zoomChanged && this.transitionEffect !== 'resize') {\n- this.removeBackBuffer();\n- }\n-\n- if (!zoomChanged || this.transitionEffect === 'resize') {\n- this.applyBackBuffer(resolution);\n- }\n-\n- this.initSingleTile(bounds);\n- }\n- } else {\n-\n- // if the bounds have changed such that they are not even \n- // *partially* contained by our tiles (e.g. when user has \n- // programmatically panned to the other side of the earth on\n- // zoom level 18), then moveGriddedTiles could potentially have\n- // to run through thousands of cycles, so we want to reTile\n- // instead (thus, partial true). \n- forceReTile = forceReTile ||\n- !tilesBounds.intersectsBounds(bounds, {\n- worldBounds: this.map.baseLayer.wrapDateLine &&\n- this.map.getMaxExtent()\n- });\n-\n- if (forceReTile) {\n- if (zoomChanged && (this.transitionEffect === 'resize' ||\n- this.gridResolution === resolution)) {\n- this.applyBackBuffer(resolution);\n- }\n- this.initGriddedTiles(bounds);\n- } else {\n- this.moveGriddedTiles();\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: getTileData\n- * Given a map location, retrieve a tile and the pixel offset within that\n- * tile corresponding to the location. If there is not an existing \n- * tile in the grid that covers the given location, null will be \n- * returned.\n- *\n- * Parameters:\n- * loc - {<OpenLayers.LonLat>} map location\n- *\n- * Returns:\n- * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),\n- * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n- * offset from top left).\n- */\n- getTileData: function(loc) {\n- var data = null,\n- x = loc.lon,\n- y = loc.lat,\n- numRows = this.grid.length;\n-\n- if (this.map && numRows) {\n- var res = this.map.getResolution(),\n- tileWidth = this.tileSize.w,\n- tileHeight = this.tileSize.h,\n- bounds = this.grid[0][0].bounds,\n- left = bounds.left,\n- top = bounds.top;\n-\n- if (x < left) {\n- // deal with multiple worlds\n- if (this.map.baseLayer.wrapDateLine) {\n- var worldWidth = this.map.getMaxExtent().getWidth();\n- var worldsAway = Math.ceil((left - x) / worldWidth);\n- x += worldWidth * worldsAway;\n- }\n- }\n- // tile distance to location (fractional number of tiles);\n- var dtx = (x - left) / (res * tileWidth);\n- var dty = (top - y) / (res * tileHeight);\n- // index of tile in grid\n- var col = Math.floor(dtx);\n- var row = Math.floor(dty);\n- if (row >= 0 && row < numRows) {\n- var tile = this.grid[row][col];\n- if (tile) {\n- data = {\n- tile: tile,\n- // pixel index within tile\n- i: Math.floor((dtx - col) * tileWidth),\n- j: Math.floor((dty - row) * tileHeight)\n- };\n- }\n- }\n- }\n- return data;\n- },\n-\n- /**\n- * Method: destroyTile\n- *\n- * Parameters:\n- * tile - {<OpenLayers.Tile>}\n- */\n- destroyTile: function(tile) {\n- this.removeTileMonitoringHooks(tile);\n- tile.destroy();\n- },\n-\n- /**\n- * Method: getServerResolution\n- * Return the closest server-supported resolution.\n- *\n- * Parameters:\n- * resolution - {Number} The base resolution. If undefined the\n- * map resolution is used.\n- *\n- * Returns:\n- * {Number} The closest server resolution value.\n- */\n- getServerResolution: function(resolution) {\n- var distance = Number.POSITIVE_INFINITY;\n- resolution = resolution || this.map.getResolution();\n- if (this.serverResolutions &&\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n- var i, newDistance, newResolution, serverResolution;\n- for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n- newResolution = this.serverResolutions[i];\n- newDistance = Math.abs(newResolution - resolution);\n- if (newDistance > distance) {\n- break;\n- }\n- distance = newDistance;\n- serverResolution = newResolution;\n- }\n- resolution = serverResolution;\n- }\n- return resolution;\n- },\n-\n- /**\n- * Method: getServerZoom\n- * Return the zoom value corresponding to the best matching server\n- * resolution, taking into account <serverResolutions> and <zoomOffset>.\n- *\n- * Returns:\n- * {Number} The closest server supported zoom. This is not the map zoom\n- * level, but an index of the server's resolutions array.\n- */\n- getServerZoom: function() {\n- var resolution = this.getServerResolution();\n- return this.serverResolutions ?\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n- this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n- },\n-\n- /**\n- * Method: applyBackBuffer\n- * Create, insert, scale and position a back buffer for the layer.\n- *\n- * Parameters:\n- * resolution - {Number} The resolution to transition to.\n- */\n- applyBackBuffer: function(resolution) {\n- if (this.backBufferTimerId !== null) {\n- this.removeBackBuffer();\n- }\n- var backBuffer = this.backBuffer;\n- if (!backBuffer) {\n- backBuffer = this.createBackBuffer();\n- if (!backBuffer) {\n- return;\n- }\n- if (resolution === this.gridResolution) {\n- this.div.insertBefore(backBuffer, this.div.firstChild);\n- } else {\n- this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n- }\n- this.backBuffer = backBuffer;\n-\n- // set some information in the instance for subsequent\n- // calls to applyBackBuffer where the same back buffer\n- // is reused\n- var topLeftTileBounds = this.grid[0][0].bounds;\n- this.backBufferLonLat = {\n- lon: topLeftTileBounds.left,\n- lat: topLeftTileBounds.top\n- };\n- this.backBufferResolution = this.gridResolution;\n- }\n-\n- var ratio = this.backBufferResolution / resolution;\n-\n- // scale the tiles inside the back buffer\n- var tiles = backBuffer.childNodes,\n- tile;\n- for (var i = tiles.length - 1; i >= 0; --i) {\n- tile = tiles[i];\n- tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n- tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n- tile.style.width = Math.round(ratio * tile._w) + 'px';\n- tile.style.height = Math.round(ratio * tile._h) + 'px';\n- }\n-\n- // and position it (based on the grid's top-left corner)\n- var position = this.getViewPortPxFromLonLat(\n- this.backBufferLonLat, resolution);\n- var leftOffset = this.map.layerContainerOriginPx.x;\n- var topOffset = this.map.layerContainerOriginPx.y;\n- backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n- backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n- },\n-\n- /**\n- * Method: createBackBuffer\n- * Create a back buffer.\n- *\n- * Returns:\n- * {DOMElement} The DOM element for the back buffer, undefined if the\n- * grid isn't initialized yet.\n- */\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.grid.length > 0) {\n- backBuffer = document.createElement('div');\n- backBuffer.id = this.div.id + '_bb';\n- backBuffer.className = 'olBackBuffer';\n- backBuffer.style.position = 'absolute';\n- var map = this.map;\n- backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n- this.getZIndex() - 1 :\n- // 'map-resize':\n- map.Z_INDEX_BASE.BaseLayer -\n- (map.getNumLayers() - map.getLayerIndex(this));\n- for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n- for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n- var tile = this.grid[i][j],\n- markup = this.grid[i][j].createBackBuffer();\n- if (markup) {\n- markup._i = i;\n- markup._j = j;\n- markup._w = tile.size.w;\n- markup._h = tile.size.h;\n- markup.id = tile.id + '_bb';\n- backBuffer.appendChild(markup);\n- }\n- }\n- }\n- }\n- return backBuffer;\n- },\n-\n- /**\n- * Method: removeBackBuffer\n- * Remove back buffer from DOM.\n- */\n- removeBackBuffer: function() {\n- if (this._transitionElement) {\n- for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.stopObserving(this._transitionElement,\n- this.transitionendEvents[i], this._removeBackBuffer);\n- }\n- delete this._transitionElement;\n- }\n- if (this.backBuffer) {\n- if (this.backBuffer.parentNode) {\n- this.backBuffer.parentNode.removeChild(this.backBuffer);\n- }\n- this.backBuffer = null;\n- this.backBufferResolution = null;\n- if (this.backBufferTimerId !== null) {\n- window.clearTimeout(this.backBufferTimerId);\n- this.backBufferTimerId = null;\n- }\n- }\n- },\n-\n- /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n- */\n- moveByPx: function(dx, dy) {\n- if (!this.singleTile) {\n- this.moveGriddedTiles();\n- }\n- },\n-\n- /**\n- * APIMethod: setTileSize\n- * Check if we are in singleTile mode and if so, set the size as a ratio\n- * of the map size (as specified by the layer's 'ratio' property).\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>}\n- */\n- setTileSize: function(size) {\n- if (this.singleTile) {\n- size = this.map.getSize();\n- size.h = parseInt(size.h * this.ratio, 10);\n- size.w = parseInt(size.w * this.ratio, 10);\n- }\n- OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n- },\n-\n- /**\n- * APIMethod: getTilesBounds\n- * Return the bounds of the tile grid.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the\n- * currently loaded tiles (including those partially or not at all seen \n- * onscreen).\n- */\n- getTilesBounds: function() {\n- var bounds = null;\n-\n- var length = this.grid.length;\n- if (length) {\n- var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n- width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n- height = this.grid.length * bottomLeftTileBounds.getHeight();\n-\n- bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n- bottomLeftTileBounds.bottom,\n- bottomLeftTileBounds.left + width,\n- bottomLeftTileBounds.bottom + height);\n- }\n- return bounds;\n- },\n-\n- /**\n- * Method: initSingleTile\n- * \n- * Parameters: \n- * bounds - {<OpenLayers.Bounds>}\n- */\n- initSingleTile: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- //determine new tile bounds\n- var center = bounds.getCenterLonLat();\n- var tileWidth = bounds.getWidth() * this.ratio;\n- var tileHeight = bounds.getHeight() * this.ratio;\n-\n- var tileBounds =\n- new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n- center.lat - (tileHeight / 2),\n- center.lon + (tileWidth / 2),\n- center.lat + (tileHeight / 2));\n-\n- var px = this.map.getLayerPxFromLonLat({\n- lon: tileBounds.left,\n- lat: tileBounds.top\n- });\n-\n- if (!this.grid.length) {\n- this.grid[0] = [];\n- }\n-\n- var tile = this.grid[0][0];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n-\n- this.addTileMonitoringHooks(tile);\n- tile.draw();\n- this.grid[0][0] = tile;\n- } else {\n- tile.moveTo(tileBounds, px);\n- }\n-\n- //remove all but our single tile\n- this.removeExcessTiles(1, 1);\n-\n- // store the resolution of the grid\n- this.gridResolution = this.getServerResolution();\n- },\n-\n- /** \n- * Method: calculateGridLayout\n- * Generate parameters for the grid layout.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an\n- * object with a 'left' and 'top' properties.\n- * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * resolution - {Number}\n- *\n- * Returns:\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n- */\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n-\n- var offsetlon = bounds.left - origin.lon;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n-\n- var rowSign = this.rowSign;\n-\n- var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n- var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\n-\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n- };\n-\n- },\n-\n- /**\n- * Method: getTileOrigin\n- * Determine the origin for aligning the grid of tiles. If a <tileOrigin>\n- * property is supplied, that will be returned. Otherwise, the origin\n- * will be derived from the layer's <maxExtent> property. In this case,\n- * the tile origin will be the corner of the <maxExtent> given by the \n- * <tileOriginCorner> property.\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} The tile origin.\n- */\n- getTileOrigin: function() {\n- var origin = this.tileOrigin;\n- if (!origin) {\n- var extent = this.getMaxExtent();\n- var edges = ({\n- \"tl\": [\"left\", \"top\"],\n- \"tr\": [\"right\", \"top\"],\n- \"bl\": [\"left\", \"bottom\"],\n- \"br\": [\"right\", \"bottom\"]\n- })[this.tileOriginCorner];\n- origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n- }\n- return origin;\n- },\n-\n- /**\n- * Method: getTileBoundsForGridIndex\n- *\n- * Parameters:\n- * row - {Number} The row of the grid\n- * col - {Number} The column of the grid\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n- */\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var startcol = tileLayout.startcol;\n- var startrow = tileLayout.startrow;\n- var rowSign = this.rowSign;\n- return new OpenLayers.Bounds(\n- origin.lon + (startcol + col) * tilelon,\n- origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n- origin.lon + (startcol + col + 1) * tilelon,\n- origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n- );\n- },\n-\n- /**\n- * Method: initGriddedTiles\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- initGriddedTiles: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- // work out mininum number of rows and columns; this is the number of\n- // tiles required to cover the viewport plus at least one for panning\n-\n- var viewSize = this.map.getSize();\n-\n- var origin = this.getTileOrigin();\n- var resolution = this.map.getResolution(),\n- serverResolution = this.getServerResolution(),\n- ratio = resolution / serverResolution,\n- tileSize = {\n- w: this.tileSize.w / ratio,\n- h: this.tileSize.h / ratio\n- };\n-\n- var minRows = Math.ceil(viewSize.h / tileSize.h) +\n- 2 * this.buffer + 1;\n- var minCols = Math.ceil(viewSize.w / tileSize.w) +\n- 2 * this.buffer + 1;\n-\n- var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n- this.gridLayout = tileLayout;\n-\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n-\n- var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n- var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n-\n- var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n- var startPx = this.map.getViewPortPxFromLonLat(\n- new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n- );\n- startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n- startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n-\n- var tileData = [],\n- center = this.map.getCenter();\n-\n- var rowidx = 0;\n- do {\n- var row = this.grid[rowidx];\n- if (!row) {\n- row = [];\n- this.grid.push(row);\n- }\n-\n- var colidx = 0;\n- do {\n- tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n- var px = startPx.clone();\n- px.x = px.x + colidx * Math.round(tileSize.w);\n- px.y = px.y + rowidx * Math.round(tileSize.h);\n- var tile = row[colidx];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- row.push(tile);\n- } else {\n- tile.moveTo(tileBounds, px, false);\n- }\n- var tileCenter = tileBounds.getCenterLonLat();\n- tileData.push({\n- tile: tile,\n- distance: Math.pow(tileCenter.lon - center.lon, 2) +\n- Math.pow(tileCenter.lat - center.lat, 2)\n- });\n-\n- colidx += 1;\n- } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n- colidx < minCols);\n-\n- rowidx += 1;\n- } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n- rowidx < minRows);\n-\n- //shave off exceess rows and colums\n- this.removeExcessTiles(rowidx, colidx);\n-\n- var resolution = this.getServerResolution();\n- // store the resolution of the grid\n- this.gridResolution = resolution;\n-\n- //now actually draw the tiles\n- tileData.sort(function(a, b) {\n- return a.distance - b.distance;\n- });\n- for (var i = 0, ii = tileData.length; i < ii; ++i) {\n- tileData[i].tile.draw();\n- }\n- },\n-\n- /**\n- * Method: getMaxExtent\n- * Get this layer's maximum extent. (Implemented as a getter for\n- * potential specific implementations in sub-classes.)\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getMaxExtent: function() {\n- return this.maxExtent;\n- },\n-\n- /**\n- * APIMethod: addTile\n- * Create a tile, initialize it, and add it to the layer div. \n- *\n- * Parameters\n- * bounds - {<OpenLayers.Bounds>}\n- * position - {<OpenLayers.Pixel>}\n- *\n- * Returns:\n- * {<OpenLayers.Tile>} The added OpenLayers.Tile\n- */\n- addTile: function(bounds, position) {\n- var tile = new this.tileClass(\n- this, position, bounds, null, this.tileSize, this.tileOptions\n- );\n- this.events.triggerEvent(\"addtile\", {\n- tile: tile\n- });\n- return tile;\n- },\n-\n- /** \n- * Method: addTileMonitoringHooks\n- * This function takes a tile as input and adds the appropriate hooks to \n- * the tile so that the layer can keep track of the loading tiles.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n- */\n- addTileMonitoringHooks: function(tile) {\n-\n- var replacingCls = 'olTileReplacing';\n-\n- tile.onLoadStart = function() {\n- //if that was first tile then trigger a 'loadstart' on the layer\n- if (this.loading === false) {\n- this.loading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.events.triggerEvent(\"tileloadstart\", {\n- tile: tile\n- });\n- this.numLoadingTiles++;\n- if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n- }\n- };\n-\n- tile.onLoadEnd = function(evt) {\n- this.numLoadingTiles--;\n- var aborted = evt.type === 'unload';\n- this.events.triggerEvent(\"tileloaded\", {\n- tile: tile,\n- aborted: aborted\n- });\n- if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- var tileDiv = tile.getTile();\n- if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n- var bufferTile = document.getElementById(tile.id + '_bb');\n- if (bufferTile) {\n- bufferTile.parentNode.removeChild(bufferTile);\n- }\n- }\n- OpenLayers.Element.removeClass(tileDiv, replacingCls);\n- }\n- //if that was the last tile, then trigger a 'loadend' on the layer\n- if (this.numLoadingTiles === 0) {\n- if (this.backBuffer) {\n- if (this.backBuffer.childNodes.length === 0) {\n- // no tiles transitioning, remove immediately\n- this.removeBackBuffer();\n- } else {\n- // wait until transition has ended or delay has passed\n- this._transitionElement = aborted ?\n- this.div.lastChild : tile.imgDiv;\n- var transitionendEvents = this.transitionendEvents;\n- for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.observe(this._transitionElement,\n- transitionendEvents[i],\n- this._removeBackBuffer);\n- }\n- // the removal of the back buffer is delayed to prevent\n- // flash effects due to the animation of tile displaying\n- this.backBufferTimerId = window.setTimeout(\n- this._removeBackBuffer, this.removeBackBufferDelay\n- );\n- }\n- }\n- this.loading = false;\n- this.events.triggerEvent(\"loadend\");\n- }\n- };\n-\n- tile.onLoadError = function() {\n- this.events.triggerEvent(\"tileerror\", {\n- tile: tile\n- });\n- };\n-\n- tile.events.on({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n-\n- /** \n- * Method: removeTileMonitoringHooks\n- * This function takes a tile as input and removes the tile hooks \n- * that were added in addTileMonitoringHooks()\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n- */\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: moveGriddedTiles\n- */\n- moveGriddedTiles: function() {\n- var buffer = this.buffer + 1;\n- while (true) {\n- var tlTile = this.grid[0][0];\n- var tlViewPort = {\n- x: tlTile.position.x +\n- this.map.layerContainerOriginPx.x,\n- y: tlTile.position.y +\n- this.map.layerContainerOriginPx.y\n- };\n- var ratio = this.getServerResolution() / this.map.getResolution();\n- var tileSize = {\n- w: Math.round(this.tileSize.w * ratio),\n- h: Math.round(this.tileSize.h * ratio)\n- };\n- if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n- this.shiftColumn(true, tileSize);\n- } else if (tlViewPort.x < -tileSize.w * buffer) {\n- this.shiftColumn(false, tileSize);\n- } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n- this.shiftRow(true, tileSize);\n- } else if (tlViewPort.y < -tileSize.h * buffer) {\n- this.shiftRow(false, tileSize);\n- } else {\n- break;\n- }\n- }\n- },\n-\n- /**\n- * Method: shiftRow\n- * Shifty grid work\n- *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n- */\n- shiftRow: function(prepend, tileSize) {\n- var grid = this.grid;\n- var rowIndex = prepend ? 0 : (grid.length - 1);\n- var sign = prepend ? -1 : 1;\n- var rowSign = this.rowSign;\n- var tileLayout = this.gridLayout;\n- tileLayout.startrow += sign * rowSign;\n-\n- var modelRow = grid[rowIndex];\n- var row = grid[prepend ? 'pop' : 'shift']();\n- for (var i = 0, len = row.length; i < len; i++) {\n- var tile = row[i];\n- var position = modelRow[i].position.clone();\n- position.y += tileSize.h * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n- }\n- grid[prepend ? 'unshift' : 'push'](row);\n- },\n-\n- /**\n- * Method: shiftColumn\n- * Shift grid work in the other dimension\n- *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n- */\n- shiftColumn: function(prepend, tileSize) {\n- var grid = this.grid;\n- var colIndex = prepend ? 0 : (grid[0].length - 1);\n- var sign = prepend ? -1 : 1;\n- var tileLayout = this.gridLayout;\n- tileLayout.startcol += sign;\n-\n- for (var i = 0, len = grid.length; i < len; i++) {\n- var row = grid[i];\n- var position = row[colIndex].position.clone();\n- var tile = row[prepend ? 'pop' : 'shift']();\n- position.x += tileSize.w * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n- row[prepend ? 'unshift' : 'push'](tile);\n- }\n- },\n-\n- /**\n- * Method: removeExcessTiles\n- * When the size of the map or the buffer changes, we may need to\n- * remove some excess rows and columns.\n- * \n- * Parameters:\n- * rows - {Integer} Maximum number of rows we want our grid to have.\n- * columns - {Integer} Maximum number of columns we want our grid to have.\n- */\n- removeExcessTiles: function(rows, columns) {\n- var i, l;\n-\n- // remove extra rows\n- while (this.grid.length > rows) {\n- var row = this.grid.pop();\n- for (i = 0, l = row.length; i < l; i++) {\n- var tile = row[i];\n- this.destroyTile(tile);\n- }\n- }\n-\n- // remove extra columns\n- for (i = 0, l = this.grid.length; i < l; i++) {\n- while (this.grid[i].length > columns) {\n- var row = this.grid[i];\n- var tile = row.pop();\n- this.destroyTile(tile);\n- }\n- }\n- },\n-\n- /**\n- * Method: onMapResize\n- * For singleTile layers, this will set a new tile size according to the\n- * dimensions of the map pane.\n- */\n- onMapResize: function() {\n- if (this.singleTile) {\n- this.clearGrid();\n- this.setTileSize();\n- }\n- },\n-\n- /**\n- * APIMethod: getTileBounds\n- * Returns The tile bounds for a layer given a pixel location.\n- *\n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n- */\n- getTileBounds: function(viewPortPx) {\n- var maxExtent = this.maxExtent;\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = maxExtent.left + (tileMapWidth *\n- Math.floor((mapPoint.lon -\n- maxExtent.left) /\n- tileMapWidth));\n- var tileBottom = maxExtent.bottom + (tileMapHeight *\n- Math.floor((mapPoint.lat -\n- maxExtent.bottom) /\n- tileMapHeight));\n- return new OpenLayers.Bounds(tileLeft, tileBottom,\n- tileLeft + tileMapWidth,\n- tileBottom + tileMapHeight);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Grid\"\n-});\n-/* ======================================================================\n- OpenLayers/TileManager.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes.js\n- * @requires OpenLayers/BaseTypes/Element.js\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Tile/Image.js\n- */\n-\n-/**\n- * Class: OpenLayers.TileManager\n- * Provides queueing of image requests and caching of image elements.\n- *\n- * Queueing avoids unnecessary image requests while changing zoom levels\n- * quickly, and helps improve dragging performance on mobile devices that show\n- * a lag in dragging when loading of new images starts. <zoomDelay> and\n- * <moveDelay> are the configuration options to control this behavior.\n- *\n- * Caching avoids setting the src on image elements for images that have already\n- * been used. Several maps can share a TileManager instance, in which case each\n- * map gets its own tile queue, but all maps share the same tile cache.\n- */\n-OpenLayers.TileManager = OpenLayers.Class({\n-\n- /**\n- * APIProperty: cacheSize\n- * {Number} Number of image elements to keep referenced in this instance's\n- * cache for fast reuse. Default is 256.\n- */\n- cacheSize: 256,\n-\n- /**\n- * APIProperty: tilesPerFrame\n- * {Number} Number of queued tiles to load per frame (see <frameDelay>).\n- * Default is 2.\n- */\n- tilesPerFrame: 2,\n-\n- /**\n- * APIProperty: frameDelay\n- * {Number} Delay between tile loading frames (see <tilesPerFrame>) in\n- * milliseconds. Default is 16.\n- */\n- frameDelay: 16,\n-\n- /**\n- * APIProperty: moveDelay\n- * {Number} Delay in milliseconds after a map's move event before loading\n- * tiles. Default is 100.\n- */\n- moveDelay: 100,\n-\n- /**\n- * APIProperty: zoomDelay\n- * {Number} Delay in milliseconds after a map's zoomend event before loading\n- * tiles. Default is 200.\n- */\n- zoomDelay: 200,\n-\n- /**\n- * Property: maps\n- * {Array(<OpenLayers.Map>)} The maps to manage tiles on.\n- */\n- maps: null,\n-\n- /**\n- * Property: tileQueueId\n- * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.\n- */\n- tileQueueId: null,\n-\n- /**\n- * Property: tileQueue\n- * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by\n- * map id.\n- */\n- tileQueue: null,\n-\n- /**\n- * Property: tileCache\n- * {Object} Cached image elements, keyed by URL.\n- */\n- tileCache: null,\n-\n- /**\n- * Property: tileCacheIndex\n- * {Array(String)} URLs of cached tiles. First entry is the least recently\n- * used.\n- */\n- tileCacheIndex: null,\n-\n- /** \n- * Constructor: OpenLayers.TileManager\n- * Constructor for a new <OpenLayers.TileManager> instance.\n- * \n- * Parameters:\n- * options - {Object} Configuration for this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.maps = [];\n- this.tileQueueId = {};\n- this.tileQueue = {};\n- this.tileCache = {};\n- this.tileCacheIndex = [];\n- },\n-\n- /**\n- * Method: addMap\n- * Binds this instance to a map\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- addMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- this.maps.push(map);\n- this.tileQueue[map.id] = [];\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.addLayer({\n- layer: map.layers[i]\n- });\n- }\n- map.events.on({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: removeMap\n- * Unbinds this instance from a map\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- window.clearTimeout(this.tileQueueId[map.id]);\n- if (map.layers) {\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.removeLayer({\n- layer: map.layers[i]\n- });\n- }\n- }\n- if (map.events) {\n- map.events.un({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n- }\n- delete this.tileQueue[map.id];\n- delete this.tileQueueId[map.id];\n- OpenLayers.Util.removeItem(this.maps, map);\n- },\n-\n- /**\n- * Method: move\n- * Handles the map's move event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- move: function(evt) {\n- this.updateTimeout(evt.object, this.moveDelay, true);\n- },\n-\n- /**\n- * Method: zoomEnd\n- * Handles the map's zoomEnd event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- zoomEnd: function(evt) {\n- this.updateTimeout(evt.object, this.zoomDelay);\n- },\n-\n- /**\n- * Method: changeLayer\n- * Handles the map's changeLayer event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- changeLayer: function(evt) {\n- if (evt.property === 'visibility' || evt.property === 'params') {\n- this.updateTimeout(evt.object, 0);\n- }\n- },\n-\n- /**\n- * Method: addLayer\n- * Handles the map's addlayer event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- addLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- layer.events.on({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.addTile({\n- tile: tile\n- });\n- if (tile.url && !tile.imgDiv) {\n- this.manageTileCache({\n- object: tile\n- });\n- }\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: removeLayer\n- * Handles the map's preremovelayer event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- removeLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- this.clearTileQueue({\n- object: layer\n- });\n- if (layer.events) {\n- layer.events.un({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- }\n- if (layer.grid) {\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.unloadTile({\n- object: tile\n- });\n- }\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: updateTimeout\n- * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,\n- * and schedules more queue processing after <frameDelay> if there are still\n- * tiles in the queue.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} The map to update the timeout for\n- * delay - {Number} The delay to apply\n- * nice - {Boolean} If true, the timeout function will only be created if\n- * the tilequeue is not empty. This is used by the move handler to\n- * avoid impacts on dragging performance. For other events, the tile\n- * queue may not be populated yet, so we need to set the timer\n- * regardless of the queue size.\n- */\n- updateTimeout: function(map, delay, nice) {\n- window.clearTimeout(this.tileQueueId[map.id]);\n- var tileQueue = this.tileQueue[map.id];\n- if (!nice || tileQueue.length) {\n- this.tileQueueId[map.id] = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.drawTilesFromQueue(map);\n- if (tileQueue.length) {\n- this.updateTimeout(map, this.frameDelay);\n- }\n- }, this), delay\n- );\n- }\n- },\n-\n- /**\n- * Method: addTile\n- * Listener for the layer's addtile event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- addTile: function(evt) {\n- if (evt.tile instanceof OpenLayers.Tile.Image) {\n- evt.tile.events.on({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- } else {\n- // Layer has the wrong tile type, so don't handle it any longer\n- this.removeLayer({\n- layer: evt.tile.layer\n- });\n- }\n- },\n-\n- /**\n- * Method: unloadTile\n- * Listener for the tile's unload event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- unloadTile: function(evt) {\n- var tile = evt.object;\n- tile.events.un({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n- },\n-\n- /**\n- * Method: queueTileDraw\n- * Adds a tile to the queue that will draw it.\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the tile's beforedraw event\n- */\n- queueTileDraw: function(evt) {\n- var tile = evt.object;\n- var queued = false;\n- var layer = tile.layer;\n- var url = layer.getURL(tile.bounds);\n- var img = this.tileCache[url];\n- if (img && img.className !== 'olTileImage') {\n- // cached image no longer valid, e.g. because we're olTileReplacing\n- delete this.tileCache[url];\n- OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n- img = null;\n- }\n- // queue only if image with same url not cached already\n- if (layer.url && (layer.async || !img)) {\n- // add to queue only if not in queue already\n- var tileQueue = this.tileQueue[layer.map.id];\n- if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n- tileQueue.push(tile);\n- }\n- queued = true;\n- }\n- return !queued;\n- },\n-\n- /**\n- * Method: drawTilesFromQueue\n- * Draws tiles from the tileQueue, and unqueues the tiles\n- */\n- drawTilesFromQueue: function(map) {\n- var tileQueue = this.tileQueue[map.id];\n- var limit = this.tilesPerFrame;\n- var animating = map.zoomTween && map.zoomTween.playing;\n- while (!animating && tileQueue.length && limit) {\n- tileQueue.shift().draw(true);\n- --limit;\n- }\n- },\n-\n- /**\n- * Method: manageTileCache\n- * Adds, updates, removes and fetches cache entries.\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the tile's beforeload event\n- */\n- manageTileCache: function(evt) {\n- var tile = evt.object;\n- var img = this.tileCache[tile.url];\n- if (img) {\n- // if image is on its layer's backbuffer, remove it from backbuffer\n- if (img.parentNode &&\n- OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n- img.parentNode.removeChild(img);\n- img.id = null;\n- }\n- // only use image from cache if it is not on a layer already\n- if (!img.parentNode) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- tile.setImage(img);\n- // LRU - move tile to the end of the array to mark it as the most\n- // recently used\n- OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n- this.tileCacheIndex.push(tile.url);\n- }\n- }\n- },\n-\n- /**\n- * Method: addToCache\n- *\n- * Parameters:\n- * evt - {Object} Listener argument for the tile's loadend event\n- */\n- addToCache: function(evt) {\n- var tile = evt.object;\n- if (!this.tileCache[tile.url]) {\n- if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n- if (this.tileCacheIndex.length >= this.cacheSize) {\n- delete this.tileCache[this.tileCacheIndex[0]];\n- this.tileCacheIndex.shift();\n- }\n- this.tileCache[tile.url] = tile.imgDiv;\n- this.tileCacheIndex.push(tile.url);\n- }\n- }\n- },\n-\n- /**\n- * Method: clearTileQueue\n- * Clears the tile queue from tiles of a specific layer\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the layer's retile event\n- */\n- clearTileQueue: function(evt) {\n- var layer = evt.object;\n- var tileQueue = this.tileQueue[layer.map.id];\n- for (var i = tileQueue.length - 1; i >= 0; --i) {\n- if (tileQueue[i].layer === layer) {\n- tileQueue.splice(i, 1);\n- }\n- }\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- for (var i = this.maps.length - 1; i >= 0; --i) {\n- this.removeMap(this.maps[i]);\n- }\n- this.maps = null;\n- this.tileQueue = null;\n- this.tileQueueId = null;\n- this.tileCache = null;\n- this.tileCacheIndex = null;\n- this._destroyed = true;\n- }\n-\n-});\n-/* ======================================================================\n- OpenLayers/Control.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control\n- * Controls affect the display or behavior of the map. They allow everything\n- * from panning and zooming to displaying a scale indicator. Controls by \n- * default are added to the map they are contained within however it is\n- * possible to add a control to an external div by passing the div in the\n- * options parameter.\n- * \n- * Example:\n- * The following example shows how to add many of the common controls\n- * to a map.\n- * \n- * > var map = new OpenLayers.Map('map', { controls: [] });\n- * >\n- * > map.addControl(new OpenLayers.Control.PanZoomBar());\n- * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));\n- * > map.addControl(new OpenLayers.Control.Permalink());\n- * > map.addControl(new OpenLayers.Control.Permalink('permalink'));\n- * > map.addControl(new OpenLayers.Control.MousePosition());\n- * > map.addControl(new OpenLayers.Control.OverviewMap());\n- * > map.addControl(new OpenLayers.Control.KeyboardDefaults());\n- *\n- * The next code fragment is a quick example of how to intercept \n- * shift-mouse click to display the extent of the bounding box\n- * dragged out by the user. Usually controls are not created\n- * in exactly this manner. See the source for a more complete \n- * example:\n- *\n- * > var control = new OpenLayers.Control();\n- * > OpenLayers.Util.extend(control, {\n- * > draw: function () {\n- * > // this Handler.Box will intercept the shift-mousedown\n- * > // before Control.MouseDefault gets to see it\n- * > this.box = new OpenLayers.Handler.Box( control, \n- * > {\"done\": this.notice},\n- * > {keyMask: OpenLayers.Handler.MOD_SHIFT});\n- * > this.box.activate();\n- * > },\n- * >\n- * > notice: function (bounds) {\n- * > OpenLayers.Console.userError(bounds);\n- * > }\n- * > }); \n- * > map.addControl(control);\n- * \n- */\n-OpenLayers.Control = OpenLayers.Class({\n-\n- /** \n- * Property: id \n- * {String} \n- */\n- id: null,\n-\n- /** \n- * Property: map \n- * {<OpenLayers.Map>} this gets set in the addControl() function in\n- * OpenLayers.Map \n- */\n- map: null,\n-\n- /** \n- * APIProperty: div \n- * {DOMElement} The element that contains the control, if not present the \n- * control is placed inside the map.\n- */\n- div: null,\n-\n- /** \n- * APIProperty: type \n- * {Number} Controls can have a 'type'. The type determines the type of\n- * interactions which are possible with them when they are placed in an\n- * <OpenLayers.Control.Panel>. \n- */\n- type: null,\n-\n- /** \n- * Property: allowSelection\n- * {Boolean} By default, controls do not allow selection, because\n- * it may interfere with map dragging. If this is true, OpenLayers\n- * will not prevent selection of the control.\n- * Default is false.\n- */\n- allowSelection: false,\n-\n- /** \n- * Property: displayClass \n- * {string} This property is used for CSS related to the drawing of the\n- * Control. \n- */\n- displayClass: \"\",\n-\n- /**\n- * APIProperty: title \n- * {string} This property is used for showing a tooltip over the \n- * Control. \n- */\n- title: \"\",\n-\n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * false.\n- */\n- autoActivate: false,\n-\n- /** \n- * APIProperty: active \n- * {Boolean} The control is active (read-only). Use <activate> and \n- * <deactivate> to change control state.\n- */\n- active: null,\n-\n- /**\n- * Property: handlerOptions\n- * {Object} Used to set non-default properties on the control's handler\n- */\n- handlerOptions: null,\n-\n- /** \n- * Property: handler \n- * {<OpenLayers.Handler>} null\n- */\n- handler: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- */\n- eventListeners: null,\n-\n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to control.events.object (a reference\n- * to the control).\n- * element - {DOMElement} A reference to control.events.element (which\n- * will be null unless documented otherwise).\n- *\n- * Supported map event types:\n- * activate - Triggered when activated.\n- * deactivate - Triggered when deactivated.\n- */\n- events: null,\n-\n- /**\n- * Constructor: OpenLayers.Control\n- * Create an OpenLayers Control. The options passed as a parameter\n- * directly extend the control. For example passing the following:\n- * \n- * > var control = new OpenLayers.Control({div: myDiv});\n- *\n- * Overrides the default div attribute value of null.\n- * \n- * Parameters:\n- * options - {Object} \n- */\n- initialize: function(options) {\n- // We do this before the extend so that instances can override\n- // className in options.\n- this.displayClass =\n- this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n-\n- OpenLayers.Util.extend(this, options);\n-\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- }\n- },\n-\n- /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n- */\n- destroy: function() {\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- this.events = null;\n- }\n- this.eventListeners = null;\n-\n- // eliminate circular references\n- if (this.handler) {\n- this.handler.destroy();\n- this.handler = null;\n- }\n- if (this.handlers) {\n- for (var key in this.handlers) {\n- if (this.handlers.hasOwnProperty(key) &&\n- typeof this.handlers[key].destroy == \"function\") {\n- this.handlers[key].destroy();\n- }\n- }\n- this.handlers = null;\n- }\n- if (this.map) {\n- this.map.removeControl(this);\n- this.map = null;\n- }\n- this.div = null;\n- },\n-\n- /** \n- * Method: setMap\n- * Set the map property for the control. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- this.map = map;\n- if (this.handler) {\n- this.handler.setMap(map);\n- }\n- },\n-\n- /**\n- * Method: draw\n- * The draw method is called when the control is ready to be displayed\n- * on the page. If a div has not been created one is created. Controls\n- * with a visual component will almost always want to override this method \n- * to customize the look of control. \n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>} The top-left pixel position of the control\n- * or null.\n- *\n- * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the control\n- */\n- draw: function(px) {\n- if (this.div == null) {\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.className = this.displayClass;\n- if (!this.allowSelection) {\n- this.div.className += \" olControlNoSelect\";\n- this.div.setAttribute(\"unselectable\", \"on\", 0);\n- this.div.onselectstart = OpenLayers.Function.False;\n- }\n- if (this.title != \"\") {\n- this.div.title = this.title;\n- }\n- }\n- if (px != null) {\n- this.position = px.clone();\n- }\n- this.moveTo(this.position);\n- return this.div;\n- },\n-\n- /**\n- * Method: moveTo\n- * Sets the left and top style attributes to the passed in pixel \n- * coordinates.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- */\n- moveTo: function(px) {\n- if ((px != null) && (this.div != null)) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\";\n- }\n- },\n-\n- /**\n- * APIMethod: activate\n- * Explicitly activates a control and it's associated\n- * handler if one has been set. Controls can be\n- * deactivated by calling the deactivate() method.\n- * \n- * Returns:\n- * {Boolean} True if the control was successfully activated or\n- * false if the control was already active.\n- */\n- activate: function() {\n- if (this.active) {\n- return false;\n- }\n- if (this.handler) {\n- this.handler.activate();\n- }\n- this.active = true;\n- if (this.map) {\n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv,\n- this.displayClass.replace(/ /g, \"\") + \"Active\"\n- );\n- }\n- this.events.triggerEvent(\"activate\");\n- return true;\n- },\n-\n- /**\n- * APIMethod: deactivate\n- * Deactivates a control and it's associated handler if any. The exact\n- * effect of this depends on the control itself.\n- * \n- * Returns:\n- * {Boolean} True if the control was effectively deactivated or false\n- * if the control was already inactive.\n- */\n- deactivate: function() {\n- if (this.active) {\n- if (this.handler) {\n- this.handler.deactivate();\n- }\n- this.active = false;\n- if (this.map) {\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv,\n- this.displayClass.replace(/ /g, \"\") + \"Active\"\n- );\n- }\n- this.events.triggerEvent(\"deactivate\");\n- return true;\n- }\n- return false;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_BUTTON\n- */\n-OpenLayers.Control.TYPE_BUTTON = 1;\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_TOGGLE\n- */\n-OpenLayers.Control.TYPE_TOGGLE = 2;\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_TOOL\n- */\n-OpenLayers.Control.TYPE_TOOL = 3;\n-/* ======================================================================\n- OpenLayers/Protocol.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Protocol\n- * Abstract vector layer protocol class. Not to be instantiated directly. Use\n- * one of the protocol subclasses instead.\n- */\n-OpenLayers.Protocol = OpenLayers.Class({\n-\n- /**\n- * Property: format\n- * {<OpenLayers.Format>} The format used by this protocol.\n- */\n- format: null,\n-\n- /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n- */\n- options: null,\n-\n- /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the protocol can set autoDestroy to false\n- * to fully control when the protocol is destroyed. Defaults to\n- * true.\n- */\n- autoDestroy: true,\n-\n- /**\n- * Property: defaultFilter\n- * {<OpenLayers.Filter>} Optional default filter to read requests\n- */\n- defaultFilter: null,\n-\n- /**\n- * Constructor: OpenLayers.Protocol\n- * Abstract class for vector protocols. Create instances of a subclass.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n- initialize: function(options) {\n- options = options || {};\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- },\n-\n- /**\n- * Method: mergeWithDefaultFilter\n- * Merge filter passed to the read method with the default one\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>}\n- */\n- mergeWithDefaultFilter: function(filter) {\n- var merged;\n- if (filter && this.defaultFilter) {\n- merged = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.defaultFilter, filter]\n- });\n- } else {\n- merged = filter || this.defaultFilter || undefined;\n- }\n- return merged;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n- */\n- destroy: function() {\n- this.options = null;\n- this.format = null;\n- },\n-\n- /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n- *\n- * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- read: function(options) {\n- options = options || {};\n- options.filter = this.mergeWithDefaultFilter(options.filter);\n- },\n-\n-\n- /**\n- * APIMethod: create\n- * Construct a request for writing newly created features.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- create: function() {},\n-\n- /**\n- * APIMethod: update\n- * Construct a request updating modified features.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- update: function() {},\n-\n- /**\n- * APIMethod: delete\n- * Construct a request deleting a removed feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- \"delete\": function() {},\n-\n- /**\n- * APIMethod: commit\n- * Go over the features and for each take action\n- * based on the feature state. Possible actions are create,\n- * update and delete.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})}\n- * options - {Object} Object whose possible keys are \"create\", \"update\",\n- * \"delete\", \"callback\" and \"scope\", the values referenced by the\n- * first three are objects as passed to the \"create\", \"update\", and\n- * \"delete\" methods, the value referenced by the \"callback\" key is\n- * a function which is called when the commit operation is complete\n- * using the scope referenced by the \"scope\" key.\n- *\n- * Returns:\n- * {Array({<OpenLayers.Protocol.Response>})} An array of\n- * <OpenLayers.Protocol.Response> objects.\n- */\n- commit: function() {},\n-\n- /**\n- * Method: abort\n- * Abort an ongoing request.\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>}\n- */\n- abort: function(response) {},\n-\n- /**\n- * Method: createCallback\n- * Returns a function that applies the given public method with resp and\n- * options arguments.\n- *\n- * Parameters:\n- * method - {Function} The method to be applied by the callback.\n- * response - {<OpenLayers.Protocol.Response>} The protocol response object.\n- * options - {Object} Options sent to the protocol method\n- */\n- createCallback: function(method, response, options) {\n- return OpenLayers.Function.bind(function() {\n- method.apply(this, [response, options]);\n- }, this);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Protocol\"\n-});\n-\n-/**\n- * Class: OpenLayers.Protocol.Response\n- * Protocols return Response objects to their users.\n- */\n-OpenLayers.Protocol.Response = OpenLayers.Class({\n- /**\n- * Property: code\n- * {Number} - OpenLayers.Protocol.Response.SUCCESS or\n- * OpenLayers.Protocol.Response.FAILURE\n- */\n- code: null,\n-\n- /**\n- * Property: requestType\n- * {String} The type of request this response corresponds to. Either\n- * \"create\", \"read\", \"update\" or \"delete\".\n- */\n- requestType: null,\n-\n- /**\n- * Property: last\n- * {Boolean} - true if this is the last response expected in a commit,\n- * false otherwise, defaults to true.\n- */\n- last: true,\n-\n- /**\n- * Property: features\n- * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n- * The features returned in the response by the server. Depending on the \n- * protocol's read payload, either features or data will be populated.\n- */\n- features: null,\n-\n- /**\n- * Property: data\n- * {Object}\n- * The data returned in the response by the server. Depending on the \n- * protocol's read payload, either features or data will be populated.\n- */\n- data: null,\n-\n- /**\n- * Property: reqFeatures\n- * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n- * The features provided by the user and placed in the request by the\n- * protocol.\n- */\n- reqFeatures: null,\n-\n- /**\n- * Property: priv\n- */\n- priv: null,\n-\n- /**\n- * Property: error\n- * {Object} The error object in case a service exception was encountered.\n- */\n- error: null,\n-\n- /**\n- * Constructor: OpenLayers.Protocol.Response\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: success\n- *\n- * Returns:\n- * {Boolean} - true on success, false otherwise\n- */\n- success: function() {\n- return this.code > 0;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Protocol.Response\"\n-});\n-\n-OpenLayers.Protocol.Response.SUCCESS = 1;\n-OpenLayers.Protocol.Response.FAILURE = 0;\n-/* ======================================================================\n- OpenLayers/Symbolizer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Symbolizer\n- * Base class representing a symbolizer used for feature rendering.\n- */\n-OpenLayers.Symbolizer = OpenLayers.Class({\n-\n-\n- /**\n- * APIProperty: zIndex\n- * {Number} The zIndex determines the rendering order for a symbolizer.\n- * Symbolizers with larger zIndex values are rendered over symbolizers\n- * with smaller zIndex values. Default is 0.\n- */\n- zIndex: 0,\n-\n- /**\n- * Constructor: OpenLayers.Symbolizer\n- * Instances of this class are not useful. See one of the subclasses.\n- *\n- * Parameters:\n- * config - {Object} An object containing properties to be set on the \n- * symbolizer. Any documented symbolizer property can be set at \n- * construction.\n- *\n- * Returns:\n- * A new symbolizer.\n- */\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config);\n- },\n-\n- /** \n- * APIMethod: clone\n- * Create a copy of this symbolizer.\n- *\n- * Returns a symbolizer of the same type with the same properties.\n- */\n- clone: function() {\n- var Type = eval(this.CLASS_NAME);\n- return new Type(OpenLayers.Util.extend({}, this));\n- },\n-\n- CLASS_NAME: \"OpenLayers.Symbolizer\"\n-\n-});\n-\n-/* ======================================================================\n- OpenLayers/Rule.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Rule\n- * This class represents an SLD Rule, as being used for rule-based SLD styling.\n- */\n-OpenLayers.Rule = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n-\n- /**\n- * APIProperty: name\n- * {String} name of this rule\n- */\n- name: null,\n-\n- /**\n- * Property: title\n- * {String} Title of this rule (set if included in SLD)\n- */\n- title: null,\n-\n- /**\n- * Property: description\n- * {String} Description of this rule (set if abstract is included in SLD)\n- */\n- description: null,\n-\n- /**\n- * Property: context\n- * {Object} An optional object with properties that the rule should be\n- * evaluated against. If no context is specified, feature.attributes will\n- * be used.\n- */\n- context: null,\n-\n- /**\n- * Property: filter\n- * {<OpenLayers.Filter>} Optional filter for the rule.\n- */\n- filter: null,\n-\n- /**\n- * Property: elseFilter\n- * {Boolean} Determines whether this rule is only to be applied only if\n- * no other rules match (ElseFilter according to the SLD specification). \n- * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n- * false, the rule will always apply. For subclasses, the else property is \n- * ignored.\n- */\n- elseFilter: false,\n-\n- /**\n- * Property: symbolizer\n- * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n- * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n- * latter if useful if it is required to style e.g. vertices of a line\n- * with a point symbolizer. Note, however, that this is not implemented\n- * yet in OpenLayers, but it is the way how symbolizers are defined in\n- * SLD.\n- */\n- symbolizer: null,\n-\n- /**\n- * Property: symbolizers\n- * {Array} Collection of symbolizers associated with this rule. If \n- * provided at construction, the symbolizers array has precedence\n- * over the deprecated symbolizer property. Note that multiple \n- * symbolizers are not currently supported by the vector renderers.\n- * Rules with multiple symbolizers are currently only useful for\n- * maintaining elements in an SLD document.\n- */\n- symbolizers: null,\n-\n- /**\n- * APIProperty: minScaleDenominator\n- * {Number} or {String} minimum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n- */\n- minScaleDenominator: null,\n-\n- /**\n- * APIProperty: maxScaleDenominator\n- * {Number} or {String} maximum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n- */\n- maxScaleDenominator: null,\n-\n- /** \n- * Constructor: OpenLayers.Rule\n- * Creates a Rule.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\n- * {<OpenLayers.Rule>}\n- */\n- initialize: function(options) {\n- this.symbolizer = {};\n- OpenLayers.Util.extend(this, options);\n- if (this.symbolizers) {\n- delete this.symbolizer;\n- }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null;\n- }\n- this.symbolizer = null;\n- delete this.symbolizers;\n- },\n-\n- /**\n- * APIMethod: evaluate\n- * evaluates this rule for a specific feature\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n- * \n- * Returns:\n- * {Boolean} true if the rule applies, false if it does not.\n- * This rule is the default rule and always returns true.\n- */\n- evaluate: function(feature) {\n- var context = this.getContext(feature);\n- var applies = true;\n-\n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale();\n- }\n-\n- // check if within minScale/maxScale bounds\n- if (this.minScaleDenominator) {\n- applies = scale >= OpenLayers.Style.createLiteral(\n- this.minScaleDenominator, context);\n- }\n- if (applies && this.maxScaleDenominator) {\n- applies = scale < OpenLayers.Style.createLiteral(\n- this.maxScaleDenominator, context);\n- }\n-\n- // check if optional filter applies\n- if (applies && this.filter) {\n- // feature id filters get the feature, others get the context\n- if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n- applies = this.filter.evaluate(feature);\n- } else {\n- applies = this.filter.evaluate(context);\n- }\n- }\n-\n- return applies;\n- },\n-\n- /**\n- * Method: getContext\n- * Gets the context for evaluating this rule\n- * \n- * Paramters:\n- * feature - {<OpenLayers.Feature>} feature to take the context from if\n- * none is specified.\n- */\n- getContext: function(feature) {\n- var context = this.context;\n- if (!context) {\n- context = feature.attributes || feature.data;\n- }\n- if (typeof this.context == \"function\") {\n- context = this.context(feature);\n- }\n- return context;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this rule.\n- * \n- * Returns:\n- * {<OpenLayers.Rule>} Clone of this rule.\n- */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.symbolizers) {\n- // clone symbolizers\n- var len = this.symbolizers.length;\n- options.symbolizers = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- options.symbolizers[i] = this.symbolizers[i].clone();\n- }\n- } else {\n- // clone symbolizer\n- options.symbolizer = {};\n- var value, type;\n- for (var key in this.symbolizer) {\n- value = this.symbolizer[key];\n- type = typeof value;\n- if (type === \"object\") {\n- options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n- } else if (type === \"string\") {\n- options.symbolizer[key] = value;\n- }\n- }\n- }\n- // clone filter\n- options.filter = this.filter && this.filter.clone();\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- return new OpenLayers.Rule(options);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry\n- * A Geometry is a description of a geographic object. Create an instance of\n- * this class with the <OpenLayers.Geometry> constructor. This is a base class,\n- * typical geometry types are described by subclasses of this class.\n- *\n- * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must\n- * explicitly include the OpenLayers.Format.WKT in your build.\n- */\n-OpenLayers.Geometry = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique identifier for this geometry.\n- */\n- id: null,\n-\n- /**\n- * Property: parent\n- * {<OpenLayers.Geometry>}This is set when a Geometry is added as component\n- * of another geometry\n- */\n- parent: null,\n-\n- /**\n- * Property: bounds \n- * {<OpenLayers.Bounds>} The bounds of this geometry\n- */\n- bounds: null,\n-\n- /**\n- * Constructor: OpenLayers.Geometry\n- * Creates a geometry object. \n- */\n- initialize: function() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /**\n- * Method: destroy\n- * Destroy this geometry.\n- */\n- destroy: function() {\n- this.id = null;\n- this.bounds = null;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Create a clone of this geometry. Does not set any non-standard\n- * properties of the cloned geometry.\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} An exact clone of this geometry.\n- */\n- clone: function() {\n- return new OpenLayers.Geometry();\n- },\n-\n- /**\n- * Method: setBounds\n- * Set the bounds for this Geometry.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- */\n- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone();\n- }\n- },\n-\n- /**\n- * Method: clearBounds\n- * Nullify this components bounds and that of its parent as well.\n- */\n- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds();\n- }\n- },\n-\n- /**\n- * Method: extendBounds\n- * Extend the existing bounds to include the new bounds. \n- * If geometry's bounds is not yet set, then set a new Bounds.\n- * \n- * Parameters:\n- * newBounds - {<OpenLayers.Bounds>} \n- */\n- extendBounds: function(newBounds) {\n- var bounds = this.getBounds();\n- if (!bounds) {\n- this.setBounds(newBounds);\n- } else {\n- this.bounds.extend(newBounds);\n- }\n- },\n-\n- /**\n- * APIMethod: getBounds\n- * Get the bounds for this Geometry. If bounds is not set, it \n- * is calculated again, this makes queries faster.\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds();\n- }\n- return this.bounds;\n- },\n-\n- /** \n- * APIMethod: calculateBounds\n- * Recalculate the bounds for the geometry. \n- */\n- calculateBounds: function() {\n- //\n- // This should be overridden by subclasses.\n- //\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options depend on the specific geometry type.\n- * \n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {},\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {},\n-\n- /**\n- * Method: atPoint\n- * Note - This is only an approximation based on the bounds of the \n- * geometry.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * toleranceLon - {float} Optional tolerance in Geometric Coords\n- * toleranceLat - {float} Optional tolerance in Geographic Coords\n- * \n- * Returns:\n- * {Boolean} Whether or not the geometry is at the specified location\n- */\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if ((bounds != null) && (lonlat != null)) {\n-\n- var dX = (toleranceLon != null) ? toleranceLon : 0;\n- var dY = (toleranceLat != null) ? toleranceLat : 0;\n-\n- var toleranceBounds =\n- new OpenLayers.Bounds(this.bounds.left - dX,\n- this.bounds.bottom - dY,\n- this.bounds.right + dX,\n- this.bounds.top + dY);\n-\n- atPoint = toleranceBounds.containsLonLat(lonlat);\n- }\n- return atPoint;\n- },\n-\n- /**\n- * Method: getLength\n- * Calculate the length of this geometry. This method is defined in\n- * subclasses.\n- * \n- * Returns:\n- * {Float} The length of the collection by summing its parts\n- */\n- getLength: function() {\n- //to be overridden by geometries that actually have a length\n- //\n- return 0.0;\n- },\n-\n- /**\n- * Method: getArea\n- * Calculate the area of this geometry. This method is defined in subclasses.\n- * \n- * Returns:\n- * {Float} The area of the collection by summing its parts\n- */\n- getArea: function() {\n- //to be overridden by geometries that actually have an area\n- //\n- return 0.0;\n- },\n-\n- /**\n- * APIMethod: getCentroid\n- * Calculate the centroid of this geometry. This method is defined in subclasses.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} The centroid of the collection\n- */\n- getCentroid: function() {\n- return null;\n- },\n-\n- /**\n- * Method: toString\n- * Returns a text representation of the geometry. If the WKT format is\n- * included in a build, this will be the Well-Known Text \n- * representation.\n- *\n- * Returns:\n- * {String} String representation of this geometry.\n- */\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- string = OpenLayers.Format.WKT.prototype.write(\n- new OpenLayers.Feature.Vector(this)\n- );\n- } else {\n- string = Object.prototype.toString.call(this);\n- }\n- return string;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry\"\n-});\n-\n-/**\n- * Function: OpenLayers.Geometry.fromWKT\n- * Generate a geometry given a Well-Known Text string. For this method to\n- * work, you must include the OpenLayers.Format.WKT in your build \n- * explicitly.\n- *\n- * Parameters:\n- * wkt - {String} A string representing the geometry in Well-Known Text.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry of the appropriate class.\n- */\n-OpenLayers.Geometry.fromWKT = function(wkt) {\n- var geom;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- var format = OpenLayers.Geometry.fromWKT.format;\n- if (!format) {\n- format = new OpenLayers.Format.WKT();\n- OpenLayers.Geometry.fromWKT.format = format;\n- }\n- var result = format.read(wkt);\n- if (result instanceof OpenLayers.Feature.Vector) {\n- geom = result.geometry;\n- } else if (OpenLayers.Util.isArray(result)) {\n- var len = result.length;\n- var components = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- components[i] = result[i].geometry;\n- }\n- geom = new OpenLayers.Geometry.Collection(components);\n- }\n- }\n- return geom;\n-};\n-\n-/**\n- * Method: OpenLayers.Geometry.segmentsIntersect\n- * Determine whether two line segments intersect. Optionally calculates\n- * and returns the intersection point. This function is optimized for\n- * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those\n- * obvious cases where there is no intersection, the function should\n- * not be called.\n- *\n- * Parameters:\n- * seg1 - {Object} Object representing a segment with properties x1, y1, x2,\n- * and y2. The start point is represented by x1 and y1. The end point\n- * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n- * seg2 - {Object} Object representing a segment with properties x1, y1, x2,\n- * and y2. The start point is represented by x1 and y1. The end point\n- * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n- * options - {Object} Optional properties for calculating the intersection.\n- *\n- * Valid options:\n- * point - {Boolean} Return the intersection point. If false, the actual\n- * intersection point will not be calculated. If true and the segments\n- * intersect, the intersection point will be returned. If true and\n- * the segments do not intersect, false will be returned. If true and\n- * the segments are coincident, true will be returned.\n- * tolerance - {Number} If a non-null value is provided, if the segments are\n- * within the tolerance distance, this will be considered an intersection.\n- * In addition, if the point option is true and the calculated intersection\n- * is within the tolerance distance of an end point, the endpoint will be\n- * returned instead of the calculated intersection. Further, if the\n- * intersection is within the tolerance of endpoints on both segments, or\n- * if two segment endpoints are within the tolerance distance of eachother\n- * (but no intersection is otherwise calculated), an endpoint on the\n- * first segment provided will be returned.\n- *\n- * Returns:\n- * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.\n- * If the point argument is true, the return will be the intersection\n- * point or false if none exists. If point is true and the segments\n- * are coincident, return will be true (and the instersection is equal\n- * to the shorter segment).\n- */\n-OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n- var point = options && options.point;\n- var tolerance = options && options.tolerance;\n- var intersection = false;\n- var x11_21 = seg1.x1 - seg2.x1;\n- var y11_21 = seg1.y1 - seg2.y1;\n- var x12_11 = seg1.x2 - seg1.x1;\n- var y12_11 = seg1.y2 - seg1.y1;\n- var y22_21 = seg2.y2 - seg2.y1;\n- var x22_21 = seg2.x2 - seg2.x1;\n- var d = (y22_21 * x12_11) - (x22_21 * y12_11);\n- var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);\n- var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);\n- if (d == 0) {\n- // parallel\n- if (n1 == 0 && n2 == 0) {\n- // coincident\n- intersection = true;\n- }\n- } else {\n- var along1 = n1 / d;\n- var along2 = n2 / d;\n- if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n- // intersect\n- if (!point) {\n- intersection = true;\n- } else {\n- // calculate the intersection point\n- var x = seg1.x1 + (along1 * x12_11);\n- var y = seg1.y1 + (along1 * y12_11);\n- intersection = new OpenLayers.Geometry.Point(x, y);\n- }\n- }\n- }\n- if (tolerance) {\n- var dist;\n- if (intersection) {\n- if (point) {\n- var segs = [seg1, seg2];\n- var seg, x, y;\n- // check segment endpoints for proximity to intersection\n- // set intersection to first endpoint within the tolerance\n- outer: for (var i = 0; i < 2; ++i) {\n- seg = segs[i];\n- for (var j = 1; j < 3; ++j) {\n- x = seg[\"x\" + j];\n- y = seg[\"y\" + j];\n- dist = Math.sqrt(\n- Math.pow(x - intersection.x, 2) +\n- Math.pow(y - intersection.y, 2)\n- );\n- if (dist < tolerance) {\n- intersection.x = x;\n- intersection.y = y;\n- break outer;\n- }\n- }\n- }\n-\n- }\n- } else {\n- // no calculated intersection, but segments could be within\n- // the tolerance of one another\n- var segs = [seg1, seg2];\n- var source, target, x, y, p, result;\n- // check segment endpoints for proximity to intersection\n- // set intersection to first endpoint within the tolerance\n- outer: for (var i = 0; i < 2; ++i) {\n- source = segs[i];\n- target = segs[(i + 1) % 2];\n- for (var j = 1; j < 3; ++j) {\n- p = {\n- x: source[\"x\" + j],\n- y: source[\"y\" + j]\n- };\n- result = OpenLayers.Geometry.distanceToSegment(p, target);\n- if (result.distance < tolerance) {\n- if (point) {\n- intersection = new OpenLayers.Geometry.Point(p.x, p.y);\n- } else {\n- intersection = true;\n- }\n- break outer;\n- }\n- }\n- }\n- }\n- }\n- return intersection;\n-};\n-\n-/**\n- * Function: OpenLayers.Geometry.distanceToSegment\n- *\n- * Parameters:\n- * point - {Object} An object with x and y properties representing the\n- * point coordinates.\n- * segment - {Object} An object with x1, y1, x2, and y2 properties\n- * representing endpoint coordinates.\n- *\n- * Returns:\n- * {Object} An object with distance, along, x, and y properties. The distance\n- * will be the shortest distance between the input point and segment.\n- * The x and y properties represent the coordinates along the segment\n- * where the shortest distance meets the segment. The along attribute\n- * describes how far between the two segment points the given point is.\n- */\n-OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n- var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n- result.distance = Math.sqrt(result.distance);\n- return result;\n-};\n-\n-/**\n- * Function: OpenLayers.Geometry.distanceSquaredToSegment\n- *\n- * Usually the distanceToSegment function should be used. This variant however\n- * can be used for comparisons where the exact distance is not important.\n- *\n- * Parameters:\n- * point - {Object} An object with x and y properties representing the\n- * point coordinates.\n- * segment - {Object} An object with x1, y1, x2, and y2 properties\n- * representing endpoint coordinates.\n- *\n- * Returns:\n- * {Object} An object with squared distance, along, x, and y properties.\n- * The distance will be the shortest distance between the input point and\n- * segment. The x and y properties represent the coordinates along the\n- * segment where the shortest distance meets the segment. The along\n- * attribute describes how far between the two segment points the given\n- * point is.\n- */\n-OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n- var x0 = point.x;\n- var y0 = point.y;\n- var x1 = segment.x1;\n- var y1 = segment.y1;\n- var x2 = segment.x2;\n- var y2 = segment.y2;\n- var dx = x2 - x1;\n- var dy = y2 - y1;\n- var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /\n- (Math.pow(dx, 2) + Math.pow(dy, 2));\n- var x, y;\n- if (along <= 0.0) {\n- x = x1;\n- y = y1;\n- } else if (along >= 1.0) {\n- x = x2;\n- y = y2;\n- } else {\n- x = x1 + along * dx;\n- y = y1 + along * dy;\n- }\n- return {\n- distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n- x: x,\n- y: y,\n- along: along\n- };\n-};\n-/* ======================================================================\n- OpenLayers/Geometry/Point.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Point\n- * Point geometry class. \n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry> \n- */\n-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n-\n- /** \n- * APIProperty: x \n- * {float} \n- */\n- x: null,\n-\n- /** \n- * APIProperty: y \n- * {float} \n- */\n- y: null,\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Point\n- * Construct a point geometry.\n- *\n- * Parameters:\n- * x - {float} \n- * y - {float}\n- * \n- */\n- initialize: function(x, y) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n-\n- this.x = parseFloat(x);\n- this.y = parseFloat(y);\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Returns:\n- * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y);\n- }\n-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- return obj;\n- },\n-\n- /** \n- * Method: calculateBounds\n- * Create a new Bounds based on the lon/lat\n- */\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y,\n- this.x, this.y);\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n+ *\n * Parameters:\n * geometry - {<OpenLayers.Geometry>} The target geometry.\n * options - {Object} Optional properties for configuring the distance\n * calculation.\n *\n * Valid options:\n * details - {Boolean} Return details from the distance calculation.\n@@ -25867,14 +12043,557 @@\n * Constant: OpenLayers.Format.WFST.DEFAULTS\n * {Object} Default properties for the WFST format.\n */\n OpenLayers.Format.WFST.DEFAULTS = {\n \"version\": \"1.0.0\"\n };\n /* ======================================================================\n+ OpenLayers/Style.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Style\n+ * This class represents a UserStyle obtained\n+ * from a SLD, containing styling rules.\n+ */\n+OpenLayers.Style = OpenLayers.Class({\n+\n+ /**\n+ * Property: id\n+ * {String} A unique id for this session.\n+ */\n+ id: null,\n+\n+ /**\n+ * APIProperty: name\n+ * {String}\n+ */\n+ name: null,\n+\n+ /**\n+ * Property: title\n+ * {String} Title of this style (set if included in SLD)\n+ */\n+ title: null,\n+\n+ /**\n+ * Property: description\n+ * {String} Description of this style (set if abstract is included in SLD)\n+ */\n+ description: null,\n+\n+ /**\n+ * APIProperty: layerName\n+ * {<String>} name of the layer that this style belongs to, usually\n+ * according to the NamedLayer attribute of an SLD document.\n+ */\n+ layerName: null,\n+\n+ /**\n+ * APIProperty: isDefault\n+ * {Boolean}\n+ */\n+ isDefault: false,\n+\n+ /** \n+ * Property: rules \n+ * {Array(<OpenLayers.Rule>)}\n+ */\n+ rules: null,\n+\n+ /**\n+ * APIProperty: context\n+ * {Object} An optional object with properties that symbolizers' property\n+ * values should be evaluated against. If no context is specified,\n+ * feature.attributes will be used\n+ */\n+ context: null,\n+\n+ /**\n+ * Property: defaultStyle\n+ * {Object} hash of style properties to use as default for merging\n+ * rule-based style symbolizers onto. If no rules are defined,\n+ * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n+ * true, the defaultStyle will only be taken into account if there are\n+ * rules defined.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * Property: defaultsPerSymbolizer\n+ * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n+ * of every rule. Properties of the <defaultStyle> will also be used to set\n+ * missing symbolizer properties if the symbolizer has stroke, fill or\n+ * graphic set to true. Default is false.\n+ */\n+ defaultsPerSymbolizer: false,\n+\n+ /**\n+ * Property: propertyStyles\n+ * {Hash of Boolean} cache of style properties that need to be parsed for\n+ * propertyNames. Property names are keys, values won't be used.\n+ */\n+ propertyStyles: null,\n+\n+\n+ /** \n+ * Constructor: OpenLayers.Style\n+ * Creates a UserStyle.\n+ *\n+ * Parameters:\n+ * style - {Object} Optional hash of style properties that will be\n+ * used as default style for this style object. This style\n+ * applies if no rules are specified. Symbolizers defined in\n+ * rules will extend this default style.\n+ * options - {Object} An optional object with properties to set on the\n+ * style.\n+ *\n+ * Valid options:\n+ * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n+ * style.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Style>}\n+ */\n+ initialize: function(style, options) {\n+\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules);\n+ }\n+\n+ // use the default style from OpenLayers.Feature.Vector if no style\n+ // was given in the constructor\n+ this.setDefaultStyle(style ||\n+ OpenLayers.Feature.Vector.style[\"default\"]);\n+\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n+\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null;\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null;\n+ },\n+\n+ /**\n+ * Method: createSymbolizer\n+ * creates a style by applying all feature-dependent rules to the base\n+ * style.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n+ * \n+ * Returns:\n+ * {Object} symbolizer hash\n+ */\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n+ OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+\n+ var rules = this.rules;\n+\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ // does the rule apply?\n+ var applies = rule.evaluate(feature);\n+\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule);\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature);\n+ }\n+ }\n+ }\n+\n+ // if no other rules apply, apply the rules with else filters\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature);\n+ }\n+ }\n+\n+ // don't display if there were rules but none applied\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\";\n+ }\n+\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label);\n+ }\n+\n+ return style;\n+ },\n+\n+ /**\n+ * Method: applySymbolizer\n+ *\n+ * Parameters:\n+ * rule - {<OpenLayers.Rule>}\n+ * style - {Object}\n+ * feature - {<OpenLayer.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {Object} A style with new symbolizer applied.\n+ */\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ?\n+ this.getSymbolizerPrefix(feature.geometry) :\n+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ });\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ });\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ });\n+ }\n+ }\n+\n+ // merge the style with the current style\n+ return this.createLiterals(\n+ OpenLayers.Util.extend(style, symbolizer), feature);\n+ },\n+\n+ /**\n+ * Method: createLiterals\n+ * creates literals for all style properties that have an entry in\n+ * <this.propertyStyles>.\n+ * \n+ * Parameters:\n+ * style - {Object} style to create literals for. Will be modified\n+ * inline.\n+ * feature - {Object}\n+ * \n+ * Returns:\n+ * {Object} the modified style\n+ */\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n+ }\n+ return style;\n+ },\n+\n+ /**\n+ * Method: findPropertyStyles\n+ * Looks into all rules for this style and the defaultStyle to collect\n+ * all the style hash property names containing ${...} strings that have\n+ * to be replaced using the createLiteral method before returning them.\n+ * \n+ * Returns:\n+ * {Object} hash of property names that need createLiteral parsing. The\n+ * name of the property is the key, and the value is true;\n+ */\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+\n+ // check the default style\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+\n+ // walk through all rules to check for properties in their symbolizer\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n+ this.addPropertyStyles(propertyStyles, value);\n+ } else {\n+ // symbolizer is a hash of style properties\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break;\n+ }\n+ }\n+ }\n+ return propertyStyles;\n+ },\n+\n+ /**\n+ * Method: addPropertyStyles\n+ * \n+ * Parameters:\n+ * propertyStyles - {Object} hash to add new property styles to. Will be\n+ * modified inline\n+ * symbolizer - {Object} search this symbolizer for property styles\n+ * \n+ * Returns:\n+ * {Object} propertyStyles hash\n+ */\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" &&\n+ property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true;\n+ }\n+ }\n+ return propertyStyles;\n+ },\n+\n+ /**\n+ * APIMethod: addRules\n+ * Adds rules to this style.\n+ * \n+ * Parameters:\n+ * rules - {Array(<OpenLayers.Rule>)}\n+ */\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles();\n+ },\n+\n+ /**\n+ * APIMethod: setDefaultStyle\n+ * Sets the default style for this style object.\n+ * \n+ * Parameters:\n+ * style - {Object} Hash of style properties\n+ */\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles();\n+ },\n+\n+ /**\n+ * Method: getSymbolizerPrefix\n+ * Returns the correct symbolizer prefix according to the\n+ * geometry type of the passed geometry\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {String} key of the according symbolizer\n+ */\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i];\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this style.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Style>} Clone of this style.\n+ */\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ // clone rules\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone());\n+ }\n+ }\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ //clone default style\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+\n+\n+/**\n+ * Function: createLiteral\n+ * converts a style value holding a combination of PropertyName and Literal\n+ * into a Literal, taking the property values from the passed features.\n+ * \n+ * Parameters:\n+ * value - {String} value to parse. If this string contains a construct like\n+ * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n+ * will be replaced by the value of the \"bar\" attribute of the passed\n+ * feature.\n+ * context - {Object} context to take attribute values from\n+ * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n+ * <OpenLayers.String.format> for evaluating functions in the\n+ * context.\n+ * property - {String} optional, name of the property for which the literal is\n+ * being created for evaluating functions in the context.\n+ * \n+ * Returns:\n+ * {String} the parsed value. In the example of the value parameter above, the\n+ * result would be \"foo valueOfBar\", assuming that the passed feature has an\n+ * attribute named \"bar\" with the value \"valueOfBar\".\n+ */\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = (isNaN(value) || !value) ? value : parseFloat(value);\n+ }\n+ return value;\n+};\n+\n+/**\n+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n+ * {Array} prefixes of the sld symbolizers. These are the\n+ * same as the main geometry types\n+ */\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n+ 'Raster'\n+];\n+/* ======================================================================\n+ OpenLayers/Filter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter\n+ * This class represents an OGC Filter.\n+ */\n+OpenLayers.Filter = OpenLayers.Class({\n+\n+ /** \n+ * Constructor: OpenLayers.Filter\n+ * This class represents a generic filter.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter>}\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ },\n+\n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to anything added.\n+ */\n+ destroy: function() {},\n+\n+ /**\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context. Instances or subclasses\n+ * are supposed to override this method.\n+ * \n+ * Parameters:\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n+ */\n+ evaluate: function(context) {\n+ return true;\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this filter. Should be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter>} Clone of this filter.\n+ */\n+ clone: function() {\n+ return null;\n+ },\n+\n+ /**\n+ * APIMethod: toString\n+ *\n+ * Returns:\n+ * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n+ * representation of the filter returned. Otherwise \"[Object object]\"\n+ * will be returned.\n+ */\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this);\n+ } else {\n+ string = Object.prototype.toString.call(this);\n+ }\n+ return string;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n+/* ======================================================================\n OpenLayers/Filter/Spatial.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -30014,672 +16733,2771 @@\n return node;\n },\n \n CLASS_NAME: \"OpenLayers.Format.Filter.v1_1_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/OWSCommon/v1_0_0.js\n+ OpenLayers/Format/OWSCommon/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/OWSCommon/v1.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.OWSCommon.v1_0_0\n+ * Parser for OWS Common version 1.0.0.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.OWSCommon.v1>\n+ */\n+OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"ows\": OpenLayers.Util.applyDefaults({\n+ \"ExceptionReport\": function(node, obj) {\n+ obj.success = false;\n+ obj.exceptionReport = {\n+ version: node.getAttribute('version'),\n+ language: node.getAttribute('language'),\n+ exceptions: []\n+ };\n+ this.readChildNodes(node, obj.exceptionReport);\n+ }\n+ }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_0_0\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WFST/v1_1_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WFST/v1.js\n+ * @requires OpenLayers/Format/Filter/v1_1_0.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFST.v1_1_0\n+ * A format for creating WFS v1.1.0 transactions. Create a new instance with the\n+ * <OpenLayers.Format.WFST.v1_1_0> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.Filter.v1_1_0>\n+ * - <OpenLayers.Format.WFST.v1>\n+ */\n+OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {\n+\n+ /**\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.1.0\",\n+\n+ /**\n+ * Property: schemaLocations\n+ * {Object} Properties are namespace aliases, values are schema locations.\n+ */\n+ schemaLocations: {\n+ \"wfs\": \"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFST.v1_1_0\n+ * A class for parsing and generating WFS v1.1.0 transactions.\n+ *\n+ * To read additional information like hit count (numberOfFeatures) from\n+ * the FeatureCollection, call the <OpenLayers.Format.WFST.v1.read> method\n+ * with {output: \"object\"} as 2nd argument. Note that it is possible to\n+ * just request the hit count from a WFS 1.1.0 server with the\n+ * resultType=\"hits\" request parameter.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);\n+ OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\n+ * first - {Boolean} Should be set to true for the first node read. This\n+ * is usually the readNode call in the read method. Without this being\n+ * set, auto-configured properties will stick on subsequent reads.\n+ *\n+ * Returns:\n+ * {Object} The input object, modified (or a new one if none was provided).\n+ */\n+ readNode: function(node, obj, first) {\n+ // Not the superclass, only the mixin classes inherit from\n+ // Format.GML.v3. We need this because we don't want to get readNode\n+ // from the superclass's superclass, which is OpenLayers.Format.XML.\n+ return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"FeatureCollection\": function(node, obj) {\n+ obj.numberOfFeatures = parseInt(node.getAttribute(\n+ \"numberOfFeatures\"));\n+ OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"][\"FeatureCollection\"].apply(\n+ this, arguments);\n+ },\n+ \"TransactionResponse\": function(node, obj) {\n+ obj.insertIds = [];\n+ obj.success = false;\n+ this.readChildNodes(node, obj);\n+ },\n+ \"TransactionSummary\": function(node, obj) {\n+ // this is a limited test of success\n+ obj.success = true;\n+ },\n+ \"InsertResults\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Feature\": function(node, container) {\n+ var obj = {\n+ fids: []\n+ };\n+ this.readChildNodes(node, obj);\n+ container.insertIds.push(obj.fids[0]);\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.readers[\"ogc\"],\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"]\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"GetFeature\": function(options) {\n+ var node = OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"][\"GetFeature\"].apply(this, arguments);\n+ options && this.setAttributes(node, {\n+ resultType: options.resultType,\n+ startIndex: options.startIndex,\n+ count: options.count\n+ });\n+ return node;\n+ },\n+ \"Query\": function(options) {\n+ options = OpenLayers.Util.extend({\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ featureType: this.featureType,\n+ srsName: this.srsName\n+ }, options);\n+ var prefix = options.featurePrefix;\n+ var node = this.createElementNSPlus(\"wfs:Query\", {\n+ attributes: {\n+ typeName: (prefix ? prefix + \":\" : \"\") +\n+ options.featureType,\n+ srsName: options.srsName\n+ }\n+ });\n+ if (options.featureNS) {\n+ node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n+ }\n+ if (options.propertyNames) {\n+ for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n+ this.writeNode(\n+ \"wfs:PropertyName\", {\n+ property: options.propertyNames[i]\n+ },\n+ node\n+ );\n+ }\n+ }\n+ if (options.filter) {\n+ OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter);\n+ this.writeNode(\"ogc:Filter\", options.filter, node);\n+ }\n+ return node;\n+ },\n+ \"PropertyName\": function(obj) {\n+ return this.createElementNSPlus(\"wfs:PropertyName\", {\n+ value: obj.property\n+ });\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFST.v1_1_0\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WPSExecute.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/WCSGetCoverage.js\n+ * @requires OpenLayers/Format/WFST/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSExecute version 1.0.0\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML,\n+ OpenLayers.Format.Filter.v1_1_0, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ gml: \"http://www.opengis.net/gml\",\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ wfs: \"http://www.opengis.net/wfs\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ wcs: \"http://www.opengis.net/wcs\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location\n+ */\n+ schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n+\n+ schemaLocationAttr: function(options) {\n+ return undefined;\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSExecute\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Method: write\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object.\n+ *\n+ * Returns:\n+ * {String} An WPS Execute request XML string.\n+ */\n+ write: function(options) {\n+ var doc;\n+ if (window.ActiveXObject) {\n+ doc = new ActiveXObject(\"Microsoft.XMLDOM\");\n+ this.xmldom = doc;\n+ } else {\n+ doc = document.implementation.createDocument(\"\", \"\", null);\n+ }\n+ var node = this.writeNode(\"wps:Execute\", options, doc);\n+ this.setAttributeNS(\n+ node, this.namespaces.xsi,\n+ \"xsi:schemaLocation\", this.schemaLocation\n+ );\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Parse a WPS Execute and return an object with its information.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object}\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var info = {};\n+ this.readNode(data, info);\n+ return info;\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wps\": {\n+ \"Execute\": function(options) {\n+ var node = this.createElementNSPlus(\"wps:Execute\", {\n+ attributes: {\n+ version: this.VERSION,\n+ service: 'WPS'\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", options.identifier, node);\n+ this.writeNode(\"wps:DataInputs\", options.dataInputs, node);\n+ this.writeNode(\"wps:ResponseForm\", options.responseForm, node);\n+ return node;\n+ },\n+ \"ResponseForm\": function(responseForm) {\n+ var node = this.createElementNSPlus(\"wps:ResponseForm\", {});\n+ if (responseForm.rawDataOutput) {\n+ this.writeNode(\"wps:RawDataOutput\", responseForm.rawDataOutput, node);\n+ }\n+ if (responseForm.responseDocument) {\n+ this.writeNode(\"wps:ResponseDocument\", responseForm.responseDocument, node);\n+ }\n+ return node;\n+ },\n+ \"ResponseDocument\": function(responseDocument) {\n+ var node = this.createElementNSPlus(\"wps:ResponseDocument\", {\n+ attributes: {\n+ storeExecuteResponse: responseDocument.storeExecuteResponse,\n+ lineage: responseDocument.lineage,\n+ status: responseDocument.status\n+ }\n+ });\n+ if (responseDocument.outputs) {\n+ for (var i = 0, len = responseDocument.outputs.length; i < len; i++) {\n+ this.writeNode(\"wps:Output\", responseDocument.outputs[i], node);\n+ }\n+ }\n+ return node;\n+ },\n+ \"Output\": function(output) {\n+ var node = this.createElementNSPlus(\"wps:Output\", {\n+ attributes: {\n+ asReference: output.asReference,\n+ mimeType: output.mimeType,\n+ encoding: output.encoding,\n+ schema: output.schema\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", output.identifier, node);\n+ this.writeNode(\"ows:Title\", output.title, node);\n+ this.writeNode(\"ows:Abstract\", output[\"abstract\"], node);\n+ return node;\n+ },\n+ \"RawDataOutput\": function(rawDataOutput) {\n+ var node = this.createElementNSPlus(\"wps:RawDataOutput\", {\n+ attributes: {\n+ mimeType: rawDataOutput.mimeType,\n+ encoding: rawDataOutput.encoding,\n+ schema: rawDataOutput.schema\n+ }\n+ });\n+ this.writeNode(\"ows:Identifier\", rawDataOutput.identifier, node);\n+ return node;\n+ },\n+ \"DataInputs\": function(dataInputs) {\n+ var node = this.createElementNSPlus(\"wps:DataInputs\", {});\n+ for (var i = 0, ii = dataInputs.length; i < ii; ++i) {\n+ this.writeNode(\"wps:Input\", dataInputs[i], node);\n+ }\n+ return node;\n+ },\n+ \"Input\": function(input) {\n+ var node = this.createElementNSPlus(\"wps:Input\", {});\n+ this.writeNode(\"ows:Identifier\", input.identifier, node);\n+ if (input.title) {\n+ this.writeNode(\"ows:Title\", input.title, node);\n+ }\n+ if (input.data) {\n+ this.writeNode(\"wps:Data\", input.data, node);\n+ }\n+ if (input.reference) {\n+ this.writeNode(\"wps:Reference\", input.reference, node);\n+ }\n+ if (input.boundingBoxData) {\n+ this.writeNode(\"wps:BoundingBoxData\", input.boundingBoxData, node);\n+ }\n+ return node;\n+ },\n+ \"Data\": function(data) {\n+ var node = this.createElementNSPlus(\"wps:Data\", {});\n+ if (data.literalData) {\n+ this.writeNode(\"wps:LiteralData\", data.literalData, node);\n+ } else if (data.complexData) {\n+ this.writeNode(\"wps:ComplexData\", data.complexData, node);\n+ } else if (data.boundingBoxData) {\n+ this.writeNode(\"ows:BoundingBox\", data.boundingBoxData, node);\n+ }\n+ return node;\n+ },\n+ \"LiteralData\": function(literalData) {\n+ var node = this.createElementNSPlus(\"wps:LiteralData\", {\n+ attributes: {\n+ uom: literalData.uom\n+ },\n+ value: literalData.value\n+ });\n+ return node;\n+ },\n+ \"ComplexData\": function(complexData) {\n+ var node = this.createElementNSPlus(\"wps:ComplexData\", {\n+ attributes: {\n+ mimeType: complexData.mimeType,\n+ encoding: complexData.encoding,\n+ schema: complexData.schema\n+ }\n+ });\n+ var data = complexData.value;\n+ if (typeof data === \"string\") {\n+ node.appendChild(\n+ this.getXMLDoc().createCDATASection(complexData.value)\n+ );\n+ } else {\n+ node.appendChild(data);\n+ }\n+ return node;\n+ },\n+ \"Reference\": function(reference) {\n+ var node = this.createElementNSPlus(\"wps:Reference\", {\n+ attributes: {\n+ mimeType: reference.mimeType,\n+ \"xlink:href\": reference.href,\n+ method: reference.method,\n+ encoding: reference.encoding,\n+ schema: reference.schema\n+ }\n+ });\n+ if (reference.body) {\n+ this.writeNode(\"wps:Body\", reference.body, node);\n+ }\n+ return node;\n+ },\n+ \"BoundingBoxData\": function(node, obj) {\n+ this.writers['ows']['BoundingBox'].apply(this, [node, obj, \"wps:BoundingBoxData\"]);\n+ },\n+ \"Body\": function(body) {\n+ var node = this.createElementNSPlus(\"wps:Body\", {});\n+ if (body.wcs) {\n+ this.writeNode(\"wcs:GetCoverage\", body.wcs, node);\n+ } else if (body.wfs) {\n+ // OpenLayers.Format.WFST expects these to be on the \n+ // instance and not in the options\n+ this.featureType = body.wfs.featureType;\n+ this.version = body.wfs.version;\n+ this.writeNode(\"wfs:GetFeature\", body.wfs, node);\n+ } else {\n+ this.writeNode(\"wps:Execute\", body, node);\n+ }\n+ return node;\n+ }\n+ },\n+ \"wcs\": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,\n+ \"wfs\": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wps\": {\n+ \"ExecuteResponse\": function(node, obj) {\n+ obj.executeResponse = {\n+ lang: node.getAttribute(\"lang\"),\n+ statusLocation: node.getAttribute(\"statusLocation\"),\n+ serviceInstance: node.getAttribute(\"serviceInstance\"),\n+ service: node.getAttribute(\"service\")\n+ };\n+ this.readChildNodes(node, obj.executeResponse);\n+ },\n+ \"Process\": function(node, obj) {\n+ obj.process = {};\n+ this.readChildNodes(node, obj.process);\n+ },\n+ \"Status\": function(node, obj) {\n+ obj.status = {\n+ creationTime: node.getAttribute(\"creationTime\")\n+ };\n+ this.readChildNodes(node, obj.status);\n+ },\n+ \"ProcessSucceeded\": function(node, obj) {\n+ obj.processSucceeded = true;\n+ },\n+ \"ProcessOutputs\": function(node, processDescription) {\n+ processDescription.processOutputs = [];\n+ this.readChildNodes(node, processDescription.processOutputs);\n+ },\n+ \"Output\": function(node, processOutputs) {\n+ var output = {};\n+ this.readChildNodes(node, output);\n+ processOutputs.push(output);\n+ },\n+ \"Reference\": function(node, output) {\n+ output.reference = {\n+ href: node.getAttribute(\"href\"),\n+ mimeType: node.getAttribute(\"mimeType\"),\n+ encoding: node.getAttribute(\"encoding\"),\n+ schema: node.getAttribute(\"schema\")\n+ };\n+ },\n+ \"Data\": function(node, output) {\n+ output.data = {};\n+ this.readChildNodes(node, output);\n+ },\n+ \"LiteralData\": function(node, output) {\n+ output.literalData = {\n+ dataType: node.getAttribute(\"dataType\"),\n+ uom: node.getAttribute(\"uom\"),\n+ value: this.getChildValue(node)\n+ };\n+ },\n+ \"ComplexData\": function(node, output) {\n+ output.complexData = {\n+ mimeType: node.getAttribute(\"mimeType\"),\n+ schema: node.getAttribute(\"schema\"),\n+ encoding: node.getAttribute(\"encoding\"),\n+ value: \"\"\n+ };\n+\n+ // try to get *some* value, ignore the empty text values\n+ if (this.isSimpleContent(node)) {\n+ var child;\n+ for (child = node.firstChild; child; child = child.nextSibling) {\n+ switch (child.nodeType) {\n+ case 3: // text node\n+ case 4: // cdata section\n+ output.complexData.value += child.nodeValue;\n+ }\n+ }\n+ } else {\n+ for (child = node.firstChild; child; child = child.nextSibling) {\n+ if (child.nodeType == 1) {\n+ output.complexData.value = child;\n+ }\n+ }\n+ }\n+\n+ },\n+ \"BoundingBox\": function(node, output) {\n+ output.boundingBoxData = {\n+ dimensions: node.getAttribute(\"dimensions\"),\n+ crs: node.getAttribute(\"crs\")\n+ };\n+ this.readChildNodes(node, output.boundingBoxData);\n+ }\n+ },\n+\n+ // TODO: we should add Exception parsing here\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSExecute\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Events.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Util.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Event\n+ * Utility functions for event handling.\n+ */\n+OpenLayers.Event = {\n+\n+ /** \n+ * Property: observers \n+ * {Object} A hashtable cache of the event observers. Keyed by\n+ * element._eventCacheID \n+ */\n+ observers: false,\n+\n+ /**\n+ * Constant: KEY_SPACE\n+ * {int}\n+ */\n+ KEY_SPACE: 32,\n+\n+ /** \n+ * Constant: KEY_BACKSPACE \n+ * {int} \n+ */\n+ KEY_BACKSPACE: 8,\n+\n+ /** \n+ * Constant: KEY_TAB \n+ * {int} \n+ */\n+ KEY_TAB: 9,\n+\n+ /** \n+ * Constant: KEY_RETURN \n+ * {int} \n+ */\n+ KEY_RETURN: 13,\n+\n+ /** \n+ * Constant: KEY_ESC \n+ * {int} \n+ */\n+ KEY_ESC: 27,\n+\n+ /** \n+ * Constant: KEY_LEFT \n+ * {int} \n+ */\n+ KEY_LEFT: 37,\n+\n+ /** \n+ * Constant: KEY_UP \n+ * {int} \n+ */\n+ KEY_UP: 38,\n+\n+ /** \n+ * Constant: KEY_RIGHT \n+ * {int} \n+ */\n+ KEY_RIGHT: 39,\n+\n+ /** \n+ * Constant: KEY_DOWN \n+ * {int} \n+ */\n+ KEY_DOWN: 40,\n+\n+ /** \n+ * Constant: KEY_DELETE \n+ * {int} \n+ */\n+ KEY_DELETE: 46,\n+\n+\n+ /**\n+ * Method: element\n+ * Cross browser event element detection.\n+ * \n+ * Parameters:\n+ * event - {Event} \n+ * \n+ * Returns:\n+ * {DOMElement} The element that caused the event \n+ */\n+ element: function(event) {\n+ return event.target || event.srcElement;\n+ },\n+\n+ /**\n+ * Method: isSingleTouch\n+ * Determine whether event was caused by a single touch\n+ *\n+ * Parameters:\n+ * event - {Event}\n+ *\n+ * Returns:\n+ * {Boolean}\n+ */\n+ isSingleTouch: function(event) {\n+ return event.touches && event.touches.length == 1;\n+ },\n+\n+ /**\n+ * Method: isMultiTouch\n+ * Determine whether event was caused by a multi touch\n+ *\n+ * Parameters:\n+ * event - {Event}\n+ *\n+ * Returns:\n+ * {Boolean}\n+ */\n+ isMultiTouch: function(event) {\n+ return event.touches && event.touches.length > 1;\n+ },\n+\n+ /**\n+ * Method: isLeftClick\n+ * Determine whether event was caused by a left click. \n+ *\n+ * Parameters:\n+ * event - {Event} \n+ * \n+ * Returns:\n+ * {Boolean}\n+ */\n+ isLeftClick: function(event) {\n+ return (((event.which) && (event.which == 1)) ||\n+ ((event.button) && (event.button == 1)));\n+ },\n+\n+ /**\n+ * Method: isRightClick\n+ * Determine whether event was caused by a right mouse click. \n+ *\n+ * Parameters:\n+ * event - {Event} \n+ * \n+ * Returns:\n+ * {Boolean}\n+ */\n+ isRightClick: function(event) {\n+ return (((event.which) && (event.which == 3)) ||\n+ ((event.button) && (event.button == 2)));\n+ },\n+\n+ /**\n+ * Method: stop\n+ * Stops an event from propagating. \n+ *\n+ * Parameters: \n+ * event - {Event} \n+ * allowDefault - {Boolean} If true, we stop the event chain but \n+ * still allow the default browser behaviour (text selection,\n+ * radio-button clicking, etc). Default is false.\n+ */\n+ stop: function(event, allowDefault) {\n+\n+ if (!allowDefault) {\n+ OpenLayers.Event.preventDefault(event);\n+ }\n+\n+ if (event.stopPropagation) {\n+ event.stopPropagation();\n+ } else {\n+ event.cancelBubble = true;\n+ }\n+ },\n+\n+ /**\n+ * Method: preventDefault\n+ * Cancels the event if it is cancelable, without stopping further\n+ * propagation of the event.\n+ *\n+ * Parameters:\n+ * event - {Event}\n+ */\n+ preventDefault: function(event) {\n+ if (event.preventDefault) {\n+ event.preventDefault();\n+ } else {\n+ event.returnValue = false;\n+ }\n+ },\n+\n+ /** \n+ * Method: findElement\n+ * \n+ * Parameters:\n+ * event - {Event} \n+ * tagName - {String} \n+ * \n+ * Returns:\n+ * {DOMElement} The first node with the given tagName, starting from the\n+ * node the event was triggered on and traversing the DOM upwards\n+ */\n+ findElement: function(event, tagName) {\n+ var element = OpenLayers.Event.element(event);\n+ while (element.parentNode && (!element.tagName ||\n+ (element.tagName.toUpperCase() != tagName.toUpperCase()))) {\n+ element = element.parentNode;\n+ }\n+ return element;\n+ },\n+\n+ /** \n+ * Method: observe\n+ * \n+ * Parameters:\n+ * elementParam - {DOMElement || String} \n+ * name - {String} \n+ * observer - {function} \n+ * useCapture - {Boolean} \n+ */\n+ observe: function(elementParam, name, observer, useCapture) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ useCapture = useCapture || false;\n+\n+ if (name == 'keypress' &&\n+ (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.attachEvent)) {\n+ name = 'keydown';\n+ }\n+\n+ //if observers cache has not yet been created, create it\n+ if (!this.observers) {\n+ this.observers = {};\n+ }\n+\n+ //if not already assigned, make a new unique cache ID\n+ if (!element._eventCacheID) {\n+ var idPrefix = \"eventCacheID_\";\n+ if (element.id) {\n+ idPrefix = element.id + \"_\" + idPrefix;\n+ }\n+ element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);\n+ }\n+\n+ var cacheID = element._eventCacheID;\n+\n+ //if there is not yet a hash entry for this element, add one\n+ if (!this.observers[cacheID]) {\n+ this.observers[cacheID] = [];\n+ }\n+\n+ //add a new observer to this element's list\n+ this.observers[cacheID].push({\n+ 'element': element,\n+ 'name': name,\n+ 'observer': observer,\n+ 'useCapture': useCapture\n+ });\n+\n+ //add the actual browser event listener\n+ if (element.addEventListener) {\n+ element.addEventListener(name, observer, useCapture);\n+ } else if (element.attachEvent) {\n+ element.attachEvent('on' + name, observer);\n+ }\n+ },\n+\n+ /** \n+ * Method: stopObservingElement\n+ * Given the id of an element to stop observing, cycle through the \n+ * element's cached observers, calling stopObserving on each one, \n+ * skipping those entries which can no longer be removed.\n+ * \n+ * parameters:\n+ * elementParam - {DOMElement || String} \n+ */\n+ stopObservingElement: function(elementParam) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\n+\n+ this._removeElementObservers(OpenLayers.Event.observers[cacheID]);\n+ },\n+\n+ /**\n+ * Method: _removeElementObservers\n+ *\n+ * Parameters:\n+ * elementObservers - {Array(Object)} Array of (element, name, \n+ * observer, usecapture) objects, \n+ * taken directly from hashtable\n+ */\n+ _removeElementObservers: function(elementObservers) {\n+ if (elementObservers) {\n+ for (var i = elementObservers.length - 1; i >= 0; i--) {\n+ var entry = elementObservers[i];\n+ OpenLayers.Event.stopObserving.apply(this, [\n+ entry.element, entry.name, entry.observer, entry.useCapture\n+ ]);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: stopObserving\n+ * \n+ * Parameters:\n+ * elementParam - {DOMElement || String} \n+ * name - {String} \n+ * observer - {function} \n+ * useCapture - {Boolean} \n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the event observer was removed\n+ */\n+ stopObserving: function(elementParam, name, observer, useCapture) {\n+ useCapture = useCapture || false;\n+\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\n+\n+ if (name == 'keypress') {\n+ if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) ||\n+ element.detachEvent) {\n+ name = 'keydown';\n+ }\n+ }\n+\n+ // find element's entry in this.observers cache and remove it\n+ var foundEntry = false;\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ if (elementObservers) {\n+\n+ // find the specific event type in the element's list\n+ var i = 0;\n+ while (!foundEntry && i < elementObservers.length) {\n+ var cacheEntry = elementObservers[i];\n+\n+ if ((cacheEntry.name == name) &&\n+ (cacheEntry.observer == observer) &&\n+ (cacheEntry.useCapture == useCapture)) {\n+\n+ elementObservers.splice(i, 1);\n+ if (elementObservers.length == 0) {\n+ delete OpenLayers.Event.observers[cacheID];\n+ }\n+ foundEntry = true;\n+ break;\n+ }\n+ i++;\n+ }\n+ }\n+\n+ //actually remove the event listener from browser\n+ if (foundEntry) {\n+ if (element.removeEventListener) {\n+ element.removeEventListener(name, observer, useCapture);\n+ } else if (element && element.detachEvent) {\n+ element.detachEvent('on' + name, observer);\n+ }\n+ }\n+ return foundEntry;\n+ },\n+\n+ /** \n+ * Method: unloadCache\n+ * Cycle through all the element entries in the events cache and call\n+ * stopObservingElement on each. \n+ */\n+ unloadCache: function() {\n+ // check for OpenLayers.Event before checking for observers, because\n+ // OpenLayers.Event may be undefined in IE if no map instance was\n+ // created\n+ if (OpenLayers.Event && OpenLayers.Event.observers) {\n+ for (var cacheID in OpenLayers.Event.observers) {\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ OpenLayers.Event._removeElementObservers.apply(this,\n+ [elementObservers]);\n+ }\n+ OpenLayers.Event.observers = false;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Event\"\n+};\n+\n+/* prevent memory leaks in IE */\n+OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);\n+\n+/**\n+ * Class: OpenLayers.Events\n+ */\n+OpenLayers.Events = OpenLayers.Class({\n+\n+ /** \n+ * Constant: BROWSER_EVENTS\n+ * {Array(String)} supported events \n+ */\n+ BROWSER_EVENTS: [\n+ \"mouseover\", \"mouseout\",\n+ \"mousedown\", \"mouseup\", \"mousemove\",\n+ \"click\", \"dblclick\", \"rightclick\", \"dblrightclick\",\n+ \"resize\", \"focus\", \"blur\",\n+ \"touchstart\", \"touchmove\", \"touchend\",\n+ \"keydown\"\n+ ],\n+\n+ /** \n+ * Property: listeners \n+ * {Object} Hashtable of Array(Function): events listener functions \n+ */\n+ listeners: null,\n+\n+ /** \n+ * Property: object \n+ * {Object} the code object issuing application events \n+ */\n+ object: null,\n+\n+ /** \n+ * Property: element \n+ * {DOMElement} the DOM element receiving browser events \n+ */\n+ element: null,\n+\n+ /** \n+ * Property: eventHandler \n+ * {Function} bound event handler attached to elements \n+ */\n+ eventHandler: null,\n+\n+ /** \n+ * APIProperty: fallThrough \n+ * {Boolean} \n+ */\n+ fallThrough: null,\n+\n+ /** \n+ * APIProperty: includeXY\n+ * {Boolean} Should the .xy property automatically be created for browser\n+ * mouse events? In general, this should be false. If it is true, then\n+ * mouse events will automatically generate a '.xy' property on the \n+ * event object that is passed. (Prior to OpenLayers 2.7, this was true\n+ * by default.) Otherwise, you can call the getMousePosition on the\n+ * relevant events handler on the object available via the 'evt.object'\n+ * property of the evt object. So, for most events, you can call:\n+ * function named(evt) { \n+ * this.xy = this.object.events.getMousePosition(evt) \n+ * } \n+ *\n+ * This option typically defaults to false for performance reasons:\n+ * when creating an events object whose primary purpose is to manage\n+ * relatively positioned mouse events within a div, it may make\n+ * sense to set it to true.\n+ *\n+ * This option is also used to control whether the events object caches\n+ * offsets. If this is false, it will not: the reason for this is that\n+ * it is only expected to be called many times if the includeXY property\n+ * is set to true. If you set this to true, you are expected to clear \n+ * the offset cache manually (using this.clearMouseCache()) if:\n+ * the border of the element changes\n+ * the location of the element in the page changes\n+ */\n+ includeXY: false,\n+\n+ /**\n+ * APIProperty: extensions\n+ * {Object} Event extensions registered with this instance. Keys are\n+ * event types, values are {OpenLayers.Events.*} extension instances or\n+ * {Boolean} for events that an instantiated extension provides in\n+ * addition to the one it was created for.\n+ *\n+ * Extensions create an event in addition to browser events, which usually\n+ * fires when a sequence of browser events is completed. Extensions are\n+ * automatically instantiated when a listener is registered for an event\n+ * provided by an extension.\n+ *\n+ * Extensions are created in the <OpenLayers.Events> namespace using\n+ * <OpenLayers.Class>, and named after the event they provide.\n+ * The constructor receives the target <OpenLayers.Events> instance as\n+ * argument. Extensions that need to capture browser events before they\n+ * propagate can register their listeners events using <register>, with\n+ * {extension: true} as 4th argument.\n+ *\n+ * If an extension creates more than one event, an alias for each event\n+ * type should be created and reference the same class. The constructor\n+ * should set a reference in the target's extensions registry to itself.\n+ *\n+ * Below is a minimal extension that provides the \"foostart\" and \"fooend\"\n+ * event types, which replace the native \"click\" event type if clicked on\n+ * an element with the css class \"foo\":\n+ *\n+ * (code)\n+ * OpenLayers.Events.foostart = OpenLayers.Class({\n+ * initialize: function(target) {\n+ * this.target = target;\n+ * this.target.register(\"click\", this, this.doStuff, {extension: true});\n+ * // only required if extension provides more than one event type\n+ * this.target.extensions[\"foostart\"] = true;\n+ * this.target.extensions[\"fooend\"] = true;\n+ * },\n+ * destroy: function() {\n+ * var target = this.target;\n+ * target.unregister(\"click\", this, this.doStuff);\n+ * delete this.target;\n+ * // only required if extension provides more than one event type\n+ * delete target.extensions[\"foostart\"];\n+ * delete target.extensions[\"fooend\"];\n+ * },\n+ * doStuff: function(evt) {\n+ * var propagate = true;\n+ * if (OpenLayers.Event.element(evt).className === \"foo\") {\n+ * propagate = false;\n+ * var target = this.target;\n+ * target.triggerEvent(\"foostart\");\n+ * window.setTimeout(function() {\n+ * target.triggerEvent(\"fooend\");\n+ * }, 1000);\n+ * }\n+ * return propagate;\n+ * }\n+ * });\n+ * // only required if extension provides more than one event type\n+ * OpenLayers.Events.fooend = OpenLayers.Events.foostart;\n+ * (end)\n+ * \n+ */\n+ extensions: null,\n+\n+ /**\n+ * Property: extensionCount\n+ * {Object} Keys are event types (like in <listeners>), values are the\n+ * number of extension listeners for each event type.\n+ */\n+ extensionCount: null,\n+\n+ /**\n+ * Method: clearMouseListener\n+ * A version of <clearMouseCache> that is bound to this instance so that\n+ * it can be used with <OpenLayers.Event.observe> and\n+ * <OpenLayers.Event.stopObserving>.\n+ */\n+ clearMouseListener: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Events\n+ * Construct an OpenLayers.Events object.\n+ *\n+ * Parameters:\n+ * object - {Object} The js object to which this Events object is being added\n+ * element - {DOMElement} A dom element to respond to browser events\n+ * eventTypes - {Array(String)} Deprecated. Array of custom application\n+ * events. A listener may be registered for any named event, regardless\n+ * of the values provided here.\n+ * fallThrough - {Boolean} Allow events to fall through after these have\n+ * been handled?\n+ * options - {Object} Options for the events object.\n+ */\n+ initialize: function(object, element, eventTypes, fallThrough, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.object = object;\n+ this.fallThrough = fallThrough;\n+ this.listeners = {};\n+ this.extensions = {};\n+ this.extensionCount = {};\n+ this._msTouches = [];\n+\n+ // if a dom element is specified, add a listeners list \n+ // for browser events on the element and register them\n+ if (element != null) {\n+ this.attachToElement(element);\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ for (var e in this.extensions) {\n+ if (typeof this.extensions[e] !== \"boolean\") {\n+ this.extensions[e].destroy();\n+ }\n+ }\n+ this.extensions = null;\n+ if (this.element) {\n+ OpenLayers.Event.stopObservingElement(this.element);\n+ if (this.element.hasScrollEvent) {\n+ OpenLayers.Event.stopObserving(\n+ window, \"scroll\", this.clearMouseListener\n+ );\n+ }\n+ }\n+ this.element = null;\n+\n+ this.listeners = null;\n+ this.object = null;\n+ this.fallThrough = null;\n+ this.eventHandler = null;\n+ },\n+\n+ /**\n+ * APIMethod: addEventType\n+ * Deprecated. Any event can be triggered without adding it first.\n+ * \n+ * Parameters:\n+ * eventName - {String}\n+ */\n+ addEventType: function(eventName) {},\n+\n+ /**\n+ * Method: attachToElement\n+ *\n+ * Parameters:\n+ * element - {HTMLDOMElement} a DOM element to attach browser events to\n+ */\n+ attachToElement: function(element) {\n+ if (this.element) {\n+ OpenLayers.Event.stopObservingElement(this.element);\n+ } else {\n+ // keep a bound copy of handleBrowserEvent() so that we can\n+ // pass the same function to both Event.observe() and .stopObserving()\n+ this.eventHandler = OpenLayers.Function.bindAsEventListener(\n+ this.handleBrowserEvent, this\n+ );\n+\n+ // to be used with observe and stopObserving\n+ this.clearMouseListener = OpenLayers.Function.bind(\n+ this.clearMouseCache, this\n+ );\n+ }\n+ this.element = element;\n+ var msTouch = !!window.navigator.msMaxTouchPoints;\n+ var type;\n+ for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {\n+ type = this.BROWSER_EVENTS[i];\n+ // register the event cross-browser\n+ OpenLayers.Event.observe(element, type, this.eventHandler);\n+ if (msTouch && type.indexOf('touch') === 0) {\n+ this.addMsTouchListener(element, type, this.eventHandler);\n+ }\n+ }\n+ // disable dragstart in IE so that mousedown/move/up works normally\n+ OpenLayers.Event.observe(element, \"dragstart\", OpenLayers.Event.stop);\n+ },\n+\n+ /**\n+ * APIMethod: on\n+ * Convenience method for registering listeners with a common scope.\n+ * Internally, this method calls <register> as shown in the examples\n+ * below.\n+ *\n+ * Example use:\n+ * (code)\n+ * // register a single listener for the \"loadstart\" event\n+ * events.on({\"loadstart\": loadStartListener});\n+ *\n+ * // this is equivalent to the following\n+ * events.register(\"loadstart\", undefined, loadStartListener);\n+ *\n+ * // register multiple listeners to be called with the same `this` object\n+ * events.on({\n+ * \"loadstart\": loadStartListener,\n+ * \"loadend\": loadEndListener,\n+ * scope: object\n+ * });\n+ *\n+ * // this is equivalent to the following\n+ * events.register(\"loadstart\", object, loadStartListener);\n+ * events.register(\"loadend\", object, loadEndListener);\n+ * (end)\n+ *\n+ * Parameters:\n+ * object - {Object} \n+ */\n+ on: function(object) {\n+ for (var type in object) {\n+ if (type != \"scope\" && object.hasOwnProperty(type)) {\n+ this.register(type, object.scope, object[type]);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: register\n+ * Register an event on the events object.\n+ *\n+ * When the event is triggered, the 'func' function will be called, in the\n+ * context of 'obj'. Imagine we were to register an event, specifying an \n+ * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the \n+ * context in the callback function will be our Bounds object. This means\n+ * that within our callback function, we can access the properties and \n+ * methods of the Bounds object through the \"this\" variable. So our \n+ * callback could execute something like: \n+ * : leftStr = \"Left: \" + this.left;\n+ * \n+ * or\n+ * \n+ * : centerStr = \"Center: \" + this.getCenterLonLat();\n+ *\n+ * Parameters:\n+ * type - {String} Name of the event to register\n+ * obj - {Object} The object to bind the context to for the callback#.\n+ * If no object is specified, default is the Events's 'object' property.\n+ * func - {Function} The callback function. If no callback is \n+ * specified, this function does nothing.\n+ * priority - {Boolean|Object} If true, adds the new listener to the\n+ * *front* of the events queue instead of to the end.\n+ *\n+ * Valid options for priority:\n+ * extension - {Boolean} If true, then the event will be registered as\n+ * extension event. Extension events are handled before all other\n+ * events.\n+ */\n+ register: function(type, obj, func, priority) {\n+ if (type in OpenLayers.Events && !this.extensions[type]) {\n+ this.extensions[type] = new OpenLayers.Events[type](this);\n+ }\n+ if (func != null) {\n+ if (obj == null) {\n+ obj = this.object;\n+ }\n+ var listeners = this.listeners[type];\n+ if (!listeners) {\n+ listeners = [];\n+ this.listeners[type] = listeners;\n+ this.extensionCount[type] = 0;\n+ }\n+ var listener = {\n+ obj: obj,\n+ func: func\n+ };\n+ if (priority) {\n+ listeners.splice(this.extensionCount[type], 0, listener);\n+ if (typeof priority === \"object\" && priority.extension) {\n+ this.extensionCount[type]++;\n+ }\n+ } else {\n+ listeners.push(listener);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: registerPriority\n+ * Same as register() but adds the new listener to the *front* of the\n+ * events queue instead of to the end.\n+ * \n+ * TODO: get rid of this in 3.0 - Decide whether listeners should be \n+ * called in the order they were registered or in reverse order.\n+ *\n+ *\n+ * Parameters:\n+ * type - {String} Name of the event to register\n+ * obj - {Object} The object to bind the context to for the callback#.\n+ * If no object is specified, default is the Events's \n+ * 'object' property.\n+ * func - {Function} The callback function. If no callback is \n+ * specified, this function does nothing.\n+ */\n+ registerPriority: function(type, obj, func) {\n+ this.register(type, obj, func, true);\n+ },\n+\n+ /**\n+ * APIMethod: un\n+ * Convenience method for unregistering listeners with a common scope.\n+ * Internally, this method calls <unregister> as shown in the examples\n+ * below.\n+ *\n+ * Example use:\n+ * (code)\n+ * // unregister a single listener for the \"loadstart\" event\n+ * events.un({\"loadstart\": loadStartListener});\n+ *\n+ * // this is equivalent to the following\n+ * events.unregister(\"loadstart\", undefined, loadStartListener);\n+ *\n+ * // unregister multiple listeners with the same `this` object\n+ * events.un({\n+ * \"loadstart\": loadStartListener,\n+ * \"loadend\": loadEndListener,\n+ * scope: object\n+ * });\n+ *\n+ * // this is equivalent to the following\n+ * events.unregister(\"loadstart\", object, loadStartListener);\n+ * events.unregister(\"loadend\", object, loadEndListener);\n+ * (end)\n+ */\n+ un: function(object) {\n+ for (var type in object) {\n+ if (type != \"scope\" && object.hasOwnProperty(type)) {\n+ this.unregister(type, object.scope, object[type]);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: unregister\n+ *\n+ * Parameters:\n+ * type - {String} \n+ * obj - {Object} If none specified, defaults to this.object\n+ * func - {Function} \n+ */\n+ unregister: function(type, obj, func) {\n+ if (obj == null) {\n+ obj = this.object;\n+ }\n+ var listeners = this.listeners[type];\n+ if (listeners != null) {\n+ for (var i = 0, len = listeners.length; i < len; i++) {\n+ if (listeners[i].obj == obj && listeners[i].func == func) {\n+ listeners.splice(i, 1);\n+ break;\n+ }\n+ }\n+ }\n+ },\n+\n+ /** \n+ * Method: remove\n+ * Remove all listeners for a given event type. If type is not registered,\n+ * does nothing.\n+ *\n+ * Parameters:\n+ * type - {String} \n+ */\n+ remove: function(type) {\n+ if (this.listeners[type] != null) {\n+ this.listeners[type] = [];\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: triggerEvent\n+ * Trigger a specified registered event. \n+ * \n+ * Parameters:\n+ * type - {String} \n+ * evt - {Event || Object} will be passed to the listeners.\n+ *\n+ * Returns:\n+ * {Boolean} The last listener return. If a listener returns false, the\n+ * chain of listeners will stop getting called.\n+ */\n+ triggerEvent: function(type, evt) {\n+ var listeners = this.listeners[type];\n+\n+ // fast path\n+ if (!listeners || listeners.length == 0) {\n+ return undefined;\n+ }\n+\n+ // prep evt object with object & div references\n+ if (evt == null) {\n+ evt = {};\n+ }\n+ evt.object = this.object;\n+ evt.element = this.element;\n+ if (!evt.type) {\n+ evt.type = type;\n+ }\n+\n+ // execute all callbacks registered for specified type\n+ // get a clone of the listeners array to\n+ // allow for splicing during callbacks\n+ listeners = listeners.slice();\n+ var continueChain;\n+ for (var i = 0, len = listeners.length; i < len; i++) {\n+ var callback = listeners[i];\n+ // bind the context to callback.obj\n+ continueChain = callback.func.apply(callback.obj, [evt]);\n+\n+ if ((continueChain != undefined) && (continueChain == false)) {\n+ // if callback returns false, execute no more callbacks.\n+ break;\n+ }\n+ }\n+ // don't fall through to other DOM elements\n+ if (!this.fallThrough) {\n+ OpenLayers.Event.stop(evt, true);\n+ }\n+ return continueChain;\n+ },\n+\n+ /**\n+ * Method: handleBrowserEvent\n+ * Basically just a wrapper to the triggerEvent() function, but takes \n+ * care to set a property 'xy' on the event with the current mouse \n+ * position.\n+ *\n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ handleBrowserEvent: function(evt) {\n+ var type = evt.type,\n+ listeners = this.listeners[type];\n+ if (!listeners || listeners.length == 0) {\n+ // noone's listening, bail out\n+ return;\n+ }\n+ // add clientX & clientY to all events - corresponds to average x, y\n+ var touches = evt.touches;\n+ if (touches && touches[0]) {\n+ var x = 0;\n+ var y = 0;\n+ var num = touches.length;\n+ var touch;\n+ for (var i = 0; i < num; ++i) {\n+ touch = this.getTouchClientXY(touches[i]);\n+ x += touch.clientX;\n+ y += touch.clientY;\n+ }\n+ evt.clientX = x / num;\n+ evt.clientY = y / num;\n+ }\n+ if (this.includeXY) {\n+ evt.xy = this.getMousePosition(evt);\n+ }\n+ this.triggerEvent(type, evt);\n+ },\n+\n+ /**\n+ * Method: getTouchClientXY\n+ * WebKit has a few bugs for clientX/clientY. This method detects them\n+ * and calculate the correct values.\n+ *\n+ * Parameters:\n+ * evt - {Touch} a Touch object from a TouchEvent\n+ * \n+ * Returns:\n+ * {Object} An object with only clientX and clientY properties with the\n+ * calculated values.\n+ */\n+ getTouchClientXY: function(evt) {\n+ // olMochWin is to override window, used for testing\n+ var win = window.olMockWin || window,\n+ winPageX = win.pageXOffset,\n+ winPageY = win.pageYOffset,\n+ x = evt.clientX,\n+ y = evt.clientY;\n+\n+ if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||\n+ evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {\n+ // iOS4 include scroll offset in clientX/Y\n+ x = x - winPageX;\n+ y = y - winPageY;\n+ } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX)) {\n+ // Some Android browsers have totally bogus values for clientX/Y\n+ // when scrolling/zooming a page\n+ x = evt.pageX - winPageX;\n+ y = evt.pageY - winPageY;\n+ }\n+\n+ evt.olClientX = x;\n+ evt.olClientY = y;\n+\n+ return {\n+ clientX: x,\n+ clientY: y\n+ };\n+ },\n+\n+ /**\n+ * APIMethod: clearMouseCache\n+ * Clear cached data about the mouse position. This should be called any \n+ * time the element that events are registered on changes position \n+ * within the page.\n+ */\n+ clearMouseCache: function() {\n+ this.element.scrolls = null;\n+ this.element.lefttop = null;\n+ this.element.offsets = null;\n+ },\n+\n+ /**\n+ * Method: getMousePosition\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted\n+ * for offsets\n+ */\n+ getMousePosition: function(evt) {\n+ if (!this.includeXY) {\n+ this.clearMouseCache();\n+ } else if (!this.element.hasScrollEvent) {\n+ OpenLayers.Event.observe(window, \"scroll\", this.clearMouseListener);\n+ this.element.hasScrollEvent = true;\n+ }\n+\n+ if (!this.element.scrolls) {\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ this.element.scrolls = [\n+ window.pageXOffset || viewportElement.scrollLeft,\n+ window.pageYOffset || viewportElement.scrollTop\n+ ];\n+ }\n+\n+ if (!this.element.lefttop) {\n+ this.element.lefttop = [\n+ (document.documentElement.clientLeft || 0),\n+ (document.documentElement.clientTop || 0)\n+ ];\n+ }\n+\n+ if (!this.element.offsets) {\n+ this.element.offsets = OpenLayers.Util.pagePosition(this.element);\n+ }\n+\n+ return new OpenLayers.Pixel(\n+ (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0] -\n+ this.element.lefttop[0],\n+ (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1] -\n+ this.element.lefttop[1]\n+ );\n+ },\n+\n+ /**\n+ * Method: addMsTouchListener\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\n+ */\n+ addMsTouchListener: function(element, type, handler) {\n+ var eventHandler = this.eventHandler;\n+ var touches = this._msTouches;\n+\n+ function msHandler(evt) {\n+ handler(OpenLayers.Util.applyDefaults({\n+ stopPropagation: function() {\n+ for (var i = touches.length - 1; i >= 0; --i) {\n+ touches[i].stopPropagation();\n+ }\n+ },\n+ preventDefault: function() {\n+ for (var i = touches.length - 1; i >= 0; --i) {\n+ touches[i].preventDefault();\n+ }\n+ },\n+ type: type\n+ }, evt));\n+ }\n+\n+ switch (type) {\n+ case 'touchstart':\n+ return this.addMsTouchListenerStart(element, type, msHandler);\n+ case 'touchend':\n+ return this.addMsTouchListenerEnd(element, type, msHandler);\n+ case 'touchmove':\n+ return this.addMsTouchListenerMove(element, type, msHandler);\n+ default:\n+ throw 'Unknown touch event type';\n+ }\n+ },\n+\n+ /**\n+ * Method: addMsTouchListenerStart\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\n+ */\n+ addMsTouchListenerStart: function(element, type, handler) {\n+ var touches = this._msTouches;\n+\n+ var cb = function(e) {\n+\n+ var alreadyInArray = false;\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ alreadyInArray = true;\n+ break;\n+ }\n+ }\n+ if (!alreadyInArray) {\n+ touches.push(e);\n+ }\n+\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n+\n+ OpenLayers.Event.observe(element, 'MSPointerDown', cb);\n+\n+ // Need to also listen for end events to keep the _msTouches list\n+ // accurate\n+ var internalCb = function(e) {\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches.splice(i, 1);\n+ break;\n+ }\n+ }\n+ };\n+ OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);\n+ },\n+\n+ /**\n+ * Method: addMsTouchListenerMove\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\n+ */\n+ addMsTouchListenerMove: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n+\n+ //Don't fire touch moves when mouse isn't down\n+ if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {\n+ return;\n+ }\n+\n+ if (touches.length == 1 && touches[0].pageX == e.pageX &&\n+ touches[0].pageY == e.pageY) {\n+ // don't trigger event when pointer has not moved\n+ return;\n+ }\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches[i] = e;\n+ break;\n+ }\n+ }\n+\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n+\n+ OpenLayers.Event.observe(element, 'MSPointerMove', cb);\n+ },\n+\n+ /**\n+ * Method: addMsTouchListenerEnd\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The DOM element to register the listener on\n+ * type - {String} The event type\n+ * handler - {Function} the handler\n+ */\n+ addMsTouchListenerEnd: function(element, type, handler) {\n+ var touches = this._msTouches;\n+\n+ var cb = function(e) {\n+\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches.splice(i, 1);\n+ break;\n+ }\n+ }\n+\n+ e.touches = touches.slice();\n+ handler(e);\n+ };\n+\n+ OpenLayers.Event.observe(element, 'MSPointerUp', cb);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Events\"\n+});\n+/* ======================================================================\n+ OpenLayers/Request/XMLHttpRequest.js\n+ ====================================================================== */\n+\n+// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+// http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+/**\n+ * @requires OpenLayers/Request.js\n+ */\n+\n+(function() {\n+\n+ // Save reference to earlier defined object implementation (if any)\n+ var oXMLHttpRequest = window.XMLHttpRequest;\n+\n+ // Define on browser type\n+ var bGecko = !!window.controllers,\n+ bIE = window.document.all && !window.opera,\n+ bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n+\n+ // Enables \"XMLHttpRequest()\" call next to \"new XMLHttpReques()\"\n+ function fXMLHttpRequest() {\n+ this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n+ this._listeners = [];\n+ };\n+\n+ // Constructor\n+ function cXMLHttpRequest() {\n+ return new fXMLHttpRequest;\n+ };\n+ cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n+\n+ // BUGFIX: Firefox with Firebug installed would break pages if not executed\n+ if (bGecko && oXMLHttpRequest.wrapped)\n+ cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n+\n+ // Constants\n+ cXMLHttpRequest.UNSENT = 0;\n+ cXMLHttpRequest.OPENED = 1;\n+ cXMLHttpRequest.HEADERS_RECEIVED = 2;\n+ cXMLHttpRequest.LOADING = 3;\n+ cXMLHttpRequest.DONE = 4;\n+\n+ // Public Properties\n+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n+ cXMLHttpRequest.prototype.responseText = '';\n+ cXMLHttpRequest.prototype.responseXML = null;\n+ cXMLHttpRequest.prototype.status = 0;\n+ cXMLHttpRequest.prototype.statusText = '';\n+\n+ // Priority proposal\n+ cXMLHttpRequest.prototype.priority = \"NORMAL\";\n+\n+ // Instance-level Events Handlers\n+ cXMLHttpRequest.prototype.onreadystatechange = null;\n+\n+ // Class-level Events Handlers\n+ cXMLHttpRequest.onreadystatechange = null;\n+ cXMLHttpRequest.onopen = null;\n+ cXMLHttpRequest.onsend = null;\n+ cXMLHttpRequest.onabort = null;\n+\n+ // Public Methods\n+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n+ // Delete headers, required when object is reused\n+ delete this._headers;\n+\n+ // When bAsync parameter value is omitted, use true as default\n+ if (arguments.length < 3)\n+ bAsync = true;\n+\n+ // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests\n+ this._async = bAsync;\n+\n+ // Set the onreadystatechange handler\n+ var oRequest = this,\n+ nState = this.readyState,\n+ fOnUnload;\n+\n+ // BUGFIX: IE - memory leak on page unload (inter-page leak)\n+ if (bIE && bAsync) {\n+ fOnUnload = function() {\n+ if (nState != cXMLHttpRequest.DONE) {\n+ fCleanTransport(oRequest);\n+ // Safe to abort here since onreadystatechange handler removed\n+ oRequest.abort();\n+ }\n+ };\n+ window.attachEvent(\"onunload\", fOnUnload);\n+ }\n+\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onopen)\n+ cXMLHttpRequest.onopen.apply(this, arguments);\n+\n+ if (arguments.length > 4)\n+ this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else\n+ if (arguments.length > 3)\n+ this._object.open(sMethod, sUrl, bAsync, sUser);\n+ else\n+ this._object.open(sMethod, sUrl, bAsync);\n+\n+ this.readyState = cXMLHttpRequest.OPENED;\n+ fReadyStateChange(this);\n+\n+ this._object.onreadystatechange = function() {\n+ if (bGecko && !bAsync)\n+ return;\n+\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\n+\n+ //\n+ fSynchronizeValues(oRequest);\n+\n+ // BUGFIX: Firefox fires unnecessary DONE when aborting\n+ if (oRequest._aborted) {\n+ // Reset readyState to UNSENT\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+\n+ // Return now\n+ return;\n+ }\n+\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ // Free up queue\n+ delete oRequest._data;\n+ /* if (bAsync)\n+ fQueue_remove(oRequest);*/\n+ //\n+ fCleanTransport(oRequest);\n+ // Uncomment this block if you need a fix for IE cache\n+ /*\n+ // BUGFIX: IE - cache issue\n+ if (!oRequest._object.getResponseHeader(\"Date\")) {\n+ // Save object to cache\n+ oRequest._cached = oRequest._object;\n+\n+ // Instantiate a new transport object\n+ cXMLHttpRequest.call(oRequest);\n+\n+ // Re-send request\n+ if (sUser) {\n+ if (sPassword)\n+ oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else\n+ oRequest._object.open(sMethod, sUrl, bAsync, sUser);\n+ }\n+ else\n+ oRequest._object.open(sMethod, sUrl, bAsync);\n+ oRequest._object.setRequestHeader(\"If-Modified-Since\", oRequest._cached.getResponseHeader(\"Last-Modified\") || new window.Date(0));\n+ // Copy headers set\n+ if (oRequest._headers)\n+ for (var sHeader in oRequest._headers)\n+ if (typeof oRequest._headers[sHeader] == \"string\") // Some frameworks prototype objects with functions\n+ oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);\n+\n+ oRequest._object.onreadystatechange = function() {\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\n+\n+ if (oRequest._aborted) {\n+ //\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+\n+ // Return\n+ return;\n+ }\n+\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ // Clean Object\n+ fCleanTransport(oRequest);\n+\n+ // get cached request\n+ if (oRequest.status == 304)\n+ oRequest._object = oRequest._cached;\n+\n+ //\n+ delete oRequest._cached;\n+\n+ //\n+ fSynchronizeValues(oRequest);\n+\n+ //\n+ fReadyStateChange(oRequest);\n+\n+ // BUGFIX: IE - memory leak in interrupted\n+ if (bIE && bAsync)\n+ window.detachEvent(\"onunload\", fOnUnload);\n+ }\n+ };\n+ oRequest._object.send(null);\n+\n+ // Return now - wait until re-sent request is finished\n+ return;\n+ };\n+ */\n+ // BUGFIX: IE - memory leak in interrupted\n+ if (bIE && bAsync)\n+ window.detachEvent(\"onunload\", fOnUnload);\n+ }\n+\n+ // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice\n+ if (nState != oRequest.readyState)\n+ fReadyStateChange(oRequest);\n+\n+ nState = oRequest.readyState;\n+ }\n+ };\n+\n+ function fXMLHttpRequest_send(oRequest) {\n+ oRequest._object.send(oRequest._data);\n+\n+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests\n+ if (bGecko && !oRequest._async) {\n+ oRequest.readyState = cXMLHttpRequest.OPENED;\n+\n+ // Synchronize state\n+ fSynchronizeValues(oRequest);\n+\n+ // Simulate missing states\n+ while (oRequest.readyState < cXMLHttpRequest.DONE) {\n+ oRequest.readyState++;\n+ fReadyStateChange(oRequest);\n+ // Check if we are aborted\n+ if (oRequest._aborted)\n+ return;\n+ }\n+ }\n+ };\n+ cXMLHttpRequest.prototype.send = function(vData) {\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onsend)\n+ cXMLHttpRequest.onsend.apply(this, arguments);\n+\n+ if (!arguments.length)\n+ vData = null;\n+\n+ // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required\n+ // BUGFIX: IE - rewrites any custom mime-type to \"text/xml\" in case an XMLNode is sent\n+ // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)\n+ if (vData && vData.nodeType) {\n+ vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;\n+ if (!this._headers[\"Content-Type\"])\n+ this._object.setRequestHeader(\"Content-Type\", \"application/xml\");\n+ }\n+\n+ this._data = vData;\n+ /*\n+ // Add to queue\n+ if (this._async)\n+ fQueue_add(this);\n+ else*/\n+ fXMLHttpRequest_send(this);\n+ };\n+ cXMLHttpRequest.prototype.abort = function() {\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onabort)\n+ cXMLHttpRequest.onabort.apply(this, arguments);\n+\n+ // BUGFIX: Gecko - unnecessary DONE when aborting\n+ if (this.readyState > cXMLHttpRequest.UNSENT)\n+ this._aborted = true;\n+\n+ this._object.abort();\n+\n+ // BUGFIX: IE - memory leak\n+ fCleanTransport(this);\n+\n+ this.readyState = cXMLHttpRequest.UNSENT;\n+\n+ delete this._data;\n+ /* if (this._async)\n+ fQueue_remove(this);*/\n+ };\n+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n+ return this._object.getAllResponseHeaders();\n+ };\n+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n+ return this._object.getResponseHeader(sName);\n+ };\n+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n+ // BUGFIX: IE - cache issue\n+ if (!this._headers)\n+ this._headers = {};\n+ this._headers[sName] = sValue;\n+\n+ return this._object.setRequestHeader(sName, sValue);\n+ };\n+\n+ // EventTarget interface implementation\n+ cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n+ return;\n+ // Add listener\n+ this._listeners.push([sName, fHandler, bUseCapture]);\n+ };\n+\n+ cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n+ break;\n+ // Remove listener\n+ if (oListener)\n+ this._listeners.splice(nIndex, 1);\n+ };\n+\n+ cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n+ var oEventPseudo = {\n+ 'type': oEvent.type,\n+ 'target': this,\n+ 'currentTarget': this,\n+ 'eventPhase': 2,\n+ 'bubbles': oEvent.bubbles,\n+ 'cancelable': oEvent.cancelable,\n+ 'timeStamp': oEvent.timeStamp,\n+ 'stopPropagation': function() {}, // There is no flow\n+ 'preventDefault': function() {}, // There is no default action\n+ 'initEvent': function() {} // Original event object should be initialized\n+ };\n+\n+ // Execute onreadystatechange\n+ if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)\n+ (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n+\n+ // Execute listeners\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == oEventPseudo.type && !oListener[2])\n+ (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);\n+ };\n+\n+ //\n+ cXMLHttpRequest.prototype.toString = function() {\n+ return '[' + \"object\" + ' ' + \"XMLHttpRequest\" + ']';\n+ };\n+\n+ cXMLHttpRequest.toString = function() {\n+ return '[' + \"XMLHttpRequest\" + ']';\n+ };\n+\n+ // Helper function\n+ function fReadyStateChange(oRequest) {\n+ // Sniffing code\n+ if (cXMLHttpRequest.onreadystatechange)\n+ cXMLHttpRequest.onreadystatechange.apply(oRequest);\n+\n+ // Fake event\n+ oRequest.dispatchEvent({\n+ 'type': \"readystatechange\",\n+ 'bubbles': false,\n+ 'cancelable': false,\n+ 'timeStamp': new Date + 0\n+ });\n+ };\n+\n+ function fGetDocument(oRequest) {\n+ var oDocument = oRequest.responseXML,\n+ sResponse = oRequest.responseText;\n+ // Try parsing responseText\n+ if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n+ oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n+ oDocument.async = false;\n+ oDocument.validateOnParse = false;\n+ oDocument.loadXML(sResponse);\n+ }\n+ // Check if there is no error in document\n+ if (oDocument)\n+ if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\"))\n+ return null;\n+ return oDocument;\n+ };\n+\n+ function fSynchronizeValues(oRequest) {\n+ try {\n+ oRequest.responseText = oRequest._object.responseText;\n+ } catch (e) {}\n+ try {\n+ oRequest.responseXML = fGetDocument(oRequest._object);\n+ } catch (e) {}\n+ try {\n+ oRequest.status = oRequest._object.status;\n+ } catch (e) {}\n+ try {\n+ oRequest.statusText = oRequest._object.statusText;\n+ } catch (e) {}\n+ };\n+\n+ function fCleanTransport(oRequest) {\n+ // BUGFIX: IE - memory leak (on-page leak)\n+ oRequest._object.onreadystatechange = new window.Function;\n+ };\n+ /*\n+ // Queue manager\n+ var oQueuePending = {\"CRITICAL\":[],\"HIGH\":[],\"NORMAL\":[],\"LOW\":[],\"LOWEST\":[]},\n+ aQueueRunning = [];\n+ function fQueue_add(oRequest) {\n+ oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : \"NORMAL\"].push(oRequest);\n+ //\n+ setTimeout(fQueue_process);\n+ };\n+\n+ function fQueue_remove(oRequest) {\n+ for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)\n+ if (bFound)\n+ aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];\n+ else\n+ if (aQueueRunning[nIndex] == oRequest)\n+ bFound = true;\n+ if (bFound)\n+ aQueueRunning.length--;\n+ //\n+ setTimeout(fQueue_process);\n+ };\n+\n+ function fQueue_process() {\n+ if (aQueueRunning.length < 6) {\n+ for (var sPriority in oQueuePending) {\n+ if (oQueuePending[sPriority].length) {\n+ var oRequest = oQueuePending[sPriority][0];\n+ oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);\n+ //\n+ aQueueRunning.push(oRequest);\n+ // Send request\n+ fXMLHttpRequest_send(oRequest);\n+ break;\n+ }\n+ }\n+ }\n+ };\n+ */\n+ // Internet Explorer 5.0 (missing apply)\n+ if (!window.Function.prototype.apply) {\n+ window.Function.prototype.apply = function(oRequest, oArguments) {\n+ if (!oArguments)\n+ oArguments = [];\n+ oRequest.__func = this;\n+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n+ delete oRequest.__func;\n+ };\n+ };\n+\n+ // Register new object with window\n+ /**\n+ * Class: OpenLayers.Request.XMLHttpRequest\n+ * Standard-compliant (W3C) cross-browser implementation of the\n+ * XMLHttpRequest object. From\n+ * http://code.google.com/p/xmlhttprequest/.\n+ */\n+ if (!OpenLayers.Request) {\n+ /**\n+ * This allows for OpenLayers/Request.js to be included\n+ * before or after this script.\n+ */\n+ OpenLayers.Request = {};\n+ }\n+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;\n+})();\n+/* ======================================================================\n+ OpenLayers/Request.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/OWSCommon/v1.js\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n */\n \n /**\n- * Class: OpenLayers.Format.OWSCommon.v1_0_0\n- * Parser for OWS Common version 1.0.0.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.OWSCommon.v1>\n+ * TODO: deprecate me\n+ * Use OpenLayers.Request.proxy instead.\n */\n-OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {\n+OpenLayers.ProxyHost = \"\";\n \n+/**\n+ * Namespace: OpenLayers.Request\n+ * The OpenLayers.Request namespace contains convenience methods for working\n+ * with XMLHttpRequests. These methods work with a cross-browser\n+ * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.\n+ */\n+if (!OpenLayers.Request) {\n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n+ * This allows for OpenLayers/Request/XMLHttpRequest.js to be included\n+ * before or after this script.\n */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n+ OpenLayers.Request = {};\n+}\n+OpenLayers.Util.extend(OpenLayers.Request, {\n \n /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n+ * Constant: DEFAULT_CONFIG\n+ * {Object} Default configuration for all requests.\n */\n- readers: {\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"ExceptionReport\": function(node, obj) {\n- obj.success = false;\n- obj.exceptionReport = {\n- version: node.getAttribute('version'),\n- language: node.getAttribute('language'),\n- exceptions: []\n- };\n- this.readChildNodes(node, obj.exceptionReport);\n- }\n- }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)\n+ DEFAULT_CONFIG: {\n+ method: \"GET\",\n+ url: window.location.href,\n+ async: true,\n+ user: undefined,\n+ password: undefined,\n+ params: null,\n+ proxy: OpenLayers.ProxyHost,\n+ headers: {},\n+ data: null,\n+ callback: function() {},\n+ success: null,\n+ failure: null,\n+ scope: null\n },\n \n /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n+ * Constant: URL_SPLIT_REGEX\n */\n- writers: {\n- \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.OWSCommon.v1_0_0\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/WFST/v1_1_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WFST/v1.js\n- * @requires OpenLayers/Format/Filter/v1_1_0.js\n- * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n- */\n+ URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n \n-/**\n- * Class: OpenLayers.Format.WFST.v1_1_0\n- * A format for creating WFS v1.1.0 transactions. Create a new instance with the\n- * <OpenLayers.Format.WFST.v1_1_0> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.Filter.v1_1_0>\n- * - <OpenLayers.Format.WFST.v1>\n- */\n-OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(\n- OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the {<OpenLayers.Request>} object.\n+ *\n+ * All event listeners will receive an event object with three properties:\n+ * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.\n+ * config - {Object} The config object sent to the specific request method.\n+ * requestUrl - {String} The request url.\n+ * \n+ * Supported event types:\n+ * complete - Triggered when we have a response from the request, if a\n+ * listener returns false, no further response processing will take\n+ * place.\n+ * success - Triggered when the HTTP response has a success code (200-299).\n+ * failure - Triggered when the HTTP response does not have a success code.\n+ */\n+ events: new OpenLayers.Events(this),\n \n- /**\n- * Property: version\n- * {String} WFS version number.\n- */\n- version: \"1.1.0\",\n+ /**\n+ * Method: makeSameOrigin\n+ * Using the specified proxy, returns a same origin url of the provided url.\n+ *\n+ * Parameters:\n+ * url - {String} An arbitrary url\n+ * proxy {String|Function} The proxy to use to make the provided url a\n+ * same origin url.\n+ *\n+ * Returns\n+ * {String} the same origin url. If no proxy is provided, the returned url\n+ * will be the same as the provided url.\n+ */\n+ makeSameOrigin: function(url, proxy) {\n+ var sameOrigin = url.indexOf(\"http\") !== 0;\n+ var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n+ if (urlParts) {\n+ var location = window.location;\n+ sameOrigin =\n+ urlParts[1] == location.protocol &&\n+ urlParts[3] == location.hostname;\n+ var uPort = urlParts[4],\n+ lPort = location.port;\n+ if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n+ sameOrigin = sameOrigin && uPort == lPort;\n+ }\n+ }\n+ if (!sameOrigin) {\n+ if (proxy) {\n+ if (typeof proxy == \"function\") {\n+ url = proxy(url);\n+ } else {\n+ url = proxy + encodeURIComponent(url);\n+ }\n+ }\n+ }\n+ return url;\n+ },\n \n- /**\n- * Property: schemaLocations\n- * {Object} Properties are namespace aliases, values are schema locations.\n- */\n- schemaLocations: {\n- \"wfs\": \"http://schemas.opengis.net/wfs/1.1.0/wfs.xsd\"\n- },\n+ /**\n+ * APIMethod: issue\n+ * Create a new XMLHttpRequest object, open it, set any headers, bind\n+ * a callback to done state, and send any data. It is recommended that\n+ * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.\n+ * This method is only documented to provide detail on the configuration\n+ * options available to all request methods.\n+ *\n+ * Parameters:\n+ * config - {Object} Object containing properties for configuring the\n+ * request. Allowed configuration properties are described below.\n+ * This object is modified and should not be reused.\n+ *\n+ * Allowed config properties:\n+ * method - {String} One of GET, POST, PUT, DELETE, HEAD, or\n+ * OPTIONS. Default is GET.\n+ * url - {String} URL for the request.\n+ * async - {Boolean} Open an asynchronous request. Default is true.\n+ * user - {String} User for relevant authentication scheme. Set\n+ * to null to clear current user.\n+ * password - {String} Password for relevant authentication scheme.\n+ * Set to null to clear current password.\n+ * proxy - {String} Optional proxy. Defaults to\n+ * <OpenLayers.ProxyHost>.\n+ * params - {Object} Any key:value pairs to be appended to the\n+ * url as a query string. Assumes url doesn't already include a query\n+ * string or hash. Typically, this is only appropriate for <GET>\n+ * requests where the query string will be appended to the url.\n+ * Parameter values that are arrays will be\n+ * concatenated with a comma (note that this goes against form-encoding)\n+ * as is done with <OpenLayers.Util.getParameterString>.\n+ * headers - {Object} Object with header:value pairs to be set on\n+ * the request.\n+ * data - {String | Document} Optional data to send with the request.\n+ * Typically, this is only used with <POST> and <PUT> requests.\n+ * Make sure to provide the appropriate \"Content-Type\" header for your\n+ * data. For <POST> and <PUT> requests, the content type defaults to\n+ * \"application-xml\". If your data is a different content type, or\n+ * if you are using a different HTTP method, set the \"Content-Type\"\n+ * header to match your data type.\n+ * callback - {Function} Function to call when request is done.\n+ * To determine if the request failed, check request.status (200\n+ * indicates success).\n+ * success - {Function} Optional function to call if request status is in\n+ * the 200s. This will be called in addition to callback above and\n+ * would typically only be used as an alternative.\n+ * failure - {Function} Optional function to call if request status is not\n+ * in the 200s. This will be called in addition to callback above and\n+ * would typically only be used as an alternative.\n+ * scope - {Object} If callback is a public method on some object,\n+ * set the scope to that object.\n+ *\n+ * Returns:\n+ * {XMLHttpRequest} Request object. To abort the request before a response\n+ * is received, call abort() on the request object.\n+ */\n+ issue: function(config) {\n+ // apply default config - proxy host may have changed\n+ var defaultConfig = OpenLayers.Util.extend(\n+ this.DEFAULT_CONFIG, {\n+ proxy: OpenLayers.ProxyHost\n+ }\n+ );\n+ config = config || {};\n+ config.headers = config.headers || {};\n+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n+ config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n+ // Always set the \"X-Requested-With\" header to signal that this request\n+ // was issued through the XHR-object. Since header keys are case \n+ // insensitive and we want to allow overriding of the \"X-Requested-With\"\n+ // header through the user we cannot use applyDefaults, but have to \n+ // check manually whether we were called with a \"X-Requested-With\"\n+ // header.\n+ var customRequestedWithHeader = false,\n+ headerKey;\n+ for (headerKey in config.headers) {\n+ if (config.headers.hasOwnProperty(headerKey)) {\n+ if (headerKey.toLowerCase() === 'x-requested-with') {\n+ customRequestedWithHeader = true;\n+ }\n+ }\n+ }\n+ if (customRequestedWithHeader === false) {\n+ // we did not have a custom \"X-Requested-With\" header\n+ config.headers['X-Requested-With'] = 'XMLHttpRequest';\n+ }\n \n- /**\n- * Constructor: OpenLayers.Format.WFST.v1_1_0\n- * A class for parsing and generating WFS v1.1.0 transactions.\n- *\n- * To read additional information like hit count (numberOfFeatures) from\n- * the FeatureCollection, call the <OpenLayers.Format.WFST.v1.read> method\n- * with {output: \"object\"} as 2nd argument. Note that it is possible to\n- * just request the hit count from a WFS 1.1.0 server with the\n- * resultType=\"hits\" request parameter.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);\n- OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n- },\n+ // create request, open, and set headers\n+ var request = new OpenLayers.Request.XMLHttpRequest();\n+ var url = OpenLayers.Util.urlAppend(config.url,\n+ OpenLayers.Util.getParameterString(config.params || {}));\n+ url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n+ request.open(\n+ config.method, url, config.async, config.user, config.password\n+ );\n+ for (var header in config.headers) {\n+ request.setRequestHeader(header, config.headers[header]);\n+ }\n \n- /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n- *\n- * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n- * first - {Boolean} Should be set to true for the first node read. This\n- * is usually the readNode call in the read method. Without this being\n- * set, auto-configured properties will stick on subsequent reads.\n- *\n- * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n- */\n- readNode: function(node, obj, first) {\n- // Not the superclass, only the mixin classes inherit from\n- // Format.GML.v3. We need this because we don't want to get readNode\n- // from the superclass's superclass, which is OpenLayers.Format.XML.\n- return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, arguments);\n- },\n+ var events = this.events;\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"FeatureCollection\": function(node, obj) {\n- obj.numberOfFeatures = parseInt(node.getAttribute(\n- \"numberOfFeatures\"));\n- OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"][\"FeatureCollection\"].apply(\n- this, arguments);\n- },\n- \"TransactionResponse\": function(node, obj) {\n- obj.insertIds = [];\n- obj.success = false;\n- this.readChildNodes(node, obj);\n- },\n- \"TransactionSummary\": function(node, obj) {\n- // this is a limited test of success\n- obj.success = true;\n- },\n- \"InsertResults\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Feature\": function(node, container) {\n- var obj = {\n- fids: []\n- };\n- this.readChildNodes(node, obj);\n- container.insertIds.push(obj.fids[0]);\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.readers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.readers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.readers[\"ogc\"],\n- \"ows\": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"]\n- },\n+ // we want to execute runCallbacks with \"this\" as the\n+ // execution scope\n+ var self = this;\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"GetFeature\": function(options) {\n- var node = OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"][\"GetFeature\"].apply(this, arguments);\n- options && this.setAttributes(node, {\n- resultType: options.resultType,\n- startIndex: options.startIndex,\n- count: options.count\n- });\n- return node;\n- },\n- \"Query\": function(options) {\n- options = OpenLayers.Util.extend({\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- featureType: this.featureType,\n- srsName: this.srsName\n- }, options);\n- var prefix = options.featurePrefix;\n- var node = this.createElementNSPlus(\"wfs:Query\", {\n- attributes: {\n- typeName: (prefix ? prefix + \":\" : \"\") +\n- options.featureType,\n- srsName: options.srsName\n- }\n- });\n- if (options.featureNS) {\n- node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n- }\n- if (options.propertyNames) {\n- for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n- this.writeNode(\n- \"wfs:PropertyName\", {\n- property: options.propertyNames[i]\n- },\n- node\n- );\n- }\n- }\n- if (options.filter) {\n- OpenLayers.Format.WFST.v1_1_0.prototype.setFilterProperty.call(this, options.filter);\n- this.writeNode(\"ogc:Filter\", options.filter, node);\n+ request.onreadystatechange = function() {\n+ if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n+ var proceed = events.triggerEvent(\n+ \"complete\", {\n+ request: request,\n+ config: config,\n+ requestUrl: url\n }\n- return node;\n- },\n- \"PropertyName\": function(obj) {\n- return this.createElementNSPlus(\"wfs:PropertyName\", {\n- value: obj.property\n+ );\n+ if (proceed !== false) {\n+ self.runCallbacks({\n+ request: request,\n+ config: config,\n+ requestUrl: url\n });\n }\n- }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v3.prototype.writers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v3.prototype.writers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WFST.v1_1_0\"\n- });\n-/* ======================================================================\n- OpenLayers/Format/WPSExecute.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- * @requires OpenLayers/Format/WCSGetCoverage.js\n- * @requires OpenLayers/Format/WFST/v1_1_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WPSExecute version 1.0.0\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML,\n- OpenLayers.Format.Filter.v1_1_0, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- gml: \"http://www.opengis.net/gml\",\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- wfs: \"http://www.opengis.net/wfs\",\n- ogc: \"http://www.opengis.net/ogc\",\n- wcs: \"http://www.opengis.net/wcs\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n+ }\n+ };\n \n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+ // send request (optionally with data) and return\n+ // call in a timeout for asynchronous requests so the return is\n+ // available before readyState == 4 for cached docs\n+ if (config.async === false) {\n+ request.send(config.data);\n+ } else {\n+ window.setTimeout(function() {\n+ if (request.readyState !== 0) { // W3C: 0-UNSENT\n+ request.send(config.data);\n+ }\n+ }, 0);\n+ }\n+ return request;\n+ },\n \n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n+ /**\n+ * Method: runCallbacks\n+ * Calls the complete, success and failure callbacks. Application\n+ * can listen to the \"complete\" event, have the listener \n+ * display a confirm window and always return false, and\n+ * execute OpenLayers.Request.runCallbacks if the user\n+ * hits \"yes\" in the confirm window.\n+ *\n+ * Parameters:\n+ * options - {Object} Hash containing request, config and requestUrl keys\n+ */\n+ runCallbacks: function(options) {\n+ var request = options.request;\n+ var config = options.config;\n \n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n+ // bind callbacks to readyState 4 (done)\n+ var complete = (config.scope) ?\n+ OpenLayers.Function.bind(config.callback, config.scope) :\n+ config.callback;\n \n- schemaLocationAttr: function(options) {\n- return undefined;\n- },\n+ // optional success callback\n+ var success;\n+ if (config.success) {\n+ success = (config.scope) ?\n+ OpenLayers.Function.bind(config.success, config.scope) :\n+ config.success;\n+ }\n \n- /**\n- * Constructor: OpenLayers.Format.WPSExecute\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n+ // optional failure callback\n+ var failure;\n+ if (config.failure) {\n+ failure = (config.scope) ?\n+ OpenLayers.Function.bind(config.failure, config.scope) :\n+ config.failure;\n+ }\n \n- /**\n- * Method: write\n- *\n- * Parameters:\n- * options - {Object} Optional object.\n- *\n- * Returns:\n- * {String} An WPS Execute request XML string.\n- */\n- write: function(options) {\n- var doc;\n- if (window.ActiveXObject) {\n- doc = new ActiveXObject(\"Microsoft.XMLDOM\");\n- this.xmldom = doc;\n- } else {\n- doc = document.implementation.createDocument(\"\", \"\", null);\n- }\n- var node = this.writeNode(\"wps:Execute\", options, doc);\n- this.setAttributeNS(\n- node, this.namespaces.xsi,\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n+ if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" &&\n+ request.responseText) {\n+ request.status = 200;\n+ }\n+ complete(request);\n \n- /**\n- * APIMethod: read\n- * Parse a WPS Execute and return an object with its information.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object}\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ if (!request.status || (request.status >= 200 && request.status < 300)) {\n+ this.events.triggerEvent(\"success\", options);\n+ if (success) {\n+ success(request);\n }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n+ }\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"failure\", options);\n+ if (failure) {\n+ failure(request);\n }\n- var info = {};\n- this.readNode(data, info);\n- return info;\n- },\n+ }\n+ },\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wps\": {\n- \"Execute\": function(options) {\n- var node = this.createElementNSPlus(\"wps:Execute\", {\n- attributes: {\n- version: this.VERSION,\n- service: 'WPS'\n- }\n- });\n- this.writeNode(\"ows:Identifier\", options.identifier, node);\n- this.writeNode(\"wps:DataInputs\", options.dataInputs, node);\n- this.writeNode(\"wps:ResponseForm\", options.responseForm, node);\n- return node;\n- },\n- \"ResponseForm\": function(responseForm) {\n- var node = this.createElementNSPlus(\"wps:ResponseForm\", {});\n- if (responseForm.rawDataOutput) {\n- this.writeNode(\"wps:RawDataOutput\", responseForm.rawDataOutput, node);\n- }\n- if (responseForm.responseDocument) {\n- this.writeNode(\"wps:ResponseDocument\", responseForm.responseDocument, node);\n- }\n- return node;\n- },\n- \"ResponseDocument\": function(responseDocument) {\n- var node = this.createElementNSPlus(\"wps:ResponseDocument\", {\n- attributes: {\n- storeExecuteResponse: responseDocument.storeExecuteResponse,\n- lineage: responseDocument.lineage,\n- status: responseDocument.status\n- }\n- });\n- if (responseDocument.outputs) {\n- for (var i = 0, len = responseDocument.outputs.length; i < len; i++) {\n- this.writeNode(\"wps:Output\", responseDocument.outputs[i], node);\n- }\n- }\n- return node;\n- },\n- \"Output\": function(output) {\n- var node = this.createElementNSPlus(\"wps:Output\", {\n- attributes: {\n- asReference: output.asReference,\n- mimeType: output.mimeType,\n- encoding: output.encoding,\n- schema: output.schema\n- }\n- });\n- this.writeNode(\"ows:Identifier\", output.identifier, node);\n- this.writeNode(\"ows:Title\", output.title, node);\n- this.writeNode(\"ows:Abstract\", output[\"abstract\"], node);\n- return node;\n- },\n- \"RawDataOutput\": function(rawDataOutput) {\n- var node = this.createElementNSPlus(\"wps:RawDataOutput\", {\n- attributes: {\n- mimeType: rawDataOutput.mimeType,\n- encoding: rawDataOutput.encoding,\n- schema: rawDataOutput.schema\n- }\n- });\n- this.writeNode(\"ows:Identifier\", rawDataOutput.identifier, node);\n- return node;\n- },\n- \"DataInputs\": function(dataInputs) {\n- var node = this.createElementNSPlus(\"wps:DataInputs\", {});\n- for (var i = 0, ii = dataInputs.length; i < ii; ++i) {\n- this.writeNode(\"wps:Input\", dataInputs[i], node);\n- }\n- return node;\n- },\n- \"Input\": function(input) {\n- var node = this.createElementNSPlus(\"wps:Input\", {});\n- this.writeNode(\"ows:Identifier\", input.identifier, node);\n- if (input.title) {\n- this.writeNode(\"ows:Title\", input.title, node);\n- }\n- if (input.data) {\n- this.writeNode(\"wps:Data\", input.data, node);\n- }\n- if (input.reference) {\n- this.writeNode(\"wps:Reference\", input.reference, node);\n- }\n- if (input.boundingBoxData) {\n- this.writeNode(\"wps:BoundingBoxData\", input.boundingBoxData, node);\n- }\n- return node;\n- },\n- \"Data\": function(data) {\n- var node = this.createElementNSPlus(\"wps:Data\", {});\n- if (data.literalData) {\n- this.writeNode(\"wps:LiteralData\", data.literalData, node);\n- } else if (data.complexData) {\n- this.writeNode(\"wps:ComplexData\", data.complexData, node);\n- } else if (data.boundingBoxData) {\n- this.writeNode(\"ows:BoundingBox\", data.boundingBoxData, node);\n- }\n- return node;\n- },\n- \"LiteralData\": function(literalData) {\n- var node = this.createElementNSPlus(\"wps:LiteralData\", {\n- attributes: {\n- uom: literalData.uom\n- },\n- value: literalData.value\n- });\n- return node;\n- },\n- \"ComplexData\": function(complexData) {\n- var node = this.createElementNSPlus(\"wps:ComplexData\", {\n- attributes: {\n- mimeType: complexData.mimeType,\n- encoding: complexData.encoding,\n- schema: complexData.schema\n- }\n- });\n- var data = complexData.value;\n- if (typeof data === \"string\") {\n- node.appendChild(\n- this.getXMLDoc().createCDATASection(complexData.value)\n- );\n- } else {\n- node.appendChild(data);\n- }\n- return node;\n- },\n- \"Reference\": function(reference) {\n- var node = this.createElementNSPlus(\"wps:Reference\", {\n- attributes: {\n- mimeType: reference.mimeType,\n- \"xlink:href\": reference.href,\n- method: reference.method,\n- encoding: reference.encoding,\n- schema: reference.schema\n- }\n- });\n- if (reference.body) {\n- this.writeNode(\"wps:Body\", reference.body, node);\n- }\n- return node;\n- },\n- \"BoundingBoxData\": function(node, obj) {\n- this.writers['ows']['BoundingBox'].apply(this, [node, obj, \"wps:BoundingBoxData\"]);\n- },\n- \"Body\": function(body) {\n- var node = this.createElementNSPlus(\"wps:Body\", {});\n- if (body.wcs) {\n- this.writeNode(\"wcs:GetCoverage\", body.wcs, node);\n- } else if (body.wfs) {\n- // OpenLayers.Format.WFST expects these to be on the \n- // instance and not in the options\n- this.featureType = body.wfs.featureType;\n- this.version = body.wfs.version;\n- this.writeNode(\"wfs:GetFeature\", body.wfs, node);\n- } else {\n- this.writeNode(\"wps:Execute\", body, node);\n- }\n- return node;\n- }\n- },\n- \"wcs\": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,\n- \"wfs\": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers.ogc,\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows\n- },\n+ /**\n+ * APIMethod: GET\n+ * Send an HTTP GET request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to GET.\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ GET: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"GET\"\n+ });\n+ return OpenLayers.Request.issue(config);\n+ },\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wps\": {\n- \"ExecuteResponse\": function(node, obj) {\n- obj.executeResponse = {\n- lang: node.getAttribute(\"lang\"),\n- statusLocation: node.getAttribute(\"statusLocation\"),\n- serviceInstance: node.getAttribute(\"serviceInstance\"),\n- service: node.getAttribute(\"service\")\n- };\n- this.readChildNodes(node, obj.executeResponse);\n- },\n- \"Process\": function(node, obj) {\n- obj.process = {};\n- this.readChildNodes(node, obj.process);\n- },\n- \"Status\": function(node, obj) {\n- obj.status = {\n- creationTime: node.getAttribute(\"creationTime\")\n- };\n- this.readChildNodes(node, obj.status);\n- },\n- \"ProcessSucceeded\": function(node, obj) {\n- obj.processSucceeded = true;\n- },\n- \"ProcessOutputs\": function(node, processDescription) {\n- processDescription.processOutputs = [];\n- this.readChildNodes(node, processDescription.processOutputs);\n- },\n- \"Output\": function(node, processOutputs) {\n- var output = {};\n- this.readChildNodes(node, output);\n- processOutputs.push(output);\n- },\n- \"Reference\": function(node, output) {\n- output.reference = {\n- href: node.getAttribute(\"href\"),\n- mimeType: node.getAttribute(\"mimeType\"),\n- encoding: node.getAttribute(\"encoding\"),\n- schema: node.getAttribute(\"schema\")\n- };\n- },\n- \"Data\": function(node, output) {\n- output.data = {};\n- this.readChildNodes(node, output);\n- },\n- \"LiteralData\": function(node, output) {\n- output.literalData = {\n- dataType: node.getAttribute(\"dataType\"),\n- uom: node.getAttribute(\"uom\"),\n- value: this.getChildValue(node)\n- };\n- },\n- \"ComplexData\": function(node, output) {\n- output.complexData = {\n- mimeType: node.getAttribute(\"mimeType\"),\n- schema: node.getAttribute(\"schema\"),\n- encoding: node.getAttribute(\"encoding\"),\n- value: \"\"\n- };\n+ /**\n+ * APIMethod: POST\n+ * Send a POST request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to POST and \"Content-Type\" header set to \"application/xml\".\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties. The\n+ * default \"Content-Type\" header will be set to \"application-xml\" if\n+ * none is provided. This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ POST: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"POST\"\n+ });\n+ // set content type to application/xml if it isn't already set\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\";\n+ }\n+ return OpenLayers.Request.issue(config);\n+ },\n \n- // try to get *some* value, ignore the empty text values\n- if (this.isSimpleContent(node)) {\n- var child;\n- for (child = node.firstChild; child; child = child.nextSibling) {\n- switch (child.nodeType) {\n- case 3: // text node\n- case 4: // cdata section\n- output.complexData.value += child.nodeValue;\n- }\n- }\n- } else {\n- for (child = node.firstChild; child; child = child.nextSibling) {\n- if (child.nodeType == 1) {\n- output.complexData.value = child;\n- }\n- }\n- }\n+ /**\n+ * APIMethod: PUT\n+ * Send an HTTP PUT request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to PUT and \"Content-Type\" header set to \"application/xml\".\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties. The\n+ * default \"Content-Type\" header will be set to \"application-xml\" if\n+ * none is provided. This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ PUT: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"PUT\"\n+ });\n+ // set content type to application/xml if it isn't already set\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\";\n+ }\n+ return OpenLayers.Request.issue(config);\n+ },\n \n- },\n- \"BoundingBox\": function(node, output) {\n- output.boundingBoxData = {\n- dimensions: node.getAttribute(\"dimensions\"),\n- crs: node.getAttribute(\"crs\")\n- };\n- this.readChildNodes(node, output.boundingBoxData);\n- }\n- },\n+ /**\n+ * APIMethod: DELETE\n+ * Send an HTTP DELETE request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to DELETE.\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ DELETE: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"DELETE\"\n+ });\n+ return OpenLayers.Request.issue(config);\n+ },\n \n- // TODO: we should add Exception parsing here\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n+ /**\n+ * APIMethod: HEAD\n+ * Send an HTTP HEAD request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to HEAD.\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ HEAD: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"HEAD\"\n+ });\n+ return OpenLayers.Request.issue(config);\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.WPSExecute\"\n+ /**\n+ * APIMethod: OPTIONS\n+ * Send an HTTP OPTIONS request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to OPTIONS.\n+ *\n+ * Parameters:\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n+ */\n+ OPTIONS: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"OPTIONS\"\n+ });\n+ return OpenLayers.Request.issue(config);\n+ }\n \n- });\n+});\n /* ======================================================================\n OpenLayers/WPSProcess.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -31180,138 +19998,447 @@\n OpenLayers.Util.extend(this, options);\n },\n \n CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\n \n });\n /* ======================================================================\n- OpenLayers/Strategy.js\n+ OpenLayers/Symbolizer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Strategy\n- * Abstract vector layer strategy class. Not to be instantiated directly. Use\n- * one of the strategy subclasses instead.\n+ * Class: OpenLayers.Symbolizer\n+ * Base class representing a symbolizer used for feature rendering.\n */\n-OpenLayers.Strategy = OpenLayers.Class({\n+OpenLayers.Symbolizer = OpenLayers.Class({\n+\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n+ * APIProperty: zIndex\n+ * {Number} The zIndex determines the rendering order for a symbolizer.\n+ * Symbolizers with larger zIndex values are rendered over symbolizers\n+ * with smaller zIndex values. Default is 0.\n */\n- layer: null,\n+ zIndex: 0,\n \n /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n+ * Constructor: OpenLayers.Symbolizer\n+ * Instances of this class are not useful. See one of the subclasses.\n+ *\n+ * Parameters:\n+ * config - {Object} An object containing properties to be set on the \n+ * symbolizer. Any documented symbolizer property can be set at \n+ * construction.\n+ *\n+ * Returns:\n+ * A new symbolizer.\n */\n- options: null,\n+ initialize: function(config) {\n+ OpenLayers.Util.extend(this, config);\n+ },\n \n /** \n- * Property: active \n- * {Boolean} The control is active.\n+ * APIMethod: clone\n+ * Create a copy of this symbolizer.\n+ *\n+ * Returns a symbolizer of the same type with the same properties.\n+ */\n+ clone: function() {\n+ var Type = eval(this.CLASS_NAME);\n+ return new Type(OpenLayers.Util.extend({}, this));\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Symbolizer\"\n+\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Control.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control\n+ * Controls affect the display or behavior of the map. They allow everything\n+ * from panning and zooming to displaying a scale indicator. Controls by \n+ * default are added to the map they are contained within however it is\n+ * possible to add a control to an external div by passing the div in the\n+ * options parameter.\n+ * \n+ * Example:\n+ * The following example shows how to add many of the common controls\n+ * to a map.\n+ * \n+ * > var map = new OpenLayers.Map('map', { controls: [] });\n+ * >\n+ * > map.addControl(new OpenLayers.Control.PanZoomBar());\n+ * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));\n+ * > map.addControl(new OpenLayers.Control.Permalink());\n+ * > map.addControl(new OpenLayers.Control.Permalink('permalink'));\n+ * > map.addControl(new OpenLayers.Control.MousePosition());\n+ * > map.addControl(new OpenLayers.Control.OverviewMap());\n+ * > map.addControl(new OpenLayers.Control.KeyboardDefaults());\n+ *\n+ * The next code fragment is a quick example of how to intercept \n+ * shift-mouse click to display the extent of the bounding box\n+ * dragged out by the user. Usually controls are not created\n+ * in exactly this manner. See the source for a more complete \n+ * example:\n+ *\n+ * > var control = new OpenLayers.Control();\n+ * > OpenLayers.Util.extend(control, {\n+ * > draw: function () {\n+ * > // this Handler.Box will intercept the shift-mousedown\n+ * > // before Control.MouseDefault gets to see it\n+ * > this.box = new OpenLayers.Handler.Box( control, \n+ * > {\"done\": this.notice},\n+ * > {keyMask: OpenLayers.Handler.MOD_SHIFT});\n+ * > this.box.activate();\n+ * > },\n+ * >\n+ * > notice: function (bounds) {\n+ * > OpenLayers.Console.userError(bounds);\n+ * > }\n+ * > }); \n+ * > map.addControl(control);\n+ * \n+ */\n+OpenLayers.Control = OpenLayers.Class({\n+\n+ /** \n+ * Property: id \n+ * {String} \n+ */\n+ id: null,\n+\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} this gets set in the addControl() function in\n+ * OpenLayers.Map \n+ */\n+ map: null,\n+\n+ /** \n+ * APIProperty: div \n+ * {DOMElement} The element that contains the control, if not present the \n+ * control is placed inside the map.\n+ */\n+ div: null,\n+\n+ /** \n+ * APIProperty: type \n+ * {Number} Controls can have a 'type'. The type determines the type of\n+ * interactions which are possible with them when they are placed in an\n+ * <OpenLayers.Control.Panel>. \n+ */\n+ type: null,\n+\n+ /** \n+ * Property: allowSelection\n+ * {Boolean} By default, controls do not allow selection, because\n+ * it may interfere with map dragging. If this is true, OpenLayers\n+ * will not prevent selection of the control.\n+ * Default is false.\n+ */\n+ allowSelection: false,\n+\n+ /** \n+ * Property: displayClass \n+ * {string} This property is used for CSS related to the drawing of the\n+ * Control. \n+ */\n+ displayClass: \"\",\n+\n+ /**\n+ * APIProperty: title \n+ * {string} This property is used for showing a tooltip over the \n+ * Control. \n+ */\n+ title: \"\",\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * false.\n+ */\n+ autoActivate: false,\n+\n+ /** \n+ * APIProperty: active \n+ * {Boolean} The control is active (read-only). Use <activate> and \n+ * <deactivate> to change control state.\n */\n active: null,\n \n /**\n- * Property: autoActivate\n- * {Boolean} The creator of the strategy can set autoActivate to false\n- * to fully control when the protocol is activated and deactivated.\n- * Defaults to true.\n+ * Property: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n */\n- autoActivate: true,\n+ handlerOptions: null,\n+\n+ /** \n+ * Property: handler \n+ * {<OpenLayers.Handler>} null\n+ */\n+ handler: null,\n \n /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the strategy can set autoDestroy to false\n- * to fully control when the strategy is destroyed. Defaults to\n- * true.\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n */\n- autoDestroy: true,\n+ eventListeners: null,\n+\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to control.events.object (a reference\n+ * to the control).\n+ * element - {DOMElement} A reference to control.events.element (which\n+ * will be null unless documented otherwise).\n+ *\n+ * Supported map event types:\n+ * activate - Triggered when activated.\n+ * deactivate - Triggered when deactivated.\n+ */\n+ events: null,\n \n /**\n- * Constructor: OpenLayers.Strategy\n- * Abstract class for vector strategies. Create instances of a subclass.\n+ * Constructor: OpenLayers.Control\n+ * Create an OpenLayers Control. The options passed as a parameter\n+ * directly extend the control. For example passing the following:\n+ * \n+ * > var control = new OpenLayers.Control({div: myDiv});\n *\n+ * Overrides the default div attribute value of null.\n+ * \n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * options - {Object} \n */\n initialize: function(options) {\n+ // We do this before the extend so that instances can override\n+ // className in options.\n+ this.displayClass =\n+ this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n+\n OpenLayers.Util.extend(this, options);\n- this.options = options;\n- // set the active property here, so that user cannot override it\n- this.active = false;\n+\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ }\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n */\n destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ }\n+ this.events.destroy();\n+ this.events = null;\n+ }\n+ this.eventListeners = null;\n+\n+ // eliminate circular references\n+ if (this.handler) {\n+ this.handler.destroy();\n+ this.handler = null;\n+ }\n+ if (this.handlers) {\n+ for (var key in this.handlers) {\n+ if (this.handlers.hasOwnProperty(key) &&\n+ typeof this.handlers[key].destroy == \"function\") {\n+ this.handlers[key].destroy();\n+ }\n+ }\n+ this.handlers = null;\n+ }\n+ if (this.map) {\n+ this.map.removeControl(this);\n+ this.map = null;\n+ }\n+ this.div = null;\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * Set the map property for the control. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ this.map = map;\n+ if (this.handler) {\n+ this.handler.setMap(map);\n+ }\n },\n \n /**\n- * Method: setLayer\n- * Called to set the <layer> property.\n+ * Method: draw\n+ * The draw method is called when the control is ready to be displayed\n+ * on the page. If a div has not been created one is created. Controls\n+ * with a visual component will almost always want to override this method \n+ * to customize the look of control. \n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>}\n+ * px - {<OpenLayers.Pixel>} The top-left pixel position of the control\n+ * or null.\n+ *\n+ * Returns:\n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n */\n- setLayer: function(layer) {\n- this.layer = layer;\n+ draw: function(px) {\n+ if (this.div == null) {\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.className = this.displayClass;\n+ if (!this.allowSelection) {\n+ this.div.className += \" olControlNoSelect\";\n+ this.div.setAttribute(\"unselectable\", \"on\", 0);\n+ this.div.onselectstart = OpenLayers.Function.False;\n+ }\n+ if (this.title != \"\") {\n+ this.div.title = this.title;\n+ }\n+ }\n+ if (px != null) {\n+ this.position = px.clone();\n+ }\n+ this.moveTo(this.position);\n+ return this.div;\n },\n \n /**\n- * Method: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n+ * Method: moveTo\n+ * Sets the left and top style attributes to the passed in pixel \n+ * coordinates.\n *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ */\n+ moveTo: function(px) {\n+ if ((px != null) && (this.div != null)) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\";\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ * Explicitly activates a control and it's associated\n+ * handler if one has been set. Controls can be\n+ * deactivated by calling the deactivate() method.\n+ * \n * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n+ * {Boolean} True if the control was successfully activated or\n+ * false if the control was already active.\n */\n activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n+ if (this.active) {\n+ return false;\n }\n- return false;\n+ if (this.handler) {\n+ this.handler.activate();\n+ }\n+ this.active = true;\n+ if (this.map) {\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv,\n+ this.displayClass.replace(/ /g, \"\") + \"Active\"\n+ );\n+ }\n+ this.events.triggerEvent(\"activate\");\n+ return true;\n },\n \n /**\n- * Method: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n- *\n+ * APIMethod: deactivate\n+ * Deactivates a control and it's associated handler if any. The exact\n+ * effect of this depends on the control itself.\n+ * \n * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n+ * {Boolean} True if the control was effectively deactivated or false\n+ * if the control was already inactive.\n */\n deactivate: function() {\n if (this.active) {\n+ if (this.handler) {\n+ this.handler.deactivate();\n+ }\n this.active = false;\n+ if (this.map) {\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv,\n+ this.displayClass.replace(/ /g, \"\") + \"Active\"\n+ );\n+ }\n+ this.events.triggerEvent(\"deactivate\");\n return true;\n }\n return false;\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy\"\n+ CLASS_NAME: \"OpenLayers.Control\"\n });\n+\n+/**\n+ * Constant: OpenLayers.Control.TYPE_BUTTON\n+ */\n+OpenLayers.Control.TYPE_BUTTON = 1;\n+\n+/**\n+ * Constant: OpenLayers.Control.TYPE_TOGGLE\n+ */\n+OpenLayers.Control.TYPE_TOGGLE = 2;\n+\n+/**\n+ * Constant: OpenLayers.Control.TYPE_TOOL\n+ */\n+OpenLayers.Control.TYPE_TOOL = 3;\n /* ======================================================================\n OpenLayers/StyleMap.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -31470,942 +20597,253 @@\n }\n this.styles[renderIntent].addRules(rules);\n },\n \n CLASS_NAME: \"OpenLayers.StyleMap\"\n });\n /* ======================================================================\n- OpenLayers/Format/WPSDescribeProcess.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WPSDescribeProcess\n- * Read WPS DescribeProcess responses. \n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n-\n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- ows: \"http://www.opengis.net/ows/1.1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n-\n- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"wps\",\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WPSDescribeProcess\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Parse a WPS DescribeProcess and return an object with its information.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object}\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var info = {};\n- this.readNode(data, info);\n- return info;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wps\": {\n- \"ProcessDescriptions\": function(node, obj) {\n- obj.processDescriptions = {};\n- this.readChildNodes(node, obj.processDescriptions);\n- },\n- \"ProcessDescription\": function(node, processDescriptions) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var processDescription = {\n- processVersion: processVersion,\n- statusSupported: (node.getAttribute(\"statusSupported\") === \"true\"),\n- storeSupported: (node.getAttribute(\"storeSupported\") === \"true\")\n- };\n- this.readChildNodes(node, processDescription);\n- processDescriptions[processDescription.identifier] = processDescription;\n- },\n- \"DataInputs\": function(node, processDescription) {\n- processDescription.dataInputs = [];\n- this.readChildNodes(node, processDescription.dataInputs);\n- },\n- \"ProcessOutputs\": function(node, processDescription) {\n- processDescription.processOutputs = [];\n- this.readChildNodes(node, processDescription.processOutputs);\n- },\n- \"Output\": function(node, processOutputs) {\n- var output = {};\n- this.readChildNodes(node, output);\n- processOutputs.push(output);\n- },\n- \"ComplexOutput\": function(node, output) {\n- output.complexOutput = {};\n- this.readChildNodes(node, output.complexOutput);\n- },\n- \"LiteralOutput\": function(node, output) {\n- output.literalOutput = {};\n- this.readChildNodes(node, output.literalOutput);\n- },\n- \"Input\": function(node, dataInputs) {\n- var input = {\n- maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n- minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n- };\n- this.readChildNodes(node, input);\n- dataInputs.push(input);\n- },\n- \"BoundingBoxData\": function(node, input) {\n- input.boundingBoxData = {};\n- this.readChildNodes(node, input.boundingBoxData);\n- },\n- \"CRS\": function(node, obj) {\n- if (!obj.CRSs) {\n- obj.CRSs = {};\n- }\n- obj.CRSs[this.getChildValue(node)] = true;\n- },\n- \"LiteralData\": function(node, input) {\n- input.literalData = {};\n- this.readChildNodes(node, input.literalData);\n- },\n- \"ComplexData\": function(node, input) {\n- input.complexData = {};\n- this.readChildNodes(node, input.complexData);\n- },\n- \"Default\": function(node, complexData) {\n- complexData[\"default\"] = {};\n- this.readChildNodes(node, complexData[\"default\"]);\n- },\n- \"Supported\": function(node, complexData) {\n- complexData[\"supported\"] = {};\n- this.readChildNodes(node, complexData[\"supported\"]);\n- },\n- \"Format\": function(node, obj) {\n- var format = {};\n- this.readChildNodes(node, format);\n- if (!obj.formats) {\n- obj.formats = {};\n- }\n- obj.formats[format.mimeType] = true;\n- },\n- \"MimeType\": function(node, format) {\n- format.mimeType = this.getChildValue(node);\n- }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/WPSClient.js\n+ OpenLayers/Rule.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n \n /**\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/WPSProcess.js\n- * @requires OpenLayers/Format/WPSDescribeProcess.js\n- * @requires OpenLayers/Request.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n */\n \n /**\n- * Class: OpenLayers.WPSClient\n- * High level API for interaction with Web Processing Services (WPS).\n- * An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess>\n- * instances for servers known to the WPSClient. The WPSClient also caches\n- * DescribeProcess responses to reduce the number of requests sent to servers\n- * when processes are created.\n+ * Class: OpenLayers.Rule\n+ * This class represents an SLD Rule, as being used for rule-based SLD styling.\n */\n-OpenLayers.WPSClient = OpenLayers.Class({\n+OpenLayers.Rule = OpenLayers.Class({\n \n /**\n- * Property: servers\n- * {Object} Service metadata, keyed by a local identifier.\n- *\n- * Properties:\n- * url - {String} the url of the server\n- * version - {String} WPS version of the server\n- * processDescription - {Object} Cache of raw DescribeProcess\n- * responses, keyed by process identifier.\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- servers: null,\n+ id: null,\n \n /**\n- * Property: version\n- * {String} The default WPS version to use if none is configured. Default\n- * is '1.0.0'.\n+ * APIProperty: name\n+ * {String} name of this rule\n */\n- version: '1.0.0',\n+ name: null,\n \n /**\n- * Property: lazy\n- * {Boolean} Should the DescribeProcess be deferred until a process is\n- * fully configured? Default is false.\n+ * Property: title\n+ * {String} Title of this rule (set if included in SLD)\n */\n- lazy: false,\n+ title: null,\n \n /**\n- * Property: events\n- * {<OpenLayers.Events>}\n- *\n- * Supported event types:\n- * describeprocess - Fires when the process description is available.\n- * Listeners receive an object with a 'raw' property holding the raw\n- * DescribeProcess response, and an 'identifier' property holding the\n- * process identifier of the described process.\n+ * Property: description\n+ * {String} Description of this rule (set if abstract is included in SLD)\n */\n- events: null,\n+ description: null,\n \n /**\n- * Constructor: OpenLayers.WPSClient\n- *\n- * Parameters:\n- * options - {Object} Object whose properties will be set on the instance.\n- *\n- * Avaliable options:\n- * servers - {Object} Mandatory. Service metadata, keyed by a local\n- * identifier. Can either be a string with the service url or an\n- * object literal with additional metadata:\n- *\n- * (code)\n- * servers: {\n- * local: '/geoserver/wps'\n- * }, {\n- * opengeo: {\n- * url: 'http://demo.opengeo.org/geoserver/wps',\n- * version: '1.0.0'\n- * }\n- * }\n- * (end)\n- *\n- * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be\n- * requested until a process is fully configured. Default is false.\n+ * Property: context\n+ * {Object} An optional object with properties that the rule should be\n+ * evaluated against. If no context is specified, feature.attributes will\n+ * be used.\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- this.servers = {};\n- for (var s in options.servers) {\n- this.servers[s] = typeof options.servers[s] == 'string' ? {\n- url: options.servers[s],\n- version: this.version,\n- processDescription: {}\n- } : options.servers[s];\n- }\n- },\n+ context: null,\n \n /**\n- * APIMethod: execute\n- * Shortcut to execute a process with a single function call. This is\n- * equivalent to using <getProcess> and then calling execute on the\n- * process.\n- *\n- * Parameters:\n- * options - {Object} Options for the execute operation.\n- *\n- * Available options:\n- * server - {String} Mandatory. One of the local identifiers of the\n- * configured servers.\n- * process - {String} Mandatory. A process identifier known to the\n- * server.\n- * inputs - {Object} The inputs for the process, keyed by input identifier.\n- * For spatial data inputs, the value of an input is usually an\n- * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of\n- * geometries or features.\n- * output - {String} The identifier of an output to parse. Optional. If not\n- * provided, the first output will be parsed.\n- * success - {Function} Callback to call when the process is complete.\n- * This function is called with an outputs object as argument, which\n- * will have a property with the identifier of the requested output\n- * (e.g. 'result'). For processes that generate spatial output, the\n- * value will either be a single <OpenLayers.Feature.Vector> or an\n- * array of features.\n- * scope - {Object} Optional scope for the success callback.\n+ * Property: filter\n+ * {<OpenLayers.Filter>} Optional filter for the rule.\n */\n- execute: function(options) {\n- var process = this.getProcess(options.server, options.process);\n- process.execute({\n- inputs: options.inputs,\n- success: options.success,\n- scope: options.scope\n- });\n- },\n+ filter: null,\n \n /**\n- * APIMethod: getProcess\n- * Creates an <OpenLayers.WPSProcess>.\n- *\n- * Parameters:\n- * serverID - {String} Local identifier from the servers that this instance\n- * was constructed with.\n- * processID - {String} Process identifier known to the server.\n- *\n- * Returns:\n- * {<OpenLayers.WPSProcess>}\n+ * Property: elseFilter\n+ * {Boolean} Determines whether this rule is only to be applied only if\n+ * no other rules match (ElseFilter according to the SLD specification). \n+ * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n+ * false, the rule will always apply. For subclasses, the else property is \n+ * ignored.\n */\n- getProcess: function(serverID, processID) {\n- var process = new OpenLayers.WPSProcess({\n- client: this,\n- server: serverID,\n- identifier: processID\n- });\n- if (!this.lazy) {\n- process.describe();\n- }\n- return process;\n- },\n+ elseFilter: false,\n \n /**\n- * Method: describeProcess\n- *\n- * Parameters:\n- * serverID - {String} Identifier of the server\n- * processID - {String} Identifier of the requested process\n- * callback - {Function} Callback to call when the description is available\n- * scope - {Object} Optional execution scope for the callback function\n+ * Property: symbolizer\n+ * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n+ * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n+ * latter if useful if it is required to style e.g. vertices of a line\n+ * with a point symbolizer. Note, however, that this is not implemented\n+ * yet in OpenLayers, but it is the way how symbolizers are defined in\n+ * SLD.\n */\n- describeProcess: function(serverID, processID, callback, scope) {\n- var server = this.servers[serverID];\n- if (!server.processDescription[processID]) {\n- if (!(processID in server.processDescription)) {\n- // set to null so we know a describeFeature request is pending\n- server.processDescription[processID] = null;\n- OpenLayers.Request.GET({\n- url: server.url,\n- params: {\n- SERVICE: 'WPS',\n- VERSION: server.version,\n- REQUEST: 'DescribeProcess',\n- IDENTIFIER: processID\n- },\n- success: function(response) {\n- server.processDescription[processID] = response.responseText;\n- this.events.triggerEvent('describeprocess', {\n- identifier: processID,\n- raw: response.responseText\n- });\n- },\n- scope: this\n- });\n- } else {\n- // pending request\n- this.events.register('describeprocess', this, function describe(evt) {\n- if (evt.identifier === processID) {\n- this.events.unregister('describeprocess', this, describe);\n- callback.call(scope, evt.raw);\n- }\n- });\n- }\n- } else {\n- window.setTimeout(function() {\n- callback.call(scope, server.processDescription[processID]);\n- }, 0);\n- }\n- },\n+ symbolizer: null,\n \n /**\n- * Method: destroy\n- */\n- destroy: function() {\n- this.events.destroy();\n- this.events = null;\n- this.servers = null;\n- },\n-\n- CLASS_NAME: 'OpenLayers.WPSClient'\n-\n-});\n-/* ======================================================================\n- OpenLayers/Renderer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Renderer \n- * This is the base class for all renderers.\n- *\n- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n- * It is largely composed of virtual functions that are to be implemented\n- * in technology-specific subclasses, but there is some generic code too.\n- * \n- * The functions that *are* implemented here merely deal with the maintenance\n- * of the size and extent variables, as well as the cached 'resolution' \n- * value. \n- * \n- * A note to the user that all subclasses should use getResolution() instead\n- * of directly accessing this.resolution in order to correctly use the \n- * cacheing system.\n- *\n- */\n-OpenLayers.Renderer = OpenLayers.Class({\n-\n- /** \n- * Property: container\n- * {DOMElement} \n+ * Property: symbolizers\n+ * {Array} Collection of symbolizers associated with this rule. If \n+ * provided at construction, the symbolizers array has precedence\n+ * over the deprecated symbolizer property. Note that multiple \n+ * symbolizers are not currently supported by the vector renderers.\n+ * Rules with multiple symbolizers are currently only useful for\n+ * maintaining elements in an SLD document.\n */\n- container: null,\n+ symbolizers: null,\n \n /**\n- * Property: root\n- * {DOMElement}\n- */\n- root: null,\n-\n- /** \n- * Property: extent\n- * {<OpenLayers.Bounds>}\n+ * APIProperty: minScaleDenominator\n+ * {Number} or {String} minimum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n */\n- extent: null,\n+ minScaleDenominator: null,\n \n /**\n- * Property: locked\n- * {Boolean} If the renderer is currently in a state where many things\n- * are changing, the 'locked' property is set to true. This means \n- * that renderers can expect at least one more drawFeature event to be\n- * called with the 'locked' property set to 'true': In some renderers,\n- * this might make sense to use as a 'only update local information'\n- * flag. \n+ * APIProperty: maxScaleDenominator\n+ * {Number} or {String} maximum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n */\n- locked: false,\n+ maxScaleDenominator: null,\n \n /** \n- * Property: size\n- * {<OpenLayers.Size>} \n- */\n- size: null,\n-\n- /**\n- * Property: resolution\n- * {Float} cache of current map resolution\n- */\n- resolution: null,\n-\n- /**\n- * Property: map \n- * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n- */\n- map: null,\n-\n- /**\n- * Property: featureDx\n- * {Number} Feature offset in x direction. Will be calculated for and\n- * applied to the current feature while rendering (see\n- * <calculateFeatureDx>).\n- */\n- featureDx: 0,\n-\n- /**\n- * Constructor: OpenLayers.Renderer \n+ * Constructor: OpenLayers.Rule\n+ * Creates a Rule.\n *\n * Parameters:\n- * containerID - {<String>} \n- * options - {Object} options for this renderer. See sublcasses for\n- * supported options.\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n+ * \n+ * Returns:\n+ * {<OpenLayers.Rule>}\n */\n- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n+ initialize: function(options) {\n+ this.symbolizer = {};\n OpenLayers.Util.extend(this, options);\n+ if (this.symbolizers) {\n+ delete this.symbolizer;\n+ }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n- /**\n+ /** \n * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n destroy: function() {\n- this.container = null;\n- this.extent = null;\n- this.size = null;\n- this.resolution = null;\n- this.map = null;\n- },\n-\n- /**\n- * APIMethod: supported\n- * This should be overridden by specific subclasses\n- * \n- * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n- */\n- supported: function() {\n- return false;\n- },\n-\n- /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n- *\n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n- * We nullify the resolution cache (this.resolution) if resolutionChanged\n- * is set to true - this way it will be re-computed on the next\n- * getResolution() request.\n- *\n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n- *\n- * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n- */\n- setExtent: function(extent, resolutionChanged) {\n- this.extent = extent.clone();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio);\n- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n- }\n- if (resolutionChanged) {\n- this.resolution = null;\n+ for (var i in this.symbolizer) {\n+ this.symbolizer[i] = null;\n }\n- return true;\n+ this.symbolizer = null;\n+ delete this.symbolizers;\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n+ * APIMethod: evaluate\n+ * evaluates this rule for a specific feature\n * \n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n- *\n * Parameters:\n- * size - {<OpenLayers.Size>} \n- */\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null;\n- },\n-\n- /** \n- * Method: getResolution\n- * Uses cached copy of resolution if available to minimize computing\n+ * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n * \n * Returns:\n- * {Float} The current map's resolution\n+ * {Boolean} true if the rule applies, false if it does not.\n+ * This rule is the default rule and always returns true.\n */\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution;\n- },\n+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n \n- /**\n- * Method: drawFeature\n- * Draw the feature. The optional style argument can be used\n- * to override the feature's own style. This method should only\n- * be called from layer.drawFeature().\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {<Object>}\n- * \n- * Returns:\n- * {Boolean} true if the feature has been drawn completely, false if not,\n- * undefined if the feature had no geometry\n- */\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style;\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale();\n }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n- };\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds);\n- }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n \n- var location = feature.geometry.getCentroid();\n- if (style.labelXOffset || style.labelYOffset) {\n- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n- var res = this.getResolution();\n- location.move(xOffset * res, yOffset * res);\n- }\n- this.drawText(feature.id, style, location);\n- } else {\n- this.removeText(feature.id);\n- }\n- return rendered;\n- }\n+ // check if within minScale/maxScale bounds\n+ if (this.minScaleDenominator) {\n+ applies = scale >= OpenLayers.Style.createLiteral(\n+ this.minScaleDenominator, context);\n }\n- },\n-\n- /**\n- * Method: calculateFeatureDx\n- * {Number} Calculates the feature offset in x direction. Looking at the\n- * center of the feature bounds and the renderer extent, we calculate how\n- * many world widths the two are away from each other. This distance is\n- * used to shift the feature as close as possible to the center of the\n- * current enderer extent, which ensures that the feature is visible in the\n- * current viewport.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n- * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n- */\n- calculateFeatureDx: function(bounds, worldBounds) {\n- this.featureDx = 0;\n- if (worldBounds) {\n- var worldWidth = worldBounds.getWidth(),\n- rendererCenterX = (this.extent.left + this.extent.right) / 2,\n- featureCenterX = (bounds.left + bounds.right) / 2,\n- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n- this.featureDx = worldsAway * worldWidth;\n+ if (applies && this.maxScaleDenominator) {\n+ applies = scale < OpenLayers.Style.createLiteral(\n+ this.maxScaleDenominator, context);\n }\n- },\n-\n- /** \n- * Method: drawGeometry\n- * \n- * Draw a geometry. This should only be called from the renderer itself.\n- * Use layer.drawFeature() from outside the renderer.\n- * virtual function\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * style - {Object} \n- * featureId - {<String>} \n- */\n- drawGeometry: function(geometry, style, featureId) {},\n-\n- /**\n- * Method: drawText\n- * Function for drawing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- * style -\n- * location - {<OpenLayers.Geometry.Point>}\n- */\n- drawText: function(featureId, style, location) {},\n-\n- /**\n- * Method: removeText\n- * Function for removing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- */\n- removeText: function(featureId) {},\n \n- /**\n- * Method: clear\n- * Clear all vectors from the renderer.\n- * virtual function.\n- */\n- clear: function() {},\n+ // check if optional filter applies\n+ if (applies && this.filter) {\n+ // feature id filters get the feature, others get the context\n+ if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n+ applies = this.filter.evaluate(feature);\n+ } else {\n+ applies = this.filter.evaluate(context);\n+ }\n+ }\n \n- /**\n- * Method: getFeatureIdFromEvent\n- * Returns a feature id from an event on the renderer. \n- * How this happens is specific to the renderer. This should be\n- * called from layer.getFeatureFromEvent().\n- * Virtual function.\n- * \n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n- *\n- * Returns:\n- * {String} A feature id or undefined.\n- */\n- getFeatureIdFromEvent: function(evt) {},\n+ return applies;\n+ },\n \n /**\n- * Method: eraseFeatures \n- * This is called by the layer to erase features\n+ * Method: getContext\n+ * Gets the context for evaluating this rule\n * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * Paramters:\n+ * feature - {<OpenLayers.Feature>} feature to take the context from if\n+ * none is specified.\n */\n- eraseFeatures: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n+ getContext: function(feature) {\n+ var context = this.context;\n+ if (!context) {\n+ context = feature.attributes || feature.data;\n }\n- for (var i = 0, len = features.length; i < len; ++i) {\n- var feature = features[i];\n- this.eraseGeometry(feature.geometry, feature.id);\n- this.removeText(feature.id);\n+ if (typeof this.context == \"function\") {\n+ context = this.context(feature);\n }\n+ return context;\n },\n \n /**\n- * Method: eraseGeometry\n- * Remove a geometry from the renderer (by id).\n- * virtual function.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * featureId - {String}\n- */\n- eraseGeometry: function(geometry, featureId) {},\n-\n- /**\n- * Method: moveRoot\n- * moves this renderer's root to a (different) renderer.\n- * To be implemented by subclasses that require a common renderer root for\n- * feature selection.\n- * \n- * Parameters:\n- * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n- */\n- moveRoot: function(renderer) {},\n-\n- /**\n- * Method: getRenderLayerId\n- * Gets the layer that this renderer's output appears on. If moveRoot was\n- * used, this will be different from the id of the layer containing the\n- * features rendered by this renderer.\n- * \n- * Returns:\n- * {String} the id of the output layer.\n- */\n- getRenderLayerId: function() {\n- return this.container.id;\n- },\n-\n- /**\n- * Method: applyDefaultSymbolizer\n- * \n- * Parameters:\n- * symbolizer - {Object}\n+ * APIMethod: clone\n+ * Clones this rule.\n * \n * Returns:\n- * {Object}\n+ * {<OpenLayers.Rule>} Clone of this rule.\n */\n- applyDefaultSymbolizer: function(symbolizer) {\n- var result = OpenLayers.Util.extend({},\n- OpenLayers.Renderer.defaultSymbolizer);\n- if (symbolizer.stroke === false) {\n- delete result.strokeWidth;\n- delete result.strokeColor;\n- }\n- if (symbolizer.fill === false) {\n- delete result.fillColor;\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.symbolizers) {\n+ // clone symbolizers\n+ var len = this.symbolizers.length;\n+ options.symbolizers = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ options.symbolizers[i] = this.symbolizers[i].clone();\n+ }\n+ } else {\n+ // clone symbolizer\n+ options.symbolizer = {};\n+ var value, type;\n+ for (var key in this.symbolizer) {\n+ value = this.symbolizer[key];\n+ type = typeof value;\n+ if (type === \"object\") {\n+ options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n+ } else if (type === \"string\") {\n+ options.symbolizer[key] = value;\n+ }\n+ }\n }\n- OpenLayers.Util.extend(result, symbolizer);\n- return result;\n+ // clone filter\n+ options.filter = this.filter && this.filter.clone();\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ return new OpenLayers.Rule(options);\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Rule\"\n });\n-\n-/**\n- * Constant: OpenLayers.Renderer.defaultSymbolizer\n- * {Object} Properties from this symbolizer will be applied to symbolizers\n- * with missing properties. This can also be used to set a global\n- * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n- * following code before rendering any vector features:\n- * (code)\n- * OpenLayers.Renderer.defaultSymbolizer = {\n- * fillColor: \"#808080\",\n- * fillOpacity: 1,\n- * strokeColor: \"#000000\",\n- * strokeOpacity: 1,\n- * strokeWidth: 1,\n- * pointRadius: 3,\n- * graphicName: \"square\"\n- * };\n- * (end)\n- */\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: 'cm'\n-};\n-\n-\n-\n-/**\n- * Constant: OpenLayers.Renderer.symbol\n- * Coordinate arrays for well known (named) symbols.\n- */\n-OpenLayers.Renderer.symbol = {\n- \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n- 303, 215, 231, 161, 321, 161, 350, 75\n- ],\n- \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n- 4, 0\n- ],\n- \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n- \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n- \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n-};\n-/* ======================================================================\n- OpenLayers/Spherical.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n-\n-/**\n- * Namespace: Spherical\n- * The OpenLayers.Spherical namespace includes utility functions for\n- * calculations on the basis of a spherical earth (ignoring ellipsoidal\n- * effects), which is accurate enough for most purposes.\n- *\n- * Relevant links:\n- * * http://www.movable-type.co.uk/scripts/latlong.html\n- * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical\n- */\n-\n-OpenLayers.Spherical = OpenLayers.Spherical || {};\n-\n-OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n-\n-/**\n- * APIFunction: computeDistanceBetween\n- * Computes the distance between two LonLats.\n- *\n- * Parameters:\n- * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n- * JavaScript literal with lon lat properties.\n- * radius - {Float} The radius. Optional. Defaults to 6378137 meters.\n- *\n- * Returns:\n- * {Float} The distance in meters.\n- */\n-OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n- var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n- var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n- var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n- var a = sinHalfDeltaLat * sinHalfDeltaLat +\n- sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n- return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n-};\n-\n-\n-/**\n- * APIFunction: computeHeading\n- * Computes the heading from one LonLat to another LonLat.\n- *\n- * Parameters:\n- * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n- * JavaScript literal with lon lat properties.\n- *\n- * Returns:\n- * {Float} The heading in degrees.\n- */\n-OpenLayers.Spherical.computeHeading = function(from, to) {\n- var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n- var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) -\n- Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n- return 180 * Math.atan2(y, x) / Math.PI;\n-};\n /* ======================================================================\n OpenLayers/Symbolizer/Point.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -32958,3662 +21396,4130 @@\n }\n return new OpenLayers.Style2(config);\n },\n \n CLASS_NAME: \"OpenLayers.Style2\"\n });\n /* ======================================================================\n- OpenLayers/Layer/TileCache.js\n+ OpenLayers/Util/vendorPrefix.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/SingleFile.js\n */\n \n+OpenLayers.Util = OpenLayers.Util || {};\n /**\n- * Class: OpenLayers.Layer.TileCache\n- * A read only TileCache layer. Used to requests tiles cached by TileCache in\n- * a web accessible cache. This means that you have to pre-populate your\n- * cache before this layer can be used. It is meant only to read tiles\n- * created by TileCache, and not to make calls to TileCache for tile\n- * creation. Create a new instance with the\n- * <OpenLayers.Layer.TileCache> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Namespace: OpenLayers.Util.vendorPrefix\n+ * A collection of utility functions to detect vendor prefixed features\n */\n-OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Util.vendorPrefix = (function() {\n+ \"use strict\";\n \n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} Treat this layer as a base layer. Default is true.\n- */\n- isBaseLayer: true,\n+ var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n+ divStyle = document.createElement(\"div\").style,\n+ cssCache = {},\n+ jsCache = {};\n \n- /** \n- * APIProperty: format\n- * {String} Mime type of the images returned. Default is image/png.\n- */\n- format: 'image/png',\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer. (b) The map can work with resolutions\n- * that aren't supported by the server, i.e. that aren't in\n- * <serverResolutions>. When the map is displayed in such a resolution\n- * data for the closest server-supported resolution is loaded and the\n- * layer div is stretched as necessary.\n+ * Function: domToCss\n+ * Converts a upper camel case DOM style property name to a CSS property\n+ * i.e. transformOrigin -> transform-origin\n+ * or WebkitTransformOrigin -> -webkit-transform-origin\n+ *\n+ * Parameters:\n+ * prefixedDom - {String} The property to convert\n+ *\n+ * Returns:\n+ * {String} The CSS property\n */\n- serverResolutions: null,\n+ function domToCss(prefixedDom) {\n+ if (!prefixedDom) {\n+ return null;\n+ }\n+ return prefixedDom.\n+ replace(/([A-Z])/g, function(c) {\n+ return \"-\" + c.toLowerCase();\n+ }).\n+ replace(/^ms-/, \"-ms-\");\n+ }\n \n /**\n- * Constructor: OpenLayers.Layer.TileCache\n- * Create a new read only TileCache layer.\n+ * APIMethod: css\n+ * Detect which property is used for a CSS property\n *\n * Parameters:\n- * name - {String} Name of the layer displayed in the interface\n- * url - {String} Location of the web accessible cache (not the location of\n- * your tilecache script!)\n- * layername - {String} Layer name as defined in the TileCache \n- * configuration\n- * options - {Object} Optional object with properties to be set on the\n- * layer. Note that you should speficy your resolutions to match\n- * your TileCache configuration. This can be done by setting\n- * the resolutions array directly (here or on the map), by setting\n- * maxResolution and numZoomLevels, or by using scale based properties.\n+ * property - {String} The standard (unprefixed) CSS property name\n+ *\n+ * Returns:\n+ * {String} The standard CSS property, prefixed property or null if not\n+ * supported\n */\n- initialize: function(name, url, layername, options) {\n- this.layername = layername;\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this,\n- [name, url, {}, options]);\n- this.extension = this.format.split('/')[1].toLowerCase();\n- this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;\n- },\n+ function css(property) {\n+ if (cssCache[property] === undefined) {\n+ var domProperty = property.\n+ replace(/(-[\\s\\S])/g, function(c) {\n+ return c.charAt(1).toUpperCase();\n+ });\n+ var prefixedDom = style(domProperty);\n+ cssCache[property] = domToCss(prefixedDom);\n+ }\n+ return cssCache[property];\n+ }\n \n /**\n- * APIMethod: clone\n- * obj - {Object} \n- * \n+ * APIMethod: js\n+ * Detect which property is used for a JS property/method\n+ *\n+ * Parameters:\n+ * obj - {Object} The object to test on\n+ * property - {String} The standard (unprefixed) JS property name\n+ *\n * Returns:\n- * {<OpenLayers.Layer.TileCache>} An exact clone of this \n- * <OpenLayers.Layer.TileCache>\n+ * {String} The standard JS property, prefixed property or null if not\n+ * supported\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.TileCache(this.name,\n- this.url,\n- this.layername,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ function js(obj, property) {\n+ if (jsCache[property] === undefined) {\n+ var tmpProp,\n+ i = 0,\n+ l = VENDOR_PREFIXES.length,\n+ prefix,\n+ isStyleObj = (typeof obj.cssText !== \"undefined\");\n \n- // copy/set any non-init, non-simple values here\n+ jsCache[property] = null;\n+ for (; i < l; i++) {\n+ prefix = VENDOR_PREFIXES[i];\n+ if (prefix) {\n+ if (!isStyleObj) {\n+ // js prefix should be lower-case, while style\n+ // properties have upper case on first character\n+ prefix = prefix.toLowerCase();\n+ }\n+ tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);\n+ } else {\n+ tmpProp = property;\n+ }\n \n- return obj;\n- },\n+ if (obj[tmpProp] !== undefined) {\n+ jsCache[property] = tmpProp;\n+ break;\n+ }\n+ }\n+ }\n+ return jsCache[property];\n+ }\n \n /**\n- * Method: getURL\n+ * APIMethod: style\n+ * Detect which property is used for a DOM style property\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n+ * property - {String} The standard (unprefixed) style property name\n+ *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as parameters.\n+ * {String} The standard style property, prefixed property or null if not\n+ * supported\n */\n- getURL: function(bounds) {\n- var res = this.getServerResolution();\n- var bbox = this.maxExtent;\n- var size = this.tileSize;\n- var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));\n- var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));\n- var tileZ = this.serverResolutions != null ?\n- OpenLayers.Util.indexOf(this.serverResolutions, res) :\n- this.map.getZoom();\n+ function style(property) {\n+ return js(divStyle, property);\n+ }\n \n- var components = [\n- this.layername,\n- OpenLayers.Number.zeroPad(tileZ, 2),\n- OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3),\n- OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension\n- ];\n- var path = components.join('/');\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n- }\n- url = (url.charAt(url.length - 1) == '/') ? url : url + '/';\n- return url + path;\n- },\n+ return {\n+ css: css,\n+ js: js,\n+ style: style,\n \n- CLASS_NAME: \"OpenLayers.Layer.TileCache\"\n-});\n+ // used for testing\n+ cssCache: cssCache,\n+ jsCache: jsCache\n+ };\n+}());\n /* ======================================================================\n- OpenLayers/Layer/EventPane.js\n+ OpenLayers/Animation.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/SingleFile.js\n+ * @requires OpenLayers/Util/vendorPrefix.js\n */\n \n /**\n- * Class: OpenLayers.Layer.EventPane\n- * Base class for 3rd party layers, providing a DOM element which isolates\n- * the 3rd-party layer from mouse events.\n- * Only used by Google layers.\n- *\n- * Automatically instantiated by the Google constructor, and not usually instantiated directly.\n- *\n- * Create a new event pane layer with the\n- * <OpenLayers.Layer.EventPane> constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer>\n+ * Namespace: OpenLayers.Animation\n+ * A collection of utility functions for executing methods that repaint a \n+ * portion of the browser window. These methods take advantage of the\n+ * browser's scheduled repaints where requestAnimationFrame is available.\n */\n-OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n+OpenLayers.Animation = (function(window) {\n \n /**\n- * APIProperty: smoothDragPan\n- * {Boolean} smoothDragPan determines whether non-public/internal API\n- * methods are used for better performance while dragging EventPane \n- * layers. When not in sphericalMercator mode, the smoother dragging \n- * doesn't actually move north/south directly with the number of \n- * pixels moved, resulting in a slight offset when you drag your mouse \n- * north south with this option on. If this visual disparity bothers \n- * you, you should turn this option off, or use spherical mercator. \n- * Default is on.\n+ * Property: isNative\n+ * {Boolean} true if a native requestAnimationFrame function is available\n */\n- smoothDragPan: true,\n+ var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n+ var isNative = !!(requestAnimationFrame);\n \n /**\n- * Property: isBaseLayer\n- * {Boolean} EventPaned layers are always base layers, by necessity.\n+ * Function: requestFrame\n+ * Schedule a function to be called at the next available animation frame.\n+ * Uses the native method where available. Where requestAnimationFrame is\n+ * not available, setTimeout will be called with a 16ms delay.\n+ *\n+ * Parameters:\n+ * callback - {Function} The function to be called at the next animation frame.\n+ * element - {DOMElement} Optional element that visually bounds the animation.\n */\n- isBaseLayer: true,\n+ var requestFrame = (function() {\n+ var request = window[requestAnimationFrame] ||\n+ function(callback, element) {\n+ window.setTimeout(callback, 16);\n+ };\n+ // bind to window to avoid illegal invocation of native function\n+ return function(callback, element) {\n+ request.apply(window, [callback, element]);\n+ };\n+ })();\n+\n+ // private variables for animation loops\n+ var counter = 0;\n+ var loops = {};\n \n /**\n- * APIProperty: isFixed\n- * {Boolean} EventPaned layers are fixed by default.\n+ * Function: start\n+ * Executes a method with <requestFrame> in series for some \n+ * duration.\n+ *\n+ * Parameters:\n+ * callback - {Function} The function to be called at the next animation frame.\n+ * duration - {Number} Optional duration for the loop. If not provided, the\n+ * animation loop will execute indefinitely.\n+ * element - {DOMElement} Optional element that visually bounds the animation.\n+ *\n+ * Returns:\n+ * {Number} Identifier for the animation loop. Used to stop animations with\n+ * <stop>.\n */\n- isFixed: true,\n+ function start(callback, duration, element) {\n+ duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n+ var id = ++counter;\n+ var start = +new Date;\n+ loops[id] = function() {\n+ if (loops[id] && +new Date - start <= duration) {\n+ callback();\n+ if (loops[id]) {\n+ requestFrame(loops[id], element);\n+ }\n+ } else {\n+ delete loops[id];\n+ }\n+ };\n+ requestFrame(loops[id], element);\n+ return id;\n+ }\n \n /**\n- * Property: pane\n- * {DOMElement} A reference to the element that controls the events.\n+ * Function: stop\n+ * Terminates an animation loop started with <start>.\n+ *\n+ * Parameters:\n+ * id - {Number} Identifier returned from <start>.\n */\n- pane: null,\n+ function stop(id) {\n+ delete loops[id];\n+ }\n+\n+ return {\n+ isNative: isNative,\n+ requestFrame: requestFrame,\n+ start: start,\n+ stop: stop\n+ };\n+\n+})(window);\n+/* ======================================================================\n+ OpenLayers/Kinetic.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.js\n+ */\n \n+OpenLayers.Kinetic = OpenLayers.Class({\n \n /**\n- * Property: mapObject\n- * {Object} This is the object which will be used to load the 3rd party library\n- * in the case of the google layer, this will be of type GMap, \n- * in the case of the ve layer, this will be of type VEMap\n+ * Property: threshold\n+ * In most cases changing the threshold isn't needed.\n+ * In px/ms, default to 0.\n */\n- mapObject: null,\n+ threshold: 0,\n+\n+ /**\n+ * Property: deceleration\n+ * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n+ */\n+ deceleration: 0.0035,\n \n+ /**\n+ * Property: nbPoints\n+ * {Integer} the number of points we use to calculate the kinetic\n+ * initial values.\n+ */\n+ nbPoints: 100,\n \n /**\n- * Constructor: OpenLayers.Layer.EventPane\n- * Create a new event pane layer\n+ * Property: delay\n+ * {Float} time to consider to calculate the kinetic initial values.\n+ * In ms, default to 200.\n+ */\n+ delay: 200,\n+\n+ /**\n+ * Property: points\n+ * List of points use to calculate the kinetic initial values.\n+ */\n+ points: undefined,\n+\n+ /**\n+ * Property: timerId\n+ * ID of the timer.\n+ */\n+ timerId: undefined,\n+\n+ /**\n+ * Constructor: OpenLayers.Kinetic\n *\n * Parameters:\n- * name - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * options - {Object}\n */\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (this.pane == null) {\n- this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\");\n- }\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * APIMethod: destroy\n- * Deconstruct this layer.\n+ * Method: begin\n+ * Begins the dragging.\n */\n- destroy: function() {\n- this.mapObject = null;\n- this.pane = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = [];\n },\n \n-\n /**\n- * Method: setMap\n- * Set the map property for the layer. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n+ * Method: update\n+ * Updates during the dragging.\n *\n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * xy - {<OpenLayers.Pixel>} The new position.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n-\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n- this.pane.style.display = this.div.style.display;\n- this.pane.style.width = \"100%\";\n- this.pane.style.height = \"100%\";\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.pane.style.background =\n- \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\";\n- }\n-\n- if (this.isFixed) {\n- this.map.viewPortDiv.appendChild(this.pane);\n- } else {\n- this.map.layerContainerDiv.appendChild(this.pane);\n- }\n-\n- // once our layer has been added to the map, we can load it\n- this.loadMapObject();\n-\n- // if map didn't load, display warning\n- if (this.mapObject == null) {\n- this.loadWarningMessage();\n+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: new Date().getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop();\n }\n },\n \n /**\n- * APIMethod: removeMap\n- * On being removed from the map, we'll like to remove the invisible 'pane'\n- * div that we added to it on creation. \n- * \n+ * Method: end\n+ * Ends the dragging, start the kinetic.\n+ *\n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * xy - {<OpenLayers.Pixel>} The last position.\n+ *\n+ * Returns:\n+ * {Object} An object with two properties: \"speed\", and \"theta\". The\n+ * \"speed\" and \"theta\" values are to be passed to the move \n+ * function when starting the animation.\n */\n- removeMap: function(map) {\n- if (this.pane && this.pane.parentNode) {\n- this.pane.parentNode.removeChild(this.pane);\n+ end: function(xy) {\n+ var last, now = new Date().getTime();\n+ for (var i = 0, l = this.points.length, point; i < l; i++) {\n+ point = this.points[i];\n+ if (now - point.tick > this.delay) {\n+ break;\n+ }\n+ last = point;\n }\n- OpenLayers.Layer.prototype.removeMap.apply(this, arguments);\n+ if (!last) {\n+ return;\n+ }\n+ var time = new Date().getTime() - last.tick;\n+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n+ Math.pow(xy.y - last.xy.y, 2));\n+ var speed = dist / time;\n+ if (speed == 0 || speed < this.threshold) {\n+ return;\n+ }\n+ var theta = Math.asin((xy.y - last.xy.y) / dist);\n+ if (last.xy.x <= xy.x) {\n+ theta = Math.PI - theta;\n+ }\n+ return {\n+ speed: speed,\n+ theta: theta\n+ };\n },\n \n /**\n- * Method: loadWarningMessage\n- * If we can't load the map lib, then display an error message to the \n- * user and tell them where to go for help.\n- * \n- * This function sets up the layout for the warning message. Each 3rd\n- * party layer must implement its own getWarningHTML() function to \n- * provide the actual warning message.\n+ * Method: move\n+ * Launch the kinetic move pan.\n+ *\n+ * Parameters:\n+ * info - {Object} An object with two properties, \"speed\", and \"theta\".\n+ * These values are those returned from the \"end\" call.\n+ * callback - {Function} Function called on every step of the animation,\n+ * receives x, y (values to pan), end (is the last point).\n */\n- loadWarningMessage: function() {\n+ move: function(info, callback) {\n+ var v0 = info.speed;\n+ var fx = Math.cos(info.theta);\n+ var fy = -Math.sin(info.theta);\n \n- this.div.style.backgroundColor = \"darkblue\";\n+ var initialTime = new Date().getTime();\n \n- var viewSize = this.map.getSize();\n+ var lastX = 0;\n+ var lastY = 0;\n \n- var msgW = Math.min(viewSize.w, 300);\n- var msgH = Math.min(viewSize.h, 200);\n- var size = new OpenLayers.Size(msgW, msgH);\n+ var timerCallback = function() {\n+ if (this.timerId == null) {\n+ return;\n+ }\n \n- var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n+ var t = new Date().getTime() - initialTime;\n \n- var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n+ var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n+ var x = p * fx;\n+ var y = p * fy;\n \n- var div = OpenLayers.Util.createDiv(this.name + \"_warning\",\n- topLeft,\n- size,\n- null,\n- null,\n- null,\n- \"auto\");\n+ var args = {};\n+ args.end = false;\n+ var v = -this.deceleration * t + v0;\n \n- div.style.padding = \"7px\";\n- div.style.backgroundColor = \"yellow\";\n+ if (v <= 0) {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = null;\n+ args.end = true;\n+ }\n \n- div.innerHTML = this.getWarningHTML();\n- this.div.appendChild(div);\n+ args.x = x - lastX;\n+ args.y = y - lastY;\n+ lastX = x;\n+ lastY = y;\n+ callback(args.x, args.y, args.end);\n+ };\n+\n+ this.timerId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(timerCallback, this)\n+ );\n },\n \n- /** \n- * Method: getWarningHTML\n- * To be implemented by subclasses.\n- * \n- * Returns:\n- * {String} String with information on why layer is broken, how to get\n- * it working.\n+ CLASS_NAME: \"OpenLayers.Kinetic\"\n+});\n+/* ======================================================================\n+ OpenLayers/Tween.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Tween\n+ */\n+OpenLayers.Tween = OpenLayers.Class({\n+\n+ /**\n+ * APIProperty: easing\n+ * {<OpenLayers.Easing>(Function)} Easing equation used for the animation\n+ * Defaultly set to OpenLayers.Easing.Expo.easeOut\n */\n- getWarningHTML: function() {\n- //should be implemented by subclasses\n- return \"\";\n- },\n+ easing: null,\n \n /**\n- * Method: display\n- * Set the display on the pane\n+ * APIProperty: begin\n+ * {Object} Values to start the animation with\n+ */\n+ begin: null,\n+\n+ /**\n+ * APIProperty: finish\n+ * {Object} Values to finish the animation with\n+ */\n+ finish: null,\n+\n+ /**\n+ * APIProperty: duration\n+ * {int} duration of the tween (number of steps)\n+ */\n+ duration: null,\n+\n+ /**\n+ * APIProperty: callbacks\n+ * {Object} An object with start, eachStep and done properties whose values\n+ * are functions to be call during the animation. They are passed the\n+ * current computed value as argument.\n+ */\n+ callbacks: null,\n+\n+ /**\n+ * Property: time\n+ * {int} Step counter\n+ */\n+ time: null,\n+\n+ /**\n+ * APIProperty: minFrameRate\n+ * {Number} The minimum framerate for animations in frames per second. After\n+ * each step, the time spent in the animation is compared to the calculated\n+ * time at this frame rate. If the animation runs longer than the calculated\n+ * time, the next step is skipped. Default is 30.\n+ */\n+ minFrameRate: null,\n+\n+ /**\n+ * Property: startTime\n+ * {Number} The timestamp of the first execution step. Used for skipping\n+ * frames\n+ */\n+ startTime: null,\n+\n+ /**\n+ * Property: animationId\n+ * {int} Loop id returned by OpenLayers.Animation.start\n+ */\n+ animationId: null,\n+\n+ /**\n+ * Property: playing\n+ * {Boolean} Tells if the easing is currently playing\n+ */\n+ playing: false,\n+\n+ /** \n+ * Constructor: OpenLayers.Tween\n+ * Creates a Tween.\n *\n * Parameters:\n- * display - {Boolean}\n+ * easing - {<OpenLayers.Easing>(Function)} easing function method to use\n */\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- this.pane.style.display = this.div.style.display;\n+ initialize: function(easing) {\n+ this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;\n },\n \n /**\n- * Method: setZIndex\n- * Set the z-index order for the pane.\n+ * APIMethod: start\n+ * Plays the Tween, and calls the callback method on each step\n * \n * Parameters:\n- * zIndex - {int}\n+ * begin - {Object} values to start the animation with\n+ * finish - {Object} values to finish the animation with\n+ * duration - {int} duration of the tween (number of steps)\n+ * options - {Object} hash of options (callbacks (start, eachStep, done),\n+ * minFrameRate)\n */\n- setZIndex: function(zIndex) {\n- OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n+ start: function(begin, finish, duration, options) {\n+ this.playing = true;\n+ this.begin = begin;\n+ this.finish = finish;\n+ this.duration = duration;\n+ this.callbacks = options.callbacks;\n+ this.minFrameRate = options.minFrameRate || 30;\n+ this.time = 0;\n+ this.startTime = new Date().getTime();\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ if (this.callbacks && this.callbacks.start) {\n+ this.callbacks.start.call(this, this.begin);\n+ }\n+ this.animationId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(this.play, this)\n+ );\n },\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector. To be implemented by subclasses.\n- *\n- * Parameters:\n- * dx - {Number} The x coord of the displacement vector.\n- * dy - {Number} The y coord of the displacement vector.\n+ * APIMethod: stop\n+ * Stops the Tween, and calls the done callback\n+ * Doesn't do anything if animation is already finished\n */\n- moveByPx: function(dx, dy) {\n- OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n+ stop: function() {\n+ if (!this.playing) {\n+ return;\n+ }\n \n- if (this.dragPanMapObject) {\n- this.dragPanMapObject(dx, -dy);\n- } else {\n- this.moveTo(this.map.getCachedCenter());\n+ if (this.callbacks && this.callbacks.done) {\n+ this.callbacks.done.call(this, this.finish);\n }\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ this.playing = false;\n },\n \n /**\n- * Method: moveTo\n- * Handle calls to move the layer.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n+ * Method: play\n+ * Calls the appropriate easing method\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- if (this.mapObject != null) {\n-\n- var newCenter = this.map.getCenter();\n- var newZoom = this.map.getZoom();\n-\n- if (newCenter != null) {\n-\n- var moOldCenter = this.getMapObjectCenter();\n- var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n-\n- var moOldZoom = this.getMapObjectZoom();\n- var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n+ play: function() {\n+ var value = {};\n+ for (var i in this.begin) {\n+ var b = this.begin[i];\n+ var f = this.finish[i];\n+ if (b == null || f == null || isNaN(b) || isNaN(f)) {\n+ throw new TypeError('invalid value for Tween');\n+ }\n \n- if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {\n+ var c = f - b;\n+ value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);\n+ }\n+ this.time++;\n \n- if (!zoomChanged && oldCenter && this.dragPanMapObject &&\n- this.smoothDragPan) {\n- var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n- var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n- this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y);\n- } else {\n- var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n- var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n- this.setMapObjectCenter(center, zoom, dragging);\n- }\n- }\n+ if (this.callbacks && this.callbacks.eachStep) {\n+ // skip frames if frame rate drops below threshold\n+ if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {\n+ this.callbacks.eachStep.call(this, value);\n }\n }\n+\n+ if (this.time > this.duration) {\n+ this.stop();\n+ }\n },\n \n+ /**\n+ * Create empty functions for all easing methods.\n+ */\n+ CLASS_NAME: \"OpenLayers.Tween\"\n+});\n \n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /********************************************************/\n+/**\n+ * Namespace: OpenLayers.Easing\n+ * \n+ * Credits:\n+ * Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>\n+ */\n+OpenLayers.Easing = {\n+ /**\n+ * Create empty functions for all easing methods.\n+ */\n+ CLASS_NAME: \"OpenLayers.Easing\"\n+};\n+\n+/**\n+ * Namespace: OpenLayers.Easing.Linear\n+ */\n+OpenLayers.Easing.Linear = {\n \n /**\n- * Method: getLonLatFromViewPortPx\n- * Get a map location from a pixel location\n+ * Function: easeIn\n * \n * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>}\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n *\n * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n- * port OpenLayers.Pixel, translated into lon/lat by map lib\n- * If the map lib is not loaded or not centered, returns null\n+ * {Float}\n */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if ((this.mapObject != null) &&\n- (this.getMapObjectCenter() != null)) {\n- var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n- var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n- lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);\n- }\n- return lonlat;\n+ easeIn: function(t, b, c, d) {\n+ return c * t / d + b;\n },\n \n-\n /**\n- * Method: getViewPortPxFromLonLat\n- * Get a pixel location from a map location\n- *\n+ * Function: easeOut\n+ * \n * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n *\n * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n- * OpenLayers.LonLat, translated into view port pixels by map lib\n- * If map lib is not loaded or not centered, returns null\n+ * {Float}\n */\n- getViewPortPxFromLonLat: function(lonlat) {\n- var viewPortPx = null;\n- if ((this.mapObject != null) &&\n- (this.getMapObjectCenter() != null)) {\n-\n- var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n- var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n+ easeOut: function(t, b, c, d) {\n+ return c * t / d + b;\n+ },\n \n- viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);\n- }\n- return viewPortPx;\n+ /**\n+ * Function: easeInOut\n+ * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n+ * Returns:\n+ * {Float}\n+ */\n+ easeInOut: function(t, b, c, d) {\n+ return c * t / d + b;\n },\n \n- /********************************************************/\n- /* */\n- /* Translation Functions */\n- /* */\n- /* The following functions translate Map Object and */\n- /* OL formats for Pixel, LonLat */\n- /* */\n- /********************************************************/\n+ CLASS_NAME: \"OpenLayers.Easing.Linear\"\n+};\n \n- //\n- // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat\n- //\n+/**\n+ * Namespace: OpenLayers.Easing.Expo\n+ */\n+OpenLayers.Easing.Expo = {\n \n /**\n- * Method: getOLLonLatFromMapObjectLonLat\n- * Get an OL style map location from a 3rd party style map location\n- *\n- * Parameters\n- * moLonLat - {Object}\n+ * Function: easeIn\n * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in \n- * MapObject LonLat\n- * Returns null if null value is passed in\n+ * {Float}\n */\n- getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n- var olLonLat = null;\n- if (moLonLat != null) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- olLonLat = new OpenLayers.LonLat(lon, lat);\n- }\n- return olLonLat;\n+ easeIn: function(t, b, c, d) {\n+ return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;\n },\n \n /**\n- * Method: getMapObjectLonLatFromOLLonLat\n- * Get a 3rd party map location from an OL map location.\n- *\n+ * Function: easeOut\n+ * \n * Parameters:\n- * olLonLat - {<OpenLayers.LonLat>}\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n+ * Returns:\n+ * {Float}\n+ */\n+ easeOut: function(t, b, c, d) {\n+ return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;\n+ },\n+\n+ /**\n+ * Function: easeInOut\n * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n * Returns:\n- * {Object} A MapObject LonLat, translated from the passed in \n- * OpenLayers.LonLat\n- * Returns null if null value is passed in\n+ * {Float}\n */\n- getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n- var moLatLng = null;\n- if (olLonLat != null) {\n- moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,\n- olLonLat.lat);\n- }\n- return moLatLng;\n+ easeInOut: function(t, b, c, d) {\n+ if (t == 0) return b;\n+ if (t == d) return b + c;\n+ if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;\n+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;\n },\n \n+ CLASS_NAME: \"OpenLayers.Easing.Expo\"\n+};\n \n- //\n- // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel\n- //\n+/**\n+ * Namespace: OpenLayers.Easing.Quad\n+ */\n+OpenLayers.Easing.Quad = {\n \n /**\n- * Method: getOLPixelFromMapObjectPixel\n- * Get an OL pixel location from a 3rd party pixel location.\n- *\n- * Parameters:\n- * moPixel - {Object}\n+ * Function: easeIn\n * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in \n- * MapObject Pixel\n- * Returns null if null value is passed in\n+ * {Float}\n */\n- getOLPixelFromMapObjectPixel: function(moPixel) {\n- var olPixel = null;\n- if (moPixel != null) {\n- var x = this.getXFromMapObjectPixel(moPixel);\n- var y = this.getYFromMapObjectPixel(moPixel);\n- olPixel = new OpenLayers.Pixel(x, y);\n- }\n- return olPixel;\n+ easeIn: function(t, b, c, d) {\n+ return c * (t /= d) * t + b;\n },\n \n /**\n- * Method: getMapObjectPixelFromOLPixel\n- * Get a 3rd party pixel location from an OL pixel location\n- *\n+ * Function: easeOut\n+ * \n * Parameters:\n- * olPixel - {<OpenLayers.Pixel>}\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n+ * Returns:\n+ * {Float}\n+ */\n+ easeOut: function(t, b, c, d) {\n+ return -c * (t /= d) * (t - 2) + b;\n+ },\n+\n+ /**\n+ * Function: easeInOut\n * \n+ * Parameters:\n+ * t - {Float} time\n+ * b - {Float} beginning position\n+ * c - {Float} total change\n+ * d - {Float} duration of the transition\n+ *\n * Returns:\n- * {Object} A MapObject Pixel, translated from the passed in \n- * OpenLayers.Pixel\n- * Returns null if null value is passed in\n+ * {Float}\n */\n- getMapObjectPixelFromOLPixel: function(olPixel) {\n- var moPixel = null;\n- if (olPixel != null) {\n- moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);\n- }\n- return moPixel;\n+ easeInOut: function(t, b, c, d) {\n+ if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n+ return -c / 2 * ((--t) * (t - 2) - 1) + b;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n-});\n+ CLASS_NAME: \"OpenLayers.Easing.Quad\"\n+};\n /* ======================================================================\n- OpenLayers/Layer/WorldWind.js\n+ OpenLayers/Projection.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Layer.WorldWind\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Namespace: OpenLayers.Projection\n+ * Methods for coordinate transforms between coordinate systems. By default,\n+ * OpenLayers ships with the ability to transform coordinates between\n+ * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)\n+ * coordinate reference systems. See the <transform> method for details\n+ * on usage.\n+ *\n+ * Additional transforms may be added by using the <proj4js at http://proj4js.org/>\n+ * library. If the proj4js library is included, the <transform> method \n+ * will work between any two coordinate reference systems with proj4js \n+ * definitions.\n+ *\n+ * If the proj4js library is not included, or if you wish to allow transforms\n+ * between arbitrary coordinate reference systems, use the <addTransform>\n+ * method to register a custom transform method.\n */\n-OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- DEFAULT_PARAMS: {},\n+OpenLayers.Projection = OpenLayers.Class({\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} WorldWind layer is a base layer by default.\n+ * Property: proj\n+ * {Object} Proj4js.Proj instance.\n */\n- isBaseLayer: true,\n+ proj: null,\n \n- /** \n- * APIProperty: lzd\n- * {Float} LevelZeroTileSizeDegrees\n+ /**\n+ * Property: projCode\n+ * {String}\n */\n- lzd: null,\n+ projCode: null,\n \n /**\n- * APIProperty: zoomLevels\n- * {Integer} Number of zoom levels.\n+ * Property: titleRegEx\n+ * {RegExp} regular expression to strip the title from a proj4js definition\n */\n- zoomLevels: null,\n+ titleRegEx: /\\+title=[^\\+]*/,\n \n /**\n- * Constructor: OpenLayers.Layer.WorldWind\n- * \n+ * Constructor: OpenLayers.Projection\n+ * This class offers several methods for interacting with a wrapped \n+ * pro4js projection object. \n+ *\n * Parameters:\n- * name - {String} Name of Layer\n- * url - {String} Base URL \n- * lzd - {Float} Level zero tile size degrees \n- * zoomLevels - {Integer} number of zoom levels\n- * params - {Object} additional parameters\n- * options - {Object} additional options\n+ * projCode - {String} A string identifying the Well Known Identifier for\n+ * the projection.\n+ * options - {Object} An optional object to set additional properties\n+ * on the projection.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Projection>} A projection object.\n */\n- initialize: function(name, url, lzd, zoomLevels, params, options) {\n- this.lzd = lzd;\n- this.zoomLevels = zoomLevels;\n- var newArguments = [];\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- this.params = OpenLayers.Util.applyDefaults(\n- this.params, this.DEFAULT_PARAMS\n- );\n+ initialize: function(projCode, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.projCode = projCode;\n+ if (typeof Proj4js == \"object\") {\n+ this.proj = new Proj4js.Proj(projCode);\n+ }\n },\n \n /**\n- * Method: getZoom\n- * Convert map zoom to WW zoom.\n+ * APIMethod: getCode\n+ * Get the string SRS code.\n+ *\n+ * Returns:\n+ * {String} The SRS code.\n */\n- getZoom: function() {\n- var zoom = this.map.getZoom();\n- var extent = this.map.getMaxExtent();\n- zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);\n- return zoom;\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode;\n },\n \n /**\n- * Method: getURL\n+ * APIMethod: getUnits\n+ * Get the units string for the projection -- returns null if \n+ * proj4js is not available.\n *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n+ * Returns:\n+ * {String} The units abbreviation.\n+ */\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null;\n+ },\n+\n+ /**\n+ * Method: toString\n+ * Convert projection to string (getCode wrapper).\n *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n+ * {String} The projection code.\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var zoom = this.getZoom();\n- var extent = this.map.getMaxExtent();\n- var deg = this.lzd / Math.pow(2, this.getZoom());\n- var x = Math.floor((bounds.left - extent.left) / deg);\n- var y = Math.floor((bounds.bottom - extent.bottom) / deg);\n- if (this.map.getResolution() <= (this.lzd / 512) &&\n- this.getZoom() <= this.zoomLevels) {\n- return this.getFullRequestString({\n- L: zoom,\n- X: x,\n- Y: y\n- });\n- } else {\n- return OpenLayers.Util.getImageLocation(\"blank.gif\");\n+ toString: function() {\n+ return this.getCode();\n+ },\n+\n+ /**\n+ * Method: equals\n+ * Test equality of two projection instances. Determines equality based\n+ * soley on the projection code.\n+ *\n+ * Returns:\n+ * {Boolean} The two projections are equivalent.\n+ */\n+ equals: function(projection) {\n+ var p = projection,\n+ equals = false;\n+ if (p) {\n+ if (!(p instanceof OpenLayers.Projection)) {\n+ p = new OpenLayers.Projection(p);\n+ }\n+ if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n+ equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n+ p.proj.defData.replace(this.titleRegEx, \"\");\n+ } else if (p.getCode) {\n+ var source = this.getCode(),\n+ target = p.getCode();\n+ equals = source == target ||\n+ !!OpenLayers.Projection.transforms[source] &&\n+ OpenLayers.Projection.transforms[source][target] ===\n+ OpenLayers.Projection.nullTransform;\n+ }\n }\n+ return equals;\n+ },\n \n+ /* Method: destroy\n+ * Destroy projection object.\n+ */\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.WorldWind\"\n+ CLASS_NAME: \"OpenLayers.Projection\"\n });\n+\n+/**\n+ * Property: transforms\n+ * {Object} Transforms is an object, with from properties, each of which may\n+ * have a to property. This allows you to define projections without \n+ * requiring support for proj4js to be included.\n+ *\n+ * This object has keys which correspond to a 'source' projection object. The\n+ * keys should be strings, corresponding to the projection.getCode() value.\n+ * Each source projection object should have a set of destination projection\n+ * keys included in the object. \n+ * \n+ * Each value in the destination object should be a transformation function,\n+ * where the function is expected to be passed an object with a .x and a .y\n+ * property. The function should return the object, with the .x and .y\n+ * transformed according to the transformation function.\n+ *\n+ * Note - Properties on this object should not be set directly. To add a\n+ * transform method to this object, use the <addTransform> method. For an\n+ * example of usage, see the OpenLayers.Layer.SphericalMercator file.\n+ */\n+OpenLayers.Projection.transforms = {};\n+\n+/**\n+ * APIProperty: defaults\n+ * {Object} Defaults for the SRS codes known to OpenLayers (currently\n+ * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,\n+ * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,\n+ * maxExtent (the validity extent for the SRS) and yx (true if this SRS is\n+ * known to have a reverse axis order).\n+ */\n+OpenLayers.Projection.defaults = {\n+ \"EPSG:4326\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90],\n+ yx: true\n+ },\n+ \"CRS:84\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90]\n+ },\n+ \"EPSG:900913\": {\n+ units: \"m\",\n+ maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n+ }\n+};\n+\n+/**\n+ * APIMethod: addTransform\n+ * Set a custom transform method between two projections. Use this method in\n+ * cases where the proj4js lib is not available or where custom projections\n+ * need to be handled.\n+ *\n+ * Parameters:\n+ * from - {String} The code for the source projection\n+ * to - {String} the code for the destination projection\n+ * method - {Function} A function that takes a point as an argument and\n+ * transforms that point from the source to the destination projection\n+ * in place. The original point should be modified.\n+ */\n+OpenLayers.Projection.addTransform = function(from, to, method) {\n+ if (method === OpenLayers.Projection.nullTransform) {\n+ var defaults = OpenLayers.Projection.defaults[from];\n+ if (defaults && !OpenLayers.Projection.defaults[to]) {\n+ OpenLayers.Projection.defaults[to] = defaults;\n+ }\n+ }\n+ if (!OpenLayers.Projection.transforms[from]) {\n+ OpenLayers.Projection.transforms[from] = {};\n+ }\n+ OpenLayers.Projection.transforms[from][to] = method;\n+};\n+\n+/**\n+ * APIMethod: transform\n+ * Transform a point coordinate from one projection to another. Note that\n+ * the input point is transformed in place.\n+ * \n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y\n+ * properties representing coordinates in those dimensions.\n+ * source - {OpenLayers.Projection} Source map coordinate system\n+ * dest - {OpenLayers.Projection} Destination map coordinate system\n+ *\n+ * Returns:\n+ * point - {object} A transformed coordinate. The original point is modified.\n+ */\n+OpenLayers.Projection.transform = function(point, source, dest) {\n+ if (source && dest) {\n+ if (!(source instanceof OpenLayers.Projection)) {\n+ source = new OpenLayers.Projection(source);\n+ }\n+ if (!(dest instanceof OpenLayers.Projection)) {\n+ dest = new OpenLayers.Projection(dest);\n+ }\n+ if (source.proj && dest.proj) {\n+ point = Proj4js.transform(source.proj, dest.proj, point);\n+ } else {\n+ var sourceCode = source.getCode();\n+ var destCode = dest.getCode();\n+ var transforms = OpenLayers.Projection.transforms;\n+ if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n+ transforms[sourceCode][destCode](point);\n+ }\n+ }\n+ }\n+ return point;\n+};\n+\n+/**\n+ * APIFunction: nullTransform\n+ * A null transformation - useful for defining projection aliases when\n+ * proj4js is not available:\n+ *\n+ * (code)\n+ * OpenLayers.Projection.addTransform(\"EPSG:3857\", \"EPSG:900913\",\n+ * OpenLayers.Projection.nullTransform);\n+ * OpenLayers.Projection.addTransform(\"EPSG:900913\", \"EPSG:3857\",\n+ * OpenLayers.Projection.nullTransform);\n+ * (end)\n+ */\n+OpenLayers.Projection.nullTransform = function(point) {\n+ return point;\n+};\n+\n+/**\n+ * Note: Transforms for web mercator <-> geographic\n+ * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.\n+ * OpenLayers originally started referring to EPSG:900913 as web mercator.\n+ * The EPSG has declared EPSG:3857 to be web mercator.\n+ * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as\n+ * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.\n+ * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and\n+ * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis\n+ * order for EPSG:4326. \n+ */\n+(function() {\n+\n+ var pole = 20037508.34;\n+\n+ function inverseMercator(xy) {\n+ xy.x = 180 * xy.x / pole;\n+ xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);\n+ return xy;\n+ }\n+\n+ function forwardMercator(xy) {\n+ xy.x = xy.x * pole / 180;\n+ var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n+ xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n+ return xy;\n+ }\n+\n+ function map(base, codes) {\n+ var add = OpenLayers.Projection.addTransform;\n+ var same = OpenLayers.Projection.nullTransform;\n+ var i, len, code, other, j;\n+ for (i = 0, len = codes.length; i < len; ++i) {\n+ code = codes[i];\n+ add(base, code, forwardMercator);\n+ add(code, base, inverseMercator);\n+ for (j = i + 1; j < len; ++j) {\n+ other = codes[j];\n+ add(code, other, same);\n+ add(other, code, same);\n+ }\n+ }\n+ }\n+\n+ // list of equivalent codes for web mercator\n+ var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n+ geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n+ i;\n+ for (i = mercator.length - 1; i >= 0; --i) {\n+ map(mercator[i], geographic);\n+ }\n+ for (i = geographic.length - 1; i >= 0; --i) {\n+ map(geographic[i], mercator);\n+ }\n+\n+})();\n /* ======================================================================\n- OpenLayers/Layer/TMS.js\n+ OpenLayers/Map.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Util/vendorPrefix.js\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Tween.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Layer.TMS\n- * Create a layer for accessing tiles from services that conform with the \n- * Tile Map Service Specification \n- * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification).\n- *\n- * Example:\n- * (code)\n- * var layer = new OpenLayers.Layer.TMS(\n- * \"My Layer\", // name for display in LayerSwitcher\n- * \"http://tilecache.osgeo.org/wms-c/Basic.py/\", // service endpoint\n- * {layername: \"basic\", type: \"png\"} // required properties\n- * );\n- * (end)\n+ * Class: OpenLayers.Map\n+ * Instances of OpenLayers.Map are interactive maps embedded in a web page.\n+ * Create a new map with the <OpenLayers.Map> constructor.\n * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * On their own maps do not provide much functionality. To extend a map\n+ * it's necessary to add controls (<OpenLayers.Control>) and \n+ * layers (<OpenLayers.Layer>) to the map. \n */\n-OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Map = OpenLayers.Class({\n \n /**\n- * APIProperty: serviceVersion\n- * {String} Service version for tile requests. Default is \"1.0.0\".\n+ * Constant: Z_INDEX_BASE\n+ * {Object} Base z-indexes for different classes of thing \n */\n- serviceVersion: \"1.0.0\",\n+ Z_INDEX_BASE: {\n+ BaseLayer: 100,\n+ Overlay: 325,\n+ Feature: 725,\n+ Popup: 750,\n+ Control: 1000\n+ },\n \n /**\n- * APIProperty: layername\n- * {String} The identifier for the <TileMap> as advertised by the service. \n- * For example, if the service advertises a <TileMap> with \n- * 'href=\"http://tms.osgeo.org/1.0.0/vmap0\"', the <layername> property \n- * would be set to \"vmap0\".\n+ * APIProperty: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * map.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to map.events.object.\n+ * element - {DOMElement} A reference to map.events.element.\n+ *\n+ * Browser events have the following additional properties:\n+ * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative\n+ * to the the map viewport).\n+ *\n+ * Supported map event types:\n+ * preaddlayer - triggered before a layer has been added. The event\n+ * object will include a *layer* property that references the layer \n+ * to be added. When a listener returns \"false\" the adding will be \n+ * aborted.\n+ * addlayer - triggered after a layer has been added. The event object\n+ * will include a *layer* property that references the added layer.\n+ * preremovelayer - triggered before a layer has been removed. The event\n+ * object will include a *layer* property that references the layer \n+ * to be removed. When a listener returns \"false\" the removal will be \n+ * aborted.\n+ * removelayer - triggered after a layer has been removed. The event\n+ * object will include a *layer* property that references the removed\n+ * layer.\n+ * changelayer - triggered after a layer name change, order change,\n+ * opacity change, params change, visibility change (actual visibility,\n+ * not the layer's visibility property) or attribution change (due to\n+ * extent change). Listeners will receive an event object with *layer*\n+ * and *property* properties. The *layer* property will be a reference\n+ * to the changed layer. The *property* property will be a key to the\n+ * changed property (name, order, opacity, params, visibility or\n+ * attribution).\n+ * movestart - triggered after the start of a drag, pan, or zoom. The event\n+ * object may include a *zoomChanged* property that tells whether the\n+ * zoom has changed.\n+ * move - triggered after each drag, pan, or zoom\n+ * moveend - triggered after a drag, pan, or zoom completes\n+ * zoomend - triggered after a zoom completes\n+ * mouseover - triggered after mouseover the map\n+ * mouseout - triggered after mouseout the map\n+ * mousemove - triggered after mousemove the map\n+ * changebaselayer - triggered after the base layer changes\n+ * updatesize - triggered after the <updateSize> method was executed\n */\n- layername: null,\n \n /**\n- * APIProperty: type\n- * {String} The format extension corresponding to the requested tile image\n- * type. This is advertised in a <TileFormat> element as the \n- * \"extension\" attribute. For example, if the service advertises a \n- * <TileMap> with <TileFormat width=\"256\" height=\"256\" mime-type=\"image/jpeg\" extension=\"jpg\" />,\n- * the <type> property would be set to \"jpg\".\n+ * Property: id\n+ * {String} Unique identifier for the map\n */\n- type: null,\n+ id: null,\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Make this layer a base layer. Default is true. Set false to\n- * use the layer as an overlay.\n+ * Property: fractionalZoom\n+ * {Boolean} For a base layer that supports it, allow the map resolution\n+ * to be set to a value between one of the values in the resolutions\n+ * array. Default is false.\n+ *\n+ * When fractionalZoom is set to true, it is possible to zoom to\n+ * an arbitrary extent. This requires a base layer from a source\n+ * that supports requests for arbitrary extents (i.e. not cached\n+ * tiles on a regular lattice). This means that fractionalZoom\n+ * will not work with commercial layers (Google, Yahoo, VE), layers\n+ * using TileCache, or any other pre-cached data sources.\n+ *\n+ * If you are using fractionalZoom, then you should also use\n+ * <getResolutionForZoom> instead of layer.resolutions[zoom] as the\n+ * former works for non-integer zoom levels.\n */\n- isBaseLayer: true,\n+ fractionalZoom: false,\n \n /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n- * If provided, requests for tiles at all resolutions will be aligned\n- * with this location (no tiles shall overlap this location). If\n- * not provided, the grid of tiles will be aligned with the bottom-left\n- * corner of the map's <maxExtent>. Default is ``null``.\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the map\n+ */\n+ events: null,\n+\n+ /**\n+ * APIProperty: allOverlays\n+ * {Boolean} Allow the map to function with \"overlays\" only. Defaults to\n+ * false. If true, the lowest layer in the draw order will act as\n+ * the base layer. In addition, if set to true, all layers will\n+ * have isBaseLayer set to false when they are added to the map.\n *\n- * Example:\n- * (code)\n- * var layer = new OpenLayers.Layer.TMS(\n- * \"My Layer\",\n- * \"http://tilecache.osgeo.org/wms-c/Basic.py/\",\n- * {\n- * layername: \"basic\", \n- * type: \"png\",\n- * // set if different than the bottom left of map.maxExtent\n- * tileOrigin: new OpenLayers.LonLat(-180, -90)\n- * }\n- * );\n- * (end)\n+ * Note:\n+ * If you set map.allOverlays to true, then you *cannot* use\n+ * map.setBaseLayer or layer.setIsBaseLayer. With allOverlays true,\n+ * the lowest layer in the draw layer is the base layer. So, to change\n+ * the base layer, use <setLayerIndex> or <raiseLayer> to set the layer\n+ * index to 0.\n */\n- tileOrigin: null,\n+ allOverlays: false,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * APIProperty: div\n+ * {DOMElement|String} The element that contains the map (or an id for\n+ * that element). If the <OpenLayers.Map> constructor is called\n+ * with two arguments, this should be provided as the first argument.\n+ * Alternatively, the map constructor can be called with the options\n+ * object as the only argument. In this case (one argument), a\n+ * div property may or may not be provided. If the div property\n+ * is not provided, the map can be rendered to a container later\n+ * using the <render> method.\n+ * \n+ * Note:\n+ * If you are calling <render> after map construction, do not use\n+ * <maxResolution> auto. Instead, divide your <maxExtent> by your\n+ * maximum expected dimension.\n */\n- serverResolutions: null,\n+ div: null,\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * Property: dragging\n+ * {Boolean} The map is currently being dragged.\n */\n- zoomOffset: 0,\n+ dragging: false,\n \n /**\n- * Constructor: OpenLayers.Layer.TMS\n- * \n- * Parameters:\n- * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher>\n- * url - {String} Service endpoint (without the version number). E.g.\n- * \"http://tms.osgeo.org/\".\n- * options - {Object} Additional properties to be set on the layer. The\n- * <layername> and <type> properties must be set here.\n+ * Property: size\n+ * {<OpenLayers.Size>} Size of the main div (this.div)\n */\n- initialize: function(name, url, options) {\n- var newArguments = [];\n- newArguments.push(name, url, {}, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- },\n+ size: null,\n \n /**\n- * APIMethod: clone\n- * Create a complete copy of this layer.\n+ * Property: viewPortDiv\n+ * {HTMLDivElement} The element that represents the map viewport\n+ */\n+ viewPortDiv: null,\n+\n+ /**\n+ * Property: layerContainerOrigin\n+ * {<OpenLayers.LonLat>} The lonlat at which the later container was\n+ * re-initialized (on-zoom)\n+ */\n+ layerContainerOrigin: null,\n+\n+ /**\n+ * Property: layerContainerDiv\n+ * {HTMLDivElement} The element that contains the layers.\n+ */\n+ layerContainerDiv: null,\n+\n+ /**\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: controls\n+ * {Array(<OpenLayers.Control>)} List of controls associated with the map.\n *\n- * Parameters:\n- * obj - {Object} Should only be provided by subclasses that call this\n- * method.\n- * \n- * Returns:\n- * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>\n+ * If not provided in the map options at construction, the map will\n+ * by default be given the following controls if present in the build:\n+ * - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>\n+ * - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>\n+ * - <OpenLayers.Control.ArgParser>\n+ * - <OpenLayers.Control.Attribution>\n+ */\n+ controls: null,\n+\n+ /**\n+ * Property: popups\n+ * {Array(<OpenLayers.Popup>)} List of popups associated with the map\n+ */\n+ popups: null,\n+\n+ /**\n+ * APIProperty: baseLayer\n+ * {<OpenLayers.Layer>} The currently selected base layer. This determines\n+ * min/max zoom level, projection, etc.\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.TMS(this.name,\n- this.url,\n- this.getOptions());\n- }\n+ baseLayer: null,\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ /**\n+ * Property: center\n+ * {<OpenLayers.LonLat>} The current center of the map\n+ */\n+ center: null,\n \n- // copy/set any non-init, non-simple values here\n+ /**\n+ * Property: resolution\n+ * {Float} The resolution of the map.\n+ */\n+ resolution: null,\n \n- return obj;\n- },\n+ /**\n+ * Property: zoom\n+ * {Integer} The current zoom level of the map\n+ */\n+ zoom: 0,\n \n /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n+ * Property: panRatio\n+ * {Float} The ratio of the current extent within\n+ * which panning will tween.\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- var path = this.serviceVersion + \"/\" + this.layername + \"/\" + z + \"/\" + x + \"/\" + y + \".\" + this.type;\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n- }\n- return url + path;\n- },\n+ panRatio: 1.5,\n \n- /** \n- * Method: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ /**\n+ * APIProperty: options\n+ * {Object} The options object passed to the class constructor. Read-only.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n- this.map.maxExtent.bottom);\n- }\n- },\n+ options: null,\n \n- CLASS_NAME: \"OpenLayers.Layer.TMS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/XYZ.js\n- ====================================================================== */\n+ // Options\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * APIProperty: tileSize\n+ * {<OpenLayers.Size>} Set in the map options to override the default tile\n+ * size for this map.\n+ */\n+ tileSize: null,\n \n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n+ /**\n+ * APIProperty: projection\n+ * {String} Set in the map options to specify the default projection \n+ * for layers added to this map. When using a projection other than EPSG:4326\n+ * (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),\n+ * also set maxExtent, maxResolution or resolutions. Default is \"EPSG:4326\".\n+ * Note that the projection of the map is usually determined\n+ * by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).\n+ */\n+ projection: \"EPSG:4326\",\n \n-/** \n- * Class: OpenLayers.Layer.XYZ\n- * The XYZ class is designed to make it easier for people who have tiles\n- * arranged by a standard XYZ grid. \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ /**\n+ * APIProperty: units\n+ * {String} The map units. Possible values are 'degrees' (or 'dd'), 'm', \n+ * 'ft', 'km', 'mi', 'inches'. Normally taken from the projection.\n+ * Only required if both map and layers do not define a projection,\n+ * or if they define a projection which does not define units\n+ */\n+ units: null,\n \n /**\n- * APIProperty: isBaseLayer\n- * Default is true, as this is designed to be a base tile source. \n+ * APIProperty: resolutions\n+ * {Array(Float)} A list of map resolutions (map units per pixel) in \n+ * descending order. If this is not set in the layer constructor, it \n+ * will be set based on other resolution related properties \n+ * (maxExtent, maxResolution, maxScale, etc.).\n */\n- isBaseLayer: true,\n+ resolutions: null,\n \n /**\n- * APIProperty: sphericalMercator\n- * Whether the tile extents should be set to the defaults for \n- * spherical mercator. Useful for things like OpenStreetMap.\n- * Default is false, except for the OSM subclass.\n+ * APIProperty: maxResolution\n+ * {Float} Required if you are not displaying the whole world on a tile\n+ * with the size specified in <tileSize>.\n */\n- sphericalMercator: false,\n+ maxResolution: null,\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * APIProperty: minResolution\n+ * {Float}\n */\n- zoomOffset: 0,\n+ minResolution: null,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * APIProperty: maxScale\n+ * {Float}\n */\n- serverResolutions: null,\n+ maxScale: null,\n \n /**\n- * Constructor: OpenLayers.Layer.XYZ\n- *\n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * APIProperty: minScale\n+ * {Float}\n */\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options);\n- }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name || this.name, url || this.url, {},\n- options\n- ]);\n- },\n+ minScale: null,\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n- * Returns:\n- * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n+ * APIProperty: maxExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The maximum extent for the map.\n+ * Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults\n+ * (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;\n+ * else, defaults to null.\n+ * To restrict user panning and zooming of the map, use <restrictedExtent> instead.\n+ * The value for <maxExtent> will change calculations for tile URLs.\n */\n- clone: function(obj) {\n+ maxExtent: null,\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name,\n- this.url,\n- this.getOptions());\n- }\n+ /**\n+ * APIProperty: minExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The minimum extent for the map. Defaults to null.\n+ */\n+ minExtent: null,\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ /**\n+ * APIProperty: restrictedExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * Limit map navigation to this extent where possible.\n+ * If a non-null restrictedExtent is set, panning will be restricted\n+ * to the given bounds. In addition, zooming to a resolution that\n+ * displays more than the restricted extent will center the map\n+ * on the restricted extent. If you wish to limit the zoom level\n+ * or resolution, use maxResolution.\n+ */\n+ restrictedExtent: null,\n \n- return obj;\n- },\n+ /**\n+ * APIProperty: numZoomLevels\n+ * {Integer} Number of zoom levels for the map. Defaults to 16. Set a\n+ * different value in the map options if needed.\n+ */\n+ numZoomLevels: 16,\n \n /**\n- * Method: getURL\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n+ * APIProperty: theme\n+ * {String} Relative path to a CSS file from which to load theme styles.\n+ * Specify null in the map options (e.g. {theme: null}) if you \n+ * want to get cascading style declarations - by putting links to \n+ * stylesheets or style declarations directly in your page.\n */\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = '' + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url);\n- }\n+ theme: null,\n \n- return OpenLayers.String.format(url, xyz);\n- },\n+ /** \n+ * APIProperty: displayProjection\n+ * {<OpenLayers.Projection>} Requires proj4js support for projections other\n+ * than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by\n+ * several controls to display data to user. If this property is set,\n+ * it will be set on any control which has a null displayProjection\n+ * property at the time the control is added to the map. \n+ */\n+ displayProjection: null,\n \n /**\n- * Method: getXYZ\n- * Calculates x, y and z for the given bounds.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {Object} - an object with x, y and z properties.\n+ * APIProperty: tileManager\n+ * {<OpenLayers.TileManager>|Object} By default, and if the build contains\n+ * TileManager.js, the map will use the TileManager to queue image requests\n+ * and to cache tile image elements. To create a map without a TileManager\n+ * configure the map with tileManager: null. To create a TileManager with\n+ * non-default options, supply the options instead or alternatively supply\n+ * an instance of {<OpenLayers.TileManager>}.\n */\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) /\n- (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) /\n- (res * this.tileSize.h));\n- var z = this.getServerZoom();\n \n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = ((x % limit) + limit) % limit;\n- }\n-\n- return {\n- 'x': x,\n- 'y': y,\n- 'z': z\n- };\n- },\n+ /**\n+ * APIProperty: fallThrough\n+ * {Boolean} Should OpenLayers allow events on the map to fall through to\n+ * other elements on the page, or should it swallow them? (#457)\n+ * Default is to swallow.\n+ */\n+ fallThrough: false,\n \n- /* APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ /**\n+ * APIProperty: autoUpdateSize\n+ * {Boolean} Should OpenLayers automatically update the size of the map\n+ * when the resize event is fired. Default is true.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n- this.maxExtent.bottom);\n- }\n- },\n+ autoUpdateSize: true,\n \n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/ArcGISCache.js\n- ====================================================================== */\n+ /**\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n+ */\n+ eventListeners: null,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Property: panTween\n+ * {<OpenLayers.Tween>} Animated panning tween object, see panTo()\n+ */\n+ panTween: null,\n \n-/** \n- * @requires OpenLayers/Layer/XYZ.js\n- */\n+ /**\n+ * APIProperty: panMethod\n+ * {Function} The Easing function to be used for tweening. Default is\n+ * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off\n+ * animated panning.\n+ */\n+ panMethod: OpenLayers.Easing.Expo.easeOut,\n \n-/** \n- * Class: OpenLayers.Layer.ArcGISCache \n- * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. \n- * Tile must already be cached for this layer to access it. This does not require \n- * ArcGIS Server itself.\n- * \n- * A few attempts have been made at this kind of layer before. See \n- * http://trac.osgeo.org/openlayers/ticket/1967 \n- * and \n- * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js\n- *\n- * Typically the problem encountered is that the tiles seem to \"jump around\".\n- * This is due to the fact that the actual max extent for the tiles on AGS layers\n- * changes at each zoom level due to the way these caches are constructed.\n- * We have attempted to use the resolutions, tile size, and tile origin\n- * from the cache meta data to make the appropriate changes to the max extent\n- * of the tile to compensate for this behavior. This must be done as zoom levels change\n- * and before tiles are requested, which is why methods from base classes are overridden.\n- *\n- * For reference, you can access mapcache meta data in two ways. For accessing a \n- * mapcache through ArcGIS Server, you can simply go to the landing page for the\n- * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer)\n- * For accessing it directly through HTTP, there should always be a conf.xml file\n- * in the root directory. \n- * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml)\n- * \n- *Inherits from: \n- * - <OpenLayers.Layer.XYZ> \n- */\n-OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ /**\n+ * Property: panDuration\n+ * {Integer} The number of steps to be passed to the\n+ * OpenLayers.Tween.start() method when the map is\n+ * panned.\n+ * Default is 50.\n+ */\n+ panDuration: 50,\n \n /**\n- * APIProperty: url\n- * {String | Array} The base URL for the layer cache. You can also\n- * provide a list of URL strings for the layer if your cache is\n- * available from multiple origins. This must be set before the layer\n- * is drawn.\n+ * Property: zoomTween\n+ * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()\n */\n- url: null,\n+ zoomTween: null,\n \n /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} The location of the tile origin for the cache.\n- * An ArcGIS cache has it's origin at the upper-left (lowest x value\n- * and highest y value of the coordinate system). The units for the\n- * tile origin should be the same as the units for the cached data.\n+ * APIProperty: zoomMethod\n+ * {Function} The Easing function to be used for tweening. Default is\n+ * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off\n+ * animated zooming.\n */\n- tileOrigin: null,\n+ zoomMethod: OpenLayers.Easing.Quad.easeOut,\n \n /**\n- * APIProperty: tileSize\n- * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels.\n+ * Property: zoomDuration\n+ * {Integer} The number of steps to be passed to the\n+ * OpenLayers.Tween.start() method when the map is zoomed.\n+ * Default is 20.\n */\n- tileSize: new OpenLayers.Size(256, 256),\n+ zoomDuration: 20,\n \n /**\n- * APIProperty: useAGS\n- * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS)\n- * cache via an AGS MapServer or directly through HTTP. When accessing via\n- * AGS the path structure uses a standard z/y/x structure. But AGS actually\n- * stores the tile images on disk using a hex based folder structure that looks\n- * like \"http://example.com/mylayer/L00/R00000000/C00000000.png\". Learn more\n- * about this here:\n- * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx\n- * Defaults to true;\n+ * Property: paddingForPopups\n+ * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent \n+ * the popup from getting too close to the map border.\n */\n- useArcGISServer: true,\n+ paddingForPopups: null,\n \n /**\n- * APIProperty: type\n- * {String} Image type for the layer. This becomes the filename extension\n- * in tile requests. Default is \"png\" (generating a url like\n- * \"http://example.com/mylayer/L00/R00000000/C00000000.png\").\n+ * Property: layerContainerOriginPx\n+ * {Object} Cached object representing the layer container origin (in pixels).\n */\n- type: 'png',\n+ layerContainerOriginPx: null,\n \n /**\n- * APIProperty: useScales\n- * {Boolean} Optional override to indicate that the layer should use 'scale' information\n- * returned from the server capabilities object instead of 'resolution' information.\n- * This can be important if your tile server uses an unusual DPI for the tiles.\n+ * Property: minPx\n+ * {Object} An object with a 'x' and 'y' values that is the lower\n+ * left of maxExtent in viewport pixel space.\n+ * Used to verify in moveByPx that the new location we're moving to\n+ * is valid. It is also used in the getLonLatFromViewPortPx function\n+ * of Layer.\n */\n- useScales: false,\n+ minPx: null,\n \n /**\n- * APIProperty: overrideDPI\n- * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based \n- * on the tile information in the server capabilities object. This can be useful \n- * if your server has a non-standard DPI setting on its tiles, and you're only using \n- * tiles with that DPI. This value is used while OpenLayers is calculating resolution\n- * using scales, and is not necessary if you have resolution information. (This is\n- * typically the case) Regardless, this setting can be useful, but is dangerous\n- * because it will impact other layers while calculating resolution. Only use this\n- * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale)\n+ * Property: maxPx\n+ * {Object} An object with a 'x' and 'y' values that is the top\n+ * right of maxExtent in viewport pixel space.\n+ * Used to verify in moveByPx that the new location we're moving to\n+ * is valid.\n */\n- overrideDPI: false,\n+ maxPx: null,\n \n /**\n- * Constructor: OpenLayers.Layer.ArcGISCache \n- * Creates a new instance of this class \n+ * Constructor: OpenLayers.Map\n+ * Constructor for a new OpenLayers.Map instance. There are two possible\n+ * ways to call the map constructor. See the examples below.\n+ *\n+ * Parameters:\n+ * div - {DOMElement|String} The element or id of an element in your page\n+ * that will contain the map. May be omitted if the <div> option is\n+ * provided or if you intend to call the <render> method later.\n+ * options - {Object} Optional object with properties to tag onto the map.\n+ *\n+ * Valid options (in addition to the listed API properties):\n+ * center - {<OpenLayers.LonLat>|Array} The default initial center of the map.\n+ * If provided as array, the first value is the x coordinate,\n+ * and the 2nd value is the y coordinate.\n+ * Only specify if <layers> is provided.\n+ * Note that if an ArgParser/Permalink control is present,\n+ * and the querystring contains coordinates, center will be set\n+ * by that, and this option will be ignored.\n+ * zoom - {Number} The initial zoom level for the map. Only specify if\n+ * <layers> is provided.\n+ * Note that if an ArgParser/Permalink control is present,\n+ * and the querystring contains a zoom level, zoom will be set\n+ * by that, and this option will be ignored.\n+ * extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.\n+ * If provided as an array, the array should consist of\n+ * four values (left, bottom, right, top).\n+ * Only specify if <center> and <zoom> are not provided.\n * \n- * Parameters: \n- * name - {String} \n- * url - {String} \n- * options - {Object} extra layer options\n+ * Examples:\n+ * (code)\n+ * // create a map with default options in an element with the id \"map1\"\n+ * var map = new OpenLayers.Map(\"map1\");\n+ *\n+ * // create a map with non-default options in an element with id \"map2\"\n+ * var options = {\n+ * projection: \"EPSG:3857\",\n+ * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),\n+ * center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)\n+ * };\n+ * var map = new OpenLayers.Map(\"map2\", options);\n+ *\n+ * // map with non-default options - same as above but with a single argument,\n+ * // a restricted extent, and using arrays for bounds and center\n+ * var map = new OpenLayers.Map({\n+ * div: \"map_id\",\n+ * projection: \"EPSG:3857\",\n+ * maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],\n+ * restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],\n+ * center: [-12356463.476333, 5621521.4854095]\n+ * });\n+ *\n+ * // create a map without a reference to a container - call render later\n+ * var map = new OpenLayers.Map({\n+ * projection: \"EPSG:3857\",\n+ * maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)\n+ * });\n+ * (end)\n */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ initialize: function(div, options) {\n \n- if (this.resolutions) {\n- this.serverResolutions = this.resolutions;\n- this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);\n+ // If only one argument is provided, check if it is an object.\n+ if (arguments.length === 1 && typeof div === \"object\") {\n+ options = div;\n+ div = options && options.div;\n }\n \n- // this block steps through translating the values from the server layer JSON \n- // capabilities object into values that we can use. This is also a helpful\n- // reference when configuring this layer directly.\n- if (this.layerInfo) {\n- // alias the object\n- var info = this.layerInfo;\n+ // Simple-type defaults are set in class definition. \n+ // Now set complex-type defaults \n+ this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,\n+ OpenLayers.Map.TILE_HEIGHT);\n \n- // build our extents\n- var startingTileExtent = new OpenLayers.Bounds(\n- info.fullExtent.xmin,\n- info.fullExtent.ymin,\n- info.fullExtent.xmax,\n- info.fullExtent.ymax\n- );\n+ this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n \n- // set our projection based on the given spatial reference.\n- // esri uses slightly different IDs, so this may not be comprehensive\n- this.projection = 'EPSG:' + info.spatialReference.wkid;\n- this.sphericalMercator = (info.spatialReference.wkid == 102100);\n+ this.theme = OpenLayers._getScriptLocation() +\n+ 'theme/default/style.css';\n \n- // convert esri units into openlayers units (basic feet or meters only)\n- this.units = (info.units == \"esriFeet\") ? 'ft' : 'm';\n+ // backup original options\n+ this.options = OpenLayers.Util.extend({}, options);\n \n- // optional extended section based on whether or not the server returned\n- // specific tile information\n- if (!!info.tileInfo) {\n- // either set the tiles based on rows/columns, or specific width/height\n- this.tileSize = new OpenLayers.Size(\n- info.tileInfo.width || info.tileInfo.cols,\n- info.tileInfo.height || info.tileInfo.rows\n- );\n+ // now override default options \n+ OpenLayers.Util.extend(this, options);\n \n- // this must be set when manually configuring this layer\n- this.tileOrigin = new OpenLayers.LonLat(\n- info.tileInfo.origin.x,\n- info.tileInfo.origin.y\n- );\n+ var projCode = this.projection instanceof OpenLayers.Projection ?\n+ this.projection.projCode : this.projection;\n+ OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n \n- var upperLeft = new OpenLayers.Geometry.Point(\n- startingTileExtent.left,\n- startingTileExtent.top\n- );\n+ // allow extents and center to be arrays\n+ if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n+ this.maxExtent = new OpenLayers.Bounds(this.maxExtent);\n+ }\n+ if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n+ this.minExtent = new OpenLayers.Bounds(this.minExtent);\n+ }\n+ if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n+ this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);\n+ }\n+ if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n+ this.center = new OpenLayers.LonLat(this.center);\n+ }\n \n- var bottomRight = new OpenLayers.Geometry.Point(\n- startingTileExtent.right,\n- startingTileExtent.bottom\n- );\n+ // initialize layers array\n+ this.layers = [];\n \n- if (this.useScales) {\n- this.scales = [];\n- } else {\n- this.resolutions = [];\n- }\n+ this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n \n- this.lods = [];\n- for (var key in info.tileInfo.lods) {\n- if (info.tileInfo.lods.hasOwnProperty(key)) {\n- var lod = info.tileInfo.lods[key];\n- if (this.useScales) {\n- this.scales.push(lod.scale);\n- } else {\n- this.resolutions.push(lod.resolution);\n- }\n+ this.div = OpenLayers.Util.getElement(div);\n+ if (!this.div) {\n+ this.div = document.createElement(\"div\");\n+ this.div.style.height = \"1px\";\n+ this.div.style.width = \"1px\";\n+ }\n \n- var start = this.getContainingTileCoords(upperLeft, lod.resolution);\n- lod.startTileCol = start.x;\n- lod.startTileRow = start.y;\n+ OpenLayers.Element.addClass(this.div, 'olMap');\n \n- var end = this.getContainingTileCoords(bottomRight, lod.resolution);\n- lod.endTileCol = end.x;\n- lod.endTileRow = end.y;\n- this.lods.push(lod);\n- }\n- }\n+ // the viewPortDiv is the outermost div we modify\n+ var id = this.id + \"_OpenLayers_ViewPort\";\n+ this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,\n+ \"relative\", null,\n+ \"hidden\");\n+ this.viewPortDiv.style.width = \"100%\";\n+ this.viewPortDiv.style.height = \"100%\";\n+ this.viewPortDiv.className = \"olMapViewport\";\n+ this.div.appendChild(this.viewPortDiv);\n \n- this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);\n- this.serverResolutions = this.resolutions;\n- if (this.overrideDPI && info.tileInfo.dpi) {\n- // see comment above for 'overrideDPI'\n- OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;\n- }\n+ this.events = new OpenLayers.Events(\n+ this, this.viewPortDiv, null, this.fallThrough, {\n+ includeXY: true\n+ }\n+ );\n+\n+ if (OpenLayers.TileManager && this.tileManager !== null) {\n+ if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n+ this.tileManager = new OpenLayers.TileManager(this.tileManager);\n }\n+ this.tileManager.addMap(this);\n }\n- },\n \n- /** \n- * Method: getContainingTileCoords\n- * Calculates the x/y pixel corresponding to the position of the tile\n- * that contains the given point and for the for the given resolution.\n- * \n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} \n- * res - {Float} The resolution for which to compute the extent.\n- * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n- */\n- getContainingTileCoords: function(point, res) {\n- return new OpenLayers.Pixel(\n- Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0),\n- Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0)\n- );\n- },\n+ // the layerContainerDiv is the one that holds all the layers\n+ id = this.id + \"_OpenLayers_Container\";\n+ this.layerContainerDiv = OpenLayers.Util.createDiv(id);\n+ this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1;\n+ this.layerContainerOriginPx = {\n+ x: 0,\n+ y: 0\n+ };\n+ this.applyTransform();\n \n- /** \n- * Method: calculateMaxExtentWithLOD\n- * Given a Level of Detail object from the server, this function\n- * calculates the actual max extent\n- * \n- * Parameters: \n- * lod - {Object} a Level of Detail Object from the server capabilities object \n- representing a particular zoom level\n- * \n- * Returns: \n- * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n- */\n- calculateMaxExtentWithLOD: function(lod) {\n- // the max extent we're provided with just overlaps some tiles\n- // our real extent is the bounds of all the tiles we touch\n+ this.viewPortDiv.appendChild(this.layerContainerDiv);\n \n- var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;\n- var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;\n+ this.updateSize();\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n \n- var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);\n- var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);\n+ if (this.autoUpdateSize === true) {\n+ // updateSize on catching the window's resize\n+ // Note that this is ok, as updateSize() does nothing if the \n+ // map's size has not actually changed.\n+ this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize,\n+ this);\n+ OpenLayers.Event.observe(window, 'resize',\n+ this.updateSizeDestroy);\n+ }\n \n- var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);\n- var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n- },\n+ // only append link stylesheet if the theme property is set\n+ if (this.theme) {\n+ // check existing links for equivalent url\n+ var addNode = true;\n+ var nodes = document.getElementsByTagName('link');\n+ for (var i = 0, len = nodes.length; i < len; ++i) {\n+ if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,\n+ this.theme)) {\n+ addNode = false;\n+ break;\n+ }\n+ }\n+ // only add a new node if one with an equivalent url hasn't already\n+ // been added\n+ if (addNode) {\n+ var cssNode = document.createElement('link');\n+ cssNode.setAttribute('rel', 'stylesheet');\n+ cssNode.setAttribute('type', 'text/css');\n+ cssNode.setAttribute('href', this.theme);\n+ document.getElementsByTagName('head')[0].appendChild(cssNode);\n+ }\n+ }\n \n- /** \n- * Method: calculateMaxExtentWithExtent\n- * Given a 'suggested' max extent from the server, this function uses\n- * information about the actual tile sizes to determine the actual\n- * extent of the layer.\n- * \n- * Parameters: \n- * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer\n- * res - {Float} The resolution for which to compute the extent.\n- * \n- * Returns: \n- * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n- */\n- calculateMaxExtentWithExtent: function(extent, res) {\n- var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);\n- var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);\n- var start = this.getContainingTileCoords(upperLeft, res);\n- var end = this.getContainingTileCoords(bottomRight, res);\n- var lod = {\n- resolution: res,\n- startTileCol: start.x,\n- startTileRow: start.y,\n- endTileCol: end.x,\n- endTileRow: end.y\n- };\n- return this.calculateMaxExtentWithLOD(lod);\n- },\n+ if (this.controls == null) { // default controls\n+ this.controls = [];\n+ if (OpenLayers.Control != null) { // running full or lite?\n+ // Navigation or TouchNavigation depending on what is in build\n+ if (OpenLayers.Control.Navigation) {\n+ this.controls.push(new OpenLayers.Control.Navigation());\n+ } else if (OpenLayers.Control.TouchNavigation) {\n+ this.controls.push(new OpenLayers.Control.TouchNavigation());\n+ }\n+ if (OpenLayers.Control.Zoom) {\n+ this.controls.push(new OpenLayers.Control.Zoom());\n+ } else if (OpenLayers.Control.PanZoom) {\n+ this.controls.push(new OpenLayers.Control.PanZoom());\n+ }\n \n- /** \n- * Method: getUpperLeftTileCoord\n- * Calculates the x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n- * \n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n- * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n- */\n- getUpperLeftTileCoord: function(res) {\n- var upperLeft = new OpenLayers.Geometry.Point(\n- this.maxExtent.left,\n- this.maxExtent.top);\n- return this.getContainingTileCoords(upperLeft, res);\n- },\n+ if (OpenLayers.Control.ArgParser) {\n+ this.controls.push(new OpenLayers.Control.ArgParser());\n+ }\n+ if (OpenLayers.Control.Attribution) {\n+ this.controls.push(new OpenLayers.Control.Attribution());\n+ }\n+ }\n+ }\n \n- /** \n- * Method: getLowerRightTileCoord\n- * Calculates the x/y pixel corresponding to the position \n- * of the lower right tile for the given resolution.\n- * \n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n- * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position\n- * of the lower right tile for the given resolution.\n- */\n- getLowerRightTileCoord: function(res) {\n- var bottomRight = new OpenLayers.Geometry.Point(\n- this.maxExtent.right,\n- this.maxExtent.bottom);\n- return this.getContainingTileCoords(bottomRight, res);\n- },\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.addControlToMap(this.controls[i]);\n+ }\n \n- /** \n- * Method: getMaxExtentForResolution\n- * Since the max extent of a set of tiles can change from zoom level\n- * to zoom level, we need to be able to calculate that max extent \n- * for a given resolution.\n- *\n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n- * \n- * Returns: \n- * {<OpenLayers.Bounds>} The extent for this resolution\n- */\n- getMaxExtentForResolution: function(res) {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n+ this.popups = [];\n \n- var numTileCols = (end.x - start.x) + 1;\n- var numTileRows = (end.y - start.y) + 1;\n+ this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);\n \n- var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);\n- var maxX = minX + (numTileCols * this.tileSize.w * res);\n \n- var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);\n- var minY = maxY - (numTileRows * this.tileSize.h * res);\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n+ // always call map.destroy()\n+ OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);\n+\n+ // add any initial layers\n+ if (options && options.layers) {\n+ /** \n+ * If you have set options.center, the map center property will be\n+ * set at this point. However, since setCenter has not been called,\n+ * addLayers gets confused. So we delete the map center in this \n+ * case. Because the check below uses options.center, it will\n+ * be properly set below.\n+ */\n+ delete this.center;\n+ delete this.zoom;\n+ this.addLayers(options.layers);\n+ // set center (and optionally zoom)\n+ if (options.center && !this.getCenter()) {\n+ // zoom can be undefined here\n+ this.setCenter(options.center, options.zoom);\n+ }\n+ }\n+\n+ if (this.panMethod) {\n+ this.panTween = new OpenLayers.Tween(this.panMethod);\n+ }\n+ if (this.zoomMethod && this.applyTransform.transform) {\n+ this.zoomTween = new OpenLayers.Tween(this.zoomMethod);\n+ }\n },\n \n /** \n- * APIMethod: clone \n- * Returns an exact clone of this OpenLayers.Layer.ArcGISCache\n- * \n- * Parameters: \n- * [obj] - {Object} optional object to assign the cloned instance to.\n- * \n- * Returns: \n- * {<OpenLayers.Layer.ArcGISCache>} clone of this instance \n+ * APIMethod: getViewport\n+ * Get the DOMElement representing the view port.\n+ *\n+ * Returns:\n+ * {DOMElement}\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);\n- }\n- return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ getViewport: function() {\n+ return this.viewPortDiv;\n },\n \n /**\n- * Method: initGriddedTiles\n+ * APIMethod: render\n+ * Render the map to a specified container.\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * div - {String|DOMElement} The container that the map should be rendered\n+ * to. If different than the current container, the map viewport\n+ * will be moved from the current to the new container.\n */\n- initGriddedTiles: function(bounds) {\n- delete this._tileOrigin;\n- OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments);\n+ render: function(div) {\n+ this.div = OpenLayers.Util.getElement(div);\n+ OpenLayers.Element.addClass(this.div, 'olMap');\n+ this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n+ this.div.appendChild(this.viewPortDiv);\n+ this.updateSize();\n },\n \n /**\n- * Method: getMaxExtent\n- * Get this layer's maximum extent.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>}\n+ * Method: unloadDestroy\n+ * Function that is called to destroy the map on page unload. stored here\n+ * so that if map is manually destroyed, we can unregister this.\n */\n- getMaxExtent: function() {\n- var resolution = this.map.getResolution();\n- return this.maxExtent = this.getMaxExtentForResolution(resolution);\n- },\n+ unloadDestroy: null,\n \n /**\n- * Method: getTileOrigin\n- * Determine the origin for aligning the grid of tiles. \n- * The origin will be derived from the layer's <maxExtent> property. \n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} The tile origin.\n+ * Method: updateSizeDestroy\n+ * When the map is destroyed, we need to stop listening to updateSize\n+ * events: this method stores the function we need to unregister in \n+ * non-IE browsers.\n */\n- getTileOrigin: function() {\n- if (!this._tileOrigin) {\n- var extent = this.getMaxExtent();\n- this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom);\n- }\n- return this._tileOrigin;\n- },\n+ updateSizeDestroy: null,\n \n /**\n- * Method: getURL\n- * Determine the URL for a tile given the tile bounds. This is should support\n- * urls that access tiles through an ArcGIS Server MapServer or directly through\n- * the hex folder structure using HTTP. Just be sure to set the useArcGISServer\n- * property appropriately! This is basically the same as \n- * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing,\n- * and tile rounding.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {String} The URL for a tile based on given bounds.\n+ * APIMethod: destroy\n+ * Destroy this map.\n+ * Note that if you are using an application which removes a container\n+ * of the map from the DOM, you need to ensure that you destroy the\n+ * map *before* this happens; otherwise, the page unload handler\n+ * will fail because the DOM elements that map.destroy() wants\n+ * to clean up will be gone. (See \n+ * http://trac.osgeo.org/openlayers/ticket/2277 for more information).\n+ * This will apply to GeoExt and also to other applications which\n+ * modify the DOM of the container of the OpenLayers Map.\n */\n- getURL: function(bounds) {\n- var res = this.getResolution();\n+ destroy: function() {\n+ // if unloadDestroy is null, we've already been destroyed\n+ if (!this.unloadDestroy) {\n+ return false;\n+ }\n \n- // tile center\n- var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2));\n- var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2));\n+ // make sure panning doesn't continue after destruction\n+ if (this.panTween) {\n+ this.panTween.stop();\n+ this.panTween = null;\n+ }\n+ // make sure zooming doesn't continue after destruction\n+ if (this.zoomTween) {\n+ this.zoomTween.stop();\n+ this.zoomTween = null;\n+ }\n \n- var center = bounds.getCenterLonLat();\n- var point = {\n- x: center.lon,\n- y: center.lat\n- };\n- var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))));\n- var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))));\n- var z = this.map.getZoom();\n+ // map has been destroyed. dont do it again!\n+ OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);\n+ this.unloadDestroy = null;\n \n- // this prevents us from getting pink tiles (non-existant tiles)\n- if (this.lods) {\n- var lod = this.lods[this.map.getZoom()];\n- if ((x < lod.startTileCol || x > lod.endTileCol) ||\n- (y < lod.startTileRow || y > lod.endTileRow)) {\n- return null;\n- }\n- } else {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n- if ((x < start.x || x >= end.x) ||\n- (y < start.y || y >= end.y)) {\n- return null;\n- }\n+ if (this.updateSizeDestroy) {\n+ OpenLayers.Event.stopObserving(window, 'resize',\n+ this.updateSizeDestroy);\n }\n \n- // Construct the url string\n- var url = this.url;\n- var s = '' + x + y + z;\n+ this.paddingForPopups = null;\n \n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(s, url);\n+ if (this.controls != null) {\n+ for (var i = this.controls.length - 1; i >= 0; --i) {\n+ this.controls[i].destroy();\n+ }\n+ this.controls = null;\n+ }\n+ if (this.layers != null) {\n+ for (var i = this.layers.length - 1; i >= 0; --i) {\n+ //pass 'false' to destroy so that map wont try to set a new \n+ // baselayer after each baselayer is removed\n+ this.layers[i].destroy(false);\n+ }\n+ this.layers = null;\n+ }\n+ if (this.viewPortDiv && this.viewPortDiv.parentNode) {\n+ this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n }\n+ this.viewPortDiv = null;\n \n- // Accessing tiles through ArcGIS Server uses a different path\n- // structure than direct access via the folder structure.\n- if (this.useArcGISServer) {\n- // AGS MapServers have pretty url access to tiles\n- url = url + '/tile/${z}/${y}/${x}';\n- } else {\n- // The tile images are stored using hex values on disk.\n- x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16);\n- y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16);\n- z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10);\n- url = url + '/${z}/${y}/${x}.' + this.type;\n+ if (this.tileManager) {\n+ this.tileManager.removeMap(this);\n+ this.tileManager = null;\n }\n \n- // Write the values into our formatted url\n- url = OpenLayers.String.format(url, {\n- 'x': x,\n- 'y': y,\n- 'z': z\n- });\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ this.eventListeners = null;\n+ }\n+ this.events.destroy();\n+ this.events = null;\n \n- return OpenLayers.Util.urlAppend(\n- url, OpenLayers.Util.getParameterString(this.params)\n- );\n+ this.options = null;\n },\n \n- CLASS_NAME: 'OpenLayers.Layer.ArcGISCache'\n-});\n-/* ======================================================================\n- OpenLayers/Format/ArcXML.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * APIMethod: setOptions\n+ * Change the map options\n+ *\n+ * Parameters:\n+ * options - {Object} Hashtable of options to tag to the map\n+ */\n+ setOptions: function(options) {\n+ var updatePxExtent = this.minPx &&\n+ options.restrictedExtent != this.restrictedExtent;\n+ OpenLayers.Util.extend(this, options);\n+ // force recalculation of minPx and maxPx\n+ updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {\n+ forceZoomChange: true\n+ });\n+ },\n \n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/MultiPolygon.js\n- * @requires OpenLayers/Geometry/LinearRing.js\n- */\n+ /**\n+ * APIMethod: getTileSize\n+ * Get the tile size for the map\n+ *\n+ * Returns:\n+ * {<OpenLayers.Size>}\n+ */\n+ getTileSize: function() {\n+ return this.tileSize;\n+ },\n \n-/**\n- * Class: OpenLayers.Format.ArcXML\n- * Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {\n \n /**\n- * Property: fontStyleKeys\n- * {Array} List of keys used in font styling.\n+ * APIMethod: getBy\n+ * Get a list of objects given a property and a match item.\n+ *\n+ * Parameters:\n+ * array - {String} A property on the map whose value is an array.\n+ * property - {String} A property on each item of the given array.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(map[array][i][property]) evaluates to true, the item will\n+ * be included in the array returned. If no items are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array} An array of items where the given property matches the given\n+ * criteria.\n */\n- fontStyleKeys: [\n- 'antialiasing', 'blockout', 'font', 'fontcolor', 'fontsize', 'fontstyle',\n- 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency'\n- ],\n+ getBy: function(array, property, match) {\n+ var test = (typeof match.test == \"function\");\n+ var found = OpenLayers.Array.filter(this[array], function(item) {\n+ return item[property] == match || (test && match.test(item[property]));\n+ });\n+ return found;\n+ },\n \n /**\n- * Property: request\n- * A get_image request destined for an ArcIMS server.\n+ * APIMethod: getLayersBy\n+ * Get a list of layers with properties matching the given criteria.\n+ *\n+ * Parameters:\n+ * property - {String} A layer property to be matched.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(layer[property]) evaluates to true, the layer will be\n+ * included in the array returned. If no layers are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.\n+ * An empty array is returned if no matches are found.\n */\n- request: null,\n+ getLayersBy: function(property, match) {\n+ return this.getBy(\"layers\", property, match);\n+ },\n \n /**\n- * Property: response\n- * A parsed response from an ArcIMS server.\n+ * APIMethod: getLayersByName\n+ * Get a list of layers with names matching the given name.\n+ *\n+ * Parameters:\n+ * match - {String | Object} A layer name. The name can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * name.test(layer.name) evaluates to true, the layer will be included\n+ * in the list of layers returned. If no layers are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.\n+ * An empty array is returned if no matches are found.\n */\n- response: null,\n+ getLayersByName: function(match) {\n+ return this.getLayersBy(\"name\", match);\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.ArcXML\n- * Create a new parser/writer for ArcXML. Create an instance of this class\n- * to begin authoring a request to an ArcIMS service. This is used\n- * primarily by the ArcIMS layer, but could be used to do other wild\n- * stuff, like geocoding.\n+ * APIMethod: getLayersByClass\n+ * Get a list of layers of a given class (CLASS_NAME).\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * match - {String | Object} A layer class name. The match can also be a\n+ * regular expression literal or object. In addition, it can be any\n+ * object with a method named test. For reqular expressions or other,\n+ * if type.test(layer.CLASS_NAME) evaluates to true, the layer will\n+ * be included in the list of layers returned. If no layers are\n+ * found, an empty array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- initialize: function(options) {\n- this.request = new OpenLayers.Format.ArcXML.Request();\n- this.response = new OpenLayers.Format.ArcXML.Response();\n-\n- if (options) {\n- if (options.requesttype == \"feature\") {\n- this.request.get_image = null;\n-\n- var qry = this.request.get_feature.query;\n- this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);\n- this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);\n-\n- if (options.polygon) {\n- qry.isspatial = true;\n- qry.spatialfilter.polygon = options.polygon;\n- } else if (options.envelope) {\n- qry.isspatial = true;\n- qry.spatialfilter.envelope = {\n- minx: 0,\n- miny: 0,\n- maxx: 0,\n- maxy: 0\n- };\n- this.parseEnvelope(qry.spatialfilter.envelope, options.envelope);\n- }\n- } else if (options.requesttype == \"image\") {\n- this.request.get_feature = null;\n-\n- var props = this.request.get_image.properties;\n- this.parseEnvelope(props.envelope, options.envelope);\n-\n- this.addLayers(props.layerlist, options.layers);\n- this.addImageSize(props.imagesize, options.tileSize);\n- this.addCoordSys(props.featurecoordsys, options.featureCoordSys);\n- this.addCoordSys(props.filtercoordsys, options.filterCoordSys);\n- } else {\n- // if an arcxml object is being created with no request type, it is\n- // probably going to consume a response, so do not throw an error if\n- // the requesttype is not defined\n- this.request = null;\n- }\n- }\n-\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ getLayersByClass: function(match) {\n+ return this.getLayersBy(\"CLASS_NAME\", match);\n },\n \n /**\n- * Method: parseEnvelope\n- * Parse an array of coordinates into an ArcXML envelope structure.\n+ * APIMethod: getControlsBy\n+ * Get a list of controls with properties matching the given criteria.\n *\n * Parameters:\n- * env - {Object} An envelope object that will contain the parsed coordinates.\n- * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ]\n+ * property - {String} A control property to be matched.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(layer[property]) evaluates to true, the layer will be\n+ * included in the array returned. If no layers are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given\n+ * criteria. An empty array is returned if no matches are found.\n */\n- parseEnvelope: function(env, arr) {\n- if (arr && arr.length == 4) {\n- env.minx = arr[0];\n- env.miny = arr[1];\n- env.maxx = arr[2];\n- env.maxy = arr[3];\n- }\n+ getControlsBy: function(property, match) {\n+ return this.getBy(\"controls\", property, match);\n },\n \n- /** \n- * Method: addLayers\n- * Add a collection of layers to another collection of layers. Each layer in the list is tuple of\n- * { id, visible }. These layer collections represent the \n- * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML\n- *\n- * TODO: Add support for dynamic layer rendering.\n+ /**\n+ * APIMethod: getControlsByClass\n+ * Get a list of controls of a given class (CLASS_NAME).\n *\n * Parameters:\n- * ll - {Array({id,visible})} A list of layer definitions.\n- * lyrs - {Array({id,visible})} A list of layer definitions.\n+ * match - {String | Object} A control class name. The match can also be a\n+ * regular expression literal or object. In addition, it can be any\n+ * object with a method named test. For reqular expressions or other,\n+ * if type.test(control.CLASS_NAME) evaluates to true, the control will\n+ * be included in the list of controls returned. If no controls are\n+ * found, an empty array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given class.\n+ * An empty array is returned if no matches are found.\n */\n- addLayers: function(ll, lyrs) {\n- for (var lind = 0, len = lyrs.length; lind < len; lind++) {\n- ll.push(lyrs[lind]);\n- }\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match);\n },\n \n+ /********************************************************/\n+ /* */\n+ /* Layer Functions */\n+ /* */\n+ /* The following functions deal with adding and */\n+ /* removing Layers to and from the Map */\n+ /* */\n+ /********************************************************/\n+\n /**\n- * Method: addImageSize\n- * Set the size of the requested image.\n+ * APIMethod: getLayer\n+ * Get a layer based on its id\n *\n * Parameters:\n- * imsize - {Object} An ArcXML imagesize object.\n- * olsize - {<OpenLayers.Size>} The image size to set.\n+ * id - {String} A layer id\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's \n+ * layer collection, or null if not found.\n */\n- addImageSize: function(imsize, olsize) {\n- if (olsize !== null) {\n- imsize.width = olsize.w;\n- imsize.height = olsize.h;\n- imsize.printwidth = olsize.w;\n- imsize.printheight = olsize.h;\n+ getLayer: function(id) {\n+ var foundLayer = null;\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer.id == id) {\n+ foundLayer = layer;\n+ break;\n+ }\n }\n+ return foundLayer;\n },\n \n /**\n- * Method: addCoordSys\n- * Add the coordinate system information to an object. The object may be \n- *\n+ * Method: setLayerZIndex\n+ * \n * Parameters:\n- * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure.\n- * fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or \n- * {featurecoordsys} A projection representation. If it's a {String}, \n- * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} \n- * AND Proj4js is available, the projection number and name are extracted \n- * from there. If it's a filter or feature ArcXML structure, it is copied.\n+ * layer - {<OpenLayers.Layer>} \n+ * zIdx - {int} \n */\n- addCoordSys: function(featOrFilt, fsys) {\n- if (typeof fsys == \"string\") {\n- featOrFilt.id = parseInt(fsys);\n- featOrFilt.string = fsys;\n- }\n- // is this a proj4js instance?\n- else if (typeof fsys == \"object\" && fsys.proj !== null) {\n- featOrFilt.id = fsys.proj.srsProjNumber;\n- featOrFilt.string = fsys.proj.srsCode;\n- } else {\n- featOrFilt = fsys;\n+ setLayerZIndex: function(layer, zIdx) {\n+ layer.setZIndex(\n+ this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] +\n+ zIdx * 5);\n+ },\n+\n+ /**\n+ * Method: resetLayersZIndex\n+ * Reset each layer's z-index based on layer's array index\n+ */\n+ resetLayersZIndex: function() {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ this.setLayerZIndex(layer, i);\n }\n },\n \n /**\n- * APIMethod: iserror\n- * Check to see if the response from the server was an error.\n+ * APIMethod: addLayer\n *\n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse. If nothing is supplied,\n- * the current response is examined.\n+ * layer - {<OpenLayers.Layer>} \n *\n * Returns:\n- * {Boolean} true if the response was an error.\n+ * {Boolean} True if the layer has been added to the map.\n */\n- iserror: function(data) {\n- var ret = null;\n+ addLayer: function(layer) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ if (this.layers[i] == layer) {\n+ return false;\n+ }\n+ }\n+ if (this.events.triggerEvent(\"preaddlayer\", {\n+ layer: layer\n+ }) === false) {\n+ return false;\n+ }\n+ if (this.allOverlays) {\n+ layer.isBaseLayer = false;\n+ }\n \n- if (!data) {\n- ret = (this.response.error !== '');\n+ layer.div.className = \"olLayerDiv\";\n+ layer.div.style.overflow = \"\";\n+ this.setLayerZIndex(layer, this.layers.length);\n+\n+ if (layer.isFixed) {\n+ this.viewPortDiv.appendChild(layer.div);\n } else {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- var errorNodes = data.documentElement.getElementsByTagName(\"ERROR\");\n- ret = (errorNodes !== null && errorNodes.length > 0);\n+ this.layerContainerDiv.appendChild(layer.div);\n }\n+ this.layers.push(layer);\n+ layer.setMap(this);\n \n- return ret;\n+ if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {\n+ if (this.baseLayer == null) {\n+ // set the first baselaye we add as the baselayer\n+ this.setBaseLayer(layer);\n+ } else {\n+ layer.setVisibility(false);\n+ }\n+ } else {\n+ layer.redraw();\n+ }\n+\n+ this.events.triggerEvent(\"addlayer\", {\n+ layer: layer\n+ });\n+ layer.events.triggerEvent(\"added\", {\n+ map: this,\n+ layer: layer\n+ });\n+ layer.afterAdd();\n+\n+ return true;\n },\n \n /**\n- * APIMethod: read\n- * Read data from a string, and return an response. \n+ * APIMethod: addLayers \n+ *\n+ * Parameters:\n+ * layers - {Array(<OpenLayers.Layer>)} \n+ */\n+ addLayers: function(layers) {\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ this.addLayer(layers[i]);\n+ }\n+ },\n+\n+ /** \n+ * APIMethod: removeLayer\n+ * Removes a layer from the map by removing its visual element (the \n+ * layer.div property), then removing it from the map's internal list \n+ * of layers, setting the layer's map property to null. \n+ * \n+ * a \"removelayer\" event is triggered.\n+ * \n+ * very worthy of mention is that simply removing a layer from a map\n+ * will not cause the removal of any popups which may have been created\n+ * by the layer. this is due to the fact that it was decided at some\n+ * point that popups would not belong to layers. thus there is no way \n+ * for us to know here to which layer the popup belongs.\n+ * \n+ * A simple solution to this is simply to call destroy() on the layer.\n+ * the default OpenLayers.Layer class's destroy() function\n+ * automatically takes care to remove itself from whatever map it has\n+ * been attached to. \n+ * \n+ * The correct solution is for the layer itself to register an \n+ * event-handler on \"removelayer\" and when it is called, if it \n+ * recognizes itself as the layer being removed, then it cycles through\n+ * its own personal list of popups, removing them from the map.\n * \n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response\n- * data may change in the future. \n+ * layer - {<OpenLayers.Layer>} \n+ * setNewBaseLayer - {Boolean} Default is true\n */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ removeLayer: function(layer, setNewBaseLayer) {\n+ if (this.events.triggerEvent(\"preremovelayer\", {\n+ layer: layer\n+ }) === false) {\n+ return;\n+ }\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true;\n }\n \n- var arcNode = null;\n- if (data && data.documentElement) {\n- if (data.documentElement.nodeName == \"ARCXML\") {\n- arcNode = data.documentElement;\n- } else {\n- arcNode = data.documentElement.getElementsByTagName(\"ARCXML\")[0];\n- }\n+ if (layer.isFixed) {\n+ this.viewPortDiv.removeChild(layer.div);\n+ } else {\n+ this.layerContainerDiv.removeChild(layer.div);\n }\n+ OpenLayers.Util.removeItem(this.layers, layer);\n+ layer.removeMap(this);\n+ layer.map = null;\n \n- // in Safari, arcNode will be there but will have a child named \n- // parsererror\n- if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') {\n- var error, source;\n- try {\n- error = data.firstChild.nodeValue;\n- source = data.firstChild.childNodes[1].firstChild.nodeValue;\n- } catch (err) {\n- // pass\n+ // if we removed the base layer, need to set a new one\n+ if (this.baseLayer == layer) {\n+ this.baseLayer = null;\n+ if (setNewBaseLayer) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var iLayer = this.layers[i];\n+ if (iLayer.isBaseLayer || this.allOverlays) {\n+ this.setBaseLayer(iLayer);\n+ break;\n+ }\n+ }\n }\n- throw {\n- message: \"Error parsing the ArcXML request\",\n- error: error,\n- source: source\n- };\n }\n \n- var response = this.parseResponse(arcNode);\n- return response;\n+ this.resetLayersZIndex();\n+\n+ this.events.triggerEvent(\"removelayer\", {\n+ layer: layer\n+ });\n+ layer.events.triggerEvent(\"removed\", {\n+ map: this,\n+ layer: layer\n+ });\n },\n \n /**\n- * APIMethod: write\n- * Generate an ArcXml document string for sending to an ArcIMS server. \n+ * APIMethod: getNumLayers\n * \n * Returns:\n- * {String} A string representing the ArcXML document request.\n+ * {Int} The number of layers attached to the map.\n */\n- write: function(request) {\n- if (!request) {\n- request = this.request;\n- }\n- var root = this.createElementNS(\"\", \"ARCXML\");\n- root.setAttribute(\"version\", \"1.1\");\n-\n- var reqElem = this.createElementNS(\"\", \"REQUEST\");\n-\n- if (request.get_image != null) {\n- var getElem = this.createElementNS(\"\", \"GET_IMAGE\");\n- reqElem.appendChild(getElem);\n-\n- var propElem = this.createElementNS(\"\", \"PROPERTIES\");\n- getElem.appendChild(propElem);\n+ getNumLayers: function() {\n+ return this.layers.length;\n+ },\n \n- var props = request.get_image.properties;\n- if (props.featurecoordsys != null) {\n- var feat = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n- propElem.appendChild(feat);\n+ /** \n+ * APIMethod: getLayerIndex\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer>}\n+ *\n+ * Returns:\n+ * {Integer} The current (zero-based) index of the given layer in the map's\n+ * layer stack. Returns -1 if the layer isn't on the map.\n+ */\n+ getLayerIndex: function(layer) {\n+ return OpenLayers.Util.indexOf(this.layers, layer);\n+ },\n \n- if (props.featurecoordsys.id === 0) {\n- feat.setAttribute(\"string\", props.featurecoordsys['string']);\n- } else {\n- feat.setAttribute(\"id\", props.featurecoordsys.id);\n- }\n+ /** \n+ * APIMethod: setLayerIndex\n+ * Move the given layer to the specified (zero-based) index in the layer\n+ * list, changing its z-index in the map display. Use\n+ * map.getLayerIndex() to find out the current index of a layer. Note\n+ * that this cannot (or at least should not) be effectively used to\n+ * raise base layers above overlays.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer>} \n+ * idx - {int} \n+ */\n+ setLayerIndex: function(layer, idx) {\n+ var base = this.getLayerIndex(layer);\n+ if (idx < 0) {\n+ idx = 0;\n+ } else if (idx > this.layers.length) {\n+ idx = this.layers.length;\n+ }\n+ if (base != idx) {\n+ this.layers.splice(base, 1);\n+ this.layers.splice(idx, 0, layer);\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ this.setLayerZIndex(this.layers[i], i);\n }\n-\n- if (props.filtercoordsys != null) {\n- var filt = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n- propElem.appendChild(filt);\n-\n- if (props.filtercoordsys.id === 0) {\n- filt.setAttribute(\"string\", props.filtercoordsys.string);\n- } else {\n- filt.setAttribute(\"id\", props.filtercoordsys.id);\n+ this.events.triggerEvent(\"changelayer\", {\n+ layer: layer,\n+ property: \"order\"\n+ });\n+ if (this.allOverlays) {\n+ if (idx === 0) {\n+ this.setBaseLayer(layer);\n+ } else if (this.baseLayer !== this.layers[0]) {\n+ this.setBaseLayer(this.layers[0]);\n }\n }\n+ }\n+ },\n \n- if (props.envelope != null) {\n- var env = this.createElementNS(\"\", \"ENVELOPE\");\n- propElem.appendChild(env);\n+ /** \n+ * APIMethod: raiseLayer\n+ * Change the index of the given layer by delta. If delta is positive, \n+ * the layer is moved up the map's layer stack; if delta is negative,\n+ * the layer is moved down. Again, note that this cannot (or at least\n+ * should not) be effectively used to raise base layers above overlays.\n+ *\n+ * Paremeters:\n+ * layer - {<OpenLayers.Layer>} \n+ * delta - {int} \n+ */\n+ raiseLayer: function(layer, delta) {\n+ var idx = this.getLayerIndex(layer) + delta;\n+ this.setLayerIndex(layer, idx);\n+ },\n \n- env.setAttribute(\"minx\", props.envelope.minx);\n- env.setAttribute(\"miny\", props.envelope.miny);\n- env.setAttribute(\"maxx\", props.envelope.maxx);\n- env.setAttribute(\"maxy\", props.envelope.maxy);\n- }\n+ /** \n+ * APIMethod: setBaseLayer\n+ * Allows user to specify one of the currently-loaded layers as the Map's\n+ * new base layer.\n+ * \n+ * Parameters:\n+ * newBaseLayer - {<OpenLayers.Layer>}\n+ */\n+ setBaseLayer: function(newBaseLayer) {\n \n- var imagesz = this.createElementNS(\"\", \"IMAGESIZE\");\n- propElem.appendChild(imagesz);\n+ if (newBaseLayer != this.baseLayer) {\n \n- imagesz.setAttribute(\"height\", props.imagesize.height);\n- imagesz.setAttribute(\"width\", props.imagesize.width);\n+ // ensure newBaseLayer is already loaded\n+ if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n \n- if (props.imagesize.height != props.imagesize.printheight ||\n- props.imagesize.width != props.imagesize.printwidth) {\n- imagesz.setAttribute(\"printheight\", props.imagesize.printheight);\n- imagesz.setArrtibute(\"printwidth\", props.imagesize.printwidth);\n- }\n+ // preserve center and scale when changing base layers\n+ var center = this.getCachedCenter();\n+ var newResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.getScale(), newBaseLayer.units\n+ );\n \n- if (props.background != null) {\n- var backgrnd = this.createElementNS(\"\", \"BACKGROUND\");\n- propElem.appendChild(backgrnd);\n+ // make the old base layer invisible \n+ if (this.baseLayer != null && !this.allOverlays) {\n+ this.baseLayer.setVisibility(false);\n+ }\n \n- backgrnd.setAttribute(\"color\",\n- props.background.color.r + \",\" +\n- props.background.color.g + \",\" +\n- props.background.color.b);\n+ // set new baselayer\n+ this.baseLayer = newBaseLayer;\n \n- if (props.background.transcolor !== null) {\n- backgrnd.setAttribute(\"transcolor\",\n- props.background.transcolor.r + \",\" +\n- props.background.transcolor.g + \",\" +\n- props.background.transcolor.b);\n+ if (!this.allOverlays || this.baseLayer.visibility) {\n+ this.baseLayer.setVisibility(true);\n+ // Layer may previously have been visible but not in range.\n+ // In this case we need to redraw it to make it visible.\n+ if (this.baseLayer.inRange === false) {\n+ this.baseLayer.redraw();\n+ }\n }\n- }\n-\n- if (props.layerlist != null && props.layerlist.length > 0) {\n- var layerlst = this.createElementNS(\"\", \"LAYERLIST\");\n- propElem.appendChild(layerlst);\n \n- for (var ld = 0; ld < props.layerlist.length; ld++) {\n- var ldef = this.createElementNS(\"\", \"LAYERDEF\");\n- layerlst.appendChild(ldef);\n+ // recenter the map\n+ if (center != null) {\n+ // new zoom level derived from old scale\n+ var newZoom = this.getZoomForResolution(\n+ newResolution || this.resolution, true\n+ );\n+ // zoom and force zoom change\n+ this.setCenter(center, newZoom, false, true);\n+ }\n \n- ldef.setAttribute(\"id\", props.layerlist[ld].id);\n- ldef.setAttribute(\"visible\", props.layerlist[ld].visible);\n+ this.events.triggerEvent(\"changebaselayer\", {\n+ layer: this.baseLayer\n+ });\n+ }\n+ }\n+ },\n \n- if (typeof props.layerlist[ld].query == \"object\") {\n- var query = props.layerlist[ld].query;\n \n- if (query.where.length < 0) {\n- continue;\n- }\n+ /********************************************************/\n+ /* */\n+ /* Control Functions */\n+ /* */\n+ /* The following functions deal with adding and */\n+ /* removing Controls to and from the Map */\n+ /* */\n+ /********************************************************/\n \n- var queryElem = null;\n- if (typeof query.spatialfilter == \"boolean\" && query.spatialfilter) {\n- // handle spatial filter madness\n- queryElem = this.createElementNS(\"\", \"SPATIALQUERY\");\n- } else {\n- queryElem = this.createElementNS(\"\", \"QUERY\");\n- }\n+ /**\n+ * APIMethod: addControl\n+ * Add the passed over control to the map. Optionally \n+ * position the control at the given pixel.\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>}\n+ * px - {<OpenLayers.Pixel>}\n+ */\n+ addControl: function(control, px) {\n+ this.controls.push(control);\n+ this.addControlToMap(control, px);\n+ },\n \n- queryElem.setAttribute(\"where\", query.where);\n+ /**\n+ * APIMethod: addControls\n+ * Add all of the passed over controls to the map. \n+ * You can pass over an optional second array\n+ * with pixel-objects to position the controls.\n+ * The indices of the two arrays should match and\n+ * you can add null as pixel for those controls \n+ * you want to be autopositioned. \n+ * \n+ * Parameters:\n+ * controls - {Array(<OpenLayers.Control>)}\n+ * pixels - {Array(<OpenLayers.Pixel>)}\n+ */\n+ addControls: function(controls, pixels) {\n+ var pxs = (arguments.length === 1) ? [] : pixels;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var ctrl = controls[i];\n+ var px = (pxs[i]) ? pxs[i] : null;\n+ this.addControl(ctrl, px);\n+ }\n+ },\n \n- if (typeof query.accuracy == \"number\" && query.accuracy > 0) {\n- queryElem.setAttribute(\"accuracy\", query.accuracy);\n- }\n- if (typeof query.featurelimit == \"number\" && query.featurelimit < 2000) {\n- queryElem.setAttribute(\"featurelimit\", query.featurelimit);\n- }\n- if (typeof query.subfields == \"string\" && query.subfields != \"#ALL#\") {\n- queryElem.setAttribute(\"subfields\", query.subfields);\n- }\n- if (typeof query.joinexpression == \"string\" && query.joinexpression.length > 0) {\n- queryElem.setAttribute(\"joinexpression\", query.joinexpression);\n- }\n- if (typeof query.jointables == \"string\" && query.jointables.length > 0) {\n- queryElem.setAttribute(\"jointables\", query.jointables);\n- }\n+ /**\n+ * Method: addControlToMap\n+ * \n+ * Parameters:\n+ * \n+ * control - {<OpenLayers.Control>}\n+ * px - {<OpenLayers.Pixel>}\n+ */\n+ addControlToMap: function(control, px) {\n+ // If a control doesn't have a div at this point, it belongs in the\n+ // viewport.\n+ control.outsideViewport = (control.div != null);\n \n- ldef.appendChild(queryElem);\n- }\n+ // If the map has a displayProjection, and the control doesn't, set \n+ // the display projection.\n+ if (this.displayProjection && !control.displayProjection) {\n+ control.displayProjection = this.displayProjection;\n+ }\n \n- if (typeof props.layerlist[ld].renderer == \"object\") {\n- this.addRenderer(ldef, props.layerlist[ld].renderer);\n- }\n- }\n+ control.setMap(this);\n+ var div = control.draw(px);\n+ if (div) {\n+ if (!control.outsideViewport) {\n+ div.style.zIndex = this.Z_INDEX_BASE['Control'] +\n+ this.controls.length;\n+ this.viewPortDiv.appendChild(div);\n }\n- } else if (request.get_feature != null) {\n- var getElem = this.createElementNS(\"\", \"GET_FEATURES\");\n- getElem.setAttribute(\"outputmode\", \"newxml\");\n- getElem.setAttribute(\"checkesc\", \"true\");\n+ }\n+ if (control.autoActivate) {\n+ control.activate();\n+ }\n+ },\n \n- if (request.get_feature.geometry) {\n- getElem.setAttribute(\"geometry\", request.get_feature.geometry);\n- } else {\n- getElem.setAttribute(\"geometry\", \"false\");\n+ /**\n+ * APIMethod: getControl\n+ * \n+ * Parameters:\n+ * id - {String} ID of the control to return.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Control>} The control from the map's list of controls \n+ * which has a matching 'id'. If none found, \n+ * returns null.\n+ */\n+ getControl: function(id) {\n+ var returnControl = null;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ var control = this.controls[i];\n+ if (control.id == id) {\n+ returnControl = control;\n+ break;\n }\n+ }\n+ return returnControl;\n+ },\n \n- if (request.get_feature.compact) {\n- getElem.setAttribute(\"compact\", request.get_feature.compact);\n+ /** \n+ * APIMethod: removeControl\n+ * Remove a control from the map. Removes the control both from the map \n+ * object's internal array of controls, as well as from the map's \n+ * viewPort (assuming the control was not added outsideViewport)\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control to remove.\n+ */\n+ removeControl: function(control) {\n+ //make sure control is non-null and actually part of our map\n+ if ((control) && (control == this.getControl(control.id))) {\n+ if (control.div && (control.div.parentNode == this.viewPortDiv)) {\n+ this.viewPortDiv.removeChild(control.div);\n }\n+ OpenLayers.Util.removeItem(this.controls, control);\n+ }\n+ },\n \n- if (request.get_feature.featurelimit == \"number\") {\n- getElem.setAttribute(\"featurelimit\", request.get_feature.featurelimit);\n- }\n+ /********************************************************/\n+ /* */\n+ /* Popup Functions */\n+ /* */\n+ /* The following functions deal with adding and */\n+ /* removing Popups to and from the Map */\n+ /* */\n+ /********************************************************/\n \n- getElem.setAttribute(\"globalenvelope\", \"true\");\n- reqElem.appendChild(getElem);\n+ /** \n+ * APIMethod: addPopup\n+ * \n+ * Parameters:\n+ * popup - {<OpenLayers.Popup>}\n+ * exclusive - {Boolean} If true, closes all other popups first\n+ */\n+ addPopup: function(popup, exclusive) {\n \n- if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {\n- var lyrElem = this.createElementNS(\"\", \"LAYER\");\n- lyrElem.setAttribute(\"id\", request.get_feature.layer);\n- getElem.appendChild(lyrElem);\n+ if (exclusive) {\n+ //remove all other popups from screen\n+ for (var i = this.popups.length - 1; i >= 0; --i) {\n+ this.removePopup(this.popups[i]);\n }\n+ }\n \n- var fquery = request.get_feature.query;\n- if (fquery != null) {\n- var qElem = null;\n- if (fquery.isspatial) {\n- qElem = this.createElementNS(\"\", \"SPATIALQUERY\");\n- } else {\n- qElem = this.createElementNS(\"\", \"QUERY\");\n- }\n- getElem.appendChild(qElem);\n+ popup.map = this;\n+ this.popups.push(popup);\n+ var popupDiv = popup.draw();\n+ if (popupDiv) {\n+ popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +\n+ this.popups.length;\n+ this.layerContainerDiv.appendChild(popupDiv);\n+ }\n+ },\n \n- if (typeof fquery.accuracy == \"number\") {\n- qElem.setAttribute(\"accuracy\", fquery.accuracy);\n- }\n- //qElem.setAttribute(\"featurelimit\", \"5\");\n+ /** \n+ * APIMethod: removePopup\n+ * \n+ * Parameters:\n+ * popup - {<OpenLayers.Popup>}\n+ */\n+ removePopup: function(popup) {\n+ OpenLayers.Util.removeItem(this.popups, popup);\n+ if (popup.div) {\n+ try {\n+ this.layerContainerDiv.removeChild(popup.div);\n+ } catch (e) {} // Popups sometimes apparently get disconnected\n+ // from the layerContainerDiv, and cause complaints.\n+ }\n+ popup.map = null;\n+ },\n \n- if (fquery.featurecoordsys != null) {\n- var fcsElem1 = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n+ /********************************************************/\n+ /* */\n+ /* Container Div Functions */\n+ /* */\n+ /* The following functions deal with the access to */\n+ /* and maintenance of the size of the container div */\n+ /* */\n+ /********************************************************/\n \n- if (fquery.featurecoordsys.id == 0) {\n- fcsElem1.setAttribute(\"string\", fquery.featurecoordsys.string);\n- } else {\n- fcsElem1.setAttribute(\"id\", fquery.featurecoordsys.id);\n- }\n- qElem.appendChild(fcsElem1);\n- }\n+ /**\n+ * APIMethod: getSize\n+ * \n+ * Returns:\n+ * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the \n+ * size, in pixels, of the div into which OpenLayers \n+ * has been loaded. \n+ * Note - A clone() of this locally cached variable is\n+ * returned, so as not to allow users to modify it.\n+ */\n+ getSize: function() {\n+ var size = null;\n+ if (this.size != null) {\n+ size = this.size.clone();\n+ }\n+ return size;\n+ },\n \n- if (fquery.filtercoordsys != null) {\n- var fcsElem2 = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n+ /**\n+ * APIMethod: updateSize\n+ * This function should be called by any external code which dynamically\n+ * changes the size of the map div (because mozilla wont let us catch \n+ * the \"onresize\" for an element)\n+ */\n+ updateSize: function() {\n+ // the div might have moved on the page, also\n+ var newSize = this.getCurrentSize();\n+ if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {\n+ this.events.clearMouseCache();\n+ var oldSize = this.getSize();\n+ if (oldSize == null) {\n+ this.size = oldSize = newSize;\n+ }\n+ if (!newSize.equals(oldSize)) {\n \n- if (fquery.filtercoordsys.id === 0) {\n- fcsElem2.setAttribute(\"string\", fquery.filtercoordsys.string);\n- } else {\n- fcsElem2.setAttribute(\"id\", fquery.filtercoordsys.id);\n- }\n- qElem.appendChild(fcsElem2);\n- }\n+ // store the new size\n+ this.size = newSize;\n \n- if (fquery.buffer > 0) {\n- var bufElem = this.createElementNS(\"\", \"BUFFER\");\n- bufElem.setAttribute(\"distance\", fquery.buffer);\n- qElem.appendChild(bufElem);\n+ //notify layers of mapresize\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ this.layers[i].onMapResize();\n }\n \n- if (fquery.isspatial) {\n- var spfElem = this.createElementNS(\"\", \"SPATIALFILTER\");\n- spfElem.setAttribute(\"relation\", fquery.spatialfilter.relation);\n- qElem.appendChild(spfElem);\n+ var center = this.getCachedCenter();\n \n- if (fquery.spatialfilter.envelope) {\n- var envElem = this.createElementNS(\"\", \"ENVELOPE\");\n- envElem.setAttribute(\"minx\", fquery.spatialfilter.envelope.minx);\n- envElem.setAttribute(\"miny\", fquery.spatialfilter.envelope.miny);\n- envElem.setAttribute(\"maxx\", fquery.spatialfilter.envelope.maxx);\n- envElem.setAttribute(\"maxy\", fquery.spatialfilter.envelope.maxy);\n- spfElem.appendChild(envElem);\n- } else if (typeof fquery.spatialfilter.polygon == \"object\") {\n- spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon));\n- }\n+ if (this.baseLayer != null && center != null) {\n+ var zoom = this.getZoom();\n+ this.zoom = null;\n+ this.setCenter(center, zoom);\n }\n \n- if (fquery.where != null && fquery.where.length > 0) {\n- qElem.setAttribute(\"where\", fquery.where);\n- }\n }\n }\n-\n- root.appendChild(reqElem);\n-\n- return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n+ this.events.triggerEvent(\"updatesize\");\n },\n \n+ /**\n+ * Method: getCurrentSize\n+ * \n+ * Returns:\n+ * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions \n+ * of the map div\n+ */\n+ getCurrentSize: function() {\n \n- addGroupRenderer: function(ldef, toprenderer) {\n- var topRelem = this.createElementNS(\"\", \"GROUPRENDERER\");\n- ldef.appendChild(topRelem);\n+ var size = new OpenLayers.Size(this.div.clientWidth,\n+ this.div.clientHeight);\n \n- for (var rind = 0; rind < toprenderer.length; rind++) {\n- var renderer = toprenderer[rind];\n- this.addRenderer(topRelem, renderer);\n+ if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n+ size.w = this.div.offsetWidth;\n+ size.h = this.div.offsetHeight;\n+ }\n+ if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n+ size.w = parseInt(this.div.style.width);\n+ size.h = parseInt(this.div.style.height);\n }\n+ return size;\n },\n \n+ /** \n+ * Method: calculateBounds\n+ * \n+ * Parameters:\n+ * center - {<OpenLayers.LonLat>} Default is this.getCenter()\n+ * resolution - {float} Default is this.getResolution() \n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} A bounds based on resolution, center, and \n+ * current mapsize.\n+ */\n+ calculateBounds: function(center, resolution) {\n \n- addRenderer: function(topRelem, renderer) {\n- if (OpenLayers.Util.isArray(renderer)) {\n- this.addGroupRenderer(topRelem, renderer);\n- } else {\n- var renderElem = this.createElementNS(\"\", renderer.type.toUpperCase() + \"RENDERER\");\n- topRelem.appendChild(renderElem);\n+ var extent = null;\n \n- if (renderElem.tagName == \"VALUEMAPRENDERER\") {\n- this.addValueMapRenderer(renderElem, renderer);\n- } else if (renderElem.tagName == \"VALUEMAPLABELRENDERER\") {\n- this.addValueMapLabelRenderer(renderElem, renderer);\n- } else if (renderElem.tagName == \"SIMPLELABELRENDERER\") {\n- this.addSimpleLabelRenderer(renderElem, renderer);\n- } else if (renderElem.tagName == \"SCALEDEPENDENTRENDERER\") {\n- this.addScaleDependentRenderer(renderElem, renderer);\n- }\n+ if (center == null) {\n+ center = this.getCachedCenter();\n+ }\n+ if (resolution == null) {\n+ resolution = this.getResolution();\n }\n- },\n \n+ if ((center != null) && (resolution != null)) {\n+ var halfWDeg = (this.size.w * resolution) / 2;\n+ var halfHDeg = (this.size.h * resolution) / 2;\n \n- addScaleDependentRenderer: function(renderElem, renderer) {\n- if (typeof renderer.lower == \"string\" || typeof renderer.lower == \"number\") {\n- renderElem.setAttribute(\"lower\", renderer.lower);\n- }\n- if (typeof renderer.upper == \"string\" || typeof renderer.upper == \"number\") {\n- renderElem.setAttribute(\"upper\", renderer.upper);\n+ extent = new OpenLayers.Bounds(center.lon - halfWDeg,\n+ center.lat - halfHDeg,\n+ center.lon + halfWDeg,\n+ center.lat + halfHDeg);\n }\n \n- this.addRenderer(renderElem, renderer.renderer);\n+ return extent;\n },\n \n \n- addValueMapLabelRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n- renderElem.setAttribute(\"labelfield\", renderer.labelfield);\n-\n- if (typeof renderer.exacts == \"object\") {\n- for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n- var exact = renderer.exacts[ext];\n-\n- var eelem = this.createElementNS(\"\", \"EXACT\");\n-\n- if (typeof exact.value == \"string\") {\n- eelem.setAttribute(\"value\", exact.value);\n- }\n- if (typeof exact.label == \"string\") {\n- eelem.setAttribute(\"label\", exact.label);\n- }\n- if (typeof exact.method == \"string\") {\n- eelem.setAttribute(\"method\", exact.method);\n- }\n-\n- renderElem.appendChild(eelem);\n-\n- if (typeof exact.symbol == \"object\") {\n- var selem = null;\n-\n- if (exact.symbol.type == \"text\") {\n- selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n- }\n-\n- if (selem != null) {\n- var keys = this.fontStyleKeys;\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (exact.symbol[key]) {\n- selem.setAttribute(key, exact.symbol[key]);\n- }\n- }\n- eelem.appendChild(selem);\n- }\n- }\n- } // for each exact\n+ /********************************************************/\n+ /* */\n+ /* Zoom, Center, Pan Functions */\n+ /* */\n+ /* The following functions handle the validation, */\n+ /* getting and setting of the Zoom Level and Center */\n+ /* as well as the panning of the Map */\n+ /* */\n+ /********************************************************/\n+ /**\n+ * APIMethod: getCenter\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>}\n+ */\n+ getCenter: function() {\n+ var center = null;\n+ var cachedCenter = this.getCachedCenter();\n+ if (cachedCenter) {\n+ center = cachedCenter.clone();\n }\n+ return center;\n },\n \n- addValueMapRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n-\n- if (typeof renderer.ranges == \"object\") {\n- for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) {\n- var range = renderer.ranges[rng];\n-\n- var relem = this.createElementNS(\"\", \"RANGE\");\n- relem.setAttribute(\"lower\", range.lower);\n- relem.setAttribute(\"upper\", range.upper);\n-\n- renderElem.appendChild(relem);\n-\n- if (typeof range.symbol == \"object\") {\n- var selem = null;\n-\n- if (range.symbol.type == \"simplepolygon\") {\n- selem = this.createElementNS(\"\", \"SIMPLEPOLYGONSYMBOL\");\n- }\n-\n- if (selem != null) {\n- if (typeof range.symbol.boundarycolor == \"string\") {\n- selem.setAttribute(\"boundarycolor\", range.symbol.boundarycolor);\n- }\n- if (typeof range.symbol.fillcolor == \"string\") {\n- selem.setAttribute(\"fillcolor\", range.symbol.fillcolor);\n- }\n- if (typeof range.symbol.filltransparency == \"number\") {\n- selem.setAttribute(\"filltransparency\", range.symbol.filltransparency);\n- }\n- relem.appendChild(selem);\n- }\n- }\n- } // for each range\n- } else if (typeof renderer.exacts == \"object\") {\n- for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n- var exact = renderer.exacts[ext];\n-\n- var eelem = this.createElementNS(\"\", \"EXACT\");\n- if (typeof exact.value == \"string\") {\n- eelem.setAttribute(\"value\", exact.value);\n- }\n- if (typeof exact.label == \"string\") {\n- eelem.setAttribute(\"label\", exact.label);\n- }\n- if (typeof exact.method == \"string\") {\n- eelem.setAttribute(\"method\", exact.method);\n- }\n-\n- renderElem.appendChild(eelem);\n+ /**\n+ * Method: getCachedCenter\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>}\n+ */\n+ getCachedCenter: function() {\n+ if (!this.center && this.size) {\n+ this.center = this.getLonLatFromViewPortPx({\n+ x: this.size.w / 2,\n+ y: this.size.h / 2\n+ });\n+ }\n+ return this.center;\n+ },\n \n- if (typeof exact.symbol == \"object\") {\n- var selem = null;\n+ /**\n+ * APIMethod: getZoom\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ getZoom: function() {\n+ return this.zoom;\n+ },\n \n- if (exact.symbol.type == \"simplemarker\") {\n- selem = this.createElementNS(\"\", \"SIMPLEMARKERSYMBOL\");\n- }\n+ /** \n+ * APIMethod: pan\n+ * Allows user to pan by a value of screen pixels\n+ * \n+ * Parameters:\n+ * dx - {Integer}\n+ * dy - {Integer}\n+ * options - {Object} Options to configure panning:\n+ * - *animate* {Boolean} Use panTo instead of setCenter. Default is true.\n+ * - *dragging* {Boolean} Call setCenter with dragging true. Default is\n+ * false.\n+ */\n+ pan: function(dx, dy, options) {\n+ options = OpenLayers.Util.applyDefaults(options, {\n+ animate: true,\n+ dragging: false\n+ });\n+ if (options.dragging) {\n+ if (dx != 0 || dy != 0) {\n+ this.moveByPx(dx, dy);\n+ }\n+ } else {\n+ // getCenter\n+ var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());\n \n- if (selem != null) {\n- if (typeof exact.symbol.antialiasing == \"string\") {\n- selem.setAttribute(\"antialiasing\", exact.symbol.antialiasing);\n- }\n- if (typeof exact.symbol.color == \"string\") {\n- selem.setAttribute(\"color\", exact.symbol.color);\n- }\n- if (typeof exact.symbol.outline == \"string\") {\n- selem.setAttribute(\"outline\", exact.symbol.outline);\n- }\n- if (typeof exact.symbol.overlap == \"string\") {\n- selem.setAttribute(\"overlap\", exact.symbol.overlap);\n- }\n- if (typeof exact.symbol.shadow == \"string\") {\n- selem.setAttribute(\"shadow\", exact.symbol.shadow);\n- }\n- if (typeof exact.symbol.transparency == \"number\") {\n- selem.setAttribute(\"transparency\", exact.symbol.transparency);\n- }\n- //if (typeof exact.symbol.type == \"string\")\n- // selem.setAttribute(\"type\", exact.symbol.type);\n- if (typeof exact.symbol.usecentroid == \"string\") {\n- selem.setAttribute(\"usecentroid\", exact.symbol.usecentroid);\n- }\n- if (typeof exact.symbol.width == \"number\") {\n- selem.setAttribute(\"width\", exact.symbol.width);\n- }\n+ // adjust\n+ var newCenterPx = centerPx.add(dx, dy);\n \n- eelem.appendChild(selem);\n+ if (this.dragging || !newCenterPx.equals(centerPx)) {\n+ var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);\n+ if (options.animate) {\n+ this.panTo(newCenterLonLat);\n+ } else {\n+ this.moveTo(newCenterLonLat);\n+ if (this.dragging) {\n+ this.dragging = false;\n+ this.events.triggerEvent(\"moveend\");\n }\n }\n- } // for each exact\n+ }\n }\n+\n },\n \n+ /** \n+ * APIMethod: panTo\n+ * Allows user to pan to a new lonlat\n+ * If the new lonlat is in the current extent the map will slide smoothly\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n+ */\n+ panTo: function(lonlat) {\n+ if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {\n+ var center = this.getCachedCenter();\n \n- addSimpleLabelRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"field\", renderer.field);\n- var keys = ['featureweight', 'howmanylabels', 'labelbufferratio',\n- 'labelpriorities', 'labelweight', 'linelabelposition',\n- 'rotationalangles'\n- ];\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (renderer[key]) {\n- renderElem.setAttribute(key, renderer[key]);\n+ // center will not change, don't do nothing\n+ if (lonlat.equals(center)) {\n+ return;\n }\n- }\n \n- if (renderer.symbol.type == \"text\") {\n- var symbol = renderer.symbol;\n- var selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n- renderElem.appendChild(selem);\n+ var from = this.getPixelFromLonLat(center);\n+ var to = this.getPixelFromLonLat(lonlat);\n+ var vector = {\n+ x: to.x - from.x,\n+ y: to.y - from.y\n+ };\n+ var last = {\n+ x: 0,\n+ y: 0\n+ };\n \n- var keys = this.fontStyleKeys;\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (symbol[key]) {\n- selem.setAttribute(key, renderer[key]);\n+ this.panTween.start({\n+ x: 0,\n+ y: 0\n+ }, vector, this.panDuration, {\n+ callbacks: {\n+ eachStep: OpenLayers.Function.bind(function(px) {\n+ var x = px.x - last.x,\n+ y = px.y - last.y;\n+ this.moveByPx(x, y);\n+ last.x = Math.round(px.x);\n+ last.y = Math.round(px.y);\n+ }, this),\n+ done: OpenLayers.Function.bind(function(px) {\n+ this.moveTo(lonlat);\n+ this.dragging = false;\n+ this.events.triggerEvent(\"moveend\");\n+ }, this)\n }\n- }\n+ });\n+ } else {\n+ this.setCenter(lonlat);\n }\n },\n \n- writePolygonGeometry: function(polygon) {\n- if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {\n- throw {\n- message: 'Cannot write polygon geometry to ArcXML with an ' +\n- polygon.CLASS_NAME + ' object.',\n- geometry: polygon\n- };\n+ /**\n+ * APIMethod: setCenter\n+ * Set the map center (and optionally, the zoom level).\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>|Array} The new center location.\n+ * If provided as array, the first value is the x coordinate,\n+ * and the 2nd value is the y coordinate.\n+ * zoom - {Integer} Optional zoom level.\n+ * dragging - {Boolean} Specifies whether or not to trigger \n+ * movestart/end events\n+ * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom \n+ * change events (needed on baseLayer change)\n+ *\n+ * TBD: reconsider forceZoomChange in 3.0\n+ */\n+ setCenter: function(lonlat, zoom, dragging, forceZoomChange) {\n+ if (this.panTween) {\n+ this.panTween.stop();\n }\n-\n- var polyElem = this.createElementNS(\"\", \"POLYGON\");\n-\n- for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) {\n- var ring = polygon.components[ln];\n- var ringElem = this.createElementNS(\"\", \"RING\");\n-\n- for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) {\n- var point = ring.components[rn];\n- var pointElem = this.createElementNS(\"\", \"POINT\");\n-\n- pointElem.setAttribute(\"x\", point.x);\n- pointElem.setAttribute(\"y\", point.y);\n-\n- ringElem.appendChild(pointElem);\n- }\n-\n- polyElem.appendChild(ringElem);\n+ if (this.zoomTween) {\n+ this.zoomTween.stop();\n }\n-\n- return polyElem;\n+ this.moveTo(lonlat, zoom, {\n+ 'dragging': dragging,\n+ 'forceZoomChange': forceZoomChange\n+ });\n },\n \n- /**\n- * Method: parseResponse\n- * Take an ArcXML response, and parse in into this object's internal properties.\n+ /** \n+ * Method: moveByPx\n+ * Drag the map by pixels.\n *\n * Parameters:\n- * data - {String} or {DOMElement} The ArcXML response, as either a string or the\n- * top level DOMElement of the response.\n+ * dx - {Number}\n+ * dy - {Number}\n */\n- parseResponse: function(data) {\n- if (typeof data == \"string\") {\n- var newData = new OpenLayers.Format.XML();\n- data = newData.read(data);\n+ moveByPx: function(dx, dy) {\n+ var hw = this.size.w / 2;\n+ var hh = this.size.h / 2;\n+ var x = hw + dx;\n+ var y = hh + dy;\n+ var wrapDateLine = this.baseLayer.wrapDateLine;\n+ var xRestriction = 0;\n+ var yRestriction = 0;\n+ if (this.restrictedExtent) {\n+ xRestriction = hw;\n+ yRestriction = hh;\n+ // wrapping the date line makes no sense for restricted extents\n+ wrapDateLine = false;\n }\n- var response = new OpenLayers.Format.ArcXML.Response();\n-\n- var errorNode = data.getElementsByTagName(\"ERROR\");\n-\n- if (errorNode != null && errorNode.length > 0) {\n- response.error = this.getChildValue(errorNode, \"Unknown error.\");\n- } else {\n- var responseNode = data.getElementsByTagName(\"RESPONSE\");\n-\n- if (responseNode == null || responseNode.length == 0) {\n- response.error = \"No RESPONSE tag found in ArcXML response.\";\n- return response;\n+ dx = wrapDateLine ||\n+ x <= this.maxPx.x - xRestriction &&\n+ x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;\n+ dy = y <= this.maxPx.y - yRestriction &&\n+ y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;\n+ if (dx || dy) {\n+ if (!this.dragging) {\n+ this.dragging = true;\n+ this.events.triggerEvent(\"movestart\");\n }\n-\n- var rtype = responseNode[0].firstChild.nodeName;\n- if (rtype == \"#text\") {\n- rtype = responseNode[0].firstChild.nextSibling.nodeName;\n+ this.center = null;\n+ if (dx) {\n+ this.layerContainerOriginPx.x -= dx;\n+ this.minPx.x -= dx;\n+ this.maxPx.x -= dx;\n }\n-\n- if (rtype == \"IMAGE\") {\n- var envelopeNode = data.getElementsByTagName(\"ENVELOPE\");\n- var outputNode = data.getElementsByTagName(\"OUTPUT\");\n-\n- if (envelopeNode == null || envelopeNode.length == 0) {\n- response.error = \"No ENVELOPE tag found in ArcXML response.\";\n- } else if (outputNode == null || outputNode.length == 0) {\n- response.error = \"No OUTPUT tag found in ArcXML response.\";\n- } else {\n- var envAttr = this.parseAttributes(envelopeNode[0]);\n- var outputAttr = this.parseAttributes(outputNode[0]);\n-\n- if (typeof outputAttr.type == \"string\") {\n- response.image = {\n- envelope: envAttr,\n- output: {\n- type: outputAttr.type,\n- data: this.getChildValue(outputNode[0])\n- }\n- };\n- } else {\n- response.image = {\n- envelope: envAttr,\n- output: outputAttr\n- };\n- }\n- }\n- } else if (rtype == \"FEATURES\") {\n- var features = responseNode[0].getElementsByTagName(\"FEATURES\");\n-\n- // get the feature count\n- var featureCount = features[0].getElementsByTagName(\"FEATURECOUNT\");\n- response.features.featurecount = featureCount[0].getAttribute(\"count\");\n-\n- if (response.features.featurecount > 0) {\n- // get the feature envelope\n- var envelope = features[0].getElementsByTagName(\"ENVELOPE\");\n- response.features.envelope = this.parseAttributes(envelope[0], typeof(0));\n-\n- // get the field values per feature\n- var featureList = features[0].getElementsByTagName(\"FEATURE\");\n- for (var fn = 0; fn < featureList.length; fn++) {\n- var feature = new OpenLayers.Feature.Vector();\n- var fields = featureList[fn].getElementsByTagName(\"FIELD\");\n-\n- for (var fdn = 0; fdn < fields.length; fdn++) {\n- var fieldName = fields[fdn].getAttribute(\"name\");\n- var fieldValue = fields[fdn].getAttribute(\"value\");\n- feature.attributes[fieldName] = fieldValue;\n- }\n-\n- var geom = featureList[fn].getElementsByTagName(\"POLYGON\");\n-\n- if (geom.length > 0) {\n- // if there is a polygon, create an openlayers polygon, and assign\n- // it to the .geometry property of the feature\n- var ring = geom[0].getElementsByTagName(\"RING\");\n-\n- var polys = [];\n- for (var rn = 0; rn < ring.length; rn++) {\n- var linearRings = [];\n- linearRings.push(this.parsePointGeometry(ring[rn]));\n-\n- var holes = ring[rn].getElementsByTagName(\"HOLE\");\n- for (var hn = 0; hn < holes.length; hn++) {\n- linearRings.push(this.parsePointGeometry(holes[hn]));\n- }\n- holes = null;\n- polys.push(new OpenLayers.Geometry.Polygon(linearRings));\n- linearRings = null;\n- }\n- ring = null;\n-\n- if (polys.length == 1) {\n- feature.geometry = polys[0];\n- } else {\n- feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys);\n- }\n- }\n-\n- response.features.feature.push(feature);\n- }\n+ if (dy) {\n+ this.layerContainerOriginPx.y -= dy;\n+ this.minPx.y -= dy;\n+ this.maxPx.y -= dy;\n+ }\n+ this.applyTransform();\n+ var layer, i, len;\n+ for (i = 0, len = this.layers.length; i < len; ++i) {\n+ layer = this.layers[i];\n+ if (layer.visibility &&\n+ (layer === this.baseLayer || layer.inRange)) {\n+ layer.moveByPx(dx, dy);\n+ layer.events.triggerEvent(\"move\");\n }\n- } else {\n- response.error = \"Unidentified response type.\";\n }\n+ this.events.triggerEvent(\"move\");\n }\n- return response;\n },\n \n-\n /**\n- * Method: parseAttributes\n+ * Method: adjustZoom\n *\n * Parameters:\n- * node - {<DOMElement>} An element to parse attributes from.\n+ * zoom - {Number} The zoom level to adjust\n *\n * Returns:\n- * {Object} An attributes object, with properties set to attribute values.\n+ * {Integer} Adjusted zoom level that shows a map not wider than its\n+ * <baseLayer>'s maxExtent.\n */\n- parseAttributes: function(node, type) {\n- var attributes = {};\n- for (var attr = 0; attr < node.attributes.length; attr++) {\n- if (type == \"number\") {\n- attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue);\n- } else {\n- attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue;\n+ adjustZoom: function(zoom) {\n+ if (this.baseLayer && this.baseLayer.wrapDateLine) {\n+ var resolution, resolutions = this.baseLayer.resolutions,\n+ maxResolution = this.getMaxExtent().getWidth() / this.size.w;\n+ if (this.getResolutionForZoom(zoom) > maxResolution) {\n+ if (this.fractionalZoom) {\n+ zoom = this.getZoomForResolution(maxResolution);\n+ } else {\n+ for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) {\n+ if (resolutions[i] <= maxResolution) {\n+ zoom = i;\n+ break;\n+ }\n+ }\n+ }\n }\n }\n- return attributes;\n+ return zoom;\n },\n \n-\n /**\n- * Method: parsePointGeometry\n- *\n- * Parameters:\n- * node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from.\n+ * APIMethod: getMinZoom\n+ * Returns the minimum zoom level for the current map view. If the base\n+ * layer is configured with <wrapDateLine> set to true, this will be the\n+ * first zoom level that shows no more than one world width in the current\n+ * map viewport. Components that rely on this value (e.g. zoom sliders)\n+ * should also listen to the map's \"updatesize\" event and call this method\n+ * in the \"updatesize\" listener.\n *\n * Returns:\n- * {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points.\n+ * {Number} Minimum zoom level that shows a map not wider than its\n+ * <baseLayer>'s maxExtent. This is an Integer value, unless the map is\n+ * configured with <fractionalZoom> set to true.\n */\n- parsePointGeometry: function(node) {\n- var ringPoints = [];\n- var coords = node.getElementsByTagName(\"COORDS\");\n+ getMinZoom: function() {\n+ return this.adjustZoom(0);\n+ },\n \n- if (coords.length > 0) {\n- // if coords is present, it's the only coords item\n- var coordArr = this.getChildValue(coords[0]);\n- coordArr = coordArr.split(/;/);\n- for (var cn = 0; cn < coordArr.length; cn++) {\n- var coordItems = coordArr[cn].split(/ /);\n- ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1]));\n- }\n- coords = null;\n- } else {\n- var point = node.getElementsByTagName(\"POINT\");\n- if (point.length > 0) {\n- for (var pn = 0; pn < point.length; pn++) {\n- ringPoints.push(\n- new OpenLayers.Geometry.Point(\n- parseFloat(point[pn].getAttribute(\"x\")),\n- parseFloat(point[pn].getAttribute(\"y\"))\n- )\n- );\n- }\n+ /**\n+ * Method: moveTo\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * zoom - {Integer}\n+ * options - {Object}\n+ */\n+ moveTo: function(lonlat, zoom, options) {\n+ if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {\n+ lonlat = new OpenLayers.LonLat(lonlat);\n+ }\n+ if (!options) {\n+ options = {};\n+ }\n+ if (zoom != null) {\n+ zoom = parseFloat(zoom);\n+ if (!this.fractionalZoom) {\n+ zoom = Math.round(zoom);\n }\n- point = null;\n }\n+ var requestedZoom = zoom;\n+ zoom = this.adjustZoom(zoom);\n+ if (zoom !== requestedZoom) {\n+ // zoom was adjusted, so keep old lonlat to avoid panning\n+ lonlat = this.getCenter();\n+ }\n+ // dragging is false by default\n+ var dragging = options.dragging || this.dragging;\n+ // forceZoomChange is false by default\n+ var forceZoomChange = options.forceZoomChange;\n \n- return new OpenLayers.Geometry.LinearRing(ringPoints);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.ArcXML\"\n-});\n+ if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {\n+ lonlat = this.maxExtent.getCenterLonLat();\n+ this.center = lonlat.clone();\n+ }\n \n-OpenLayers.Format.ArcXML.Request = OpenLayers.Class({\n- initialize: function(params) {\n- var defaults = {\n- get_image: {\n- properties: {\n- background: null,\n- /*{ \n- color: { r:255, g:255, b:255 },\n- transcolor: null\n- },*/\n- draw: true,\n- envelope: {\n- minx: 0,\n- miny: 0,\n- maxx: 0,\n- maxy: 0\n- },\n- featurecoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- filtercoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- imagesize: {\n- height: 0,\n- width: 0,\n- dpi: 96,\n- printheight: 0,\n- printwidth: 0,\n- scalesymbols: false\n- },\n- layerlist: [],\n- /* no support for legends */\n- output: {\n- baseurl: \"\",\n- legendbaseurl: \"\",\n- legendname: \"\",\n- legendpath: \"\",\n- legendurl: \"\",\n- name: \"\",\n- path: \"\",\n- type: \"jpg\",\n- url: \"\"\n- }\n+ if (this.restrictedExtent != null) {\n+ // In 3.0, decide if we want to change interpretation of maxExtent.\n+ if (lonlat == null) {\n+ lonlat = this.center;\n+ }\n+ if (zoom == null) {\n+ zoom = this.getZoom();\n+ }\n+ var resolution = this.getResolutionForZoom(zoom);\n+ var extent = this.calculateBounds(lonlat, resolution);\n+ if (!this.restrictedExtent.containsBounds(extent)) {\n+ var maxCenter = this.restrictedExtent.getCenterLonLat();\n+ if (extent.getWidth() > this.restrictedExtent.getWidth()) {\n+ lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);\n+ } else if (extent.left < this.restrictedExtent.left) {\n+ lonlat = lonlat.add(this.restrictedExtent.left -\n+ extent.left, 0);\n+ } else if (extent.right > this.restrictedExtent.right) {\n+ lonlat = lonlat.add(this.restrictedExtent.right -\n+ extent.right, 0);\n }\n- },\n-\n- get_feature: {\n- layer: \"\",\n- query: {\n- isspatial: false,\n- featurecoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- filtercoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- buffer: 0,\n- where: \"\",\n- spatialfilter: {\n- relation: \"envelope_intersection\",\n- envelope: null\n- }\n+ if (extent.getHeight() > this.restrictedExtent.getHeight()) {\n+ lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);\n+ } else if (extent.bottom < this.restrictedExtent.bottom) {\n+ lonlat = lonlat.add(0, this.restrictedExtent.bottom -\n+ extent.bottom);\n+ } else if (extent.top > this.restrictedExtent.top) {\n+ lonlat = lonlat.add(0, this.restrictedExtent.top -\n+ extent.top);\n }\n- },\n+ }\n+ }\n \n- environment: {\n- separators: {\n- cs: \" \",\n- ts: \";\"\n- }\n- },\n+ var zoomChanged = forceZoomChange || (\n+ (this.isValidZoomLevel(zoom)) &&\n+ (zoom != this.getZoom()));\n \n- layer: [],\n- workspaces: []\n- };\n+ var centerChanged = (this.isValidLonLat(lonlat)) &&\n+ (!lonlat.equals(this.center));\n \n- return OpenLayers.Util.extend(this, defaults);\n- },\n+ // if neither center nor zoom will change, no need to do anything\n+ if (zoomChanged || centerChanged || dragging) {\n+ dragging || this.events.triggerEvent(\"movestart\", {\n+ zoomChanged: zoomChanged\n+ });\n \n- CLASS_NAME: \"OpenLayers.Format.ArcXML.Request\"\n-});\n+ if (centerChanged) {\n+ if (!zoomChanged && this.center) {\n+ // if zoom hasnt changed, just slide layerContainer\n+ // (must be done before setting this.center to new value)\n+ this.centerLayerContainer(lonlat);\n+ }\n+ this.center = lonlat.clone();\n+ }\n \n-OpenLayers.Format.ArcXML.Response = OpenLayers.Class({\n- initialize: function(params) {\n- var defaults = {\n- image: {\n- envelope: null,\n- output: ''\n- },\n+ var res = zoomChanged ?\n+ this.getResolutionForZoom(zoom) : this.getResolution();\n+ // (re)set the layerContainerDiv's location\n+ if (zoomChanged || this.layerContainerOrigin == null) {\n+ this.layerContainerOrigin = this.getCachedCenter();\n+ this.layerContainerOriginPx.x = 0;\n+ this.layerContainerOriginPx.y = 0;\n+ this.applyTransform();\n+ var maxExtent = this.getMaxExtent({\n+ restricted: true\n+ });\n+ var maxExtentCenter = maxExtent.getCenterLonLat();\n+ var lonDelta = this.center.lon - maxExtentCenter.lon;\n+ var latDelta = maxExtentCenter.lat - this.center.lat;\n+ var extentWidth = Math.round(maxExtent.getWidth() / res);\n+ var extentHeight = Math.round(maxExtent.getHeight() / res);\n+ this.minPx = {\n+ x: (this.size.w - extentWidth) / 2 - lonDelta / res,\n+ y: (this.size.h - extentHeight) / 2 - latDelta / res\n+ };\n+ this.maxPx = {\n+ x: this.minPx.x + Math.round(maxExtent.getWidth() / res),\n+ y: this.minPx.y + Math.round(maxExtent.getHeight() / res)\n+ };\n+ }\n \n- features: {\n- featurecount: 0,\n- envelope: null,\n- feature: []\n- },\n+ if (zoomChanged) {\n+ this.zoom = zoom;\n+ this.resolution = res;\n+ }\n \n- error: ''\n- };\n+ var bounds = this.getExtent();\n \n- return OpenLayers.Util.extend(this, defaults);\n- },\n+ //send the move call to the baselayer and all the overlays \n \n- CLASS_NAME: \"OpenLayers.Format.ArcXML.Response\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/ArcIMS.js\n- ====================================================================== */\n+ if (this.baseLayer.visibility) {\n+ this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);\n+ options.dragging || this.baseLayer.events.triggerEvent(\n+ \"moveend\", {\n+ zoomChanged: zoomChanged\n+ }\n+ );\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ bounds = this.baseLayer.getExtent();\n \n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Format/ArcXML.js\n- * @requires OpenLayers/Request.js\n- */\n+ for (var i = this.layers.length - 1; i >= 0; --i) {\n+ var layer = this.layers[i];\n+ if (layer !== this.baseLayer && !layer.isBaseLayer) {\n+ var inRange = layer.calculateInRange();\n+ if (layer.inRange != inRange) {\n+ // the inRange property has changed. If the layer is\n+ // no longer in range, we turn it off right away. If\n+ // the layer is no longer out of range, the moveTo\n+ // call below will turn on the layer.\n+ layer.inRange = inRange;\n+ if (!inRange) {\n+ layer.display(false);\n+ }\n+ this.events.triggerEvent(\"changelayer\", {\n+ layer: layer,\n+ property: \"visibility\"\n+ });\n+ }\n+ if (inRange && layer.visibility) {\n+ layer.moveTo(bounds, zoomChanged, options.dragging);\n+ options.dragging || layer.events.triggerEvent(\n+ \"moveend\", {\n+ zoomChanged: zoomChanged\n+ }\n+ );\n+ }\n+ }\n+ }\n \n-/**\n- * Class: OpenLayers.Layer.ArcIMS\n- * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS\n- * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ this.events.triggerEvent(\"move\");\n+ dragging || this.events.triggerEvent(\"moveend\");\n \n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Default query string parameters.\n- */\n- DEFAULT_PARAMS: {\n- ClientVersion: \"9.2\",\n- ServiceName: ''\n+ if (zoomChanged) {\n+ //redraw popups\n+ for (var i = 0, len = this.popups.length; i < len; i++) {\n+ this.popups[i].updatePosition();\n+ }\n+ this.events.triggerEvent(\"zoomend\");\n+ }\n+ }\n },\n \n- /**\n- * APIProperty: featureCoordSys\n- * {String} Code for feature coordinate system. Default is \"4326\".\n- */\n- featureCoordSys: \"4326\",\n-\n- /**\n- * APIProperty: filterCoordSys\n- * {String} Code for filter coordinate system. Default is \"4326\".\n- */\n- filterCoordSys: \"4326\",\n-\n- /**\n- * APIProperty: layers\n- * {Array} An array of objects with layer properties.\n- */\n- layers: null,\n-\n- /**\n- * APIProperty: async\n- * {Boolean} Request images asynchronously. Default is true.\n- */\n- async: true,\n-\n- /**\n- * APIProperty: name\n- * {String} Layer name. Default is \"ArcIMS\".\n- */\n- name: \"ArcIMS\",\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is true.\n+ /** \n+ * Method: centerLayerContainer\n+ * This function takes care to recenter the layerContainerDiv.\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n */\n- isBaseLayer: true,\n+ centerLayerContainer: function(lonlat) {\n+ var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);\n+ var newPx = this.getViewPortPxFromLonLat(lonlat);\n \n- /**\n- * Constant: DEFAULT_OPTIONS\n- * {Object} Default layers properties.\n- */\n- DEFAULT_OPTIONS: {\n- tileSize: new OpenLayers.Size(512, 512),\n- featureCoordSys: \"4326\",\n- filterCoordSys: \"4326\",\n- layers: null,\n- isBaseLayer: true,\n- async: true,\n- name: \"ArcIMS\"\n+ if ((originPx != null) && (newPx != null)) {\n+ var oldLeft = this.layerContainerOriginPx.x;\n+ var oldTop = this.layerContainerOriginPx.y;\n+ var newLeft = Math.round(originPx.x - newPx.x);\n+ var newTop = Math.round(originPx.y - newPx.y);\n+ this.applyTransform(\n+ (this.layerContainerOriginPx.x = newLeft),\n+ (this.layerContainerOriginPx.y = newTop));\n+ var dx = oldLeft - newLeft;\n+ var dy = oldTop - newTop;\n+ this.minPx.x -= dx;\n+ this.maxPx.x -= dx;\n+ this.minPx.y -= dy;\n+ this.maxPx.y -= dy;\n+ }\n },\n \n /**\n- * Constructor: OpenLayers.Layer.ArcIMS\n- * Create a new ArcIMS layer object.\n- *\n- * Example:\n- * (code)\n- * var arcims = new OpenLayers.Layer.ArcIMS(\n- * \"Global Sample\",\n- * \"http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap\", \n- * {\n- * service: \"OpenLayers_Sample\", \n- * layers: [\n- * // layers to manipulate\n- * {id: \"1\", visible: true}\n- * ]\n- * }\n- * );\n- * (end)\n- *\n+ * Method: isValidZoomLevel\n+ * \n * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the ArcIMS server\n- * options - {Object} Optional object with properties to be set on the\n- * layer.\n+ * zoomLevel - {Integer}\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the zoom level passed in is non-null and \n+ * within the min/max range of zoom levels.\n */\n- initialize: function(name, url, options) {\n-\n- this.tileSize = new OpenLayers.Size(512, 512);\n-\n- // parameters\n- this.params = OpenLayers.Util.applyDefaults({\n- ServiceName: options.serviceName\n- },\n- this.DEFAULT_PARAMS\n- );\n- this.options = OpenLayers.Util.applyDefaults(\n- options, this.DEFAULT_OPTIONS\n- );\n-\n- OpenLayers.Layer.Grid.prototype.initialize.apply(\n- this, [name, url, this.params, options]\n- );\n-\n- //layer is transparent \n- if (this.transparent) {\n-\n- // unless explicitly set in options, make layer an overlay\n- if (!this.isBaseLayer) {\n- this.isBaseLayer = false;\n- }\n-\n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.format == \"image/jpeg\") {\n- this.format = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\";\n- }\n- }\n-\n- // create an empty layer list if no layers specified in the options\n- if (this.options.layers === null) {\n- this.options.layers = [];\n- }\n+ isValidZoomLevel: function(zoomLevel) {\n+ return ((zoomLevel != null) &&\n+ (zoomLevel >= 0) &&\n+ (zoomLevel < this.getNumZoomLevels()));\n },\n \n /**\n- * Method: getURL\n- * Return an image url this layer.\n- *\n+ * Method: isValidLonLat\n+ * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * \n * Returns:\n- * {String} A string with the map image's url.\n+ * {Boolean} Whether or not the lonlat passed in is non-null and within\n+ * the maxExtent bounds\n */\n- getURL: function(bounds) {\n- var url = \"\";\n- bounds = this.adjustBounds(bounds);\n-\n- // create an arcxml request to generate the image\n- var axlReq = new OpenLayers.Format.ArcXML(\n- OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- })\n- );\n-\n- // create a synchronous ajax request to get an arcims image\n- var req = new OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- data: axlReq.write(),\n- async: false\n- });\n-\n- // if the response exists\n- if (req != null) {\n- var doc = req.responseXML;\n-\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText;\n- }\n-\n- // create a new arcxml format to read the response\n- var axlResp = new OpenLayers.Format.ArcXML();\n- var arcxml = axlResp.read(doc);\n- url = this.getUrlOrImage(arcxml.image.output);\n+ isValidLonLat: function(lonlat) {\n+ var valid = false;\n+ if (lonlat != null) {\n+ var maxExtent = this.getMaxExtent();\n+ var worldBounds = this.baseLayer.wrapDateLine && maxExtent;\n+ valid = maxExtent.containsLonLat(lonlat, {\n+ worldBounds: worldBounds\n+ });\n }\n-\n- return url;\n+ return valid;\n },\n \n+ /********************************************************/\n+ /* */\n+ /* Layer Options */\n+ /* */\n+ /* Accessor functions to Layer Options parameters */\n+ /* */\n+ /********************************************************/\n \n /**\n- * Method: getURLasync\n- * Get an image url this layer asynchronously, and execute a callback\n- * when the image url is generated.\n+ * APIMethod: getProjection\n+ * This method returns a string representing the projection. In \n+ * the case of projection support, this will be the srsCode which\n+ * is loaded -- otherwise it will simply be the string value that\n+ * was passed to the projection at startup.\n *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- * callback - {Function} Function to call when image url is retrieved.\n- * scope - {Object} The scope of the callback method.\n+ * FIXME: In 3.0, we will remove getProjectionObject, and instead\n+ * return a Projection object from this function. \n+ * \n+ * Returns:\n+ * {String} The Projection string from the base layer or null. \n */\n- getURLasync: function(bounds, callback, scope) {\n- bounds = this.adjustBounds(bounds);\n-\n- // create an arcxml request to generate the image\n- var axlReq = new OpenLayers.Format.ArcXML(\n- OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- })\n- );\n-\n- // create an asynchronous ajax request to get an arcims image\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- async: true,\n- data: axlReq.write(),\n- callback: function(req) {\n- // process the response from ArcIMS, and call the callback function\n- // to set the image URL\n- var doc = req.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText;\n- }\n-\n- // create a new arcxml format to read the response\n- var axlResp = new OpenLayers.Format.ArcXML();\n- var arcxml = axlResp.read(doc);\n-\n- callback.call(scope, this.getUrlOrImage(arcxml.image.output));\n- },\n- scope: this\n- });\n+ getProjection: function() {\n+ var projection = this.getProjectionObject();\n+ return projection ? projection.getCode() : null;\n },\n \n /**\n- * Method: getUrlOrImage\n- * Extract a url or image from the ArcXML image output.\n- *\n- * Parameters:\n- * output - {Object} The image.output property of the object returned from\n- * the ArcXML format read method.\n+ * APIMethod: getProjectionObject\n+ * Returns the projection obect from the baselayer.\n *\n * Returns:\n- * {String} A URL for an image (potentially with the data protocol).\n+ * {<OpenLayers.Projection>} The Projection of the base layer.\n */\n- getUrlOrImage: function(output) {\n- var ret = \"\";\n- if (output.url) {\n- // If the image response output url is a string, then the image\n- // data is not inline.\n- ret = output.url;\n- } else if (output.data) {\n- // The image data is inline and base64 encoded, create a data\n- // url for the image. This will only work for small images,\n- // due to browser url length limits.\n- ret = \"data:image/\" + output.type +\n- \";base64,\" + output.data;\n+ getProjectionObject: function() {\n+ var projection = null;\n+ if (this.baseLayer != null) {\n+ projection = this.baseLayer.projection;\n }\n- return ret;\n+ return projection;\n },\n \n /**\n- * Method: setLayerQuery\n- * Set the query definition on this layer. Query definitions are used to\n- * render parts of the spatial data in an image, and can be used to\n- * filter features or layers in the ArcIMS service.\n- *\n- * Parameters:\n- * id - {String} The ArcIMS layer ID.\n- * querydef - {Object} The query definition to apply to this layer.\n+ * APIMethod: getMaxResolution\n+ * \n+ * Returns:\n+ * {String} The Map's Maximum Resolution\n */\n- setLayerQuery: function(id, querydef) {\n- // find the matching layer, if it exists\n- for (var lyr = 0; lyr < this.options.layers.length; lyr++) {\n- if (id == this.options.layers[lyr].id) {\n- // replace this layer definition\n- this.options.layers[lyr].query = querydef;\n- return;\n- }\n+ getMaxResolution: function() {\n+ var maxResolution = null;\n+ if (this.baseLayer != null) {\n+ maxResolution = this.baseLayer.maxResolution;\n }\n-\n- // no layer found, create a new definition\n- this.options.layers.push({\n- id: id,\n- visible: true,\n- query: querydef\n- });\n+ return maxResolution;\n },\n \n /**\n- * Method: getFeatureInfo\n- * Get feature information from ArcIMS. Using the applied geometry, apply\n- * the options to the query (buffer, area/envelope intersection), and\n- * query the ArcIMS service.\n- *\n- * A note about accuracy:\n- * ArcIMS interprets the accuracy attribute in feature requests to be\n- * something like the 'modulus' operator on feature coordinates,\n- * applied to the database geometry of the feature. It doesn't round,\n- * so your feature coordinates may be up to (1 x accuracy) offset from\n- * the actual feature coordinates. If the accuracy of the layer is not\n- * specified, the accuracy will be computed to be approximately 1\n- * feature coordinate per screen pixel.\n+ * APIMethod: getMaxExtent\n *\n * Parameters:\n- * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The\n- * geometry to use when making the query. This should be a closed\n- * polygon for behavior approximating a free selection.\n- * layer - {Object} The ArcIMS layer definition. This is an anonymous object\n- * that looks like:\n- * (code)\n- * {\n- * id: \"ArcXML layer ID\", // the ArcXML layer ID\n- * query: {\n- * where: \"STATE = 'PA'\", // the where clause of the query\n- * accuracy: 100 // the accuracy of the returned feature\n- * }\n- * }\n- * (end)\n- * options - {Object} Object with non-default properties to set on the layer.\n- * Supported properties are buffer, callback, scope, and any other\n- * properties applicable to the ArcXML format. Set the 'callback' and\n- * 'scope' for an object and function to recieve the parsed features\n- * from ArcIMS.\n+ * options - {Object} \n+ * \n+ * Allowed Options:\n+ * restricted - {Boolean} If true, returns restricted extent (if it is \n+ * available.)\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} The maxExtent property as set on the current \n+ * baselayer, unless the 'restricted' option is set, in which case\n+ * the 'restrictedExtent' option from the map is returned (if it\n+ * is set).\n */\n- getFeatureInfo: function(geometry, layer, options) {\n- // set the buffer to 1 unit (dd/m/ft?) by default\n- var buffer = options.buffer || 1;\n- // empty callback by default\n- var callback = options.callback || function() {};\n- // default scope is window (global)\n- var scope = options.scope || window;\n-\n- // apply these option to the request options\n- var requestOptions = {};\n- OpenLayers.Util.extend(requestOptions, this.options);\n-\n- // this is a feature request\n- requestOptions.requesttype = \"feature\";\n-\n- if (geometry instanceof OpenLayers.LonLat) {\n- // create an envelope if the geometry is really a lon/lat\n- requestOptions.polygon = null;\n- requestOptions.envelope = [\n- geometry.lon - buffer,\n- geometry.lat - buffer,\n- geometry.lon + buffer,\n- geometry.lat + buffer\n- ];\n- } else if (geometry instanceof OpenLayers.Geometry.Polygon) {\n- // use the polygon assigned, and empty the envelope\n- requestOptions.envelope = null;\n- requestOptions.polygon = geometry;\n- }\n-\n- // create an arcxml request to get feature requests\n- var arcxml = new OpenLayers.Format.ArcXML(requestOptions);\n-\n- // apply any get feature options to the arcxml request\n- OpenLayers.Util.extend(arcxml.request.get_feature, options);\n-\n- arcxml.request.get_feature.layer = layer.id;\n- if (typeof layer.query.accuracy == \"number\") {\n- // set the accuracy if it was specified\n- arcxml.request.get_feature.query.accuracy = layer.query.accuracy;\n- } else {\n- // guess that the accuracy is 1 per screen pixel\n- var mapCenter = this.map.getCenter();\n- var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);\n- viewPx.x++;\n- var mapOffCenter = this.map.getLonLatFromPixel(viewPx);\n- arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;\n+ getMaxExtent: function(options) {\n+ var maxExtent = null;\n+ if (options && options.restricted && this.restrictedExtent) {\n+ maxExtent = this.restrictedExtent;\n+ } else if (this.baseLayer != null) {\n+ maxExtent = this.baseLayer.maxExtent;\n }\n-\n- // set the get_feature query to be the same as the layer passed in\n- arcxml.request.get_feature.query.where = layer.query.where;\n-\n- // use area_intersection\n- arcxml.request.get_feature.query.spatialfilter.relation = \"area_intersection\";\n-\n- // create a new asynchronous request to get the feature info\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString({\n- 'CustomService': 'Query'\n- }),\n- data: arcxml.write(),\n- callback: function(request) {\n- // parse the arcxml response\n- var response = arcxml.parseResponse(request.responseText);\n-\n- if (!arcxml.iserror()) {\n- // if the arcxml is not an error, call the callback with the features parsed\n- callback.call(scope, response.features);\n- } else {\n- // if the arcxml is an error, return null features selected\n- callback.call(scope, null);\n- }\n- }\n- });\n+ return maxExtent;\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n+ * APIMethod: getNumZoomLevels\n+ * \n * Returns:\n- * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer\n+ * {Integer} The total number of zoom levels that can be displayed by the \n+ * current baseLayer.\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcIMS(this.name,\n- this.url,\n- this.getOptions());\n+ getNumZoomLevels: function() {\n+ var numZoomLevels = null;\n+ if (this.baseLayer != null) {\n+ numZoomLevels = this.baseLayer.numZoomLevels;\n }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n+ return numZoomLevels;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.ArcIMS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/OSM.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/XYZ.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.OSM\n- * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n- * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n- * a different layer instead, you need to provide a different\n- * URL to the constructor. Here's an example for using OpenCycleMap:\n- * \n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n- */\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /* The following functions, all publicly exposed */\n+ /* in the API?, are all merely wrappers to the */\n+ /* the same calls on whatever layer is set as */\n+ /* the current base layer */\n+ /* */\n+ /********************************************************/\n \n /**\n- * APIProperty: name\n- * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n- * argument to the constructor is null or undefined.\n+ * APIMethod: getExtent\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort. \n+ * If no baselayer is set, returns null.\n */\n- name: \"OpenStreetMap\",\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.baseLayer != null) {\n+ extent = this.baseLayer.getExtent();\n+ }\n+ return extent;\n+ },\n \n /**\n- * APIProperty: url\n- * {String} The tileset URL scheme. Defaults to\n- * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n- * (the official OSM tileset) if the second argument to the constructor\n- * is null or undefined. To use another tileset you can have something\n- * like this:\n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n+ * APIMethod: getResolution\n+ * \n+ * Returns:\n+ * {Float} The current resolution of the map. \n+ * If no baselayer is set, returns null.\n */\n- url: [\n- 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n- ],\n+ getResolution: function() {\n+ var resolution = null;\n+ if (this.baseLayer != null) {\n+ resolution = this.baseLayer.getResolution();\n+ } else if (this.allOverlays === true && this.layers.length > 0) {\n+ // while adding the 1st layer to the map in allOverlays mode,\n+ // this.baseLayer is not set yet when we need the resolution\n+ // for calculateInRange.\n+ resolution = this.layers[0].getResolution();\n+ }\n+ return resolution;\n+ },\n \n /**\n- * Property: attribution\n- * {String} The layer attribution.\n+ * APIMethod: getUnits\n+ * \n+ * Returns:\n+ * {Float} The current units of the map. \n+ * If no baselayer is set, returns null.\n */\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+ getUnits: function() {\n+ var units = null;\n+ if (this.baseLayer != null) {\n+ units = this.baseLayer.units;\n+ }\n+ return units;\n+ },\n \n /**\n- * Property: sphericalMercator\n- * {Boolean}\n+ * APIMethod: getScale\n+ * \n+ * Returns:\n+ * {Float} The current scale denominator of the map. \n+ * If no baselayer is set, returns null.\n */\n- sphericalMercator: true,\n+ getScale: function() {\n+ var scale = null;\n+ if (this.baseLayer != null) {\n+ var res = this.getResolution();\n+ var units = this.baseLayer.units;\n+ scale = OpenLayers.Util.getScaleFromResolution(res, units);\n+ }\n+ return scale;\n+ },\n \n- /**\n- * Property: wrapDateLine\n- * {Boolean}\n- */\n- wrapDateLine: true,\n \n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- *\n- * When using OSM tilesets other than the default ones, it may be\n- * necessary to set this to\n- *\n- * (code)\n- * {crossOriginKeyword: null}\n- * (end)\n- *\n- * if the server does not send Access-Control-Allow-Origin headers.\n+ /**\n+ * APIMethod: getZoomForExtent\n+ * \n+ * Parameters: \n+ * bounds - {<OpenLayers.Bounds>}\n+ * closest - {Boolean} Find the zoom level that most closely fits the \n+ * specified bounds. Note that this may result in a zoom that does \n+ * not exactly contain the entire extent.\n+ * Default is false.\n+ * \n+ * Returns:\n+ * {Integer} A suitable zoom level for the specified bounds.\n+ * If no baselayer is set, returns null.\n */\n- tileOptions: null,\n+ getZoomForExtent: function(bounds, closest) {\n+ var zoom = null;\n+ if (this.baseLayer != null) {\n+ zoom = this.baseLayer.getZoomForExtent(bounds, closest);\n+ }\n+ return zoom;\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.OSM\n- *\n+ * APIMethod: getResolutionForZoom\n+ * \n * Parameters:\n- * name - {String} The layer name.\n- * url - {String} The tileset URL scheme.\n- * options - {Object} Configuration options for the layer. Any inherited\n- * layer option can be set in this object (e.g.\n- * <OpenLayers.Layer.Grid.buffer>).\n+ * zoom - {Float}\n+ * \n+ * Returns:\n+ * {Float} A suitable resolution for the specified zoom. If no baselayer\n+ * is set, returns null.\n */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options && this.options.tileOptions);\n+ getResolutionForZoom: function(zoom) {\n+ var resolution = null;\n+ if (this.baseLayer) {\n+ resolution = this.baseLayer.getResolutionForZoom(zoom);\n+ }\n+ return resolution;\n },\n \n /**\n- * Method: clone\n+ * APIMethod: getZoomForResolution\n+ * \n+ * Parameters:\n+ * resolution - {Float}\n+ * closest - {Boolean} Find the zoom level that corresponds to the absolute \n+ * closest resolution, which may result in a zoom whose corresponding\n+ * resolution is actually smaller than we would have desired (if this\n+ * is being called from a getZoomForExtent() call, then this means that\n+ * the returned zoom index might not actually contain the entire \n+ * extent specified... but it'll be close).\n+ * Default is false.\n+ * \n+ * Returns:\n+ * {Integer} A suitable zoom level for the specified resolution.\n+ * If no baselayer is set, returns null.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(\n- this.name, this.url, this.getOptions());\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom = null;\n+ if (this.baseLayer != null) {\n+ zoom = this.baseLayer.getZoomForResolution(resolution, closest);\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj;\n+ return zoom;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/MapGuide.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.MapGuide\n- * Instances of OpenLayers.Layer.MapGuide are used to display\n- * data from a MapGuide OS instance.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} Treat this layer as a base layer. Default is true.\n- **/\n- isBaseLayer: true,\n-\n- /**\n- * APIProperty: useHttpTile\n- * {Boolean} use a tile cache exposed directly via a webserver rather than the \n- * via mapguide server. This does require extra configuration on the Mapguide Server,\n- * and will only work when singleTile is false. The url for the layer must be set to the\n- * webserver path rather than the Mapguide mapagent.\n- * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp\n- **/\n- useHttpTile: false,\n-\n- /** \n- * APIProperty: singleTile\n- * {Boolean} use tile server or request single tile image. \n- **/\n- singleTile: false,\n-\n- /** \n- * APIProperty: useOverlay\n- * {Boolean} flag to indicate if the layer should be retrieved using\n- * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.\n- **/\n- useOverlay: false,\n+ /********************************************************/\n+ /* */\n+ /* Zooming Functions */\n+ /* */\n+ /* The following functions, all publicly exposed */\n+ /* in the API, are all merely wrappers to the */\n+ /* the setCenter() function */\n+ /* */\n+ /********************************************************/\n \n /** \n- * APIProperty: useAsyncOverlay\n- * {Boolean} indicates if the MapGuide site supports the asynchronous \n- * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010\n- * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG \n- * is called asynchronously, allows selections to be drawn separately from \n- * the map and offers styling options.\n+ * APIMethod: zoomTo\n+ * Zoom to a specific zoom level. Zooming will be animated unless the map\n+ * is configured with {zoomMethod: null}. To zoom without animation, use\n+ * <setCenter> without a lonlat argument.\n * \n- * With older versions of MapGuide, set useAsyncOverlay=false. Note that in\n- * this case a synchronous AJAX call is issued and the mapname and session\n- * parameters must be used to initialize the layer, not the mapdefinition\n- * parameter. Also note that this will issue a synchronous AJAX request \n- * before the image request can be issued so the users browser may lock\n- * up if the MG Web tier does not respond in a timely fashion.\n- **/\n- useAsyncOverlay: true,\n+ * Parameters:\n+ * zoom - {Integer}\n+ */\n+ zoomTo: function(zoom, xy) {\n+ // non-API arguments:\n+ // xy - {<OpenLayers.Pixel>} optional zoom origin\n+\n+ var map = this;\n+ if (map.isValidZoomLevel(zoom)) {\n+ if (map.baseLayer.wrapDateLine) {\n+ zoom = map.adjustZoom(zoom);\n+ }\n+ if (map.zoomTween) {\n+ var currentRes = map.getResolution(),\n+ targetRes = map.getResolutionForZoom(zoom),\n+ start = {\n+ scale: 1\n+ },\n+ end = {\n+ scale: currentRes / targetRes\n+ };\n+ if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {\n+ // update the end scale, and reuse the running zoomTween\n+ map.zoomTween.finish = {\n+ scale: map.zoomTween.finish.scale * end.scale\n+ };\n+ } else {\n+ if (!xy) {\n+ var size = map.getSize();\n+ xy = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ };\n+ }\n+ map.zoomTween.start(start, end, map.zoomDuration, {\n+ minFrameRate: 50, // don't spend much time zooming\n+ callbacks: {\n+ eachStep: function(data) {\n+ var containerOrigin = map.layerContainerOriginPx,\n+ scale = data.scale,\n+ dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,\n+ dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;\n+ map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);\n+ },\n+ done: function(data) {\n+ map.applyTransform();\n+ var resolution = map.getResolution() / data.scale,\n+ zoom = map.getZoomForResolution(resolution, true)\n+ map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);\n+ }\n+ }\n+ });\n+ }\n+ } else {\n+ var center = xy ?\n+ map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :\n+ null;\n+ map.setCenter(center, zoom);\n+ }\n+ }\n+ },\n \n /**\n- * Constant: TILE_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for tiled layer\n+ * APIMethod: zoomIn\n+ * \n */\n- TILE_PARAMS: {\n- operation: 'GETTILEIMAGE',\n- version: '1.2.0'\n+ zoomIn: function() {\n+ this.zoomTo(this.getZoom() + 1);\n },\n \n /**\n- * Constant: SINGLE_TILE_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for untiled layer\n+ * APIMethod: zoomOut\n+ * \n */\n- SINGLE_TILE_PARAMS: {\n- operation: 'GETMAPIMAGE',\n- format: 'PNG',\n- locale: 'en',\n- clip: '1',\n- version: '1.0.0'\n+ zoomOut: function() {\n+ this.zoomTo(this.getZoom() - 1);\n },\n \n /**\n- * Constant: OVERLAY_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for untiled layer\n+ * APIMethod: zoomToExtent\n+ * Zoom to the passed in bounds, recenter\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * closest - {Boolean} Find the zoom level that most closely fits the \n+ * specified bounds. Note that this may result in a zoom that does \n+ * not exactly contain the entire extent.\n+ * Default is false.\n+ * \n */\n- OVERLAY_PARAMS: {\n- operation: 'GETDYNAMICMAPOVERLAYIMAGE',\n- format: 'PNG',\n- locale: 'en',\n- clip: '1',\n- version: '2.0.0'\n+ zoomToExtent: function(bounds, closest) {\n+ if (!(bounds instanceof OpenLayers.Bounds)) {\n+ bounds = new OpenLayers.Bounds(bounds);\n+ }\n+ var center = bounds.getCenterLonLat();\n+ if (this.baseLayer.wrapDateLine) {\n+ var maxExtent = this.getMaxExtent();\n+\n+ //fix straddling bounds (in the case of a bbox that straddles the \n+ // dateline, it's left and right boundaries will appear backwards. \n+ // we fix this by allowing a right value that is greater than the\n+ // max value at the dateline -- this allows us to pass a valid \n+ // bounds to calculate zoom)\n+ //\n+ bounds = bounds.clone();\n+ while (bounds.right < bounds.left) {\n+ bounds.right += maxExtent.getWidth();\n+ }\n+ //if the bounds was straddling (see above), then the center point \n+ // we got from it was wrong. So we take our new bounds and ask it\n+ // for the center.\n+ //\n+ center = bounds.getCenterLonLat().wrapDateLine(maxExtent);\n+ }\n+ this.setCenter(center, this.getZoomForExtent(bounds, closest));\n },\n \n /** \n- * Constant: FOLDER_PARAMS\n- * {Object} Hashtable of parameter key/value pairs which describe \n- * the folder structure for tiles as configured in the mapguide \n- * serverconfig.ini section [TileServiceProperties]\n+ * APIMethod: zoomToMaxExtent\n+ * Zoom to the full extent and recenter.\n+ *\n+ * Parameters:\n+ * options - {Object}\n+ * \n+ * Allowed Options:\n+ * restricted - {Boolean} True to zoom to restricted extent if it is \n+ * set. Defaults to true.\n */\n- FOLDER_PARAMS: {\n- tileColumnsPerFolder: 30,\n- tileRowsPerFolder: 30,\n- format: 'png',\n- querystring: null\n+ zoomToMaxExtent: function(options) {\n+ //restricted is true by default\n+ var restricted = (options) ? options.restricted : true;\n+\n+ var maxExtent = this.getMaxExtent({\n+ 'restricted': restricted\n+ });\n+ this.zoomToExtent(maxExtent);\n },\n \n /** \n- * Property: defaultSize\n- * {<OpenLayers.Size>} Tile size as produced by MapGuide server\n- **/\n- defaultSize: new OpenLayers.Size(300, 300),\n+ * APIMethod: zoomToScale\n+ * Zoom to a specified scale \n+ * \n+ * Parameters:\n+ * scale - {float}\n+ * closest - {Boolean} Find the zoom level that most closely fits the \n+ * specified scale. Note that this may result in a zoom that does \n+ * not exactly contain the entire extent.\n+ * Default is false.\n+ * \n+ */\n+ zoomToScale: function(scale, closest) {\n+ var res = OpenLayers.Util.getResolutionFromScale(scale,\n+ this.baseLayer.units);\n \n- /** \n- * Property: tileOriginCorner\n- * {String} MapGuide tile server uses top-left as tile origin\n- **/\n- tileOriginCorner: \"tl\",\n+ var halfWDeg = (this.size.w * res) / 2;\n+ var halfHDeg = (this.size.h * res) / 2;\n+ var center = this.getCachedCenter();\n+\n+ var extent = new OpenLayers.Bounds(center.lon - halfWDeg,\n+ center.lat - halfHDeg,\n+ center.lon + halfWDeg,\n+ center.lat + halfHDeg);\n+ this.zoomToExtent(extent, closest);\n+ },\n+\n+ /********************************************************/\n+ /* */\n+ /* Translation Functions */\n+ /* */\n+ /* The following functions translate between */\n+ /* LonLat, LayerPx, and ViewPortPx */\n+ /* */\n+ /********************************************************/\n+\n+ //\n+ // TRANSLATION: LonLat <-> ViewPortPx\n+ //\n \n /**\n- * Constructor: OpenLayers.Layer.MapGuide\n- * Create a new Mapguide layer, either tiled or untiled. \n- *\n- * For tiled layers, the 'groupName' and 'mapDefinition' values \n- * must be specified as parameters in the constructor.\n- *\n- * For untiled base layers, specify either combination of 'mapName' and\n- * 'session', or 'mapDefinition' and 'locale'. \n- *\n- * For older versions of MapGuide and overlay layers, set useAsyncOverlay \n- * to false and in this case mapName and session are required parameters \n- * for the constructor.\n- *\n- * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion \n- * factor that are different than the defaults used in OpenLayers, \n- * so these must be adjusted accordingly in your application. \n- * See the MapGuide example for how to set these values for MGOS.\n- *\n+ * Method: getLonLatFromViewPortPx\n+ * \n * Parameters:\n- * name - {String} Name of the layer displayed in the interface\n- * url - {String} Location of the MapGuide mapagent executable\n- * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)\n- * params - {Object} hashtable of additional parameters to use. Some\n- * parameters may require additional code on the server. The ones that\n- * you may want to use are: \n- * - mapDefinition - {String} The MapGuide resource definition\n- * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)\n- * - locale - Locale setting \n- * (for untiled overlays layers only)\n- * - mapName - {String} Name of the map as stored in the MapGuide session.\n- * (for untiled layers with a session parameter only)\n- * - session - { String} MapGuide session ID \n- * (for untiled overlays layers only)\n- * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only\n- * - format - Image format to be returned (for untiled overlay layers only)\n- * - showLayers - {String} A comma separated list of GUID's for the\n- * layers to display eg: 'cvc-xcv34,453-345-345sdf'.\n- * - hideLayers - {String} A comma separated list of GUID's for the\n- * layers to hide eg: 'cvc-xcv34,453-345-345sdf'.\n- * - showGroups - {String} A comma separated list of GUID's for the\n- * groups to display eg: 'cvc-xcv34,453-345-345sdf'.\n- * - hideGroups - {String} A comma separated list of GUID's for the\n- * groups to hide eg: 'cvc-xcv34,453-345-345sdf'\n- * - selectionXml - {String} A selection xml string Some server plumbing\n- * is required to read such a value.\n- * options - {Object} Hashtable of extra options to tag onto the layer; \n- * will vary depending if tiled or untiled maps are being requested\n+ * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view \n+ * port <OpenLayers.Pixel>, translated into lon/lat\n+ * by the current base layer.\n */\n- initialize: function(name, url, params, options) {\n-\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n-\n- // unless explicitly set in options, if the layer is transparent, \n- // it will be an overlay\n- if (options == null || options.isBaseLayer == null) {\n- this.isBaseLayer = ((this.transparent != \"true\") &&\n- (this.transparent != true));\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.baseLayer != null) {\n+ lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);\n }\n+ return lonlat;\n+ },\n \n- if (options && options.useOverlay != null) {\n- this.useOverlay = options.useOverlay;\n+ /**\n+ * APIMethod: getViewPortPxFromLonLat\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in \n+ * <OpenLayers.LonLat>, translated into view port \n+ * pixels by the current base layer.\n+ */\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var px = null;\n+ if (this.baseLayer != null) {\n+ px = this.baseLayer.getViewPortPxFromLonLat(lonlat);\n }\n+ return px;\n+ },\n \n- //initialize for untiled layers\n- if (this.singleTile) {\n- if (this.useOverlay) {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.OVERLAY_PARAMS\n- );\n- if (!this.useAsyncOverlay) {\n- this.params.version = \"1.0.0\";\n- }\n- } else {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.SINGLE_TILE_PARAMS\n- );\n- }\n- } else {\n- //initialize for tiled layers\n- if (this.useHttpTile) {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.FOLDER_PARAMS\n- );\n- } else {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.TILE_PARAMS\n- );\n- }\n- this.setTileSize(this.defaultSize);\n+ /**\n+ * Method: getZoomTargetCenter\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen\n+ * resolution - {Float} The resolution we want to get the center for\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} The location of the map center after the\n+ * transformation described by the origin xy and the target resolution.\n+ */\n+ getZoomTargetCenter: function(xy, resolution) {\n+ var lonlat = null,\n+ size = this.getSize(),\n+ deltaX = size.w / 2 - xy.x,\n+ deltaY = xy.y - size.h / 2,\n+ zoomPoint = this.getLonLatFromPixel(xy);\n+ if (zoomPoint) {\n+ lonlat = new OpenLayers.LonLat(\n+ zoomPoint.lon + deltaX * resolution,\n+ zoomPoint.lat + deltaY * resolution\n+ );\n }\n+ return lonlat;\n },\n \n+ //\n+ // CONVENIENCE TRANSLATION FUNCTIONS FOR API\n+ //\n+\n /**\n- * Method: clone\n- * Create a clone of this layer\n+ * APIMethod: getLonLatFromPixel\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with\n+ * a 'x' and 'y' properties.\n *\n * Returns:\n- * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given\n+ * OpenLayers.Pixel, translated into lon/lat by the \n+ * current base layer\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.MapGuide(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ getLonLatFromPixel: function(px) {\n+ return this.getLonLatFromViewPortPx(px);\n+ },\n \n- return obj;\n+ /**\n+ * APIMethod: getPixelFromLonLat\n+ * Returns a pixel location given a map location. The map location is\n+ * translated to an integer pixel location (in viewport pixel\n+ * coordinates) by the current base layer.\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>} A map location.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the \n+ * <OpenLayers.LonLat> translated into view port pixels by the current\n+ * base layer.\n+ */\n+ getPixelFromLonLat: function(lonlat) {\n+ var px = this.getViewPortPxFromLonLat(lonlat);\n+ px.x = Math.round(px.x);\n+ px.y = Math.round(px.y);\n+ return px;\n },\n \n /**\n- * Method: getURL\n- * Return a query string for this layer\n- *\n+ * Method: getGeodesicPixelSize\n+ * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox \n- * for the request\n- *\n+ * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If\n+ * not provided, the center pixel of the map viewport will be used.\n+ * \n * Returns:\n- * {String} A string with the layer's url and parameters and also \n- * the passed-in bounds and appropriate tile size specified \n- * as parameters.\n+ * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.\n */\n- getURL: function(bounds) {\n- var url;\n- var center = bounds.getCenterLonLat();\n- var mapSize = this.map.getSize();\n-\n- if (this.singleTile) {\n- //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with\n- //dynamic map parameters\n- var params = {\n- setdisplaydpi: OpenLayers.DOTS_PER_INCH,\n- setdisplayheight: mapSize.h * this.ratio,\n- setdisplaywidth: mapSize.w * this.ratio,\n- setviewcenterx: center.lon,\n- setviewcentery: center.lat,\n- setviewscale: this.map.getScale()\n- };\n+ getGeodesicPixelSize: function(px) {\n+ var lonlat = px ? this.getLonLatFromPixel(px) : (\n+ this.getCachedCenter() || new OpenLayers.LonLat(0, 0));\n+ var res = this.getResolution();\n+ var left = lonlat.add(-res / 2, 0);\n+ var right = lonlat.add(res / 2, 0);\n+ var bottom = lonlat.add(0, -res / 2);\n+ var top = lonlat.add(0, res / 2);\n+ var dest = new OpenLayers.Projection(\"EPSG:4326\");\n+ var source = this.getProjectionObject() || dest;\n+ if (!source.equals(dest)) {\n+ left.transform(source, dest);\n+ right.transform(source, dest);\n+ bottom.transform(source, dest);\n+ top.transform(source, dest);\n+ }\n \n- if (this.useOverlay && !this.useAsyncOverlay) {\n- //first we need to call GETVISIBLEMAPEXTENT to set the extent\n- var getVisParams = {};\n- getVisParams = OpenLayers.Util.extend(getVisParams, params);\n- getVisParams.operation = \"GETVISIBLEMAPEXTENT\";\n- getVisParams.version = \"1.0.0\";\n- getVisParams.session = this.params.session;\n- getVisParams.mapName = this.params.mapName;\n- getVisParams.format = 'text/xml';\n- url = this.getFullRequestString(getVisParams);\n+ return new OpenLayers.Size(\n+ OpenLayers.Util.distVincenty(left, right),\n+ OpenLayers.Util.distVincenty(bottom, top)\n+ );\n+ },\n \n- OpenLayers.Request.GET({\n- url: url,\n- async: false\n- });\n- }\n- //construct the full URL\n- url = this.getFullRequestString(params);\n- } else {\n \n- //tiled version\n- var currentRes = this.map.getResolution();\n- var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);\n- colidx = Math.round(colidx / this.tileSize.w);\n- var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);\n- rowidx = Math.round(rowidx / this.tileSize.h);\n \n- if (this.useHttpTile) {\n- url = this.getImageFilePath({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- });\n+ //\n+ // TRANSLATION: ViewPortPx <-> LayerPx\n+ //\n \n- } else {\n- url = this.getFullRequestString({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- });\n- }\n+ /**\n+ * APIMethod: getViewPortPxFromLayerPx\n+ * \n+ * Parameters:\n+ * layerPx - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel \n+ * coordinates\n+ */\n+ getViewPortPxFromLayerPx: function(layerPx) {\n+ var viewPortPx = null;\n+ if (layerPx != null) {\n+ var dX = this.layerContainerOriginPx.x;\n+ var dY = this.layerContainerOriginPx.y;\n+ viewPortPx = layerPx.add(dX, dY);\n }\n- return url;\n+ return viewPortPx;\n },\n \n /**\n- * Method: getFullRequestString\n- * getFullRequestString on MapGuide layers is special, because we \n- * do a regular expression replace on ',' in parameters to '+'.\n- * This is why it is subclassed here.\n- *\n+ * APIMethod: getLayerPxFromViewPortPx\n+ * \n * Parameters:\n- * altUrl - {String} Alternative base URL to use.\n- *\n+ * viewPortPx - {<OpenLayers.Pixel>}\n+ * \n * Returns:\n- * {String} A string with the layer's url appropriately encoded for MapGuide\n+ * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel \n+ * coordinates\n */\n- getFullRequestString: function(newParams, altUrl) {\n- // use layer's url unless altUrl passed in\n- var url = (altUrl == null) ? this.url : altUrl;\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will randomly select one of them in order\n- // to evenly distribute requests to different urls.\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)];\n- }\n- // requestString always starts with url\n- var requestString = url;\n-\n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- // ignore parameters that are already in the url search string\n- var urlParams = OpenLayers.Util.upperCaseObject(\n- OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n+ getLayerPxFromViewPortPx: function(viewPortPx) {\n+ var layerPx = null;\n+ if (viewPortPx != null) {\n+ var dX = -this.layerContainerOriginPx.x;\n+ var dY = -this.layerContainerOriginPx.y;\n+ layerPx = viewPortPx.add(dX, dY);\n+ if (isNaN(layerPx.x) || isNaN(layerPx.y)) {\n+ layerPx = null;\n }\n }\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ return layerPx;\n+ },\n \n- /* MapGuide needs '+' seperating things like bounds/height/width.\n- Since typically this is URL encoded, we use a slight hack: we\n- depend on the list-like functionality of getParameterString to\n- leave ',' only in the case of list items (since otherwise it is\n- encoded) then do a regular expression replace on the , characters\n- to '+' */\n- paramsString = paramsString.replace(/,/g, \"+\");\n+ //\n+ // TRANSLATION: LonLat <-> LayerPx\n+ //\n \n- if (paramsString != \"\") {\n- var lastServerChar = url.charAt(url.length - 1);\n- if ((lastServerChar == \"&\") || (lastServerChar == \"?\")) {\n- requestString += paramsString;\n- } else {\n- if (url.indexOf('?') == -1) {\n- //serverPath has no ? -- add one\n- requestString += '?' + paramsString;\n- } else {\n- //serverPath contains ?, so must already have paramsString at the end\n- requestString += '&' + paramsString;\n- }\n- }\n- }\n- return requestString;\n+ /**\n+ * Method: getLonLatFromLayerPx\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>}\n+ */\n+ getLonLatFromLayerPx: function(px) {\n+ //adjust for displacement of layerContainerDiv\n+ px = this.getViewPortPxFromLayerPx(px);\n+ return this.getLonLatFromViewPortPx(px);\n },\n \n- /** \n- * Method: getImageFilePath\n- * special handler to request mapguide tiles from an http exposed tilecache \n- *\n+ /**\n+ * APIMethod: getLayerPxFromLonLat\n+ * \n * Parameters:\n- * altUrl - {String} Alternative base URL to use.\n+ * lonlat - {<OpenLayers.LonLat>} lonlat\n *\n * Returns:\n- * {String} A string with the url for the tile image\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in \n+ * <OpenLayers.LonLat>, translated into layer pixels \n+ * by the current base layer\n */\n- getImageFilePath: function(newParams, altUrl) {\n- // use layer's url unless altUrl passed in\n- var url = (altUrl == null) ? this.url : altUrl;\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will randomly select one of them in order\n- // to evenly distribute requests to different urls.\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)];\n- }\n- // requestString always starts with url\n- var requestString = url;\n-\n- var tileRowGroup = \"\";\n- var tileColGroup = \"\";\n+ getLayerPxFromLonLat: function(lonlat) {\n+ //adjust for displacement of layerContainerDiv\n+ var px = this.getPixelFromLonLat(lonlat);\n+ return this.getLayerPxFromViewPortPx(px);\n+ },\n \n- if (newParams.tilerow < 0) {\n- tileRowGroup = '-';\n- }\n+ /**\n+ * Method: applyTransform\n+ * Applies the given transform to the <layerContainerDiv>. This method has\n+ * a 2-stage fallback from translate3d/scale3d via translate/scale to plain\n+ * style.left/style.top, in which case no scaling is supported.\n+ *\n+ * Parameters:\n+ * x - {Number} x parameter for the translation. Defaults to the x value of\n+ * the map's <layerContainerOriginPx>\n+ * y - {Number} y parameter for the translation. Defaults to the y value of\n+ * the map's <layerContainerOriginPx>\n+ * scale - {Number} scale. Defaults to 1 if not provided.\n+ */\n+ applyTransform: function(x, y, scale) {\n+ scale = scale || 1;\n+ var origin = this.layerContainerOriginPx,\n+ needTransform = scale !== 1;\n+ x = x || origin.x;\n+ y = y || origin.y;\n \n- if (newParams.tilerow == 0) {\n- tileRowGroup += '0';\n- } else {\n- tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;\n- }\n+ var style = this.layerContainerDiv.style,\n+ transform = this.applyTransform.transform,\n+ template = this.applyTransform.template;\n \n- if (newParams.tilecol < 0) {\n- tileColGroup = '-';\n+ if (transform === undefined) {\n+ transform = OpenLayers.Util.vendorPrefix.style('transform');\n+ this.applyTransform.transform = transform;\n+ if (transform) {\n+ // Try translate3d, but only if the viewPortDiv has a transform\n+ // defined in a stylesheet\n+ var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,\n+ OpenLayers.Util.vendorPrefix.css('transform'));\n+ if (!computedStyle || computedStyle !== 'none') {\n+ template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];\n+ style[transform] = [template[0], '0,0', template[1]].join('');\n+ }\n+ // If no transform is defined in the stylesheet or translate3d\n+ // does not stick, use translate and scale\n+ if (!template || !~style[transform].indexOf(template[0])) {\n+ template = ['translate(', ') ', 'scale(', ')'];\n+ }\n+ this.applyTransform.template = template;\n+ }\n }\n \n- if (newParams.tilecol == 0) {\n- tileColGroup += '0';\n+ // If we do 3d transforms, we always want to use them. If we do 2d\n+ // transforms, we only use them when we need to.\n+ if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {\n+ // Our 2d transforms are combined with style.left and style.top, so\n+ // adjust x and y values and set the origin as left and top\n+ if (needTransform === true && template[0] === 'translate(') {\n+ x -= origin.x;\n+ y -= origin.y;\n+ style.left = origin.x + 'px';\n+ style.top = origin.y + 'px';\n+ }\n+ style[transform] = [\n+ template[0], x, 'px,', y, 'px', template[1],\n+ template[2], scale, ',', scale, template[3]\n+ ].join('');\n } else {\n- tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;\n- }\n-\n- var tilePath = '/S' + Math.floor(newParams.scaleindex) +\n- '/' + this.params.basemaplayergroupname +\n- '/R' + tileRowGroup +\n- '/C' + tileColGroup +\n- '/' + (newParams.tilerow % this.params.tileRowsPerFolder) +\n- '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) +\n- '.' + this.params.format;\n-\n- if (this.params.querystring) {\n- tilePath += \"?\" + this.params.querystring;\n+ style.left = x + 'px';\n+ style.top = y + 'px';\n+ // We previously might have had needTransform, so remove transform\n+ if (transform !== null) {\n+ style[transform] = '';\n+ }\n }\n-\n- requestString += tilePath;\n- return requestString;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.MapGuide\"\n+ CLASS_NAME: \"OpenLayers.Map\"\n });\n+\n+/**\n+ * Constant: TILE_WIDTH\n+ * {Integer} 256 Default tile width (unless otherwise specified)\n+ */\n+OpenLayers.Map.TILE_WIDTH = 256;\n+/**\n+ * Constant: TILE_HEIGHT\n+ * {Integer} 256 Default tile height (unless otherwise specified)\n+ */\n+OpenLayers.Map.TILE_HEIGHT = 256;\n /* ======================================================================\n- OpenLayers/Layer/Vector.js\n+ OpenLayers/Layer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Renderer.js\n- * @requires OpenLayers/StyleMap.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Map.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Vector\n- * Instances of OpenLayers.Layer.Vector are used to render vector data from\n- * a variety of sources. Create a new vector layer with the\n- * <OpenLayers.Layer.Vector> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer>\n+ * Class: OpenLayers.Layer\n */\n-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+OpenLayers.Layer = OpenLayers.Class({\n+\n+ /**\n+ * APIProperty: id\n+ * {String}\n+ */\n+ id: null,\n+\n+ /** \n+ * APIProperty: name\n+ * {String}\n+ */\n+ name: null,\n+\n+ /** \n+ * APIProperty: div\n+ * {DOMElement}\n+ */\n+ div: null,\n+\n+ /**\n+ * APIProperty: opacity\n+ * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n+ * is 1.\n+ */\n+ opacity: 1,\n+\n+ /**\n+ * APIProperty: alwaysInRange\n+ * {Boolean} If a layer's display should not be scale-based, this should \n+ * be set to true. This will cause the layer, as an overlay, to always \n+ * be 'active', by always returning true from the calculateInRange() \n+ * function. \n+ * \n+ * If not explicitly specified for a layer, its value will be \n+ * determined on startup in initResolutions() based on whether or not \n+ * any scale-specific properties have been set as options on the \n+ * layer. If no scale-specific options have been set on the layer, we \n+ * assume that it should always be in range.\n+ * \n+ * See #987 for more info.\n+ */\n+ alwaysInRange: null,\n+\n+ /**\n+ * Constant: RESOLUTION_PROPERTIES\n+ * {Array} The properties that are used for calculating resolutions\n+ * information.\n+ */\n+ RESOLUTION_PROPERTIES: [\n+ 'scales', 'resolutions',\n+ 'maxScale', 'minScale',\n+ 'maxResolution', 'minResolution',\n+ 'numZoomLevels', 'maxZoomLevel'\n+ ],\n \n /**\n * APIProperty: events\n * {<OpenLayers.Events>}\n *\n * Register a listener for a particular event with the following syntax:\n * (code)\n@@ -36623,3647 +25529,3286 @@\n * Listeners will be called with a reference to an event object. The\n * properties of this event depends on exactly what happened.\n *\n * All event objects have at least the following properties:\n * object - {Object} A reference to layer.events.object.\n * element - {DOMElement} A reference to layer.events.element.\n *\n- * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n- * beforefeatureadded - Triggered before a feature is added. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be added. To stop the feature from being added, a\n- * listener should return false.\n- * beforefeaturesadded - Triggered before an array of features is added.\n- * Listeners will receive an object with a *features* property\n- * referencing the feature to be added. To stop the features from\n- * being added, a listener should return false.\n- * featureadded - Triggered after a feature is added. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the added feature.\n- * featuresadded - Triggered after features are added. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of added features.\n- * beforefeatureremoved - Triggered before a feature is removed. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be removed.\n- * beforefeaturesremoved - Triggered before multiple features are removed. \n- * Listeners will receive an object with a *features* property\n- * referencing the features to be removed.\n- * featureremoved - Triggerd after a feature is removed. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the removed feature.\n- * featuresremoved - Triggered after features are removed. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of removed features.\n- * beforefeatureselected - Triggered before a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be selected. To stop the feature from being selectd, a\n- * listener should return false.\n- * featureselected - Triggered after a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * selected feature.\n- * featureunselected - Triggered after a feature is unselected.\n- * Listeners will receive an object with a *feature* property\n- * referencing the unselected feature.\n- * beforefeaturemodified - Triggered when a feature is selected to \n- * be modified. Listeners will receive an object with a *feature* \n- * property referencing the selected feature.\n- * featuremodified - Triggered when a feature has been modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * afterfeaturemodified - Triggered when a feature is finished being modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * vertexmodified - Triggered when a vertex within any feature geometry\n- * has been modified. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * modification.\n- * vertexremoved - Triggered when a vertex within any feature geometry\n- * has been deleted. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * removal.\n- * sketchstarted - Triggered when a feature sketch bound for this layer\n- * is started. Listeners will receive an object with a *feature*\n- * property referencing the new sketch feature and a *vertex* property\n- * referencing the creation point.\n- * sketchmodified - Triggered when a feature sketch bound for this layer\n- * is modified. Listeners will receive an object with a *vertex*\n- * property referencing the modified vertex and a *feature* property\n- * referencing the sketch feature.\n- * sketchcomplete - Triggered when a feature sketch bound for this layer\n- * is complete. Listeners will receive an object with a *feature*\n- * property referencing the sketch feature. By returning false, a\n- * listener can stop the sketch feature from being added to the layer.\n- * refresh - Triggered when something wants a strategy to ask the protocol\n- * for a new set of features.\n+ * Supported map event types:\n+ * loadstart - Triggered when layer loading starts. When using a Vector \n+ * layer with a Fixed or BBOX strategy, the event object includes \n+ * a *filter* property holding the OpenLayers.Filter used when \n+ * calling read on the protocol.\n+ * loadend - Triggered when layer loading ends. When using a Vector layer\n+ * with a Fixed or BBOX strategy, the event object includes a \n+ * *response* property holding an OpenLayers.Protocol.Response object.\n+ * visibilitychanged - Triggered when the layer's visibility property is\n+ * changed, e.g. by turning the layer on or off in the layer switcher.\n+ * Note that the actual visibility of the layer can also change if it\n+ * gets out of range (see <calculateInRange>). If you also want to catch\n+ * these cases, register for the map's 'changelayer' event instead.\n+ * move - Triggered when layer moves (triggered with every mousemove\n+ * during a drag).\n+ * moveend - Triggered when layer is done moving, object passed as\n+ * argument has a zoomChanged boolean property which tells that the\n+ * zoom has changed.\n+ * added - Triggered after the layer is added to a map. Listeners will\n+ * receive an object with a *map* property referencing the map and a\n+ * *layer* property referencing the layer.\n+ * removed - Triggered after the layer is removed from the map. Listeners\n+ * will receive an object with a *map* property referencing the map and\n+ * a *layer* property referencing the layer.\n+ */\n+ events: null,\n+\n+ /**\n+ * APIProperty: map\n+ * {<OpenLayers.Map>} This variable is set when the layer is added to \n+ * the map, via the accessor function setMap().\n */\n+ map: null,\n \n /**\n * APIProperty: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is false. Set this property\n- * in the layer options.\n+ * {Boolean} Whether or not the layer is a base layer. This should be set \n+ * individually by all subclasses. Default is false\n */\n isBaseLayer: false,\n \n- /** \n- * APIProperty: isFixed\n- * {Boolean} Whether the layer remains in one place while dragging the\n- * map. Note that setting this to true will move the layer to the bottom\n- * of the layer stack.\n+ /**\n+ * Property: alpha\n+ * {Boolean} The layer's images have an alpha channel. Default is false.\n */\n- isFixed: false,\n+ alpha: false,\n \n /** \n- * APIProperty: features\n- * {Array(<OpenLayers.Feature.Vector>)} \n+ * APIProperty: displayInLayerSwitcher\n+ * {Boolean} Display the layer's name in the layer switcher. Default is\n+ * true.\n */\n- features: null,\n+ displayInLayerSwitcher: true,\n+\n+ /**\n+ * APIProperty: visibility\n+ * {Boolean} The layer should be displayed in the map. Default is true.\n+ */\n+ visibility: true,\n+\n+ /**\n+ * APIProperty: attribution\n+ * {String} Attribution string, displayed when an \n+ * <OpenLayers.Control.Attribution> has been added to the map.\n+ */\n+ attribution: null,\n \n /** \n- * Property: filter\n- * {<OpenLayers.Filter>} The filter set in this layer,\n- * a strategy launching read requests can combined\n- * this filter with its own filter.\n+ * Property: inRange\n+ * {Boolean} The current map resolution is within the layer's min/max \n+ * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom \n+ * changes.\n */\n- filter: null,\n+ inRange: false,\n+\n+ /**\n+ * Propery: imageSize\n+ * {<OpenLayers.Size>} For layers with a gutter, the image is larger than \n+ * the tile by twice the gutter in each dimension.\n+ */\n+ imageSize: null,\n+\n+ // OPTIONS\n \n /** \n- * Property: selectedFeatures\n- * {Array(<OpenLayers.Feature.Vector>)} \n+ * Property: options\n+ * {Object} An optional object whose properties will be set on the layer.\n+ * Any of the layer properties can be set as a property of the options\n+ * object and sent to the constructor when the layer is created.\n */\n- selectedFeatures: null,\n+ options: null,\n \n /**\n- * Property: unrenderedFeatures\n- * {Object} hash of features, keyed by feature.id, that the renderer\n- * failed to draw\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n */\n- unrenderedFeatures: null,\n+ eventListeners: null,\n \n /**\n- * APIProperty: reportError\n- * {Boolean} report friendly error message when loading of renderer\n- * fails.\n+ * APIProperty: gutter\n+ * {Integer} Determines the width (in pixels) of the gutter around image\n+ * tiles to ignore. By setting this property to a non-zero value,\n+ * images will be requested that are wider and taller than the tile\n+ * size by a value of 2 x gutter. This allows artifacts of rendering\n+ * at tile edges to be ignored. Set a gutter value that is equal to\n+ * half the size of the widest symbol that needs to be displayed.\n+ * Defaults to zero. Non-tiled layers always have zero gutter.\n */\n- reportError: true,\n+ gutter: 0,\n \n- /** \n- * APIProperty: style\n- * {Object} Default style for the layer\n+ /**\n+ * APIProperty: projection\n+ * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.\n+ * Can be set in the layer options. If not specified in the layer options,\n+ * it is set to the default projection specified in the map,\n+ * when the layer is added to the map.\n+ * Projection along with default maxExtent and resolutions\n+ * are set automatically with commercial baselayers in EPSG:3857,\n+ * such as Google, Bing and OpenStreetMap, and do not need to be specified.\n+ * Otherwise, if specifying projection, also set maxExtent,\n+ * maxResolution or resolutions as appropriate.\n+ * When using vector layers with strategies, layer projection should be set\n+ * to the projection of the source data if that is different from the map default.\n+ * \n+ * Can be either a string or an <OpenLayers.Projection> object;\n+ * if a string is passed, will be converted to an object when\n+ * the layer is added to the map.\n+ * \n */\n- style: null,\n+ projection: null,\n \n /**\n- * Property: styleMap\n- * {<OpenLayers.StyleMap>}\n+ * APIProperty: units\n+ * {String} The layer map units. Defaults to null. Possible values\n+ * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.\n+ * Normally taken from the projection.\n+ * Only required if both map and layers do not define a projection,\n+ * or if they define a projection which does not define units.\n */\n- styleMap: null,\n+ units: null,\n \n /**\n- * Property: strategies\n- * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n+ * APIProperty: scales\n+ * {Array} An array of map scales in descending order. The values in the\n+ * array correspond to the map scale denominator. Note that these\n+ * values only make sense if the display (monitor) resolution of the\n+ * client is correctly guessed by whomever is configuring the\n+ * application. In addition, the units property must also be set.\n+ * Use <resolutions> instead wherever possible.\n */\n- strategies: null,\n+ scales: null,\n \n /**\n- * Property: protocol\n- * {<OpenLayers.Protocol>} Optional protocol for the layer.\n+ * APIProperty: resolutions\n+ * {Array} A list of map resolutions (map units per pixel) in descending\n+ * order. If this is not set in the layer constructor, it will be set\n+ * based on other resolution related properties (maxExtent,\n+ * maxResolution, maxScale, etc.).\n */\n- protocol: null,\n+ resolutions: null,\n \n /**\n- * Property: renderers\n- * {Array(String)} List of supported Renderer classes. Add to this list to\n- * add support for additional renderers. This list is ordered:\n- * the first renderer which returns true for the 'supported()'\n- * method will be used, if not defined in the 'renderer' option.\n+ * APIProperty: maxExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The maximum extent for the layer. Defaults to null.\n+ * \n+ * The center of these bounds will not stray outside\n+ * of the viewport extent during panning. In addition, if\n+ * <displayOutsideMaxExtent> is set to false, data will not be\n+ * requested that falls completely outside of these bounds.\n */\n- renderers: ['SVG', 'VML', 'Canvas'],\n+ maxExtent: null,\n \n- /** \n- * Property: renderer\n- * {<OpenLayers.Renderer>}\n+ /**\n+ * APIProperty: minExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The minimum extent for the layer. Defaults to null.\n */\n- renderer: null,\n+ minExtent: null,\n \n /**\n- * APIProperty: rendererOptions\n- * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n- * supported options.\n+ * APIProperty: maxResolution\n+ * {Float} Default max is 360 deg / 256 px, which corresponds to\n+ * zoom level 0 on gmaps. Specify a different value in the layer \n+ * options if you are not using the default <OpenLayers.Map.tileSize>\n+ * and displaying the whole world.\n */\n- rendererOptions: null,\n+ maxResolution: null,\n \n- /** \n- * APIProperty: geometryType\n- * {String} geometryType allows you to limit the types of geometries this\n- * layer supports. This should be set to something like\n- * \"OpenLayers.Geometry.Point\" to limit types.\n+ /**\n+ * APIProperty: minResolution\n+ * {Float}\n */\n- geometryType: null,\n+ minResolution: null,\n \n- /** \n- * Property: drawn\n- * {Boolean} Whether the Vector Layer features have been drawn yet.\n+ /**\n+ * APIProperty: numZoomLevels\n+ * {Integer}\n */\n- drawn: false,\n+ numZoomLevels: null,\n \n- /** \n- * APIProperty: ratio\n- * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n+ /**\n+ * APIProperty: minScale\n+ * {Float}\n */\n- ratio: 1,\n+ minScale: null,\n \n /**\n- * Constructor: OpenLayers.Layer.Vector\n- * Create a new vector layer\n+ * APIProperty: maxScale\n+ * {Float}\n+ */\n+ maxScale: null,\n+\n+ /**\n+ * APIProperty: displayOutsideMaxExtent\n+ * {Boolean} Request map tiles that are completely outside of the max \n+ * extent for this layer. Defaults to false.\n+ */\n+ displayOutsideMaxExtent: false,\n+\n+ /**\n+ * APIProperty: wrapDateLine\n+ * {Boolean} Wraps the world at the international dateline, so the map can\n+ * be panned infinitely in longitudinal direction. Only use this on the\n+ * base layer, and only if the layer's maxExtent equals the world bounds.\n+ * #487 for more info. \n+ */\n+ wrapDateLine: false,\n+\n+ /**\n+ * Property: metadata\n+ * {Object} This object can be used to store additional information on a\n+ * layer object.\n+ */\n+ metadata: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer\n *\n * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector>} A new vector layer\n+ * name - {String} The layer name\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n \n- // allow user-set renderer, otherwise assign one\n- if (!this.renderer || !this.renderer.supported()) {\n- this.assignRenderer();\n- }\n+ this.metadata = {};\n \n- // if no valid renderer found, display error\n- if (!this.renderer || !this.renderer.supported()) {\n- this.renderer = null;\n- this.displayError();\n+ options = OpenLayers.Util.extend({}, options);\n+ // make sure we respect alwaysInRange if set on the prototype\n+ if (this.alwaysInRange != null) {\n+ options.alwaysInRange = this.alwaysInRange;\n }\n+ this.addOptions(options);\n \n- if (!this.styleMap) {\n- this.styleMap = new OpenLayers.StyleMap();\n- }\n+ this.name = name;\n \n- this.features = [];\n- this.selectedFeatures = [];\n- this.unrenderedFeatures = {};\n+ if (this.id == null) {\n \n- // Allow for custom layer behavior\n- if (this.strategies) {\n- for (var i = 0, len = this.strategies.length; i < len; i++) {\n- this.strategies[i].setLayer(this);\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.style.width = \"100%\";\n+ this.div.style.height = \"100%\";\n+ this.div.dir = \"ltr\";\n+\n+ this.events = new OpenLayers.Events(this, this.div);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n }\n- }\n \n+ }\n },\n \n /**\n- * APIMethod: destroy\n- * Destroy this layer\n+ * Method: destroy\n+ * Destroy is a destructor: this is to alleviate cyclic references which\n+ * the Javascript garbage cleaner can not take care of on its own.\n+ *\n+ * Parameters:\n+ * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n+ * been destroyed. Default is true.\n */\n- destroy: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoDestroy) {\n- strategy.destroy();\n- }\n- }\n- this.strategies = null;\n+ destroy: function(setNewBaseLayer) {\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true;\n }\n- if (this.protocol) {\n- if (this.protocol.autoDestroy) {\n- this.protocol.destroy();\n- }\n- this.protocol = null;\n+ if (this.map != null) {\n+ this.map.removeLayer(this, setNewBaseLayer);\n }\n- this.destroyFeatures();\n- this.features = null;\n- this.selectedFeatures = null;\n- this.unrenderedFeatures = null;\n- if (this.renderer) {\n- this.renderer.destroy();\n+ this.projection = null;\n+ this.map = null;\n+ this.name = null;\n+ this.div = null;\n+ this.options = null;\n+\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ }\n+ this.events.destroy();\n }\n- this.renderer = null;\n- this.geometryType = null;\n- this.drawn = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ this.eventListeners = null;\n+ this.events = null;\n },\n \n /**\n * Method: clone\n- * Create a clone of this layer.\n- * \n- * Note: Features of the layer are also cloned.\n+ *\n+ * Parameters:\n+ * obj - {<OpenLayers.Layer>} The layer to be cloned\n *\n * Returns:\n- * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n+ * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>\n */\n clone: function(obj) {\n \n if (obj == null) {\n- obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n+ obj = new OpenLayers.Layer(this.name, this.getOptions());\n }\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n- // copy/set any non-init, non-simple values here\n- var features = this.features;\n- var len = features.length;\n- var clonedFeatures = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- clonedFeatures[i] = features[i].clone();\n- }\n- obj.features = clonedFeatures;\n+ // a cloned layer should never have its map property set\n+ // because it has not been added to a map yet. \n+ obj.map = null;\n \n return obj;\n },\n \n /**\n- * Method: refresh\n- * Ask the layer to request features again and redraw them. Triggers\n- * the refresh event if the layer is in range and visible.\n+ * Method: getOptions\n+ * Extracts an object from the layer with the properties that were set as\n+ * options, but updates them with the values currently set on the\n+ * instance.\n+ * \n+ * Returns:\n+ * {Object} the <options> of the layer, representing the current state.\n+ */\n+ getOptions: function() {\n+ var options = {};\n+ for (var o in this.options) {\n+ options[o] = this[o];\n+ }\n+ return options;\n+ },\n+\n+ /** \n+ * APIMethod: setName\n+ * Sets the new layer name for this layer. Can trigger a changelayer event\n+ * on the map.\n *\n * Parameters:\n- * obj - {Object} Optional object with properties for any listener of\n- * the refresh event.\n+ * newName - {String} The new name.\n */\n- refresh: function(obj) {\n- if (this.calculateInRange() && this.visibility) {\n- this.events.triggerEvent(\"refresh\", obj);\n+ setName: function(newName) {\n+ if (newName != this.name) {\n+ this.name = newName;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"name\"\n+ });\n+ }\n }\n },\n \n- /** \n- * Method: assignRenderer\n- * Iterates through the available renderer implementations and selects \n- * and assigns the first one whose \"supported()\" function returns true.\n+ /**\n+ * APIMethod: addOptions\n+ * \n+ * Parameters:\n+ * newOptions - {Object}\n+ * reinitialize - {Boolean} If set to true, and if resolution options of the\n+ * current baseLayer were changed, the map will be recentered to make\n+ * sure that it is displayed with a valid resolution, and a\n+ * changebaselayer event will be triggered.\n */\n- assignRenderer: function() {\n- for (var i = 0, len = this.renderers.length; i < len; i++) {\n- var rendererClass = this.renderers[i];\n- var renderer = (typeof rendererClass == \"function\") ?\n- rendererClass :\n- OpenLayers.Renderer[rendererClass];\n- if (renderer && renderer.prototype.supported()) {\n- this.renderer = new renderer(this.div, this.rendererOptions);\n- break;\n+ addOptions: function(newOptions, reinitialize) {\n+\n+ if (this.options == null) {\n+ this.options = {};\n+ }\n+\n+ if (newOptions) {\n+ // make sure this.projection references a projection object\n+ if (typeof newOptions.projection == \"string\") {\n+ newOptions.projection = new OpenLayers.Projection(newOptions.projection);\n+ }\n+ if (newOptions.projection) {\n+ // get maxResolution, units and maxExtent from projection defaults if\n+ // they are not defined already\n+ OpenLayers.Util.applyDefaults(newOptions,\n+ OpenLayers.Projection.defaults[newOptions.projection.getCode()]);\n+ }\n+ // allow array for extents\n+ if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);\n+ }\n+ if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);\n+ }\n+ }\n+\n+ // update our copy for clone\n+ OpenLayers.Util.extend(this.options, newOptions);\n+\n+ // add new options to this\n+ OpenLayers.Util.extend(this, newOptions);\n+\n+ // get the units from the projection, if we have a projection\n+ // and it it has units\n+ if (this.projection && this.projection.getUnits()) {\n+ this.units = this.projection.getUnits();\n+ }\n+\n+ // re-initialize resolutions if necessary, i.e. if any of the\n+ // properties of the \"properties\" array defined below is set\n+ // in the new options\n+ if (this.map) {\n+ // store current resolution so we can try to restore it later\n+ var resolution = this.map.getResolution();\n+ var properties = this.RESOLUTION_PROPERTIES.concat(\n+ [\"projection\", \"units\", \"minExtent\", \"maxExtent\"]\n+ );\n+ for (var o in newOptions) {\n+ if (newOptions.hasOwnProperty(o) &&\n+ OpenLayers.Util.indexOf(properties, o) >= 0) {\n+\n+ this.initResolutions();\n+ if (reinitialize && this.map.baseLayer === this) {\n+ // update map position, and restore previous resolution\n+ this.map.setCenter(this.map.getCenter(),\n+ this.map.getZoomForResolution(resolution),\n+ false, true\n+ );\n+ // trigger a changebaselayer event to make sure that\n+ // all controls (especially\n+ // OpenLayers.Control.PanZoomBar) get notified of the\n+ // new options\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ });\n+ }\n+ break;\n+ }\n }\n }\n },\n \n- /** \n- * Method: displayError \n- * Let the user know their browser isn't supported.\n+ /**\n+ * APIMethod: onMapResize\n+ * This function can be implemented by subclasses\n */\n- displayError: function() {\n- if (this.reportError) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n- renderers: this.renderers.join('\\n')\n- }));\n+ onMapResize: function() {\n+ //this function can be implemented by subclasses \n+ },\n+\n+ /**\n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ *\n+ * Returns:\n+ * {Boolean} The layer was redrawn.\n+ */\n+ redraw: function() {\n+ var redrawn = false;\n+ if (this.map) {\n+\n+ // min/max Range may have changed\n+ this.inRange = this.calculateInRange();\n+\n+ // map's center might not yet be set\n+ var extent = this.getExtent();\n+\n+ if (extent && this.inRange && this.visibility) {\n+ var zoomChanged = true;\n+ this.moveTo(extent, zoomChanged, false);\n+ this.events.triggerEvent(\"moveend\", {\n+ \"zoomChanged\": zoomChanged\n+ });\n+ redrawn = true;\n+ }\n }\n+ return redrawn;\n },\n \n- /** \n+ /**\n+ * Method: moveTo\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n+ * do some init work in that case.\n+ * dragging - {Boolean}\n+ */\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ var display = this.visibility;\n+ if (!this.isBaseLayer) {\n+ display = display && this.inRange;\n+ }\n+ this.display(display);\n+ },\n+\n+ /**\n+ * Method: moveByPx\n+ * Move the layer based on pixel vector. To be implemented by subclasses.\n+ *\n+ * Parameters:\n+ * dx - {Number} The x coord of the displacement vector.\n+ * dy - {Number} The y coord of the displacement vector.\n+ */\n+ moveByPx: function(dx, dy) {},\n+\n+ /**\n * Method: setMap\n- * The layer has been added to the map. \n+ * Set the map property for the layer. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n * \n- * If there is no renderer set, the layer can't be used. Remove it.\n- * Otherwise, give the renderer a reference to the map and set its size.\n+ * Here we take care to bring over any of the necessary default \n+ * properties from the map. \n * \n * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * map - {<OpenLayers.Map>}\n */\n setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ if (this.map == null) {\n \n- if (!this.renderer) {\n- this.map.removeLayer(this);\n- } else {\n- this.renderer.map = this.map;\n+ this.map = map;\n \n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n+ // grab some essential layer data from the map if it hasn't already\n+ // been set\n+ this.maxExtent = this.maxExtent || this.map.maxExtent;\n+ this.minExtent = this.minExtent || this.map.minExtent;\n+\n+ this.projection = this.projection || this.map.projection;\n+ if (typeof this.projection == \"string\") {\n+ this.projection = new OpenLayers.Projection(this.projection);\n+ }\n+\n+ // Check the projection to see if we can get units -- if not, refer\n+ // to properties.\n+ this.units = this.projection.getUnits() ||\n+ this.units || this.map.units;\n+\n+ this.initResolutions();\n+\n+ if (!this.isBaseLayer) {\n+ this.inRange = this.calculateInRange();\n+ var show = ((this.visibility) && (this.inRange));\n+ this.div.style.display = show ? \"\" : \"none\";\n+ }\n+\n+ // deal with gutters\n+ this.setTileSize();\n }\n },\n \n /**\n * Method: afterAdd\n * Called at the end of the map.addLayer sequence. At this point, the map\n- * will have a base layer. Any autoActivate strategies will be\n- * activated here.\n+ * will have a base layer. To be overridden by subclasses.\n */\n- afterAdd: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.activate();\n- }\n- }\n- }\n- },\n+ afterAdd: function() {},\n \n /**\n- * Method: removeMap\n- * The layer has been removed from the map.\n- *\n+ * APIMethod: removeMap\n+ * Just as setMap() allows each layer the possibility to take a \n+ * personalized action on being added to the map, removeMap() allows\n+ * each layer to take a personalized action on being removed from it. \n+ * For now, this will be mostly unused, except for the EventPane layer,\n+ * which needs this hook so that it can remove the special invisible\n+ * pane. \n+ * \n * Parameters:\n * map - {<OpenLayers.Map>}\n */\n removeMap: function(map) {\n- this.drawn = false;\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.deactivate();\n- }\n- }\n- }\n+ //to be overridden by subclasses\n },\n \n /**\n- * Method: onMapResize\n- * Notify the renderer of the change in size. \n+ * APIMethod: getImageSize\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used\n+ * by subclasses that have to deal with different tile sizes at the\n+ * layer extent edges (e.g. Zoomify)\n * \n+ * Returns:\n+ * {<OpenLayers.Size>} The size that the image should be, taking into \n+ * account gutters.\n */\n- onMapResize: function() {\n- OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n-\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n+ getImageSize: function(bounds) {\n+ return (this.imageSize || this.tileSize);\n },\n \n /**\n- * Method: moveTo\n- * Reset the vector layer's div so that it once again is lined up with \n- * the map. Notify the renderer of the change of extent, and in the\n- * case of a change of zoom level (resolution), have the \n- * renderer redraw features.\n- * \n- * If the layer has not yet been drawn, cycle through the layer's \n- * features and draw each one.\n+ * APIMethod: setTileSize\n+ * Set the tile size based on the map size. This also sets layer.imageSize\n+ * or use by Tile.Image.\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * zoomChanged - {Boolean} \n- * dragging - {Boolean} \n+ * size - {<OpenLayers.Size>}\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- var coordSysUnchanged = true;\n- if (!dragging) {\n- this.renderer.root.style.visibility = 'hidden';\n-\n- var viewSize = this.map.getSize(),\n- viewWidth = viewSize.w,\n- viewHeight = viewSize.h,\n- offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n- offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n- offsetLeft += this.map.layerContainerOriginPx.x;\n- offsetLeft = -Math.round(offsetLeft);\n- offsetTop += this.map.layerContainerOriginPx.y;\n- offsetTop = -Math.round(offsetTop);\n-\n- this.div.style.left = offsetLeft + 'px';\n- this.div.style.top = offsetTop + 'px';\n-\n- var extent = this.map.getExtent().scale(this.ratio);\n- coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n-\n- this.renderer.root.style.visibility = 'visible';\n+ setTileSize: function(size) {\n+ var tileSize = (size) ? size :\n+ ((this.tileSize) ? this.tileSize :\n+ this.map.getTileSize());\n+ this.tileSize = tileSize;\n+ if (this.gutter) {\n+ // layers with gutters need non-null tile sizes\n+ //if(tileSize == null) {\n+ // OpenLayers.console.error(\"Error in layer.setMap() for \" +\n+ // this.name + \": layers with \" +\n+ // \"gutters need non-null tile sizes\");\n+ //}\n+ this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter),\n+ tileSize.h + (2 * this.gutter));\n+ }\n+ },\n \n- // Force a reflow on gecko based browsers to prevent jump/flicker.\n- // This seems to happen on only certain configurations; it was originally\n- // noticed in FF 2.0 and Linux.\n- if (OpenLayers.IS_GECKO === true) {\n- this.div.scrollLeft = this.div.scrollLeft;\n- }\n+ /**\n+ * APIMethod: getVisibility\n+ * \n+ * Returns:\n+ * {Boolean} The layer should be displayed (if in range).\n+ */\n+ getVisibility: function() {\n+ return this.visibility;\n+ },\n \n- if (!zoomChanged && coordSysUnchanged) {\n- for (var i in this.unrenderedFeatures) {\n- var feature = this.unrenderedFeatures[i];\n- this.drawFeature(feature);\n- }\n- }\n- }\n- if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n- this.drawn = true;\n- var feature;\n- for (var i = 0, len = this.features.length; i < len; i++) {\n- this.renderer.locked = (i !== (len - 1));\n- feature = this.features[i];\n- this.drawFeature(feature);\n+ /** \n+ * APIMethod: setVisibility\n+ * Set the visibility flag for the layer and hide/show & redraw \n+ * accordingly. Fire event unless otherwise specified\n+ * \n+ * Note that visibility is no longer simply whether or not the layer's\n+ * style.display is set to \"block\". Now we store a 'visibility' state \n+ * property on the layer class, this allows us to remember whether or \n+ * not we *desire* for a layer to be visible. In the case where the \n+ * map's resolution is out of the layer's range, this desire may be \n+ * subverted.\n+ * \n+ * Parameters:\n+ * visibility - {Boolean} Whether or not to display the layer (if in range)\n+ */\n+ setVisibility: function(visibility) {\n+ if (visibility != this.visibility) {\n+ this.visibility = visibility;\n+ this.display(visibility);\n+ this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"visibility\"\n+ });\n }\n+ this.events.triggerEvent(\"visibilitychanged\");\n }\n },\n \n /** \n * APIMethod: display\n- * Hide or show the Layer\n+ * Hide or show the Layer. This is designed to be used internally, and \n+ * is not generally the way to enable or disable the layer. For that,\n+ * use the setVisibility function instead..\n * \n * Parameters:\n * display - {Boolean}\n */\n display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- // we need to set the display style of the root in case it is attached\n- // to a foreign layer\n- var currentDisplay = this.div.style.display;\n- if (currentDisplay != this.renderer.root.style.display) {\n- this.renderer.root.style.display = currentDisplay;\n+ if (display != (this.div.style.display != \"none\")) {\n+ this.div.style.display = (display && this.calculateInRange()) ? \"block\" : \"none\";\n }\n },\n \n /**\n- * APIMethod: addFeatures\n- * Add Features to the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- * options - {Object}\n+ * APIMethod: calculateInRange\n+ * \n+ * Returns:\n+ * {Boolean} The layer is displayable at the current map's current\n+ * resolution. Note that if 'alwaysInRange' is true for the layer, \n+ * this function will always return true.\n */\n- addFeatures: function(features, options) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n+ calculateInRange: function() {\n+ var inRange = false;\n \n- var notify = !options || !options.silent;\n- if (notify) {\n- var event = {\n- features: features\n- };\n- var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n- if (ret === false) {\n- return;\n+ if (this.alwaysInRange) {\n+ inRange = true;\n+ } else {\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ inRange = ((resolution >= this.minResolution) &&\n+ (resolution <= this.maxResolution));\n }\n- features = event.features;\n }\n+ return inRange;\n+ },\n \n- // Track successfully added features for featuresadded event, since\n- // beforefeatureadded can veto single features.\n- var featuresAdded = [];\n- for (var i = 0, len = features.length; i < len; i++) {\n- if (i != (features.length - 1)) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n- var feature = features[i];\n-\n- if (this.geometryType &&\n- !(feature.geometry instanceof this.geometryType)) {\n- throw new TypeError('addFeatures: component should be an ' +\n- this.geometryType.prototype.CLASS_NAME);\n+ /** \n+ * APIMethod: setIsBaseLayer\n+ * \n+ * Parameters:\n+ * isBaseLayer - {Boolean}\n+ */\n+ setIsBaseLayer: function(isBaseLayer) {\n+ if (isBaseLayer != this.isBaseLayer) {\n+ this.isBaseLayer = isBaseLayer;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ });\n }\n+ }\n+ },\n \n- //give feature reference to its layer\n- feature.layer = this;\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /********************************************************/\n \n- if (!feature.style && this.style) {\n- feature.style = OpenLayers.Util.extend({}, this.style);\n- }\n+ /** \n+ * Method: initResolutions\n+ * This method's responsibility is to set up the 'resolutions' array \n+ * for the layer -- this array is what the layer will use to interface\n+ * between the zoom levels of the map and the resolution display \n+ * of the layer.\n+ * \n+ * The user has several options that determine how the array is set up.\n+ * \n+ * For a detailed explanation, see the following wiki from the \n+ * openlayers.org homepage:\n+ * http://trac.openlayers.org/wiki/SettingZoomLevels\n+ */\n+ initResolutions: function() {\n \n- if (notify) {\n- if (this.events.triggerEvent(\"beforefeatureadded\", {\n- feature: feature\n- }) === false) {\n- continue;\n- }\n- this.preFeatureInsert(feature);\n- }\n+ // ok we want resolutions, here's our strategy:\n+ //\n+ // 1. if resolutions are defined in the layer config, use them\n+ // 2. else, if scales are defined in the layer config then derive\n+ // resolutions from these scales\n+ // 3. else, attempt to calculate resolutions from maxResolution,\n+ // minResolution, numZoomLevels, maxZoomLevel set in the\n+ // layer config\n+ // 4. if we still don't have resolutions, and if resolutions\n+ // are defined in the same, use them\n+ // 5. else, if scales are defined in the map then derive\n+ // resolutions from these scales\n+ // 6. else, attempt to calculate resolutions from maxResolution,\n+ // minResolution, numZoomLevels, maxZoomLevel set in the\n+ // map\n+ // 7. hope for the best!\n \n- featuresAdded.push(feature);\n- this.features.push(feature);\n- this.drawFeature(feature);\n+ var i, len, p;\n+ var props = {},\n+ alwaysInRange = true;\n \n- if (notify) {\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- this.onFeatureInsert(feature);\n+ // get resolution data from layer config\n+ // (we also set alwaysInRange in the layer as appropriate)\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p];\n+ if (alwaysInRange && this.options[p]) {\n+ alwaysInRange = false;\n }\n }\n-\n- if (notify) {\n- this.events.triggerEvent(\"featuresadded\", {\n- features: featuresAdded\n- });\n+ if (this.options.alwaysInRange == null) {\n+ this.alwaysInRange = alwaysInRange;\n }\n- },\n \n-\n- /**\n- * APIMethod: removeFeatures\n- * Remove features from the layer. This erases any drawn features and\n- * removes them from the layer's control. The beforefeatureremoved\n- * and featureremoved events will be triggered for each feature. The\n- * featuresremoved event will be triggered after all features have\n- * been removed. To supress event triggering, use the silent option.\n- * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n- * removed.\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n- *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n- */\n- removeFeatures: function(features, options) {\n- if (!features || features.length === 0) {\n- return;\n- }\n- if (features === this.features) {\n- return this.removeAllFeatures(options);\n+ // if we don't have resolutions then attempt to derive them from scales\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales);\n }\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n+\n+ // if we still don't have resolutions then attempt to calculate them\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props);\n }\n- if (features === this.selectedFeatures) {\n- features = features.slice();\n+\n+ // if we couldn't calculate resolutions then we look at we have\n+ // in the map\n+ if (props.resolutions == null) {\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p] != null ?\n+ this.options[p] : this.map[p];\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales);\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props);\n+ }\n }\n \n- var notify = !options || !options.silent;\n+ // ok, we new need to set properties in the instance\n \n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n+ // get maxResolution from the config if it's defined there\n+ var maxResolution;\n+ if (this.options.maxResolution &&\n+ this.options.maxResolution !== \"auto\") {\n+ maxResolution = this.options.maxResolution;\n+ }\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.minScale, this.units);\n }\n \n- for (var i = features.length - 1; i >= 0; i--) {\n- // We remain locked so long as we're not at 0\n- // and the 'next' feature has a geometry. We do the geometry check\n- // because if all the features after the current one are 'null', we\n- // won't call eraseGeometry, so we break the 'renderer functions\n- // will always be called with locked=false *last*' rule. The end result\n- // is a possible gratiutious unlocking to save a loop through the rest \n- // of the list checking the remaining features every time. So long as\n- // null geoms are rare, this is probably okay. \n- if (i != 0 && features[i - 1].geometry) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n-\n- var feature = features[i];\n- delete this.unrenderedFeatures[feature.id];\n+ // get minResolution from the config if it's defined there\n+ var minResolution;\n+ if (this.options.minResolution &&\n+ this.options.minResolution !== \"auto\") {\n+ minResolution = this.options.minResolution;\n+ }\n+ if (this.options.maxScale) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.maxScale, this.units);\n+ }\n \n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n+ if (props.resolutions) {\n \n- this.features = OpenLayers.Util.removeItem(this.features, feature);\n- // feature has no layer at this point\n- feature.layer = null;\n+ //sort resolutions array descendingly\n+ props.resolutions.sort(function(a, b) {\n+ return (b - a);\n+ });\n \n- if (feature.geometry) {\n- this.renderer.eraseFeatures(feature);\n+ // if we still don't have a maxResolution get it from the\n+ // resolutions array\n+ if (!maxResolution) {\n+ maxResolution = props.resolutions[0];\n }\n \n- //in the case that this feature is one of the selected features, \n- // remove it from that array as well.\n- if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n- OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n+ // if we still don't have a minResolution get it from the\n+ // resolutions array\n+ if (!minResolution) {\n+ var lastIdx = props.resolutions.length - 1;\n+ minResolution = props.resolutions[lastIdx];\n }\n+ }\n \n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n+ this.resolutions = props.resolutions;\n+ if (this.resolutions) {\n+ len = this.resolutions.length;\n+ this.scales = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ this.scales[i] = OpenLayers.Util.getScaleFromResolution(\n+ this.resolutions[i], this.units);\n }\n+ this.numZoomLevels = len;\n }\n-\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n+ this.minResolution = minResolution;\n+ if (minResolution) {\n+ this.maxScale = OpenLayers.Util.getScaleFromResolution(\n+ minResolution, this.units);\n+ }\n+ this.maxResolution = maxResolution;\n+ if (maxResolution) {\n+ this.minScale = OpenLayers.Util.getScaleFromResolution(\n+ maxResolution, this.units);\n }\n },\n \n- /** \n- * APIMethod: removeAllFeatures\n- * Remove all features from the layer.\n+ /**\n+ * Method: resolutionsFromScales\n+ * Derive resolutions from scales.\n *\n * Parameters:\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n+ * scales - {Array(Number)} Scales\n *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n+ * Returns\n+ * {Array(Number)} Resolutions\n */\n- removeAllFeatures: function(options) {\n- var notify = !options || !options.silent;\n- var features = this.features;\n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n- }\n- var feature;\n- for (var i = features.length - 1; i >= 0; i--) {\n- feature = features[i];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n- feature.layer = null;\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n- }\n+ resolutionsFromScales: function(scales) {\n+ if (scales == null) {\n+ return;\n }\n- this.renderer.clear();\n- this.features = [];\n- this.unrenderedFeatures = {};\n- this.selectedFeatures = [];\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n+ var resolutions, i, len;\n+ len = scales.length;\n+ resolutions = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ resolutions[i] = OpenLayers.Util.getResolutionFromScale(\n+ scales[i], this.units);\n }\n+ return resolutions;\n },\n \n /**\n- * APIMethod: destroyFeatures\n- * Erase and destroy features on the layer.\n+ * Method: calculateResolutions\n+ * Calculate resolutions based on the provided properties.\n *\n * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n- * features to destroy. If not supplied, all features on the layer\n- * will be destroyed.\n- * options - {Object}\n+ * props - {Object} Properties\n+ *\n+ * Returns:\n+ * {Array({Number})} Array of resolutions.\n */\n- destroyFeatures: function(features, options) {\n- var all = (features == undefined); // evaluates to true if\n- // features is null\n- if (all) {\n- features = this.features;\n+ calculateResolutions: function(props) {\n+\n+ var viewSize, wRes, hRes;\n+\n+ // determine maxResolution\n+ var maxResolution = props.maxResolution;\n+ if (props.minScale != null) {\n+ maxResolution =\n+ OpenLayers.Util.getResolutionFromScale(props.minScale,\n+ this.units);\n+ } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.maxExtent.getWidth() / viewSize.w;\n+ hRes = this.maxExtent.getHeight() / viewSize.h;\n+ maxResolution = Math.max(wRes, hRes);\n }\n- if (features) {\n- this.removeFeatures(features, options);\n- for (var i = features.length - 1; i >= 0; i--) {\n- features[i].destroy();\n- }\n+\n+ // determine minResolution\n+ var minResolution = props.minResolution;\n+ if (props.maxScale != null) {\n+ minResolution =\n+ OpenLayers.Util.getResolutionFromScale(props.maxScale,\n+ this.units);\n+ } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.minExtent.getWidth() / viewSize.w;\n+ hRes = this.minExtent.getHeight() / viewSize.h;\n+ minResolution = Math.max(wRes, hRes);\n }\n- },\n \n- /**\n- * APIMethod: drawFeature\n- * Draw (or redraw) a feature on the layer. If the optional style argument\n- * is included, this style will be used. If no style is included, the\n- * feature's style will be used. If the feature doesn't have a style,\n- * the layer's style will be used.\n- * \n- * This function is not designed to be used when adding features to \n- * the layer (use addFeatures instead). It is meant to be used when\n- * the style of a feature has changed, or in some other way needs to \n- * visually updated *after* it has already been added to a layer. You\n- * must add the feature to the layer for most layer-related events to \n- * happen.\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {String | Object} Named render intent or full symbolizer object.\n- */\n- drawFeature: function(feature, style) {\n- // don't try to draw the feature with the renderer if the layer is not \n- // drawn itself\n- if (!this.drawn) {\n+ if (typeof maxResolution !== \"number\" &&\n+ typeof minResolution !== \"number\" &&\n+ this.maxExtent != null) {\n+ // maxResolution for default grid sets assumes that at zoom\n+ // level zero, the whole world fits on one tile.\n+ var tileSize = this.map.getTileSize();\n+ maxResolution = Math.max(\n+ this.maxExtent.getWidth() / tileSize.w,\n+ this.maxExtent.getHeight() / tileSize.h\n+ );\n+ }\n+\n+ // determine numZoomLevels\n+ var maxZoomLevel = props.maxZoomLevel;\n+ var numZoomLevels = props.numZoomLevels;\n+ if (typeof minResolution === \"number\" &&\n+ typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n+ var ratio = maxResolution / minResolution;\n+ numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;\n+ } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n+ numZoomLevels = maxZoomLevel + 1;\n+ }\n+\n+ // are we able to calculate resolutions?\n+ if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 ||\n+ (typeof maxResolution !== \"number\" &&\n+ typeof minResolution !== \"number\")) {\n return;\n }\n- if (typeof style != \"object\") {\n- if (!style && feature.state === OpenLayers.State.DELETE) {\n- style = \"delete\";\n- }\n- var renderIntent = style || feature.renderIntent;\n- style = feature.style || this.style;\n- if (!style) {\n- style = this.styleMap.createSymbolizer(feature, renderIntent);\n- }\n+\n+ // now we have numZoomLevels and at least one of maxResolution\n+ // or minResolution, we can populate the resolutions array\n+\n+ var resolutions = new Array(numZoomLevels);\n+ var base = 2;\n+ if (typeof minResolution == \"number\" &&\n+ typeof maxResolution == \"number\") {\n+ // if maxResolution and minResolution are set, we calculate\n+ // the base for exponential scaling that starts at\n+ // maxResolution and ends at minResolution in numZoomLevels\n+ // steps.\n+ base = Math.pow(\n+ (maxResolution / minResolution),\n+ (1 / (numZoomLevels - 1))\n+ );\n }\n \n- var drawn = this.renderer.drawFeature(feature, style);\n- //TODO remove the check for null when we get rid of Renderer.SVG\n- if (drawn === false || drawn === null) {\n- this.unrenderedFeatures[feature.id] = feature;\n+ var i;\n+ if (typeof maxResolution === \"number\") {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[i] = maxResolution / Math.pow(base, i);\n+ }\n } else {\n- delete this.unrenderedFeatures[feature.id];\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[numZoomLevels - 1 - i] =\n+ minResolution * Math.pow(base, i);\n+ }\n }\n- },\n \n- /**\n- * Method: eraseFeatures\n- * Erase features from the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- */\n- eraseFeatures: function(features) {\n- this.renderer.eraseFeatures(features);\n+ return resolutions;\n },\n \n /**\n- * Method: getFeatureFromEvent\n- * Given an event, return a feature if the event occurred over one.\n- * Otherwise, return null.\n- *\n- * Parameters:\n- * evt - {Event} \n- *\n+ * APIMethod: getResolution\n+ * \n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n+ * {Float} The currently selected resolution of the map, taken from the\n+ * resolutions array, indexed by current zoom level.\n */\n- getFeatureFromEvent: function(evt) {\n- if (!this.renderer) {\n- throw new Error('getFeatureFromEvent called on layer with no ' +\n- 'renderer. This usually means you destroyed a ' +\n- 'layer, but not some handler which is associated ' +\n- 'with it.');\n- }\n- var feature = null;\n- var featureId = this.renderer.getFeatureIdFromEvent(evt);\n- if (featureId) {\n- if (typeof featureId === \"string\") {\n- feature = this.getFeatureById(featureId);\n- } else {\n- feature = featureId;\n- }\n- }\n- return feature;\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom);\n },\n \n- /**\n- * APIMethod: getFeatureBy\n- * Given a property value, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * property - {String}\n- * value - {String}\n- *\n+ /** \n+ * APIMethod: getExtent\n+ * \n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * property value or null if there is no such feature.\n+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort.\n */\n- getFeatureBy: function(property, value) {\n- //TBD - would it be more efficient to use a hash for this.features?\n- var feature = null;\n- for (var i = 0, len = this.features.length; i < len; ++i) {\n- if (this.features[i][property] == value) {\n- feature = this.features[i];\n- break;\n- }\n- }\n- return feature;\n+ getExtent: function() {\n+ // just use stock map calculateBounds function -- passing no arguments\n+ // means it will user map's current center & resolution\n+ //\n+ return this.map.calculateBounds();\n },\n \n /**\n- * APIMethod: getFeatureById\n- * Given a feature id, return the feature if it exists in the features array\n- *\n+ * APIMethod: getZoomForExtent\n+ * \n * Parameters:\n- * featureId - {String}\n+ * extent - {<OpenLayers.Bounds>}\n+ * closest - {Boolean} Find the zoom level that most closely fits the \n+ * specified bounds. Note that this may result in a zoom that does \n+ * not exactly contain the entire extent.\n+ * Default is false.\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureId or null if there is no such feature.\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * for the passed-in extent. We do this by calculating the ideal \n+ * resolution for the given extent (based on the map size) and then \n+ * calling getZoomForResolution(), passing along the 'closest'\n+ * parameter.\n */\n- getFeatureById: function(featureId) {\n- return this.getFeatureBy('id', featureId);\n+ getZoomForExtent: function(extent, closest) {\n+ var viewSize = this.map.getSize();\n+ var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n+ extent.getHeight() / viewSize.h);\n+\n+ return this.getZoomForResolution(idealResolution, closest);\n },\n \n- /**\n- * APIMethod: getFeatureByFid\n- * Given a feature fid, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * featureFid - {String}\n- *\n+ /** \n+ * Method: getDataExtent\n+ * Calculates the max extent which includes all of the data for the layer.\n+ * This function is to be implemented by subclasses.\n+ * \n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureFid or null if there is no such feature.\n+ * {<OpenLayers.Bounds>}\n */\n- getFeatureByFid: function(featureFid) {\n- return this.getFeatureBy('fid', featureFid);\n+ getDataExtent: function() {\n+ //to be implemented by subclasses\n },\n \n /**\n- * APIMethod: getFeaturesByAttribute\n- * Returns an array of features that have the given attribute key set to the\n- * given value. Comparison of attribute values takes care of datatypes, e.g.\n- * the string '1234' is not equal to the number 1234.\n- *\n+ * APIMethod: getResolutionForZoom\n+ * \n * Parameters:\n- * attrName - {String}\n- * attrValue - {Mixed}\n- *\n+ * zoom - {Float}\n+ * \n * Returns:\n- * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n- * passed named attribute set to the given value.\n+ * {Float} A suitable resolution for the specified zoom.\n */\n- getFeaturesByAttribute: function(attrName, attrValue) {\n- var i,\n- feature,\n- len = this.features.length,\n- foundFeatures = [];\n- for (i = 0; i < len; i++) {\n- feature = this.features[i];\n- if (feature && feature.attributes) {\n- if (feature.attributes[attrName] === attrValue) {\n- foundFeatures.push(feature);\n- }\n- }\n- }\n- return foundFeatures;\n- },\n-\n- /**\n- * Unselect the selected features\n- * i.e. clears the featureSelection array\n- * change the style back\n- clearSelection: function() {\n-\n- var vectorLayer = this.map.vectorLayer;\n- for (var i = 0; i < this.map.featureSelection.length; i++) {\n- var featureSelection = this.map.featureSelection[i];\n- vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n+ getResolutionForZoom: function(zoom) {\n+ zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n+ var resolution;\n+ if (this.map.fractionalZoom) {\n+ var low = Math.floor(zoom);\n+ var high = Math.ceil(zoom);\n+ resolution = this.resolutions[low] -\n+ ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n+ } else {\n+ resolution = this.resolutions[Math.round(zoom)];\n }\n- this.map.featureSelection = [];\n+ return resolution;\n },\n- */\n-\n-\n- /**\n- * APIMethod: onFeatureInsert\n- * method called after a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something on feature updates.\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- onFeatureInsert: function(feature) {},\n \n /**\n- * APIMethod: preFeatureInsert\n- * method called before a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something when features are first added to the\n- * layer, but before they are drawn, such as adjust the style.\n- *\n+ * APIMethod: getZoomForResolution\n+ * \n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- preFeatureInsert: function(feature) {},\n-\n- /** \n- * APIMethod: getDataExtent\n- * Calculates the max extent which includes all of the features.\n+ * resolution - {Float}\n+ * closest - {Boolean} Find the zoom level that corresponds to the absolute \n+ * closest resolution, which may result in a zoom whose corresponding\n+ * resolution is actually smaller than we would have desired (if this\n+ * is being called from a getZoomForExtent() call, then this means that\n+ * the returned zoom index might not actually contain the entire \n+ * extent specified... but it'll be close).\n+ * Default is false.\n * \n * Returns:\n- * {<OpenLayers.Bounds>} or null if the layer has no features with\n- * geometries.\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * that corresponds to the best fit resolution given the passed in \n+ * value and the 'closest' specification.\n */\n- getDataExtent: function() {\n- var maxExtent = null;\n- var features = this.features;\n- if (features && (features.length > 0)) {\n- var geometry = null;\n- for (var i = 0, len = features.length; i < len; i++) {\n- geometry = features[i].geometry;\n- if (geometry) {\n- if (maxExtent === null) {\n- maxExtent = new OpenLayers.Bounds();\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom, i, len;\n+ if (this.map.fractionalZoom) {\n+ var lowZoom = 0;\n+ var highZoom = this.resolutions.length - 1;\n+ var highRes = this.resolutions[lowZoom];\n+ var lowRes = this.resolutions[highZoom];\n+ var res;\n+ for (i = 0, len = this.resolutions.length; i < len; ++i) {\n+ res = this.resolutions[i];\n+ if (res >= resolution) {\n+ highRes = res;\n+ lowZoom = i;\n+ }\n+ if (res <= resolution) {\n+ lowRes = res;\n+ highZoom = i;\n+ break;\n+ }\n+ }\n+ var dRes = highRes - lowRes;\n+ if (dRes > 0) {\n+ zoom = lowZoom + ((highRes - resolution) / dRes);\n+ } else {\n+ zoom = lowZoom;\n+ }\n+ } else {\n+ var diff;\n+ var minDiff = Number.POSITIVE_INFINITY;\n+ for (i = 0, len = this.resolutions.length; i < len; i++) {\n+ if (closest) {\n+ diff = Math.abs(this.resolutions[i] - resolution);\n+ if (diff > minDiff) {\n+ break;\n+ }\n+ minDiff = diff;\n+ } else {\n+ if (this.resolutions[i] < resolution) {\n+ break;\n }\n- maxExtent.extend(geometry.getBounds());\n }\n }\n+ zoom = Math.max(0, i - 1);\n }\n- return maxExtent;\n+ return zoom;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Vector\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/PointTrack.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.PointTrack\n- * Vector layer to display ordered point features as a line, creating one\n- * LineString feature for each pair of two points.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector> \n- */\n-OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * APIProperty: dataFrom\n- * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n- * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n- * should get the data/attributes from one of the two points it is\n- * composed of, which one should it be?\n- */\n- dataFrom: null,\n-\n- /**\n- * APIProperty: styleFrom\n- * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n- * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n- * should get the style from one of the two points it is composed of,\n- * which one should it be?\n- */\n- styleFrom: null,\n-\n- /**\n- * Constructor: OpenLayers.PointTrack\n- * Constructor for a new OpenLayers.PointTrack instance.\n- *\n- * Parameters:\n- * name - {String} name of the layer\n- * options - {Object} Optional object with properties to tag onto the\n- * instance.\n- */\n-\n /**\n- * APIMethod: addNodes\n- * Adds point features that will be used to create lines from, using point\n- * pairs. The first point of a pair will be the source node, the second\n- * will be the target node.\n+ * APIMethod: getLonLatFromViewPortPx\n * \n * Parameters:\n- * pointFeatures - {Array(<OpenLayers.Feature>)}\n- * options - {Object}\n- * \n- * Supported options:\n- * silent - {Boolean} true to suppress (before)feature(s)added events\n+ * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in \n+ * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.\n */\n- addNodes: function(pointFeatures, options) {\n- if (pointFeatures.length < 2) {\n- throw new Error(\"At least two point features have to be added to \" +\n- \"create a line from\");\n- }\n-\n- var lines = new Array(pointFeatures.length - 1);\n-\n- var pointFeature, startPoint, endPoint;\n- for (var i = 0, len = pointFeatures.length; i < len; i++) {\n- pointFeature = pointFeatures[i];\n- endPoint = pointFeature.geometry;\n-\n- if (!endPoint) {\n- var lonlat = pointFeature.lonlat;\n- endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);\n- } else if (endPoint.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n- throw new TypeError(\"Only features with point geometries are supported.\");\n- }\n-\n- if (i > 0) {\n- var attributes = (this.dataFrom != null) ?\n- (pointFeatures[i + this.dataFrom].data ||\n- pointFeatures[i + this.dataFrom].attributes) :\n- null;\n- var style = (this.styleFrom != null) ?\n- (pointFeatures[i + this.styleFrom].style) :\n- null;\n- var line = new OpenLayers.Geometry.LineString([startPoint,\n- endPoint\n- ]);\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ var map = this.map;\n+ if (viewPortPx != null && map.minPx) {\n+ var res = map.getResolution();\n+ var maxExtent = map.getMaxExtent({\n+ restricted: true\n+ });\n+ var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n+ var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n+ lonlat = new OpenLayers.LonLat(lon, lat);\n \n- lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes,\n- style);\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent);\n }\n-\n- startPoint = endPoint;\n }\n-\n- this.addFeatures(lines, options);\n+ return lonlat;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.PointTrack\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE\n- * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n- * <OpenLayers.Layer.PointTrack.styleFrom>\n- */\n-OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE\n- * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n- * <OpenLayers.Layer.PointTrack.styleFrom>\n- */\n-OpenLayers.Layer.PointTrack.TARGET_NODE = 0;\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.dataFrom\n- * {Object} with the following keys - *deprecated*\n- * - SOURCE_NODE: take data/attributes from the source node of the line\n- * - TARGET_NODE: take data/attributes from the target node of the line\n- */\n-OpenLayers.Layer.PointTrack.dataFrom = {\n- 'SOURCE_NODE': -1,\n- 'TARGET_NODE': 0\n-};\n-/* ======================================================================\n- OpenLayers/Layer/Markers.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Markers\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer> \n- */\n-OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /** \n- * APIProperty: isBaseLayer \n- * {Boolean} Markers layer is never a base layer. \n- */\n- isBaseLayer: false,\n-\n- /** \n- * APIProperty: markers \n- * {Array(<OpenLayers.Marker>)} internal marker list \n- */\n- markers: null,\n-\n-\n- /** \n- * Property: drawn \n- * {Boolean} internal state of drawing. This is a workaround for the fact\n- * that the map does not call moveTo with a zoomChanged when the map is\n- * first starting up. This lets us catch the case where we have *never*\n- * drawn the layer, and draw it even if the zoom hasn't changed.\n- */\n- drawn: false,\n-\n /**\n- * Constructor: OpenLayers.Layer.Markers \n- * Create a Markers layer.\n- *\n+ * APIMethod: getViewPortPxFromLonLat\n+ * Returns a pixel location given a map location. This method will return\n+ * fractional pixel values.\n+ * \n * Parameters:\n- * name - {String} \n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- this.markers = [];\n- },\n-\n- /**\n- * APIMethod: destroy \n+ * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or\n+ * an object with a 'lon'\n+ * and 'lat' properties.\n+ *\n+ * Returns: \n+ * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in \n+ * lonlat translated into view port pixels.\n */\n- destroy: function() {\n- this.clearMarkers();\n- this.markers = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ getViewPortPxFromLonLat: function(lonlat, resolution) {\n+ var px = null;\n+ if (lonlat != null) {\n+ resolution = resolution || this.map.getResolution();\n+ var extent = this.map.calculateBounds(null, resolution);\n+ px = new OpenLayers.Pixel(\n+ (1 / resolution * (lonlat.lon - extent.left)),\n+ (1 / resolution * (extent.top - lonlat.lat))\n+ );\n+ }\n+ return px;\n },\n \n /**\n * APIMethod: setOpacity\n- * Sets the opacity for all the markers.\n+ * Sets the opacity for the entire layer (all images)\n * \n * Parameters:\n * opacity - {Float}\n */\n setOpacity: function(opacity) {\n if (opacity != this.opacity) {\n this.opacity = opacity;\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- this.markers[i].setOpacity(this.opacity);\n+ var childNodes = this.div.childNodes;\n+ for (var i = 0, len = childNodes.length; i < len; ++i) {\n+ var element = childNodes[i].firstChild || childNodes[i];\n+ var lastChild = childNodes[i].lastChild;\n+ //TODO de-uglify this\n+ if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n+ element = lastChild.parentNode;\n+ }\n+ OpenLayers.Util.modifyDOMElement(element, null, null, null,\n+ null, null, null, opacity);\n }\n- }\n- },\n-\n- /** \n- * Method: moveTo\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * zoomChanged - {Boolean} \n- * dragging - {Boolean} \n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- if (zoomChanged || !this.drawn) {\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- this.drawMarker(this.markers[i]);\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ });\n }\n- this.drawn = true;\n- }\n- },\n-\n- /**\n- * APIMethod: addMarker\n- *\n- * Parameters:\n- * marker - {<OpenLayers.Marker>} \n- */\n- addMarker: function(marker) {\n- this.markers.push(marker);\n-\n- if (this.opacity < 1) {\n- marker.setOpacity(this.opacity);\n- }\n-\n- if (this.map && this.map.getExtent()) {\n- marker.map = this.map;\n- this.drawMarker(marker);\n }\n },\n \n /**\n- * APIMethod: removeMarker\n- *\n- * Parameters:\n- * marker - {<OpenLayers.Marker>} \n+ * Method: getZIndex\n+ * \n+ * Returns: \n+ * {Integer} the z-index of this layer\n */\n- removeMarker: function(marker) {\n- if (this.markers && this.markers.length) {\n- OpenLayers.Util.removeItem(this.markers, marker);\n- marker.erase();\n- }\n+ getZIndex: function() {\n+ return this.div.style.zIndex;\n },\n \n /**\n- * Method: clearMarkers\n- * This method removes all markers from a layer. The markers are not\n- * destroyed by this function, but are removed from the list of markers.\n- */\n- clearMarkers: function() {\n- if (this.markers != null) {\n- while (this.markers.length > 0) {\n- this.removeMarker(this.markers[0]);\n- }\n- }\n- },\n-\n- /** \n- * Method: drawMarker\n- * Calculate the pixel location for the marker, create it, and \n- * add it to the layer's div\n- *\n- * Parameters:\n- * marker - {<OpenLayers.Marker>} \n- */\n- drawMarker: function(marker) {\n- var px = this.map.getLayerPxFromLonLat(marker.lonlat);\n- if (px == null) {\n- marker.display(false);\n- } else {\n- if (!marker.isDrawn()) {\n- var markerImg = marker.draw(px);\n- this.div.appendChild(markerImg);\n- } else if (marker.icon) {\n- marker.icon.moveTo(px);\n- }\n- }\n- },\n-\n- /** \n- * APIMethod: getDataExtent\n- * Calculates the max extent which includes all of the markers.\n+ * Method: setZIndex\n * \n- * Returns:\n- * {<OpenLayers.Bounds>}\n+ * Parameters: \n+ * zIndex - {Integer}\n */\n- getDataExtent: function() {\n- var maxExtent = null;\n-\n- if (this.markers && (this.markers.length > 0)) {\n- var maxExtent = new OpenLayers.Bounds();\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- var marker = this.markers[i];\n- maxExtent.extend(marker.lonlat);\n- }\n- }\n-\n- return maxExtent;\n+ setZIndex: function(zIndex) {\n+ this.div.style.zIndex = zIndex;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Markers\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Boxes.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Layer/Markers.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Boxes\n- * Draw divs as 'boxes' on the layer. \n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n- */\n-OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {\n-\n /**\n- * Constructor: OpenLayers.Layer.Boxes\n- *\n+ * Method: adjustBounds\n+ * This function will take a bounds, and if wrapDateLine option is set\n+ * on the layer, it will return a bounds which is wrapped around the \n+ * world. We do not wrap for bounds which *cross* the \n+ * maxExtent.left/right, only bounds which are entirely to the left \n+ * or entirely to the right.\n+ * \n * Parameters:\n- * name - {String} \n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * bounds - {<OpenLayers.Bounds>}\n */\n+ adjustBounds: function(bounds) {\n \n- /**\n- * Method: drawMarker \n- * Calculate the pixel location for the marker, create it, and\n- * add it to the layer's div\n- *\n- * Parameters: \n- * marker - {<OpenLayers.Marker.Box>} \n- */\n- drawMarker: function(marker) {\n- var topleft = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.left,\n- lat: marker.bounds.top\n- });\n- var botright = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.right,\n- lat: marker.bounds.bottom\n- });\n- if (botright == null || topleft == null) {\n- marker.display(false);\n- } else {\n- var markerDiv = marker.draw(topleft, {\n- w: Math.max(1, botright.x - topleft.x),\n- h: Math.max(1, botright.y - topleft.y)\n- });\n- if (!marker.drawn) {\n- this.div.appendChild(markerDiv);\n- marker.drawn = true;\n- }\n+ if (this.gutter) {\n+ // Adjust the extent of a bounds in map units by the \n+ // layer's gutter in pixels.\n+ var mapGutter = this.gutter * this.map.getResolution();\n+ bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n+ bounds.bottom - mapGutter,\n+ bounds.right + mapGutter,\n+ bounds.top + mapGutter);\n }\n- },\n \n+ if (this.wrapDateLine) {\n+ // wrap around the date line, within the limits of rounding error\n+ var wrappingOptions = {\n+ 'rightTolerance': this.getResolution(),\n+ 'leftTolerance': this.getResolution()\n+ };\n+ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n \n- /**\n- * APIMethod: removeMarker \n- * \n- * Parameters:\n- * marker - {<OpenLayers.Marker.Box>} \n- */\n- removeMarker: function(marker) {\n- OpenLayers.Util.removeItem(this.markers, marker);\n- if ((marker.div != null) &&\n- (marker.div.parentNode == this.div)) {\n- this.div.removeChild(marker.div);\n }\n+ return bounds;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Boxes\"\n+ CLASS_NAME: \"OpenLayers.Layer\"\n });\n /* ======================================================================\n- OpenLayers/Layer/Image.js\n+ OpenLayers/Layer/HTTPRequest.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Image\n- * Instances of OpenLayers.Layer.Image are used to display data from a web\n- * accessible image as a map layer. Create a new image layer with the\n- * <OpenLayers.Layer.Image> constructor.\n- *\n- * Inherits from:\n+ * Class: OpenLayers.Layer.HTTPRequest\n+ * \n+ * Inherits from: \n * - <OpenLayers.Layer>\n */\n-OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {\n+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n \n- /**\n- * Property: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is true. Set this property\n- * in the layer options\n+ /** \n+ * Constant: URL_HASH_FACTOR\n+ * {Float} Used to hash URL param strings for multi-WMS server selection.\n+ * Set to the Golden Ratio per Knuth's recommendation.\n */\n- isBaseLayer: true,\n+ URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n \n- /**\n+ /** \n * Property: url\n- * {String} URL of the image to use\n+ * {Array(String) or String} This is either an array of url strings or \n+ * a single url string. \n */\n url: null,\n \n- /**\n- * Property: extent\n- * {<OpenLayers.Bounds>} The image bounds in map units. This extent will\n- * also be used as the default maxExtent for the layer. If you wish\n- * to have a maxExtent that is different than the image extent, set the\n- * maxExtent property of the options argument (as with any other layer).\n- */\n- extent: null,\n-\n- /**\n- * Property: size\n- * {<OpenLayers.Size>} The image size in pixels\n- */\n- size: null,\n-\n- /**\n- * Property: tile\n- * {<OpenLayers.Tile.Image>}\n+ /** \n+ * Property: params\n+ * {Object} Hashtable of key/value parameters\n */\n- tile: null,\n+ params: null,\n \n- /**\n- * Property: aspectRatio\n- * {Float} The ratio of height/width represented by a single pixel in the\n- * graphic\n+ /** \n+ * APIProperty: reproject\n+ * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n+ * for information on the replacement for this functionality. \n+ * {Boolean} Whether layer should reproject itself based on base layer \n+ * locations. This allows reprojection onto commercial layers. \n+ * Default is false: Most layers can't reproject, but layers \n+ * which can create non-square geographic pixels can, like WMS.\n+ * \n */\n- aspectRatio: null,\n+ reproject: false,\n \n /**\n- * Constructor: OpenLayers.Layer.Image\n- * Create a new image layer\n- *\n+ * Constructor: OpenLayers.Layer.HTTPRequest\n+ * \n * Parameters:\n- * name - {String} A name for the layer.\n- * url - {String} Relative or absolute path to the image\n- * extent - {<OpenLayers.Bounds>} The extent represented by the image\n- * size - {<OpenLayers.Size>} The size (in pixels) of the image\n+ * name - {String}\n+ * url - {Array(String) or String}\n+ * params - {Object}\n * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- initialize: function(name, url, extent, size, options) {\n- this.url = url;\n- this.extent = extent;\n- this.maxExtent = extent;\n- this.size = size;\n+ initialize: function(name, url, params, options) {\n OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n-\n- this.aspectRatio = (this.extent.getHeight() / this.size.h) /\n- (this.extent.getWidth() / this.size.w);\n+ this.url = url;\n+ if (!this.params) {\n+ this.params = OpenLayers.Util.extend({}, params);\n+ }\n },\n \n /**\n- * Method: destroy\n- * Destroy this layer\n+ * APIMethod: destroy\n */\n destroy: function() {\n- if (this.tile) {\n- this.removeTileMonitoringHooks(this.tile);\n- this.tile.destroy();\n- this.tile = null;\n- }\n+ this.url = null;\n+ this.params = null;\n OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Paramters:\n- * obj - {Object} An optional layer (is this ever used?)\n- *\n+ * APIMethod: clone\n+ * \n+ * Parameters:\n+ * obj - {Object}\n+ * \n * Returns:\n- * {<OpenLayers.Layer.Image>} An exact copy of this layer\n+ * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this \n+ * <OpenLayers.Layer.HTTPRequest>\n */\n clone: function(obj) {\n \n if (obj == null) {\n- obj = new OpenLayers.Layer.Image(this.name,\n+ obj = new OpenLayers.Layer.HTTPRequest(this.name,\n this.url,\n- this.extent,\n- this.size,\n+ this.params,\n this.getOptions());\n }\n \n //get all additions from superclasses\n obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n \n // copy/set any non-init, non-simple values here\n \n return obj;\n },\n \n- /**\n- * APIMethod: setMap\n+ /** \n+ * APIMethod: setUrl\n * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * newUrl - {String}\n */\n- setMap: function(map) {\n- /**\n- * If nothing to do with resolutions has been set, assume a single\n- * resolution determined by ratio*extent/size - if an image has a\n- * pixel aspect ratio different than one (as calculated above), the\n- * image will be stretched in one dimension only.\n- */\n- if (this.options.maxResolution == null) {\n- this.options.maxResolution = this.aspectRatio *\n- this.extent.getWidth() /\n- this.size.w;\n- }\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ setUrl: function(newUrl) {\n+ this.url = newUrl;\n },\n \n- /** \n- * Method: moveTo\n- * Create the tile for the image or resize it for the new resolution\n+ /**\n+ * APIMethod: mergeNewParams\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n+ * newParams - {Object}\n+ *\n+ * Returns:\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- var firstRendering = (this.tile == null);\n-\n- if (zoomChanged || firstRendering) {\n-\n- //determine new tile size\n- this.setTileSize();\n-\n- //determine new position (upper left corner of new bounds)\n- var ulPx = this.map.getLayerPxFromLonLat({\n- lon: this.extent.left,\n- lat: this.extent.top\n+ mergeNewParams: function(newParams) {\n+ this.params = OpenLayers.Util.extend(this.params, newParams);\n+ var ret = this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"params\"\n });\n-\n- if (firstRendering) {\n- //create the new tile\n- this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,\n- null, this.tileSize);\n- this.addTileMonitoringHooks(this.tile);\n- } else {\n- //just resize the tile and set it's new position\n- this.tile.size = this.tileSize.clone();\n- this.tile.position = ulPx.clone();\n- }\n- this.tile.draw();\n }\n+ return ret;\n },\n \n /**\n- * Set the tile size based on the map size.\n- */\n- setTileSize: function() {\n- var tileWidth = this.extent.getWidth() / this.map.getResolution();\n- var tileHeight = this.extent.getHeight() / this.map.getResolution();\n- this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);\n- },\n-\n- /** \n- * Method: addTileMonitoringHooks\n- * This function takes a tile as input and adds the appropriate hooks to \n- * the tile so that the layer can keep track of the loading tiles.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n- */\n- addTileMonitoringHooks: function(tile) {\n- tile.onLoadStart = function() {\n- this.events.triggerEvent(\"loadstart\");\n- };\n- tile.events.register(\"loadstart\", this, tile.onLoadStart);\n-\n- tile.onLoadEnd = function() {\n- this.events.triggerEvent(\"loadend\");\n- };\n- tile.events.register(\"loadend\", this, tile.onLoadEnd);\n- tile.events.register(\"unload\", this, tile.onLoadEnd);\n- },\n-\n- /** \n- * Method: removeTileMonitoringHooks\n- * This function takes a tile as input and removes the tile hooks \n- * that were added in <addTileMonitoringHooks>.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ *\n+ * Parameters:\n+ * force - {Boolean} Force redraw by adding random parameter.\n+ *\n+ * Returns:\n+ * {Boolean} The layer was redrawn.\n */\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- scope: this\n- });\n+ redraw: function(force) {\n+ if (force) {\n+ return this.mergeNewParams({\n+ \"_olSalt\": Math.random()\n+ });\n+ } else {\n+ return OpenLayers.Layer.prototype.redraw.apply(this, []);\n+ }\n },\n \n /**\n- * APIMethod: setUrl\n- * \n+ * Method: selectUrl\n+ * selectUrl() implements the standard floating-point multiplicative\n+ * hash function described by Knuth, and hashes the contents of the \n+ * given param string into a float between 0 and 1. This float is then\n+ * scaled to the size of the provided urls array, and used to select\n+ * a URL.\n+ *\n * Parameters:\n- * newUrl - {String}\n+ * paramString - {String}\n+ * urls - {Array(String)}\n+ * \n+ * Returns:\n+ * {String} An entry from the urls array, deterministically selected based\n+ * on the paramString.\n */\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n- this.tile.draw();\n+ selectUrl: function(paramString, urls) {\n+ var product = 1;\n+ for (var i = 0, len = paramString.length; i < len; i++) {\n+ product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n+ product -= Math.floor(product);\n+ }\n+ return urls[Math.floor(product * urls.length)];\n },\n \n /** \n- * APIMethod: getURL\n- * The url we return is always the same (the image itself never changes)\n- * so we can ignore the bounds parameter (it will always be the same, \n- * anyways) \n+ * Method: getFullRequestString\n+ * Combine url with layer's params and these newParams. \n+ * \n+ * does checking on the serverPath variable, allowing for cases when it \n+ * is supplied with trailing ? or &, as well as cases where not. \n+ *\n+ * return in formatted string like this:\n+ * \"server?key1=value1&key2=value2&key3=value3\"\n * \n+ * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n+ *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n+ * \n+ * Returns: \n+ * {String}\n */\n- getURL: function(bounds) {\n- return this.url;\n+ getFullRequestString: function(newParams, altUrl) {\n+\n+ // if not altUrl passed in, use layer's url\n+ var url = altUrl || this.url;\n+\n+ // create a new params hashtable with all the layer params and the \n+ // new params together. then convert to string\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will deterministically select one of them in \n+ // order to evenly distribute requests to different urls.\n+ //\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url);\n+ }\n+\n+ // ignore parameters that are already in the url search string\n+ var urlParams =\n+ OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key];\n+ }\n+ }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n+\n+ return OpenLayers.Util.urlAppend(url, paramsString);\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Image\"\n+ CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n });\n /* ======================================================================\n- OpenLayers/Layer/PointGrid.js\n+ OpenLayers/Tile.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Layer/Vector.js\n- * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Layer.PointGrid\n- * A point grid layer dynamically generates a regularly spaced grid of point\n- * features. This is a specialty layer for cases where an application needs\n- * a regular grid of points. It can be used, for example, in an editing\n- * environment to snap to a grid.\n- *\n- * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor.\n- * (code)\n- * // create a grid with points spaced at 10 map units\n- * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10});\n- *\n- * // create a grid with different x/y spacing rotated 15 degrees clockwise.\n- * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15});\n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n+ * Class: OpenLayers.Tile \n+ * This is a class designed to designate a single tile, however\n+ * it is explicitly designed to do relatively little. Tiles store \n+ * information about themselves -- such as the URL that they are related\n+ * to, and their size - but do not add themselves to the layer div \n+ * automatically, for example. Create a new tile with the \n+ * <OpenLayers.Tile> constructor, or a subclass. \n+ * \n+ * TBD 3.0 - remove reference to url in above paragraph\n+ * \n */\n-OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * APIProperty: dx\n- * {Number} Point grid spacing in the x-axis direction (map units). \n- * Read-only. Use the <setSpacing> method to modify this value.\n- */\n- dx: null,\n+OpenLayers.Tile = OpenLayers.Class({\n \n /**\n- * APIProperty: dy\n- * {Number} Point grid spacing in the y-axis direction (map units). \n- * Read-only. Use the <setSpacing> method to modify this value.\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types:\n+ * beforedraw - Triggered before the tile is drawn. Used to defer\n+ * drawing to an animation queue. To defer drawing, listeners need\n+ * to return false, which will abort drawing. The queue handler needs\n+ * to call <draw>(true) to actually draw the tile.\n+ * loadstart - Triggered when tile loading starts.\n+ * loadend - Triggered when tile loading ends.\n+ * loaderror - Triggered before the loadend event (i.e. when the tile is\n+ * still hidden) if the tile could not be loaded.\n+ * reload - Triggered when an already loading tile is reloaded.\n+ * unload - Triggered before a tile is unloaded.\n */\n- dy: null,\n+ events: null,\n \n /**\n- * APIProperty: ratio\n- * {Number} Ratio of the desired grid size to the map viewport size. \n- * Default is 1.5. Larger ratios mean the grid is recalculated less often \n- * while panning. The <maxFeatures> setting has precedence when determining\n- * grid size. Read-only. Use the <setRatio> method to modify this value.\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n+ *\n+ * This options can be set in the ``tileOptions`` option from\n+ * <OpenLayers.Layer.Grid>. For example, to be notified of the\n+ * ``loadend`` event of each tiles:\n+ * (code)\n+ * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n+ * tileOptions: {\n+ * eventListeners: {\n+ * 'loadend': function(evt) {\n+ * // do something on loadend\n+ * }\n+ * }\n+ * }\n+ * });\n+ * (end)\n */\n- ratio: 1.5,\n+ eventListeners: null,\n \n /**\n- * APIProperty: maxFeatures\n- * {Number} The maximum number of points to generate in the grid. Default\n- * is 250. Read-only. Use the <setMaxFeatures> method to modify this value.\n+ * Property: id \n+ * {String} null\n */\n- maxFeatures: 250,\n+ id: null,\n \n- /**\n- * APIProperty: rotation\n- * {Number} Grid rotation (in degrees clockwise from the positive x-axis).\n- * Default is 0. Read-only. Use the <setRotation> method to modify this\n- * value.\n+ /** \n+ * Property: layer \n+ * {<OpenLayers.Layer>} layer the tile is attached to \n */\n- rotation: 0,\n+ layer: null,\n \n /**\n- * APIProperty: origin\n- * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with \n- * the origin. If not set at construction, the center of the map's maximum \n- * extent is used. Read-only. Use the <setOrigin> method to modify this \n- * value.\n+ * Property: url\n+ * {String} url of the request.\n+ *\n+ * TBD 3.0 \n+ * Deprecated. The base tile class does not need an url. This should be \n+ * handled in subclasses. Does not belong here.\n */\n- origin: null,\n+ url: null,\n \n- /**\n- * Property: gridBounds\n- * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional \n- * rotation applied).\n+ /** \n+ * APIProperty: bounds \n+ * {<OpenLayers.Bounds>} null\n */\n- gridBounds: null,\n+ bounds: null,\n \n- /**\n- * Constructor: OpenLayers.Layer.PointGrid\n- * Creates a new point grid layer.\n- *\n- * Parameters:\n- * config - {Object} An object containing all configuration properties for\n- * the layer. The <dx> and <dy> properties are required to be set at \n- * construction. Any other layer properties may be set in this object.\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>} null\n */\n- initialize: function(config) {\n- config = config || {};\n- OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);\n- },\n+ size: null,\n \n /** \n- * Method: setMap\n- * The layer has been added to the map. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * Property: position \n+ * {<OpenLayers.Pixel>} Top Left pixel of the tile\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- map.events.register(\"moveend\", this, this.onMoveEnd);\n- },\n+ position: null,\n \n /**\n- * Method: removeMap\n- * The layer has been removed from the map.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * Property: isLoading\n+ * {Boolean} Is the tile loading?\n */\n- removeMap: function(map) {\n- map.events.unregister(\"moveend\", this, this.onMoveEnd);\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n- },\n+ isLoading: false,\n \n- /**\n- * APIMethod: setRatio\n- * Set the grid <ratio> property and update the grid. Can only be called\n- * after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * ratio - {Number}\n+ /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n+ * there is no need for the base tile class to have a url.\n */\n- setRatio: function(ratio) {\n- this.ratio = ratio;\n- this.updateGrid(true);\n- },\n \n- /**\n- * APIMethod: setMaxFeatures\n- * Set the grid <maxFeatures> property and update the grid. Can only be \n- * called after the layer has been added to a map with a center/extent.\n- *\n+ /** \n+ * Constructor: OpenLayers.Tile\n+ * Constructor for a new <OpenLayers.Tile> instance.\n+ * \n * Parameters:\n- * maxFeatures - {Number}\n+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n+ * position - {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * url - {<String>}\n+ * size - {<OpenLayers.Size>}\n+ * options - {Object}\n */\n- setMaxFeatures: function(maxFeatures) {\n- this.maxFeatures = maxFeatures;\n- this.updateGrid(true);\n- },\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ this.layer = layer;\n+ this.position = position.clone();\n+ this.setBounds(bounds);\n+ this.url = url;\n+ if (size) {\n+ this.size = size.clone();\n+ }\n \n- /**\n- * APIMethod: setSpacing\n- * Set the grid <dx> and <dy> properties and update the grid. If only one\n- * argument is provided, it will be set as <dx> and <dy>. Can only be \n- * called after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n- */\n- setSpacing: function(dx, dy) {\n- this.dx = dx;\n- this.dy = dy || dx;\n- this.updateGrid(true);\n+ //give the tile a unique id based on its BBOX.\n+ this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n+\n+ OpenLayers.Util.extend(this, options);\n+\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n },\n \n /**\n- * APIMethod: setOrigin\n- * Set the grid <origin> property and update the grid. Can only be called\n- * after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * origin - {<OpenLayers.LonLat>}\n+ * Method: unload\n+ * Call immediately before destroying if you are listening to tile\n+ * events, so that counters are properly handled if tile is still\n+ * loading at destroy-time. Will only fire an event if the tile is\n+ * still loading.\n */\n- setOrigin: function(origin) {\n- this.origin = origin;\n- this.updateGrid(true);\n+ unload: function() {\n+ if (this.isLoading) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"unload\");\n+ }\n },\n \n- /**\n- * APIMethod: getOrigin\n- * Get the grid <origin> property.\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} The grid origin.\n+ /** \n+ * APIMethod: destroy\n+ * Nullify references to prevent circular references and memory leaks.\n */\n- getOrigin: function() {\n- if (!this.origin) {\n- this.origin = this.map.getExtent().getCenterLonLat();\n+ destroy: function() {\n+ this.layer = null;\n+ this.bounds = null;\n+ this.size = null;\n+ this.position = null;\n+\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n }\n- return this.origin;\n+ this.events.destroy();\n+ this.eventListeners = null;\n+ this.events = null;\n },\n \n /**\n- * APIMethod: setRotation\n- * Set the grid <rotation> property and update the grid. Rotation values\n- * are in degrees clockwise from the positive x-axis (negative values\n- * for counter-clockwise rotation). Can only be called after the layer \n- * has been added to a map with a center/extent.\n+ * Method: draw\n+ * Clear whatever is currently in the tile, then return whether or not \n+ * it should actually be re-drawn. This is an example implementation\n+ * that can be overridden by subclasses. The minimum thing to do here\n+ * is to call <clear> and return the result from <shouldDraw>.\n *\n * Parameters:\n- * rotation - {Number} Degrees clockwise from the positive x-axis.\n- */\n- setRotation: function(rotation) {\n- this.rotation = rotation;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * Method: onMoveEnd\n- * Listener for map \"moveend\" events.\n+ * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n+ * event will be fired. This is used for drawing tiles asynchronously\n+ * after drawing has been cancelled by returning false from a beforedraw\n+ * listener.\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the tile should actually be drawn. Returns null\n+ * if a beforedraw listener returned false.\n */\n- onMoveEnd: function() {\n- this.updateGrid();\n+ draw: function(force) {\n+ if (!force) {\n+ //clear tile's contents and mark as not drawn\n+ this.clear();\n+ }\n+ var draw = this.shouldDraw();\n+ if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n+ draw = null;\n+ }\n+ return draw;\n },\n \n /**\n- * Method: getViewBounds\n- * Gets the (potentially rotated) view bounds for grid calculations.\n- *\n+ * Method: shouldDraw\n+ * Return whether or not the tile should actually be (re-)drawn. The only\n+ * case where we *wouldn't* want to draw the tile is if the tile is outside\n+ * its layer's maxExtent\n+ * \n * Returns:\n- * {<OpenLayers.Bounds>}\n+ * {Boolean} Whether or not the tile should actually be drawn.\n */\n- getViewBounds: function() {\n- var bounds = this.map.getExtent();\n- if (this.rotation) {\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var rect = bounds.toGeometry();\n- rect.rotate(-this.rotation, rotationOrigin);\n- bounds = rect.getBounds();\n+ shouldDraw: function() {\n+ var withinMaxExtent = false,\n+ maxExtent = this.layer.maxExtent;\n+ if (maxExtent) {\n+ var map = this.layer.map;\n+ var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n+ if (this.bounds.intersectsBounds(maxExtent, {\n+ inclusive: false,\n+ worldBounds: worldBounds\n+ })) {\n+ withinMaxExtent = true;\n+ }\n }\n- return bounds;\n+\n+ return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n },\n \n /**\n- * Method: updateGrid\n- * Update the grid.\n+ * Method: setBounds\n+ * Sets the bounds on this instance\n *\n * Parameters:\n- * force - {Boolean} Update the grid even if the previous bounds are still\n- * valid.\n+ * bounds {<OpenLayers.Bounds>}\n */\n- updateGrid: function(force) {\n- if (force || this.invalidBounds()) {\n- var viewBounds = this.getViewBounds();\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var viewBoundsWidth = viewBounds.getWidth();\n- var viewBoundsHeight = viewBounds.getHeight();\n- var aspectRatio = viewBoundsWidth / viewBoundsHeight;\n- var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);\n- var maxWidth = maxHeight * aspectRatio;\n- var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);\n- var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);\n- var center = viewBounds.getCenterLonLat();\n- this.gridBounds = new OpenLayers.Bounds(\n- center.lon - (gridWidth / 2),\n- center.lat - (gridHeight / 2),\n- center.lon + (gridWidth / 2),\n- center.lat + (gridHeight / 2)\n- );\n- var rows = Math.floor(gridHeight / this.dy);\n- var cols = Math.floor(gridWidth / this.dx);\n- var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));\n- var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));\n- var features = new Array(rows * cols);\n- var x, y, point;\n- for (var i = 0; i < cols; ++i) {\n- x = gridLeft + (i * this.dx);\n- for (var j = 0; j < rows; ++j) {\n- y = gridBottom + (j * this.dy);\n- point = new OpenLayers.Geometry.Point(x, y);\n- if (this.rotation) {\n- point.rotate(this.rotation, rotationOrigin);\n- }\n- features[(i * rows) + j] = new OpenLayers.Feature.Vector(point);\n- }\n- }\n- this.destroyFeatures(this.features, {\n- silent: true\n- });\n- this.addFeatures(features, {\n- silent: true\n+ setBounds: function(bounds) {\n+ bounds = bounds.clone();\n+ if (this.layer.map.baseLayer.wrapDateLine) {\n+ var worldExtent = this.layer.map.getMaxExtent(),\n+ tolerance = this.layer.map.getResolution();\n+ bounds = bounds.wrapDateLine(worldExtent, {\n+ leftTolerance: tolerance,\n+ rightTolerance: tolerance\n });\n }\n+ this.bounds = bounds;\n },\n \n- /**\n- * Method: invalidBounds\n- * Determine whether the previously generated point grid is invalid. \n- * This occurs when the map bounds extends beyond the previously \n- * generated grid bounds.\n+ /** \n+ * Method: moveTo\n+ * Reposition the tile.\n *\n- * Returns:\n- * {Boolean} \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * position - {<OpenLayers.Pixel>}\n+ * redraw - {Boolean} Call draw method on tile after moving.\n+ * Default is true\n */\n- invalidBounds: function() {\n- return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());\n+ moveTo: function(bounds, position, redraw) {\n+ if (redraw == null) {\n+ redraw = true;\n+ }\n+\n+ this.setBounds(bounds);\n+ this.position = position.clone();\n+ if (redraw) {\n+ this.draw();\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.PointGrid\"\n+ /** \n+ * Method: clear\n+ * Clear the tile of any bounds/position-related data so that it can \n+ * be reused in a new location.\n+ */\n+ clear: function(draw) {\n+ // to be extended by subclasses\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Tile\"\n });\n /* ======================================================================\n- OpenLayers/Layer/GeoRSS.js\n+ OpenLayers/Tile/Image.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Layer/Markers.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n+ * @requires OpenLayers/Tile.js\n+ * @requires OpenLayers/Animation.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Layer.GeoRSS\n- * Add GeoRSS Point features to your map. \n- * \n+ * Class: OpenLayers.Tile.Image\n+ * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n+ * used by various layers. Create a new image tile with the\n+ * <OpenLayers.Tile.Image> constructor.\n+ *\n * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n+ * - <OpenLayers.Tile>\n */\n-OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {\n+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n \n- /** \n- * Property: location \n- * {String} store url of text file \n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to the <OpenLayers.Tile> events):\n+ * beforeload - Triggered before an image is prepared for loading, when the\n+ * url for the image is known already. Listeners may call <setImage> on\n+ * the tile instance. If they do so, that image will be used and no new\n+ * one will be created.\n */\n- location: null,\n \n /** \n- * Property: features \n- * {Array(<OpenLayers.Feature>)} \n+ * APIProperty: url\n+ * {String} The URL of the image being requested. No default. Filled in by\n+ * layer.getURL() function. May be modified by loadstart listeners.\n */\n- features: null,\n+ url: null,\n \n- /**\n- * APIProperty: formatOptions\n- * {Object} Hash of options which should be passed to the format when it is\n- * created. Must be passed in the constructor.\n+ /** \n+ * Property: imgDiv\n+ * {HTMLImageElement} The image for this tile.\n */\n- formatOptions: null,\n+ imgDiv: null,\n \n- /** \n- * Property: selectedFeature \n- * {<OpenLayers.Feature>} \n+ /**\n+ * Property: frame\n+ * {DOMElement} The image element is appended to the frame. Any gutter on\n+ * the image will be hidden behind the frame. If no gutter is set,\n+ * this will be null.\n */\n- selectedFeature: null,\n+ frame: null,\n \n /** \n- * APIProperty: icon \n- * {<OpenLayers.Icon>}. This determines the Icon to be used on the map\n- * for this GeoRSS layer.\n+ * Property: imageReloadAttempts\n+ * {Integer} Attempts to load the image.\n */\n- icon: null,\n+ imageReloadAttempts: null,\n \n /**\n- * APIProperty: popupSize\n- * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If \n- * not provided, defaults to 250px by 120px. \n+ * Property: layerAlphaHack\n+ * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n */\n- popupSize: null,\n+ layerAlphaHack: null,\n \n- /** \n- * APIProperty: useFeedTitle \n- * {Boolean} Set layer.name to the first <title> element in the feed. Default is true. \n+ /**\n+ * Property: asyncRequestId\n+ * {Integer} ID of an request to see if request is still valid. This is a\n+ * number which increments by 1 for each asynchronous request.\n */\n- useFeedTitle: true,\n+ asyncRequestId: null,\n \n /**\n- * Constructor: OpenLayers.Layer.GeoRSS\n- * Create a GeoRSS Layer.\n+ * APIProperty: maxGetUrlLength\n+ * {Number} If set, requests that would result in GET urls with more\n+ * characters than the number provided will be made using form-encoded\n+ * HTTP POST. It is good practice to avoid urls that are longer than 2048\n+ * characters.\n *\n- * Parameters:\n- * name - {String} \n- * location - {String} \n- * options - {Object}\n+ * Caution:\n+ * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n+ * Opera versions do not fully support this option. On all browsers,\n+ * transition effects are not supported if POST requests are used.\n */\n- initialize: function(name, location, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);\n- this.location = location;\n- this.features = [];\n- },\n+ maxGetUrlLength: null,\n \n /**\n- * Method: destroy \n+ * Property: canvasContext\n+ * {CanvasRenderingContext2D} A canvas context associated with\n+ * the tile image.\n */\n- destroy: function() {\n- // Warning: Layer.Markers.destroy() must be called prior to calling\n- // clearFeatures() here, otherwise we leak memory. Indeed, if\n- // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n- // able to remove the marker image elements from the layer's div since\n- // the markers will have been destroyed by clearFeatures().\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null;\n- },\n+ canvasContext: null,\n \n /**\n- * Method: loadRSS\n- * Start the load of the RSS data. Don't do this when we first add the layer,\n- * since we may not be visible at any point, and it would therefore be a waste.\n+ * APIProperty: crossOriginKeyword\n+ * The value of the crossorigin keyword to use when loading images. This is\n+ * only relevant when using <getCanvasContext> for tiles from remote\n+ * origins and should be set to either 'anonymous' or 'use-credentials'\n+ * for servers that send Access-Control-Allow-Origin headers with their\n+ * tiles.\n */\n- loadRSS: function() {\n- if (!this.loaded) {\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- scope: this\n- });\n- this.loaded = true;\n- }\n- },\n+ crossOriginKeyword: null,\n \n- /**\n- * Method: moveTo\n- * If layer is visible and RSS has not been loaded, load RSS. \n- * \n- * Parameters:\n- * bounds - {Object} \n- * zoomChanged - {Object} \n- * minor - {Object} \n+ /** TBD 3.0 - reorder the parameters to the init function to remove \n+ * URL. the getUrl() function on the layer gets called on \n+ * each draw(), so no need to specify it here.\n */\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadRSS();\n- }\n- },\n \n- /**\n- * Method: parseData\n- * Parse the data returned from the Events call.\n- *\n+ /** \n+ * Constructor: OpenLayers.Tile.Image\n+ * Constructor for a new <OpenLayers.Tile.Image> instance.\n+ * \n * Parameters:\n- * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n+ * position - {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * url - {<String>} Deprecated. Remove me in 3.0.\n+ * size - {<OpenLayers.Size>}\n+ * options - {Object}\n */\n- parseData: function(ajaxRequest) {\n- var doc = ajaxRequest.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);\n- }\n-\n- if (this.useFeedTitle) {\n- var name = null;\n- try {\n- name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;\n- } catch (e) {\n- try {\n- name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;\n- } catch (e) {}\n- }\n- if (name) {\n- this.setName(name);\n- }\n- }\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n \n- var options = {};\n+ this.url = url; //deprecated remove me\n \n- OpenLayers.Util.extend(options, this.formatOptions);\n+ this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n \n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject();\n+ if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n+ // only create frame if it's needed\n+ this.frame = document.createElement(\"div\");\n+ this.frame.style.position = \"absolute\";\n+ this.frame.style.overflow = \"hidden\";\n }\n-\n- var format = new OpenLayers.Format.GeoRSS(options);\n- var features = format.read(doc);\n-\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n-\n- // we don't support features with no geometry in the GeoRSS\n- // layer at this time. \n- if (!feature.geometry) {\n- continue;\n- }\n-\n- var title = feature.attributes.title ?\n- feature.attributes.title : \"Untitled\";\n-\n- var description = feature.attributes.description ?\n- feature.attributes.description : \"No description.\";\n-\n- var link = feature.attributes.link ? feature.attributes.link : \"\";\n-\n- var location = feature.geometry.getBounds().getCenterLonLat();\n-\n-\n- data.icon = this.icon == null ?\n- OpenLayers.Marker.defaultIcon() :\n- this.icon.clone();\n-\n- data.popupSize = this.popupSize ?\n- this.popupSize.clone() :\n- new OpenLayers.Size(250, 120);\n-\n- if (title || description) {\n- // we have supplemental data, store them.\n- data.title = title;\n- data.description = description;\n-\n- var contentHTML = '<div class=\"olLayerGeoRSSClose\">[x]</div>';\n- contentHTML += '<div class=\"olLayerGeoRSSTitle\">';\n- if (link) {\n- contentHTML += '<a class=\"link\" href=\"' + link + '\" target=\"_blank\">';\n- }\n- contentHTML += title;\n- if (link) {\n- contentHTML += '</a>';\n- }\n- contentHTML += '</div>';\n- contentHTML += '<div style=\"\" class=\"olLayerGeoRSSDescription\">';\n- contentHTML += description;\n- contentHTML += '</div>';\n- data['popupContentHTML'] = contentHTML;\n- }\n- var feature = new OpenLayers.Feature(this, location, data);\n- this.features.push(feature);\n- var marker = feature.createMarker();\n- marker.events.register('click', feature, this.markerClick);\n- this.addMarker(marker);\n+ if (this.maxGetUrlLength != null) {\n+ OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n }\n- this.events.triggerEvent(\"loadend\");\n },\n \n- /**\n- * Method: markerClick\n- *\n- * Parameters:\n- * evt - {Event} \n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- markerClick: function(evt) {\n- var sameMarkerClicked = (this == this.layer.selectedFeature);\n- this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- if (!sameMarkerClicked) {\n- var popup = this.createPopup();\n- OpenLayers.Event.observe(popup.div, \"click\",\n- OpenLayers.Function.bind(function() {\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- }, this)\n- );\n- this.layer.map.addPopup(popup);\n+ destroy: function() {\n+ if (this.imgDiv) {\n+ this.clear();\n+ this.imgDiv = null;\n+ this.frame = null;\n }\n- OpenLayers.Event.stop(evt);\n+ // don't handle async requests any more\n+ this.asyncRequestId = null;\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: clearFeatures\n- * Destroy all features in this layer.\n+ * Method: draw\n+ * Check that a tile should be drawn, and draw it.\n+ * \n+ * Returns:\n+ * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n+ * false.\n */\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy();\n+ draw: function() {\n+ var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (shouldDraw) {\n+ // The layer's reproject option is deprecated.\n+ if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n+ // getBoundsFromBaseLayer is defined in deprecated.js.\n+ this.bounds = this.getBoundsFromBaseLayer(this.position);\n+ }\n+ if (this.isLoading) {\n+ //if we're already loading, send 'reload' instead of 'loadstart'.\n+ this._loadEvent = \"reload\";\n+ } else {\n+ this.isLoading = true;\n+ this._loadEvent = \"loadstart\";\n }\n+ this.renderTile();\n+ this.positionTile();\n+ } else if (shouldDraw === false) {\n+ this.unload();\n }\n+ return shouldDraw;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.GeoRSS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/SphericalMercator.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.SphericalMercator\n- * A mixin for layers that wraps up the pieces neccesary to have a coordinate\n- * conversion for working with commercial APIs which use a spherical\n- * mercator projection. Using this layer as a base layer, additional\n- * layers can be used as overlays if they are in the same projection.\n- *\n- * A layer is given properties of this object by setting the sphericalMercator\n- * property to true.\n- *\n- * More projection information:\n- * - http://spatialreference.org/ref/user/google-projection/\n- *\n- * Proj4 Text:\n- * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0\n- * +k=1.0 +units=m +nadgrids=@null +no_defs\n- *\n- * WKT:\n- * 900913=PROJCS[\"WGS84 / Simple Mercator\", GEOGCS[\"WGS 84\",\n- * DATUM[\"WGS_1984\", SPHEROID[\"WGS_1984\", 6378137.0, 298.257223563]], \n- * PRIMEM[\"Greenwich\", 0.0], UNIT[\"degree\", 0.017453292519943295], \n- * AXIS[\"Longitude\", EAST], AXIS[\"Latitude\", NORTH]],\n- * PROJECTION[\"Mercator_1SP_Google\"], \n- * PARAMETER[\"latitude_of_origin\", 0.0], PARAMETER[\"central_meridian\", 0.0], \n- * PARAMETER[\"scale_factor\", 1.0], PARAMETER[\"false_easting\", 0.0], \n- * PARAMETER[\"false_northing\", 0.0], UNIT[\"m\", 1.0], AXIS[\"x\", EAST],\n- * AXIS[\"y\", NORTH], AUTHORITY[\"EPSG\",\"900913\"]]\n- */\n-OpenLayers.Layer.SphericalMercator = {\n-\n /**\n- * Method: getExtent\n- * Get the map's extent.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} The map extent.\n+ * Method: renderTile\n+ * Internal function to actually initialize the image tile,\n+ * position it correctly, and set its url.\n */\n- getExtent: function() {\n- var extent = null;\n- if (this.sphericalMercator) {\n- extent = this.map.calculateBounds();\n+ renderTile: function() {\n+ if (this.layer.async) {\n+ // Asynchronous image requests call the asynchronous getURL method\n+ // on the layer to fetch an image that covers 'this.bounds'.\n+ var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n+ this.layer.getURLasync(this.bounds, function(url) {\n+ if (id == this.asyncRequestId) {\n+ this.url = url;\n+ this.initImage();\n+ }\n+ }, this);\n } else {\n- extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);\n+ // synchronous image requests get the url immediately.\n+ this.url = this.layer.getURL(this.bounds);\n+ this.initImage();\n }\n- return extent;\n- },\n-\n- /**\n- * Method: getLonLatFromViewPortPx\n- * Get a map location from a pixel location\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>}\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n- * port OpenLayers.Pixel, translated into lon/lat by map lib\n- * If the map lib is not loaded or not centered, returns null\n- */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);\n },\n \n /**\n- * Method: getViewPortPxFromLonLat\n- * Get a pixel location from a map location\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- *\n- * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n- * OpenLayers.LonLat, translated into view port pixels by map lib\n- * If map lib is not loaded or not centered, returns null\n+ * Method: positionTile\n+ * Using the properties currenty set on the layer, position the tile correctly.\n+ * This method is used both by the async and non-async versions of the Tile.Image\n+ * code.\n */\n- getViewPortPxFromLonLat: function(lonlat) {\n- return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);\n+ positionTile: function() {\n+ var style = this.getTile().style,\n+ size = this.frame ? this.size :\n+ this.layer.getImageSize(this.bounds),\n+ ratio = 1;\n+ if (this.layer instanceof OpenLayers.Layer.Grid) {\n+ ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n+ }\n+ style.left = this.position.x + \"px\";\n+ style.top = this.position.y + \"px\";\n+ style.width = Math.round(ratio * size.w) + \"px\";\n+ style.height = Math.round(ratio * size.h) + \"px\";\n },\n \n /** \n- * Method: initMercatorParameters \n- * Set up the mercator parameters on the layer: resolutions,\n- * projection, units.\n+ * Method: clear\n+ * Remove the tile from the DOM, clear it of any image related data so that\n+ * it can be reused in a new location.\n */\n- initMercatorParameters: function() {\n- // set up properties for Mercator - assume EPSG:900913\n- this.RESOLUTIONS = [];\n- var maxResolution = 156543.03390625;\n- for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n- this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);\n+ clear: function() {\n+ OpenLayers.Tile.prototype.clear.apply(this, arguments);\n+ var img = this.imgDiv;\n+ if (img) {\n+ var tile = this.getTile();\n+ if (tile.parentNode === this.layer.div) {\n+ this.layer.div.removeChild(tile);\n+ }\n+ this.setImgSrc();\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"\";\n+ }\n+ OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n }\n- this.units = \"m\";\n- this.projection = this.projection || \"EPSG:900913\";\n+ this.canvasContext = null;\n },\n \n /**\n- * APIMethod: forwardMercator\n- * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.\n- *\n- * Parameters:\n- * lon - {float} \n- * lat - {float}\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.\n- */\n- forwardMercator: (function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(lon, lat) {\n- var point = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- }, gg, sm);\n- return new OpenLayers.LonLat(point.x, point.y);\n- };\n- })(),\n-\n- /**\n- * APIMethod: inverseMercator\n- * Given a x,y in Spherical Mercator, return a point in EPSG:4326.\n- *\n- * Parameters:\n- * x - {float} A map x in Spherical Mercator.\n- * y - {float} A map y in Spherical Mercator.\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.\n+ * Method: getImage\n+ * Returns or creates and returns the tile image.\n */\n- inverseMercator: (function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(x, y) {\n- var point = OpenLayers.Projection.transform({\n- x: x,\n- y: y\n- }, sm, gg);\n- return new OpenLayers.LonLat(point.x, point.y);\n- };\n- })()\n-\n-};\n-/* ======================================================================\n- OpenLayers/Layer/FixedZoomLevels.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- */\n+ getImage: function() {\n+ if (!this.imgDiv) {\n+ this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n \n-/**\n- * Class: OpenLayers.Layer.FixedZoomLevels\n- * Some Layers will already have established zoom levels (like google \n- * or ve). Instead of trying to determine them and populate a resolutions[]\n- * Array with those values, we will hijack the resolution functionality\n- * here.\n- * \n- * When you subclass FixedZoomLevels: \n- * \n- * The initResolutions() call gets nullified, meaning no resolutions[] array \n- * is set up. Which would be a big problem getResolution() in Layer, since \n- * it merely takes map.zoom and indexes into resolutions[]... but....\n- * \n- * The getResolution() call is also overridden. Instead of using the \n- * resolutions[] array, we simply calculate the current resolution based\n- * on the current extent and the current map size. But how will we be able\n- * to calculate the current extent without knowing the resolution...?\n- * \n- * The getExtent() function is also overridden. Instead of calculating extent\n- * based on the center point and the current resolution, we instead \n- * calculate the extent by getting the lonlats at the top-left and \n- * bottom-right by using the getLonLatFromViewPortPx() translation function,\n- * taken from the pixel locations (0,0) and the size of the map. But how \n- * will we be able to do lonlat-px translation without resolution....?\n- * \n- * The getZoomForResolution() method is overridden. Instead of indexing into\n- * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in\n- * the desired resolution. With this extent, we then call getZoomForExtent() \n- * \n- * \n- * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, \n- * it is your responsibility to provide the following three functions:\n- * \n- * - getLonLatFromViewPortPx\n- * - getViewPortPxFromLonLat\n- * - getZoomForExtent\n- * \n- * ...those three functions should generally be provided by any reasonable \n- * API that you might be working from.\n- *\n- */\n-OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n+ var style = this.imgDiv.style;\n+ if (this.frame) {\n+ var left = 0,\n+ top = 0;\n+ if (this.layer.gutter) {\n+ left = this.layer.gutter / this.layer.tileSize.w * 100;\n+ top = this.layer.gutter / this.layer.tileSize.h * 100;\n+ }\n+ style.left = -left + \"%\";\n+ style.top = -top + \"%\";\n+ style.width = (2 * left + 100) + \"%\";\n+ style.height = (2 * top + 100) + \"%\";\n+ }\n+ style.visibility = \"hidden\";\n+ style.opacity = 0;\n+ if (this.layer.opacity < 1) {\n+ style.filter = 'alpha(opacity=' +\n+ (this.layer.opacity * 100) +\n+ ')';\n+ }\n+ style.position = \"absolute\";\n+ if (this.layerAlphaHack) {\n+ // move the image out of sight\n+ style.paddingTop = style.height;\n+ style.height = \"0\";\n+ style.width = \"100%\";\n+ }\n+ if (this.frame) {\n+ this.frame.appendChild(this.imgDiv);\n+ }\n+ }\n \n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /* The following functions must all be implemented */\n- /* by all base layers */\n- /* */\n- /********************************************************/\n+ return this.imgDiv;\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.FixedZoomLevels\n- * Create a new fixed zoom levels layer.\n+ * APIMethod: setImage\n+ * Sets the image element for this tile. This method should only be called\n+ * from beforeload listeners.\n+ *\n+ * Parameters\n+ * img - {HTMLImageElement} The image to use for this tile.\n */\n- initialize: function() {\n- //this class is only just to add the following functions... \n- // nothing to actually do here... but it is probably a good\n- // idea to have layers that use these functions call this \n- // inititalize() anyways, in case at some point we decide we \n- // do want to put some functionality or state in here. \n+ setImage: function(img) {\n+ this.imgDiv = img;\n },\n \n /**\n- * Method: initResolutions\n- * Populate the resolutions array\n+ * Method: initImage\n+ * Creates the content for the frame on the tile.\n */\n- initResolutions: function() {\n-\n- var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];\n-\n- for (var i = 0, len = props.length; i < len; i++) {\n- var property = props[i];\n- this[property] = (this.options[property] != null) ?\n- this.options[property] :\n- this.map[property];\n- }\n-\n- if ((this.minZoomLevel == null) ||\n- (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) {\n- this.minZoomLevel = this.MIN_ZOOM_LEVEL;\n- }\n-\n- //\n- // At this point, we know what the minimum desired zoom level is, and\n- // we must calculate the total number of zoom levels. \n- // \n- // Because we allow for the setting of either the 'numZoomLevels'\n- // or the 'maxZoomLevel' properties... on either the layer or the \n- // map, we have to define some rules to see which we take into\n- // account first in this calculation. \n- //\n- // The following is the precedence list for these properties:\n- // \n- // (1) numZoomLevels set on layer\n- // (2) maxZoomLevel set on layer\n- // (3) numZoomLevels set on map\n- // (4) maxZoomLevel set on map*\n- // (5) none of the above*\n- //\n- // *Note that options (4) and (5) are only possible if the user \n- // _explicitly_ sets the 'numZoomLevels' property on the map to \n- // null, since it is set by default to 16. \n- //\n-\n- //\n- // Note to future: In 3.0, I think we should remove the default \n- // value of 16 for map.numZoomLevels. Rather, I think that value \n- // should be set as a default on the Layer.WMS class. If someone\n- // creates a 3rd party layer and does not specify any 'minZoomLevel', \n- // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly \n- // specified any of those on the map object either.. then I think\n- // it is fair to say that s/he wants all the zoom levels available.\n- // \n- // By making map.numZoomLevels *null* by default, that will be the \n- // case. As it is, I don't feel comfortable changing that right now\n- // as it would be a glaring API change and actually would probably\n- // break many peoples' codes. \n- //\n-\n- //the number of zoom levels we'd like to have.\n- var desiredZoomLevels;\n-\n- //this is the maximum number of zoom levels the layer will allow, \n- // given the specified starting minimum zoom level.\n- var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n-\n- if (((this.options.numZoomLevels == null) &&\n- (this.options.maxZoomLevel != null)) // (2)\n- ||\n- ((this.numZoomLevels == null) &&\n- (this.maxZoomLevel != null)) // (4)\n- ) {\n- //calculate based on specified maxZoomLevel (on layer or map)\n- desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;\n- } else {\n- //calculate based on specified numZoomLevels (on layer or map)\n- // this covers cases (1) and (3)\n- desiredZoomLevels = this.numZoomLevels;\n+ initImage: function() {\n+ if (!this.url && !this.imgDiv) {\n+ // fast path out - if there is no tile url and no previous image\n+ this.isLoading = false;\n+ return;\n }\n-\n- if (desiredZoomLevels != null) {\n- //Now that we know what we would *like* the number of zoom levels\n- // to be, based on layer or map options, we have to make sure that\n- // it does not conflict with the actual limit, as specified by \n- // the constants on the layer itself (and calculated into the\n- // 'limitZoomLevels' variable). \n- this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);\n+ this.events.triggerEvent('beforeload');\n+ this.layer.div.appendChild(this.getTile());\n+ this.events.triggerEvent(this._loadEvent);\n+ var img = this.getImage();\n+ var src = img.getAttribute('src') || '';\n+ if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n+ this._loadTimeout = window.setTimeout(\n+ OpenLayers.Function.bind(this.onImageLoad, this), 0\n+ );\n } else {\n- // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was \n- // set on either the layer or the map. So we just use the \n- // maximum limit as calculated by the layer's constants.\n- this.numZoomLevels = limitZoomLevels;\n- }\n-\n- //now that the 'numZoomLevels' is appropriately, safely set, \n- // we go back and re-calculate the 'maxZoomLevel'.\n- this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n-\n- if (this.RESOLUTIONS != null) {\n- var resolutionsIndex = 0;\n- this.resolutions = [];\n- for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n- this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];\n+ this.stopLoading();\n+ if (this.crossOriginKeyword) {\n+ img.removeAttribute(\"crossorigin\");\n }\n- this.maxResolution = this.resolutions[0];\n- this.minResolution = this.resolutions[this.resolutions.length - 1];\n+ OpenLayers.Event.observe(img, \"load\",\n+ OpenLayers.Function.bind(this.onImageLoad, this)\n+ );\n+ OpenLayers.Event.observe(img, \"error\",\n+ OpenLayers.Function.bind(this.onImageError, this)\n+ );\n+ this.imageReloadAttempts = 0;\n+ this.setImgSrc(this.url);\n }\n },\n \n /**\n- * APIMethod: getResolution\n- * Get the current map resolution\n- * \n- * Returns:\n- * {Float} Map units per Pixel\n+ * Method: setImgSrc\n+ * Sets the source for the tile image\n+ *\n+ * Parameters:\n+ * url - {String} or undefined to hide the image\n */\n- getResolution: function() {\n-\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);\n+ setImgSrc: function(url) {\n+ var img = this.imgDiv;\n+ if (url) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ // don't set crossOrigin if the url is a data URL\n+ if (this.crossOriginKeyword) {\n+ if (url.substr(0, 5) !== 'data:') {\n+ img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n+ } else {\n+ img.removeAttribute(\"crossorigin\");\n+ }\n+ }\n+ img.src = url;\n } else {\n- var resolution = null;\n-\n- var viewSize = this.map.getSize();\n- var extent = this.getExtent();\n-\n- if ((viewSize != null) && (extent != null)) {\n- resolution = Math.max(extent.getWidth() / viewSize.w,\n- extent.getHeight() / viewSize.h);\n+ // Remove reference to the image, and leave it to the browser's\n+ // caching and garbage collection.\n+ this.stopLoading();\n+ this.imgDiv = null;\n+ if (img.parentNode) {\n+ img.parentNode.removeChild(img);\n }\n- return resolution;\n }\n },\n \n /**\n- * APIMethod: getExtent\n- * Calculates using px-> lonlat translation functions on tl and br \n- * corners of viewport\n- * \n+ * Method: getTile\n+ * Get the tile's markup.\n+ *\n * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n- * bounds of the current viewPort.\n+ * {DOMElement} The tile's markup\n */\n- getExtent: function() {\n- var size = this.map.getSize();\n- var tl = this.getLonLatFromViewPortPx({\n- x: 0,\n- y: 0\n- });\n- var br = this.getLonLatFromViewPortPx({\n- x: size.w,\n- y: size.h\n- });\n-\n- if ((tl != null) && (br != null)) {\n- return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);\n- } else {\n- return null;\n- }\n+ getTile: function() {\n+ return this.frame ? this.frame : this.getImage();\n },\n \n /**\n- * Method: getZoomForResolution\n- * Get the zoom level for a given resolution\n- *\n- * Parameters:\n- * resolution - {Float}\n+ * Method: createBackBuffer\n+ * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n+ * of the tile's markup, because we want to avoid the reloading of the\n+ * image. So we clone the frame, and steal the image from the tile.\n *\n * Returns:\n- * {Integer} A suitable zoom level for the specified resolution.\n- * If no baselayer is set, returns null.\n+ * {DOMElement} The markup, or undefined if the tile has no image\n+ * or if it's currently loading.\n */\n- getZoomForResolution: function(resolution) {\n-\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);\n+ createBackBuffer: function() {\n+ if (!this.imgDiv || this.isLoading) {\n+ return;\n+ }\n+ var backBuffer;\n+ if (this.frame) {\n+ backBuffer = this.frame.cloneNode(false);\n+ backBuffer.appendChild(this.imgDiv);\n } else {\n- var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n- return this.getZoomForExtent(extent);\n+ backBuffer = this.imgDiv;\n }\n+ this.imgDiv = null;\n+ return backBuffer;\n },\n \n+ /**\n+ * Method: onImageLoad\n+ * Handler for the image onload event\n+ */\n+ onImageLoad: function() {\n+ var img = this.imgDiv;\n+ this.stopLoading();\n+ img.style.visibility = 'inherit';\n+ img.style.opacity = this.layer.opacity;\n+ this.isLoading = false;\n+ this.canvasContext = null;\n+ this.events.triggerEvent(\"loadend\");\n \n-\n-\n- /********************************************************/\n- /* */\n- /* Translation Functions */\n- /* */\n- /* The following functions translate GMaps and OL */\n- /* formats for Pixel, LonLat, Bounds, and Zoom */\n- /* */\n- /********************************************************/\n-\n-\n- //\n- // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom\n- //\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter =\n+ \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n+ img.src + \"', sizingMethod='scale')\";\n+ }\n+ },\n \n /**\n- * Method: getOLZoomFromMapObjectZoom\n- * Get the OL zoom index from the map object zoom level\n- *\n- * Parameters:\n- * moZoom - {Integer}\n- * \n- * Returns:\n- * {Integer} An OpenLayers Zoom level, translated from the passed in zoom\n- * Returns null if null value is passed in\n+ * Method: onImageError\n+ * Handler for the image onerror event\n */\n- getOLZoomFromMapObjectZoom: function(moZoom) {\n- var zoom = null;\n- if (moZoom != null) {\n- zoom = moZoom - this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.map.baseLayer.getZoomForResolution(\n- this.getResolutionForZoom(zoom)\n- );\n+ onImageError: function() {\n+ var img = this.imgDiv;\n+ if (img.src != null) {\n+ this.imageReloadAttempts++;\n+ if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n+ this.setImgSrc(this.layer.getURL(this.bounds));\n+ } else {\n+ OpenLayers.Element.addClass(img, \"olImageLoadError\");\n+ this.events.triggerEvent(\"loaderror\");\n+ this.onImageLoad();\n }\n }\n- return zoom;\n },\n \n /**\n- * Method: getMapObjectZoomFromOLZoom\n- * Get the map object zoom level from the OL zoom level\n+ * Method: stopLoading\n+ * Stops a loading sequence so <onImageLoad> won't be executed.\n+ */\n+ stopLoading: function() {\n+ OpenLayers.Event.stopObservingElement(this.imgDiv);\n+ window.clearTimeout(this._loadTimeout);\n+ delete this._loadTimeout;\n+ },\n+\n+ /**\n+ * APIMethod: getCanvasContext\n+ * Returns a canvas context associated with the tile image (with\n+ * the image drawn on it).\n+ * Returns undefined if the browser does not support canvas, if\n+ * the tile has no image or if it's currently loading.\n+ *\n+ * The function returns a canvas context instance but the\n+ * underlying canvas is still available in the 'canvas' property:\n+ * (code)\n+ * var context = tile.getCanvasContext();\n+ * if (context) {\n+ * var data = context.canvas.toDataURL('image/jpeg');\n+ * }\n+ * (end)\n *\n- * Parameters:\n- * olZoom - {Integer}\n- * \n * Returns:\n- * {Integer} A MapObject level, translated from the passed in olZoom\n- * Returns null if null value is passed in\n+ * {Boolean}\n */\n- getMapObjectZoomFromOLZoom: function(olZoom) {\n- var zoom = null;\n- if (olZoom != null) {\n- zoom = olZoom + this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.getZoomForResolution(\n- this.map.baseLayer.getResolutionForZoom(zoom)\n- );\n+ getCanvasContext: function() {\n+ if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n+ if (!this.canvasContext) {\n+ var canvas = document.createElement(\"canvas\");\n+ canvas.width = this.size.w;\n+ canvas.height = this.size.h;\n+ this.canvasContext = canvas.getContext(\"2d\");\n+ this.canvasContext.drawImage(this.imgDiv, 0, 0);\n }\n+ return this.canvasContext;\n }\n- return zoom;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n+ CLASS_NAME: \"OpenLayers.Tile.Image\"\n+\n });\n \n+/** \n+ * Constant: OpenLayers.Tile.Image.IMAGE\n+ * {HTMLImageElement} The image for a tile.\n+ */\n+OpenLayers.Tile.Image.IMAGE = (function() {\n+ var img = new Image();\n+ img.className = \"olTileImage\";\n+ // avoid image gallery menu in IE6\n+ img.galleryImg = \"no\";\n+ return img;\n+}());\n+\n /* ======================================================================\n- OpenLayers/Layer/Google.js\n+ OpenLayers/Layer/Grid.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Layer/SphericalMercator.js\n- * @requires OpenLayers/Layer/EventPane.js\n- * @requires OpenLayers/Layer/FixedZoomLevels.js\n- * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Layer/HTTPRequest.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Google\n- * \n- * Provides a wrapper for Google's Maps API\n- * Normally the Terms of Use for this API do not allow wrapping, but Google\n- * have provided written consent to OpenLayers for this - see email in \n- * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html\n- * \n+ * Class: OpenLayers.Layer.Grid\n+ * Base class for layers that use a lattice of tiles. Create a new grid\n+ * layer with the <OpenLayers.Layer.Grid> constructor.\n+ *\n * Inherits from:\n- * - <OpenLayers.Layer.SphericalMercator>\n- * - <OpenLayers.Layer.EventPane>\n- * - <OpenLayers.Layer.FixedZoomLevels>\n+ * - <OpenLayers.Layer.HTTPRequest>\n */\n-OpenLayers.Layer.Google = OpenLayers.Class(\n- OpenLayers.Layer.EventPane,\n- OpenLayers.Layer.FixedZoomLevels, {\n-\n- /** \n- * Constant: MIN_ZOOM_LEVEL\n- * {Integer} 0 \n- */\n- MIN_ZOOM_LEVEL: 0,\n-\n- /** \n- * Constant: MAX_ZOOM_LEVEL\n- * {Integer} 21\n- */\n- MAX_ZOOM_LEVEL: 21,\n-\n- /** \n- * Constant: RESOLUTIONS\n- * {Array(Float)} Hardcode these resolutions so that they are more closely\n- * tied with the standard wms projection\n- */\n- RESOLUTIONS: [\n- 1.40625,\n- 0.703125,\n- 0.3515625,\n- 0.17578125,\n- 0.087890625,\n- 0.0439453125,\n- 0.02197265625,\n- 0.010986328125,\n- 0.0054931640625,\n- 0.00274658203125,\n- 0.001373291015625,\n- 0.0006866455078125,\n- 0.00034332275390625,\n- 0.000171661376953125,\n- 0.0000858306884765625,\n- 0.00004291534423828125,\n- 0.00002145767211914062,\n- 0.00001072883605957031,\n- 0.00000536441802978515,\n- 0.00000268220901489257,\n- 0.0000013411045074462891,\n- 0.00000067055225372314453\n- ],\n-\n- /**\n- * APIProperty: type\n- * {GMapType}\n- */\n- type: null,\n-\n- /**\n- * APIProperty: wrapDateLine\n- * {Boolean} Allow user to pan forever east/west. Default is true. \n- * Setting this to false only restricts panning if \n- * <sphericalMercator> is true. \n- */\n- wrapDateLine: true,\n-\n- /**\n- * APIProperty: sphericalMercator\n- * {Boolean} Should the map act as a mercator-projected map? This will\n- * cause all interactions with the map to be in the actual map \n- * projection, which allows support for vector drawing, overlaying \n- * other maps, etc. \n- */\n- sphericalMercator: false,\n-\n- /**\n- * Property: version\n- * {Number} The version of the Google Maps API\n- */\n- version: null,\n-\n- /** \n- * Constructor: OpenLayers.Layer.Google\n- * \n- * Parameters:\n- * name - {String} A name for the layer.\n- * options - {Object} An optional object whose properties will be set\n- * on the layer.\n- */\n- initialize: function(name, options) {\n- options = options || {};\n- if (!options.version) {\n- options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\";\n- }\n- var mixin = OpenLayers.Layer.Google[\"v\" +\n- options.version.replace(/\\./g, \"_\")];\n- if (mixin) {\n- OpenLayers.Util.applyDefaults(options, mixin);\n- } else {\n- throw \"Unsupported Google Maps API version: \" + options.version;\n- }\n-\n- OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n- if (options.maxExtent) {\n- options.maxExtent = options.maxExtent.clone();\n- }\n-\n- OpenLayers.Layer.EventPane.prototype.initialize.apply(this,\n- [name, options]);\n- OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,\n- [name, options]);\n-\n- if (this.sphericalMercator) {\n- OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n- this.initMercatorParameters();\n- }\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Google>} An exact clone of this layer\n- */\n- clone: function() {\n- /**\n- * This method isn't intended to be called by a subclass and it\n- * doesn't call the same method on the superclass. We don't call\n- * the super's clone because we don't want properties that are set\n- * on this layer after initialize (i.e. this.mapObject etc.).\n- */\n- return new OpenLayers.Layer.Google(\n- this.name, this.getOptions()\n- );\n- },\n-\n- /**\n- * APIMethod: setVisibility\n- * Set the visibility flag for the layer and hide/show & redraw \n- * accordingly. Fire event unless otherwise specified\n- * \n- * Note that visibility is no longer simply whether or not the layer's\n- * style.display is set to \"block\". Now we store a 'visibility' state \n- * property on the layer class, this allows us to remember whether or \n- * not we *desire* for a layer to be visible. In the case where the \n- * map's resolution is out of the layer's range, this desire may be \n- * subverted.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the layer (if in range)\n- */\n- setVisibility: function(visible) {\n- // sharing a map container, opacity has to be set per layer\n- var opacity = this.opacity == null ? 1 : this.opacity;\n- OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n- this.setOpacity(opacity);\n- },\n-\n- /** \n- * APIMethod: display\n- * Hide or show the Layer\n- * \n- * Parameters:\n- * visible - {Boolean}\n- */\n- display: function(visible) {\n- if (!this._dragging) {\n- this.setGMapVisibility(visible);\n- }\n- OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);\n- },\n-\n- /**\n- * Method: moveTo\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- this._dragging = dragging;\n- OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n- delete this._dragging;\n- },\n-\n- /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n- * \n- * Parameters:\n- * opacity - {Float}\n- */\n- setOpacity: function(opacity) {\n- if (opacity !== this.opacity) {\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- });\n- }\n- this.opacity = opacity;\n- }\n- // Though this layer's opacity may not change, we're sharing a container\n- // and need to update the opacity for the entire container.\n- if (this.getVisibility()) {\n- var container = this.getMapContainer();\n- OpenLayers.Util.modifyDOMElement(\n- container, null, null, null, null, null, null, opacity\n- );\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up this layer.\n- */\n- destroy: function() {\n- /**\n- * We have to override this method because the event pane destroy\n- * deletes the mapObject reference before removing this layer from\n- * the map.\n- */\n- if (this.map) {\n- this.setGMapVisibility(false);\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache && cache.count <= 1) {\n- this.removeGMapElements();\n- }\n- }\n- OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: removeGMapElements\n- * Remove all elements added to the dom. This should only be called if\n- * this is the last of the Google layers for the given map.\n- */\n- removeGMapElements: function() {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // remove shared elements from dom\n- var container = this.mapObject && this.getMapContainer();\n- if (container && container.parentNode) {\n- container.parentNode.removeChild(container);\n- }\n- var termsOfUse = cache.termsOfUse;\n- if (termsOfUse && termsOfUse.parentNode) {\n- termsOfUse.parentNode.removeChild(termsOfUse);\n- }\n- var poweredBy = cache.poweredBy;\n- if (poweredBy && poweredBy.parentNode) {\n- poweredBy.parentNode.removeChild(poweredBy);\n- }\n- if (this.mapObject && window.google && google.maps &&\n- google.maps.event && google.maps.event.clearListeners) {\n- google.maps.event.clearListeners(this.mapObject, 'tilesloaded');\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: removeMap\n- * On being removed from the map, also remove termsOfUse and poweredBy divs\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- // hide layer before removing\n- if (this.visibility && this.mapObject) {\n- this.setGMapVisibility(false);\n- }\n- // check to see if last Google layer in this map\n- var cache = OpenLayers.Layer.Google.cache[map.id];\n- if (cache) {\n- if (cache.count <= 1) {\n- this.removeGMapElements();\n- delete OpenLayers.Layer.Google.cache[map.id];\n- } else {\n- // decrement the layer count\n- --cache.count;\n- }\n- }\n- // remove references to gmap elements\n- delete this.termsOfUse;\n- delete this.poweredBy;\n- delete this.mapObject;\n- delete this.dragObject;\n- OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n- /**\n- * APIMethod: getOLBoundsFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the \n- * passed-in MapObject Bounds.\n- * Returns null if null value is passed in.\n- */\n- getOLBoundsFromMapObjectBounds: function(moBounds) {\n- var olBounds = null;\n- if (moBounds != null) {\n- var sw = moBounds.getSouthWest();\n- var ne = moBounds.getNorthEast();\n- if (this.sphericalMercator) {\n- sw = this.forwardMercator(sw.lng(), sw.lat());\n- ne = this.forwardMercator(ne.lng(), ne.lat());\n- } else {\n- sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n- ne = new OpenLayers.LonLat(ne.lng(), ne.lat());\n- }\n- olBounds = new OpenLayers.Bounds(sw.lon,\n- sw.lat,\n- ne.lon,\n- ne.lat);\n- }\n- return olBounds;\n- },\n-\n- /** \n- * APIMethod: getWarningHTML\n- * \n- * Returns: \n- * {String} String with information on why layer is broken, how to get\n- * it working.\n- */\n- getWarningHTML: function() {\n- return OpenLayers.i18n(\"googleWarning\");\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n \n+ /**\n+ * APIProperty: tileSize\n+ * {<OpenLayers.Size>}\n+ */\n+ tileSize: null,\n \n- // Get&Set Center, Zoom\n+ /**\n+ * Property: tileOriginCorner\n+ * {String} If the <tileOrigin> property is not provided, the tile origin \n+ * will be derived from the layer's <maxExtent>. The corner of the \n+ * <maxExtent> used is determined by this property. Acceptable values\n+ * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n+ * (bottom right). Default is \"bl\".\n+ */\n+ tileOriginCorner: \"bl\",\n \n- /**\n- * APIMethod: getMapObjectCenter\n- * \n- * Returns: \n- * {Object} The mapObject's current center in Map Object format\n- */\n- getMapObjectCenter: function() {\n- return this.mapObject.getCenter();\n- },\n+ /**\n+ * APIProperty: tileOrigin\n+ * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n+ * If provided, requests for tiles at all resolutions will be aligned\n+ * with this location (no tiles shall overlap this location). If\n+ * not provided, the grid of tiles will be aligned with the layer's\n+ * <maxExtent>. Default is ``null``.\n+ */\n+ tileOrigin: null,\n \n- /** \n- * APIMethod: getMapObjectZoom\n- * \n- * Returns:\n- * {Integer} The mapObject's current zoom, in Map Object format\n- */\n- getMapObjectZoom: function() {\n- return this.mapObject.getZoom();\n- },\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer, if supported by the tile class.\n+ */\n+ tileOptions: null,\n \n+ /**\n+ * APIProperty: tileClass\n+ * {<OpenLayers.Tile>} The tile class to use for this layer.\n+ * Defaults is OpenLayers.Tile.Image.\n+ */\n+ tileClass: OpenLayers.Tile.Image,\n \n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n+ /**\n+ * Property: grid\n+ * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is \n+ * an array of tiles.\n+ */\n+ grid: null,\n \n+ /**\n+ * APIProperty: singleTile\n+ * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n+ * will be loaded. The tile's size will be determined by the 'ratio'\n+ * property. When the tile is dragged such that it does not cover the \n+ * entire viewport, it is reloaded.\n+ */\n+ singleTile: false,\n \n- // LonLat\n+ /** APIProperty: ratio\n+ * {Float} Used only when in single-tile mode, this specifies the \n+ * ratio of the size of the single tile to the size of the map.\n+ * Default value is 1.5.\n+ */\n+ ratio: 1.5,\n \n- /**\n- * APIMethod: getLongitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Longitude of the given MapObject LonLat\n- */\n- getLongitudeFromMapObjectLonLat: function(moLonLat) {\n- return this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :\n- moLonLat.lng();\n- },\n+ /**\n+ * APIProperty: buffer\n+ * {Integer} Used only when in gridded mode, this specifies the number of \n+ * extra rows and colums of tiles on each side which will\n+ * surround the minimum grid tiles to cover the map.\n+ * For very slow loading layers, a larger value may increase\n+ * performance somewhat when dragging, but will increase bandwidth\n+ * use significantly. \n+ */\n+ buffer: 0,\n \n- /**\n- * APIMethod: getLatitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Latitude of the given MapObject LonLat\n- */\n- getLatitudeFromMapObjectLonLat: function(moLonLat) {\n- var lat = this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :\n- moLonLat.lat();\n- return lat;\n- },\n+ /**\n+ * APIProperty: transitionEffect\n+ * {String} The transition effect to use when the map is zoomed.\n+ * Two posible values:\n+ *\n+ * \"resize\" - Existing tiles are resized on zoom to provide a visual\n+ * effect of the zoom having taken place immediately. As the\n+ * new tiles become available, they are drawn on top of the\n+ * resized tiles (this is the default setting).\n+ * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n+ * base layer. New tiles for the base layer will cover existing tiles.\n+ * This setting is recommended when having an overlay duplicated during\n+ * the transition is undesirable (e.g. street labels or big transparent\n+ * fills). \n+ * null - No transition effect.\n+ *\n+ * Using \"resize\" on non-opaque layers can cause undesired visual\n+ * effects. Set transitionEffect to null in this case.\n+ */\n+ transitionEffect: \"resize\",\n \n- // Pixel\n+ /**\n+ * APIProperty: numLoadingTiles\n+ * {Integer} How many tiles are still loading?\n+ */\n+ numLoadingTiles: 0,\n \n- /**\n- * APIMethod: getXFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} X value of the MapObject Pixel\n- */\n- getXFromMapObjectPixel: function(moPixel) {\n- return moPixel.x;\n- },\n+ /**\n+ * Property: serverResolutions\n+ * {Array(Number}} This property is documented in subclasses as\n+ * an API property.\n+ */\n+ serverResolutions: null,\n \n- /**\n- * APIMethod: getYFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} Y value of the MapObject Pixel\n- */\n- getYFromMapObjectPixel: function(moPixel) {\n- return moPixel.y;\n- },\n+ /**\n+ * Property: loading\n+ * {Boolean} Indicates if tiles are being loaded.\n+ */\n+ loading: false,\n \n- CLASS_NAME: \"OpenLayers.Layer.Google\"\n- });\n+ /**\n+ * Property: backBuffer\n+ * {DOMElement} The back buffer.\n+ */\n+ backBuffer: null,\n \n-/**\n- * Property: OpenLayers.Layer.Google.cache\n- * {Object} Cache for elements that should only be created once per map.\n- */\n-OpenLayers.Layer.Google.cache = {};\n+ /**\n+ * Property: gridResolution\n+ * {Number} The resolution of the current grid. Used for backbuffer and\n+ * client zoom. This property is updated every time the grid is\n+ * initialized.\n+ */\n+ gridResolution: null,\n \n+ /**\n+ * Property: backBufferResolution\n+ * {Number} The resolution of the current back buffer. This property is\n+ * updated each time a back buffer is created.\n+ */\n+ backBufferResolution: null,\n \n-/**\n- * Constant: OpenLayers.Layer.Google.v2\n- * \n- * Mixin providing functionality specific to the Google Maps API v2.\n- * \n- * This API has been deprecated by Google.\n- * Developers are encouraged to migrate to v3 of the API; support for this\n- * is provided by <OpenLayers.Layer.Google.v3>\n- */\n-OpenLayers.Layer.Google.v2 = {\n+ /**\n+ * Property: backBufferLonLat\n+ * {Object} The top-left corner of the current back buffer. Includes lon\n+ * and lat properties. This object is updated each time a back buffer\n+ * is created.\n+ */\n+ backBufferLonLat: null,\n \n /**\n- * Property: termsOfUse\n- * {DOMElement} Div for Google's copyright and terms of use link\n+ * Property: backBufferTimerId\n+ * {Number} The id of the back buffer timer. This timer is used to\n+ * delay the removal of the back buffer, thereby preventing\n+ * flash effects caused by tile animation.\n */\n- termsOfUse: null,\n+ backBufferTimerId: null,\n \n /**\n- * Property: poweredBy\n- * {DOMElement} Div for Google's powered by logo and link\n+ * APIProperty: removeBackBufferDelay\n+ * {Number} Delay for removing the backbuffer when all tiles have finished\n+ * loading. Can be set to 0 when no css opacity transitions for the\n+ * olTileImage class are used. Default is 0 for <singleTile> layers,\n+ * 2500 for tiled layers. See <className> for more information on\n+ * tile animation.\n */\n- poweredBy: null,\n+ removeBackBufferDelay: null,\n \n /**\n- * Property: dragObject\n- * {GDraggableObject} Since 2.93, Google has exposed the ability to get\n- * the maps GDraggableObject. We can now use this for smooth panning\n+ * APIProperty: className\n+ * {String} Name of the class added to the layer div. If not set in the\n+ * options passed to the constructor then className defaults to\n+ * \"olLayerGridSingleTile\" for single tile layers (see <singleTile>),\n+ * and \"olLayerGrid\" for non single tile layers.\n+ *\n+ * Note:\n+ *\n+ * The displaying of tiles is not animated by default for single tile\n+ * layers - OpenLayers' default theme (style.css) includes this:\n+ * (code)\n+ * .olLayerGrid .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * To animate tile displaying for any grid layer the following\n+ * CSS rule can be used:\n+ * (code)\n+ * .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * In that case, to avoid flash effects, <removeBackBufferDelay>\n+ * should not be zero.\n */\n- dragObject: null,\n+ className: null,\n \n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners. If we can't \n- * load GMap2, then display a warning message.\n+ /**\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported event types:\n+ * addtile - Triggered when a tile is added to this layer. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that has been added.\n+ * tileloadstart - Triggered when a tile starts loading. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that starts loading.\n+ * tileloaded - Triggered when each new tile is\n+ * loaded, as a means of progress update to listeners.\n+ * listeners can access 'numLoadingTiles' if they wish to keep\n+ * track of the loading progress. Listeners are called with an object\n+ * with a 'tile' property as first argument, making the loaded tile\n+ * available to the listener, and an 'aborted' property, which will be\n+ * true when loading was aborted and no tile data is available.\n+ * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n+ * still hidden) if a tile failed to load. Listeners receive an object\n+ * as first argument, which has a tile property that references the\n+ * tile that could not be loaded.\n+ * retile - Triggered when the layer recreates its tile grid.\n */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = G_NORMAL_MAP;\n- }\n- var mapObject, termsOfUse, poweredBy;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- termsOfUse = cache.termsOfUse;\n- poweredBy = cache.poweredBy;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n \n- var container = this.map.viewPortDiv;\n- var div = document.createElement(\"div\");\n- div.id = this.map.id + \"_GMap2Container\";\n- div.style.position = \"absolute\";\n- div.style.width = \"100%\";\n- div.style.height = \"100%\";\n- container.appendChild(div);\n+ /**\n+ * Property: gridLayout\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n+ */\n+ gridLayout: null,\n \n- // create GMap and shuffle elements\n- try {\n- mapObject = new GMap2(div);\n+ /**\n+ * Property: rowSign\n+ * {Number} 1 for grids starting at the top, -1 for grids starting at the\n+ * bottom. This is used for several grid index and offset calculations.\n+ */\n+ rowSign: null,\n \n- // move the ToS and branding stuff up to the container div\n- termsOfUse = div.lastChild;\n- container.appendChild(termsOfUse);\n- termsOfUse.style.zIndex = \"1100\";\n- termsOfUse.style.right = \"\";\n- termsOfUse.style.bottom = \"\";\n- termsOfUse.className = \"olLayerGoogleCopyright\";\n+ /**\n+ * Property: transitionendEvents\n+ * {Array} Event names for transitionend\n+ */\n+ transitionendEvents: [\n+ 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n+ 'oTransitionEnd'\n+ ],\n \n- poweredBy = div.lastChild;\n- container.appendChild(poweredBy);\n- poweredBy.style.zIndex = \"1100\";\n- poweredBy.style.right = \"\";\n- poweredBy.style.bottom = \"\";\n- poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\";\n+ /**\n+ * Constructor: OpenLayers.Layer.Grid\n+ * Create a new grid layer\n+ *\n+ * Parameters:\n+ * name - {String}\n+ * url - {String}\n+ * params - {Object}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n+ */\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n+ arguments);\n+ this.grid = [];\n+ this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n \n- } catch (e) {\n- throw (e);\n- }\n- // cache elements for use by any other google layers added to\n- // this same map\n- OpenLayers.Layer.Google.cache[this.map.id] = {\n- mapObject: mapObject,\n- termsOfUse: termsOfUse,\n- poweredBy: poweredBy,\n- count: 1\n- };\n- }\n+ this.initProperties();\n \n- this.mapObject = mapObject;\n- this.termsOfUse = termsOfUse;\n- this.poweredBy = poweredBy;\n+ this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n+ },\n \n- // ensure this layer type is one of the mapObject types\n- if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),\n- this.type) === -1) {\n- this.mapObject.addMapType(this.type);\n+ /**\n+ * Method: initProperties\n+ * Set any properties that depend on the value of singleTile.\n+ * Currently sets removeBackBufferDelay and className\n+ */\n+ initProperties: function() {\n+ if (this.options.removeBackBufferDelay === undefined) {\n+ this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n }\n \n- //since v 2.93 getDragObject is now available.\n- if (typeof mapObject.getDragObject == \"function\") {\n- this.dragObject = mapObject.getDragObject();\n- } else {\n- this.dragPanMapObject = null;\n+ if (this.options.className === undefined) {\n+ this.className = this.singleTile ? 'olLayerGridSingleTile' :\n+ 'olLayerGrid';\n }\n+ },\n \n- if (this.isBaseLayer === false) {\n- this.setGMapVisibility(this.div.style.display !== \"none\");\n- }\n+ /**\n+ * Method: setMap\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} The map.\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n+ OpenLayers.Element.addClass(this.div, this.className);\n+ },\n \n+ /**\n+ * Method: removeMap\n+ * Called when the layer is removed from the map.\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} The map.\n+ */\n+ removeMap: function(map) {\n+ this.removeBackBuffer();\n },\n \n /**\n- * APIMethod: onMapResize\n+ * APIMethod: destroy\n+ * Deconstruct the layer and clear the grid.\n */\n- onMapResize: function() {\n- // workaround for resizing of invisible or not yet fully loaded layers\n- // where GMap2.checkResize() does not work. We need to load the GMap\n- // for the old div size, then checkResize(), and then call\n- // layer.moveTo() to trigger GMap.setCenter() (which will finish\n- // the GMap initialization).\n- if (this.visibility && this.mapObject.isLoaded()) {\n- this.mapObject.checkResize();\n- } else {\n- if (!this._resized) {\n- var layer = this;\n- var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n- GEvent.removeListener(handle);\n- delete layer._resized;\n- layer.mapObject.checkResize();\n- layer.moveTo(layer.map.getCenter(), layer.map.getZoom());\n- });\n- }\n- this._resized = true;\n- }\n+ destroy: function() {\n+ this.removeBackBuffer();\n+ this.clearGrid();\n+\n+ this.grid = null;\n+ this.tileSize = null;\n+ OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n+ * APIMethod: mergeNewParams\n+ * Refetches tiles with new params merged, keeping a backbuffer. Each\n+ * loading new tile will have a css class of '.olTileReplacing'. If a\n+ * stylesheet applies a 'display: none' style to that class, any fade-in\n+ * transition will not apply, and backbuffers for each tile will be removed\n+ * as soon as the tile is loaded.\n * \n * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n+ * newParams - {Object}\n+ *\n+ * Returns:\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject.getContainer();\n- if (visible === true) {\n- this.mapObject.setMapType(this.type);\n- container.style.display = \"\";\n- this.termsOfUse.style.left = \"\";\n- this.termsOfUse.style.display = \"\";\n- this.poweredBy.style.display = \"\";\n- cache.displayed = this.id;\n- } else {\n- if (cache.displayed === this.id) {\n- delete cache.displayed;\n- }\n- if (!cache.displayed) {\n- container.style.display = \"none\";\n- this.termsOfUse.style.display = \"none\";\n- // move ToU far to the left in addition to setting display\n- // to \"none\", because at the end of the GMap2 load\n- // sequence, display: none will be unset and ToU would be\n- // visible after loading a map with a google layer that is\n- // initially hidden. \n- this.termsOfUse.style.left = \"-9999px\";\n- this.poweredBy.style.display = \"none\";\n+\n+ /**\n+ * Method: clearGrid\n+ * Go through and remove all tiles from the grid, calling\n+ * destroy() on each of them to kill circular references\n+ */\n+ clearGrid: function() {\n+ if (this.grid) {\n+ for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n+ var row = this.grid[iRow];\n+ for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n+ var tile = row[iCol];\n+ this.destroyTile(tile);\n }\n }\n+ this.grid = [];\n+ this.gridResolution = null;\n+ this.gridLayout = null;\n }\n },\n \n /**\n- * Method: getMapContainer\n+ * APIMethod: addOptions\n * \n- * Returns:\n- * {DOMElement} the GMap container's div\n+ * Parameters:\n+ * newOptions - {Object}\n+ * reinitialize - {Boolean} If set to true, and if resolution options of the\n+ * current baseLayer were changed, the map will be recentered to make\n+ * sure that it is displayed with a valid resolution, and a\n+ * changebaselayer event will be triggered.\n */\n- getMapContainer: function() {\n- return this.mapObject.getContainer();\n+ addOptions: function(newOptions, reinitialize) {\n+ var singleTileChanged = newOptions.singleTile !== undefined &&\n+ newOptions.singleTile !== this.singleTile;\n+ OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n+ if (this.map && singleTileChanged) {\n+ this.initProperties();\n+ this.clearGrid();\n+ this.tileSize = this.options.tileSize;\n+ this.setTileSize();\n+ this.moveTo(null, true);\n+ }\n },\n \n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n+ * APIMethod: clone\n+ * Create a clone of this layer\n+ *\n * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n+ * obj - {Object} Is this ever used?\n * \n * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n+ * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid\n */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),\n- new GLatLng(ne.lat, ne.lon));\n- }\n- return moBounds;\n- },\n+ clone: function(obj) {\n \n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Grid(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n \n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n \n+ // copy/set any non-init, non-simple values here\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone();\n+ }\n \n- // Get&Set Center, Zoom\n+ // we do not want to copy reference to grid, so we make a new array\n+ obj.grid = [];\n+ obj.gridResolution = null;\n+ // same for backbuffer\n+ obj.backBuffer = null;\n+ obj.backBufferTimerId = null;\n+ obj.loading = false;\n+ obj.numLoadingTiles = 0;\n \n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n- * \n- * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n- */\n- setMapObjectCenter: function(center, zoom) {\n- this.mapObject.setCenter(center, zoom);\n+ return obj;\n },\n \n /**\n- * APIMethod: dragPanMapObject\n- * \n+ * Method: moveTo\n+ * This function is called whenever the map is moved. All the moving\n+ * of actual 'tiles' is done by the map, but moveTo's role is to accept\n+ * a bounds and make sure the data that that bounds requires is pre-loaded.\n+ *\n * Parameters:\n- * dX - {Integer}\n- * dY - {Integer}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- dragPanMapObject: function(dX, dY) {\n- this.dragObject.moveBy(new GSize(-dX, dY));\n- },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n \n+ OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n \n- // LonLat - Pixel Translation\n+ bounds = bounds || this.map.getExtent();\n \n- /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n- */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- return this.mapObject.fromContainerPixelToLatLng(moPixel);\n+ if (bounds != null) {\n+\n+ // if grid is empty or zoom has changed, we *must* re-tile\n+ var forceReTile = !this.grid.length || zoomChanged;\n+\n+ // total bounds of the tiles\n+ var tilesBounds = this.getTilesBounds();\n+\n+ // the new map resolution\n+ var resolution = this.map.getResolution();\n+\n+ // the server-supported resolution for the new map resolution\n+ var serverResolution = this.getServerResolution(resolution);\n+\n+ if (this.singleTile) {\n+\n+ // We want to redraw whenever even the slightest part of the \n+ // current bounds is not contained by our tile.\n+ // (thus, we do not specify partial -- its default is false)\n+\n+ if (forceReTile ||\n+ (!dragging && !tilesBounds.containsBounds(bounds))) {\n+\n+ // In single tile mode with no transition effect, we insert\n+ // a non-scaled backbuffer when the layer is moved. But if\n+ // a zoom occurs right after a move, i.e. before the new\n+ // image is received, we need to remove the backbuffer, or\n+ // an ill-positioned image will be visible during the zoom\n+ // transition.\n+\n+ if (zoomChanged && this.transitionEffect !== 'resize') {\n+ this.removeBackBuffer();\n+ }\n+\n+ if (!zoomChanged || this.transitionEffect === 'resize') {\n+ this.applyBackBuffer(resolution);\n+ }\n+\n+ this.initSingleTile(bounds);\n+ }\n+ } else {\n+\n+ // if the bounds have changed such that they are not even \n+ // *partially* contained by our tiles (e.g. when user has \n+ // programmatically panned to the other side of the earth on\n+ // zoom level 18), then moveGriddedTiles could potentially have\n+ // to run through thousands of cycles, so we want to reTile\n+ // instead (thus, partial true). \n+ forceReTile = forceReTile ||\n+ !tilesBounds.intersectsBounds(bounds, {\n+ worldBounds: this.map.baseLayer.wrapDateLine &&\n+ this.map.getMaxExtent()\n+ });\n+\n+ if (forceReTile) {\n+ if (zoomChanged && (this.transitionEffect === 'resize' ||\n+ this.gridResolution === resolution)) {\n+ this.applyBackBuffer(resolution);\n+ }\n+ this.initGriddedTiles(bounds);\n+ } else {\n+ this.moveGriddedTiles();\n+ }\n+ }\n+ }\n },\n \n /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n+ * Method: getTileData\n+ * Given a map location, retrieve a tile and the pixel offset within that\n+ * tile corresponding to the location. If there is not an existing \n+ * tile in the grid that covers the given location, null will be \n+ * returned.\n+ *\n * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n+ * loc - {<OpenLayers.LonLat>} map location\n+ *\n * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n+ * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),\n+ * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n+ * offset from top left).\n */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- return this.mapObject.fromLatLngToContainerPixel(moLonLat);\n- },\n+ getTileData: function(loc) {\n+ var data = null,\n+ x = loc.lon,\n+ y = loc.lat,\n+ numRows = this.grid.length;\n \n+ if (this.map && numRows) {\n+ var res = this.map.getResolution(),\n+ tileWidth = this.tileSize.w,\n+ tileHeight = this.tileSize.h,\n+ bounds = this.grid[0][0].bounds,\n+ left = bounds.left,\n+ top = bounds.top;\n \n- // Bounds\n+ if (x < left) {\n+ // deal with multiple worlds\n+ if (this.map.baseLayer.wrapDateLine) {\n+ var worldWidth = this.map.getMaxExtent().getWidth();\n+ var worldsAway = Math.ceil((left - x) / worldWidth);\n+ x += worldWidth * worldsAway;\n+ }\n+ }\n+ // tile distance to location (fractional number of tiles);\n+ var dtx = (x - left) / (res * tileWidth);\n+ var dty = (top - y) / (res * tileHeight);\n+ // index of tile in grid\n+ var col = Math.floor(dtx);\n+ var row = Math.floor(dty);\n+ if (row >= 0 && row < numRows) {\n+ var tile = this.grid[row][col];\n+ if (tile) {\n+ data = {\n+ tile: tile,\n+ // pixel index within tile\n+ i: Math.floor((dtx - col) * tileWidth),\n+ j: Math.floor((dty - row) * tileHeight)\n+ };\n+ }\n+ }\n+ }\n+ return data;\n+ },\n \n- /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n- * \n+ /**\n+ * Method: destroyTile\n+ *\n * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n+ * tile - {<OpenLayers.Tile>}\n */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n+ destroyTile: function(tile) {\n+ this.removeTileMonitoringHooks(tile);\n+ tile.destroy();\n },\n \n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n- * \n+ * Method: getServerResolution\n+ * Return the closest server-supported resolution.\n+ *\n * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n+ * resolution - {Number} The base resolution. If undefined the\n+ * map resolution is used.\n+ *\n * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n+ * {Number} The closest server resolution value.\n */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new GLatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new GLatLng(lat, lon);\n+ getServerResolution: function(resolution) {\n+ var distance = Number.POSITIVE_INFINITY;\n+ resolution = resolution || this.map.getResolution();\n+ if (this.serverResolutions &&\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n+ var i, newDistance, newResolution, serverResolution;\n+ for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n+ newResolution = this.serverResolutions[i];\n+ newDistance = Math.abs(newResolution - resolution);\n+ if (newDistance > distance) {\n+ break;\n+ }\n+ distance = newDistance;\n+ serverResolution = newResolution;\n+ }\n+ resolution = serverResolution;\n }\n- return gLatLng;\n+ return resolution;\n },\n \n- // Pixel\n-\n /**\n- * APIMethod: getMapObjectPixelFromXY\n- * \n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n+ * Method: getServerZoom\n+ * Return the zoom value corresponding to the best matching server\n+ * resolution, taking into account <serverResolutions> and <zoomOffset>.\n+ *\n * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n+ * {Number} The closest server supported zoom. This is not the map zoom\n+ * level, but an index of the server's resolutions array.\n */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new GPoint(x, y);\n- }\n+ getServerZoom: function() {\n+ var resolution = this.getServerResolution();\n+ return this.serverResolutions ?\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n+ this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n+ },\n \n-};\n-/* ======================================================================\n- OpenLayers/Layer/KaMap.js\n- ====================================================================== */\n+ /**\n+ * Method: applyBackBuffer\n+ * Create, insert, scale and position a back buffer for the layer.\n+ *\n+ * Parameters:\n+ * resolution - {Number} The resolution to transition to.\n+ */\n+ applyBackBuffer: function(resolution) {\n+ if (this.backBufferTimerId !== null) {\n+ this.removeBackBuffer();\n+ }\n+ var backBuffer = this.backBuffer;\n+ if (!backBuffer) {\n+ backBuffer = this.createBackBuffer();\n+ if (!backBuffer) {\n+ return;\n+ }\n+ if (resolution === this.gridResolution) {\n+ this.div.insertBefore(backBuffer, this.div.firstChild);\n+ } else {\n+ this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n+ }\n+ this.backBuffer = backBuffer;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // set some information in the instance for subsequent\n+ // calls to applyBackBuffer where the same back buffer\n+ // is reused\n+ var topLeftTileBounds = this.grid[0][0].bounds;\n+ this.backBufferLonLat = {\n+ lon: topLeftTileBounds.left,\n+ lat: topLeftTileBounds.top\n+ };\n+ this.backBufferResolution = this.gridResolution;\n+ }\n \n+ var ratio = this.backBufferResolution / resolution;\n \n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n+ // scale the tiles inside the back buffer\n+ var tiles = backBuffer.childNodes,\n+ tile;\n+ for (var i = tiles.length - 1; i >= 0; --i) {\n+ tile = tiles[i];\n+ tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n+ tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n+ tile.style.width = Math.round(ratio * tile._w) + 'px';\n+ tile.style.height = Math.round(ratio * tile._h) + 'px';\n+ }\n \n-/**\n- * Class: OpenLayers.Layer.KaMap\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ // and position it (based on the grid's top-left corner)\n+ var position = this.getViewPortPxFromLonLat(\n+ this.backBufferLonLat, resolution);\n+ var leftOffset = this.map.layerContainerOriginPx.x;\n+ var topOffset = this.map.layerContainerOriginPx.y;\n+ backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n+ backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n+ },\n \n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} KaMap Layer is always a base layer \n+ /**\n+ * Method: createBackBuffer\n+ * Create a back buffer.\n+ *\n+ * Returns:\n+ * {DOMElement} The DOM element for the back buffer, undefined if the\n+ * grid isn't initialized yet.\n */\n- isBaseLayer: true,\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.grid.length > 0) {\n+ backBuffer = document.createElement('div');\n+ backBuffer.id = this.div.id + '_bb';\n+ backBuffer.className = 'olBackBuffer';\n+ backBuffer.style.position = 'absolute';\n+ var map = this.map;\n+ backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n+ this.getZIndex() - 1 :\n+ // 'map-resize':\n+ map.Z_INDEX_BASE.BaseLayer -\n+ (map.getNumLayers() - map.getLayerIndex(this));\n+ for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n+ for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n+ var tile = this.grid[i][j],\n+ markup = this.grid[i][j].createBackBuffer();\n+ if (markup) {\n+ markup._i = i;\n+ markup._j = j;\n+ markup._w = tile.size.w;\n+ markup._h = tile.size.h;\n+ markup.id = tile.id + '_bb';\n+ backBuffer.appendChild(markup);\n+ }\n+ }\n+ }\n+ }\n+ return backBuffer;\n+ },\n \n /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} parameters set by default. The default parameters set \n- * the format via the 'i' parameter to 'jpeg'. \n+ * Method: removeBackBuffer\n+ * Remove back buffer from DOM.\n */\n- DEFAULT_PARAMS: {\n- i: 'jpeg',\n- map: ''\n+ removeBackBuffer: function() {\n+ if (this._transitionElement) {\n+ for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.stopObserving(this._transitionElement,\n+ this.transitionendEvents[i], this._removeBackBuffer);\n+ }\n+ delete this._transitionElement;\n+ }\n+ if (this.backBuffer) {\n+ if (this.backBuffer.parentNode) {\n+ this.backBuffer.parentNode.removeChild(this.backBuffer);\n+ }\n+ this.backBuffer = null;\n+ this.backBufferResolution = null;\n+ if (this.backBufferTimerId !== null) {\n+ window.clearTimeout(this.backBufferTimerId);\n+ this.backBufferTimerId = null;\n+ }\n+ }\n },\n \n /**\n- * Constructor: OpenLayers.Layer.KaMap\n- * \n+ * Method: moveByPx\n+ * Move the layer based on pixel vector.\n+ *\n * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object} Parameters to be sent to the HTTP server in the\n- * query string for the tile. The format can be set via the 'i'\n- * parameter (defaults to jpg) , and the map should be set via \n- * the 'map' parameter. It has been reported that ka-Map may behave\n- * inconsistently if your format parameter does not match the format\n- * parameter configured in your config.php. (See ticket #327 for more\n- * information.)\n- * options - {Object} Additional options for the layer. Any of the \n- * APIProperties listed on this layer, and any layer types it\n- * extends, can be overridden through the options parameter. \n+ * dx - {Number}\n+ * dy - {Number}\n */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n- this.params = OpenLayers.Util.applyDefaults(\n- this.params, this.DEFAULT_PARAMS\n- );\n+ moveByPx: function(dx, dy) {\n+ if (!this.singleTile) {\n+ this.moveGriddedTiles();\n+ }\n },\n \n /**\n- * Method: getURL\n+ * APIMethod: setTileSize\n+ * Check if we are in singleTile mode and if so, set the size as a ratio\n+ * of the map size (as specified by the layer's 'ratio' property).\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n+ * size - {<OpenLayers.Size>}\n+ */\n+ setTileSize: function(size) {\n+ if (this.singleTile) {\n+ size = this.map.getSize();\n+ size.h = parseInt(size.h * this.ratio, 10);\n+ size.w = parseInt(size.w * this.ratio, 10);\n+ }\n+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n+ },\n+\n+ /**\n+ * APIMethod: getTilesBounds\n+ * Return the bounds of the tile grid.\n+ *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n+ * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the\n+ * currently loaded tiles (including those partially or not at all seen \n+ * onscreen).\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n- return this.getFullRequestString({\n- t: pY,\n- l: pX,\n- s: scale\n+ getTilesBounds: function() {\n+ var bounds = null;\n+\n+ var length = this.grid.length;\n+ if (length) {\n+ var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n+ width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n+ height = this.grid.length * bottomLeftTileBounds.getHeight();\n+\n+ bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n+ bottomLeftTileBounds.bottom,\n+ bottomLeftTileBounds.left + width,\n+ bottomLeftTileBounds.bottom + height);\n+ }\n+ return bounds;\n+ },\n+\n+ /**\n+ * Method: initSingleTile\n+ * \n+ * Parameters: \n+ * bounds - {<OpenLayers.Bounds>}\n+ */\n+ initSingleTile: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+\n+ //determine new tile bounds\n+ var center = bounds.getCenterLonLat();\n+ var tileWidth = bounds.getWidth() * this.ratio;\n+ var tileHeight = bounds.getHeight() * this.ratio;\n+\n+ var tileBounds =\n+ new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n+ center.lat - (tileHeight / 2),\n+ center.lon + (tileWidth / 2),\n+ center.lat + (tileHeight / 2));\n+\n+ var px = this.map.getLayerPxFromLonLat({\n+ lon: tileBounds.left,\n+ lat: tileBounds.top\n });\n+\n+ if (!this.grid.length) {\n+ this.grid[0] = [];\n+ }\n+\n+ var tile = this.grid[0][0];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+\n+ this.addTileMonitoringHooks(tile);\n+ tile.draw();\n+ this.grid[0][0] = tile;\n+ } else {\n+ tile.moveTo(tileBounds, px);\n+ }\n+\n+ //remove all but our single tile\n+ this.removeExcessTiles(1, 1);\n+\n+ // store the resolution of the grid\n+ this.gridResolution = this.getServerResolution();\n },\n \n /** \n * Method: calculateGridLayout\n- * ka-Map uses the center point of the map as an origin for \n- * its tiles. Override calculateGridLayout to center tiles \n- * correctly for this case.\n+ * Generate parameters for the grid layout.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bound>}\n- * origin - {<OpenLayers.LonLat>}\n+ * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an\n+ * object with a 'left' and 'top' properties.\n+ * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n * resolution - {Number}\n *\n * Returns:\n * {Object} Object containing properties tilelon, tilelat, startcol,\n * startrow\n */\n calculateGridLayout: function(bounds, origin, resolution) {\n var tilelon = resolution * this.tileSize.w;\n var tilelat = resolution * this.tileSize.h;\n \n- var offsetlon = bounds.left;\n+ var offsetlon = bounds.left - origin.lon;\n var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n \n- var offsetlat = bounds.top;\n- var tilerow = Math.floor(offsetlat / tilelat) + this.buffer;\n+ var rowSign = this.rowSign;\n+\n+ var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n+ var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\n \n return {\n tilelon: tilelon,\n tilelat: tilelat,\n startcol: tilecol,\n startrow: tilerow\n };\n+\n+ },\n+\n+ /**\n+ * Method: getTileOrigin\n+ * Determine the origin for aligning the grid of tiles. If a <tileOrigin>\n+ * property is supplied, that will be returned. Otherwise, the origin\n+ * will be derived from the layer's <maxExtent> property. In this case,\n+ * the tile origin will be the corner of the <maxExtent> given by the \n+ * <tileOriginCorner> property.\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} The tile origin.\n+ */\n+ getTileOrigin: function() {\n+ var origin = this.tileOrigin;\n+ if (!origin) {\n+ var extent = this.getMaxExtent();\n+ var edges = ({\n+ \"tl\": [\"left\", \"top\"],\n+ \"tr\": [\"right\", \"top\"],\n+ \"bl\": [\"left\", \"bottom\"],\n+ \"br\": [\"right\", \"bottom\"]\n+ })[this.tileOriginCorner];\n+ origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n+ }\n+ return origin;\n },\n \n /**\n * Method: getTileBoundsForGridIndex\n *\n * Parameters:\n * row - {Number} The row of the grid\n@@ -40273,5002 +28818,5762 @@\n * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n */\n getTileBoundsForGridIndex: function(row, col) {\n var origin = this.getTileOrigin();\n var tileLayout = this.gridLayout;\n var tilelon = tileLayout.tilelon;\n var tilelat = tileLayout.tilelat;\n- var minX = (tileLayout.startcol + col) * tilelon;\n- var minY = (tileLayout.startrow - row) * tilelat;\n+ var startcol = tileLayout.startcol;\n+ var startrow = tileLayout.startrow;\n+ var rowSign = this.rowSign;\n return new OpenLayers.Bounds(\n- minX, minY,\n- minX + tilelon, minY + tilelat\n+ origin.lon + (startcol + col) * tilelon,\n+ origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n+ origin.lon + (startcol + col + 1) * tilelon,\n+ origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n );\n },\n \n /**\n- * APIMethod: clone\n- * \n- * Parameters: \n- * obj - {Object}\n+ * Method: initGriddedTiles\n * \n- * Returns:\n- * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- clone: function(obj) {\n+ initGriddedTiles: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.KaMap(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n+ // work out mininum number of rows and columns; this is the number of\n+ // tiles required to cover the viewport plus at least one for panning\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ var viewSize = this.map.getSize();\n \n- // copy/set any non-init, non-simple values here\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone();\n- }\n+ var origin = this.getTileOrigin();\n+ var resolution = this.map.getResolution(),\n+ serverResolution = this.getServerResolution(),\n+ ratio = resolution / serverResolution,\n+ tileSize = {\n+ w: this.tileSize.w / ratio,\n+ h: this.tileSize.h / ratio\n+ };\n \n- // we do not want to copy reference to grid, so we make a new array\n- obj.grid = [];\n+ var minRows = Math.ceil(viewSize.h / tileSize.h) +\n+ 2 * this.buffer + 1;\n+ var minCols = Math.ceil(viewSize.w / tileSize.w) +\n+ 2 * this.buffer + 1;\n \n- return obj;\n- },\n+ var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n+ this.gridLayout = tileLayout;\n \n- /**\n- * APIMethod: getTileBounds\n- * Returns The tile bounds for a layer given a pixel location.\n- *\n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n- */\n- getTileBounds: function(viewPortPx) {\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);\n- var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);\n- return new OpenLayers.Bounds(tileLeft, tileBottom,\n- tileLeft + tileMapWidth,\n- tileBottom + tileMapHeight);\n- },\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n \n- CLASS_NAME: \"OpenLayers.Layer.KaMap\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/Text.js\n- ====================================================================== */\n+ var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n+ var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n+ var startPx = this.map.getViewPortPxFromLonLat(\n+ new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n+ );\n+ startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n+ startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n \n-/**\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- */\n+ var tileData = [],\n+ center = this.map.getCenter();\n \n-/**\n- * Class: OpenLayers.Format.Text\n- * Read Text format. Create a new instance with the <OpenLayers.Format.Text>\n- * constructor. This reads text which is formatted like CSV text, using\n- * tabs as the seperator by default. It provides parsing of data originally\n- * used in the MapViewerService, described on the wiki. This Format is used\n- * by the <OpenLayers.Layer.Text> class.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n+ var rowidx = 0;\n+ do {\n+ var row = this.grid[rowidx];\n+ if (!row) {\n+ row = [];\n+ this.grid.push(row);\n+ }\n \n- /**\n- * APIProperty: defaultStyle\n- * defaultStyle allows one to control the default styling of the features.\n- * It should be a symbolizer hash. By default, this is set to match the\n- * Layer.Text behavior, which is to use the default OpenLayers Icon.\n- */\n- defaultStyle: null,\n+ var colidx = 0;\n+ do {\n+ tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n+ var px = startPx.clone();\n+ px.x = px.x + colidx * Math.round(tileSize.w);\n+ px.y = px.y + rowidx * Math.round(tileSize.h);\n+ var tile = row[colidx];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ row.push(tile);\n+ } else {\n+ tile.moveTo(tileBounds, px, false);\n+ }\n+ var tileCenter = tileBounds.getCenterLonLat();\n+ tileData.push({\n+ tile: tile,\n+ distance: Math.pow(tileCenter.lon - center.lon, 2) +\n+ Math.pow(tileCenter.lat - center.lat, 2)\n+ });\n \n- /**\n- * APIProperty: extractStyles\n- * set to true to extract styles from the TSV files, using information\n- * from the image or icon, iconSize and iconOffset fields. This will result\n- * in features with a symbolizer (style) property set, using the\n- * default symbolizer specified in <defaultStyle>. Set to false if you\n- * wish to use a styleMap or OpenLayers.Style options to style your\n- * layer instead.\n- */\n- extractStyles: true,\n+ colidx += 1;\n+ } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n+ colidx < minCols);\n \n- /**\n- * Constructor: OpenLayers.Format.Text\n- * Create a new parser for TSV Text.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- options = options || {};\n+ rowidx += 1;\n+ } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n+ rowidx < minRows);\n \n- if (options.extractStyles !== false) {\n- options.defaultStyle = {\n- 'externalGraphic': OpenLayers.Util.getImageLocation(\"marker.png\"),\n- 'graphicWidth': 21,\n- 'graphicHeight': 25,\n- 'graphicXOffset': -10.5,\n- 'graphicYOffset': -12.5\n- };\n+ //shave off exceess rows and colums\n+ this.removeExcessTiles(rowidx, colidx);\n+\n+ var resolution = this.getServerResolution();\n+ // store the resolution of the grid\n+ this.gridResolution = resolution;\n+\n+ //now actually draw the tiles\n+ tileData.sort(function(a, b) {\n+ return a.distance - b.distance;\n+ });\n+ for (var i = 0, ii = tileData.length; i < ii; ++i) {\n+ tileData[i].tile.draw();\n }\n+ },\n \n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\n+ /**\n+ * Method: getMaxExtent\n+ * Get this layer's maximum extent. (Implemented as a getter for\n+ * potential specific implementations in sub-classes.)\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>}\n+ */\n+ getMaxExtent: function() {\n+ return this.maxExtent;\n },\n \n /**\n- * APIMethod: read\n- * Return a list of features from a Tab Seperated Values text string.\n- * \n- * Parameters:\n- * text - {String} \n+ * APIMethod: addTile\n+ * Create a tile, initialize it, and add it to the layer div. \n+ *\n+ * Parameters\n+ * bounds - {<OpenLayers.Bounds>}\n+ * position - {<OpenLayers.Pixel>}\n *\n * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n+ * {<OpenLayers.Tile>} The added OpenLayers.Tile\n */\n- read: function(text) {\n- var lines = text.split('\\n');\n- var columns;\n- var features = [];\n- // length - 1 to allow for trailing new line\n- for (var lcv = 0; lcv < (lines.length - 1); lcv++) {\n- var currLine = lines[lcv].replace(/^\\s*/, '').replace(/\\s*$/, '');\n+ addTile: function(bounds, position) {\n+ var tile = new this.tileClass(\n+ this, position, bounds, null, this.tileSize, this.tileOptions\n+ );\n+ this.events.triggerEvent(\"addtile\", {\n+ tile: tile\n+ });\n+ return tile;\n+ },\n \n- if (currLine.charAt(0) != '#') {\n- /* not a comment */\n+ /** \n+ * Method: addTileMonitoringHooks\n+ * This function takes a tile as input and adds the appropriate hooks to \n+ * the tile so that the layer can keep track of the loading tiles.\n+ * \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n+ */\n+ addTileMonitoringHooks: function(tile) {\n \n- if (!columns) {\n- //First line is columns\n- columns = currLine.split('\\t');\n- } else {\n- var vals = currLine.split('\\t');\n- var geometry = new OpenLayers.Geometry.Point(0, 0);\n- var attributes = {};\n- var style = this.defaultStyle ?\n- OpenLayers.Util.applyDefaults({}, this.defaultStyle) :\n- null;\n- var icon, iconSize, iconOffset, overflow;\n- var set = false;\n- for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n- if (vals[valIndex]) {\n- if (columns[valIndex] == 'point') {\n- var coords = vals[valIndex].split(',');\n- geometry.y = parseFloat(coords[0]);\n- geometry.x = parseFloat(coords[1]);\n- set = true;\n- } else if (columns[valIndex] == 'lat') {\n- geometry.y = parseFloat(vals[valIndex]);\n- set = true;\n- } else if (columns[valIndex] == 'lon') {\n- geometry.x = parseFloat(vals[valIndex]);\n- set = true;\n- } else if (columns[valIndex] == 'title')\n- attributes['title'] = vals[valIndex];\n- else if (columns[valIndex] == 'image' ||\n- columns[valIndex] == 'icon' && style) {\n- style['externalGraphic'] = vals[valIndex];\n- } else if (columns[valIndex] == 'iconSize' && style) {\n- var size = vals[valIndex].split(',');\n- style['graphicWidth'] = parseFloat(size[0]);\n- style['graphicHeight'] = parseFloat(size[1]);\n- } else if (columns[valIndex] == 'iconOffset' && style) {\n- var offset = vals[valIndex].split(',');\n- style['graphicXOffset'] = parseFloat(offset[0]);\n- style['graphicYOffset'] = parseFloat(offset[1]);\n- } else if (columns[valIndex] == 'description') {\n- attributes['description'] = vals[valIndex];\n- } else if (columns[valIndex] == 'overflow') {\n- attributes['overflow'] = vals[valIndex];\n- } else {\n- // For StyleMap filtering, allow additional\n- // columns to be stored as attributes.\n- attributes[columns[valIndex]] = vals[valIndex];\n- }\n- }\n+ var replacingCls = 'olTileReplacing';\n+\n+ tile.onLoadStart = function() {\n+ //if that was first tile then trigger a 'loadstart' on the layer\n+ if (this.loading === false) {\n+ this.loading = true;\n+ this.events.triggerEvent(\"loadstart\");\n+ }\n+ this.events.triggerEvent(\"tileloadstart\", {\n+ tile: tile\n+ });\n+ this.numLoadingTiles++;\n+ if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n+ }\n+ };\n+\n+ tile.onLoadEnd = function(evt) {\n+ this.numLoadingTiles--;\n+ var aborted = evt.type === 'unload';\n+ this.events.triggerEvent(\"tileloaded\", {\n+ tile: tile,\n+ aborted: aborted\n+ });\n+ if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ var tileDiv = tile.getTile();\n+ if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n+ var bufferTile = document.getElementById(tile.id + '_bb');\n+ if (bufferTile) {\n+ bufferTile.parentNode.removeChild(bufferTile);\n }\n- if (set) {\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n+ }\n+ OpenLayers.Element.removeClass(tileDiv, replacingCls);\n+ }\n+ //if that was the last tile, then trigger a 'loadend' on the layer\n+ if (this.numLoadingTiles === 0) {\n+ if (this.backBuffer) {\n+ if (this.backBuffer.childNodes.length === 0) {\n+ // no tiles transitioning, remove immediately\n+ this.removeBackBuffer();\n+ } else {\n+ // wait until transition has ended or delay has passed\n+ this._transitionElement = aborted ?\n+ this.div.lastChild : tile.imgDiv;\n+ var transitionendEvents = this.transitionendEvents;\n+ for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.observe(this._transitionElement,\n+ transitionendEvents[i],\n+ this._removeBackBuffer);\n }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n- features.push(feature);\n+ // the removal of the back buffer is delayed to prevent\n+ // flash effects due to the animation of tile displaying\n+ this.backBufferTimerId = window.setTimeout(\n+ this._removeBackBuffer, this.removeBackBufferDelay\n+ );\n }\n }\n+ this.loading = false;\n+ this.events.triggerEvent(\"loadend\");\n }\n- }\n- return features;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.Text\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Text.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Markers.js\n- * @requires OpenLayers/Format/Text.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Text\n- * This layer creates markers given data in a text file. The <location>\n- * property of the layer (specified as a property of the options argument\n- * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited\n- * file with data used to create markers.\n- *\n- * The first row of the data file should be a header line with the column names\n- * of the data. Each column should be delimited by a tab space. The\n- * possible columns are:\n- * - *point* lat,lon of the point where a marker is to be placed\n- * - *lat* Latitude of the point where a marker is to be placed\n- * - *lon* Longitude of the point where a marker is to be placed\n- * - *icon* or *image* URL of marker icon to use.\n- * - *iconSize* Size of Icon to use.\n- * - *iconOffset* Where the top-left corner of the icon is to be placed\n- * relative to the latitude and longitude of the point.\n- * - *title* The text of the 'title' is placed inside an 'h2' marker\n- * inside a popup, which opens when the marker is clicked.\n- * - *description* The text of the 'description' is placed below the h2\n- * in the popup. this can be plain text or HTML.\n- *\n- * Example text file:\n- * (code)\n- * lat\tlon\ttitle\tdescription\ticonSize\ticonOffset\ticon\n- * 10\t20\ttitle\tdescription\t21,25\t\t-10,-25\t\thttp://www.openlayers.org/dev/img/marker.png\n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n- */\n-OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {\n-\n- /**\n- * APIProperty: location \n- * {String} URL of text file. Must be specified in the \"options\" argument\n- * of the constructor. Can not be changed once passed in. \n- */\n- location: null,\n+ };\n \n- /** \n- * Property: features\n- * {Array(<OpenLayers.Feature>)} \n- */\n- features: null,\n+ tile.onLoadError = function() {\n+ this.events.triggerEvent(\"tileerror\", {\n+ tile: tile\n+ });\n+ };\n \n- /**\n- * APIProperty: formatOptions\n- * {Object} Hash of options which should be passed to the format when it is\n- * created. Must be passed in the constructor.\n- */\n- formatOptions: null,\n+ tile.events.on({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n+ },\n \n /** \n- * Property: selectedFeature\n- * {<OpenLayers.Feature>}\n- */\n- selectedFeature: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Text\n- * Create a text layer.\n+ * Method: removeTileMonitoringHooks\n+ * This function takes a tile as input and removes the tile hooks \n+ * that were added in addTileMonitoringHooks()\n * \n- * Parameters:\n- * name - {String} \n- * options - {Object} Object with properties to be set on the layer.\n- * Must include <location> property.\n- */\n- initialize: function(name, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);\n- this.features = [];\n- },\n-\n- /**\n- * APIMethod: destroy \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n */\n- destroy: function() {\n- // Warning: Layer.Markers.destroy() must be called prior to calling\n- // clearFeatures() here, otherwise we leak memory. Indeed, if\n- // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n- // able to remove the marker image elements from the layer's div since\n- // the markers will have been destroyed by clearFeatures().\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null;\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: loadText\n- * Start the load of the Text data. Don't do this when we first add the layer,\n- * since we may not be visible at any point, and it would therefore be a waste.\n+ * Method: moveGriddedTiles\n */\n- loadText: function() {\n- if (!this.loaded) {\n- if (this.location != null) {\n-\n- var onFail = function(e) {\n- this.events.triggerEvent(\"loadend\");\n- };\n-\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- failure: onFail,\n- scope: this\n- });\n- this.loaded = true;\n+ moveGriddedTiles: function() {\n+ var buffer = this.buffer + 1;\n+ while (true) {\n+ var tlTile = this.grid[0][0];\n+ var tlViewPort = {\n+ x: tlTile.position.x +\n+ this.map.layerContainerOriginPx.x,\n+ y: tlTile.position.y +\n+ this.map.layerContainerOriginPx.y\n+ };\n+ var ratio = this.getServerResolution() / this.map.getResolution();\n+ var tileSize = {\n+ w: Math.round(this.tileSize.w * ratio),\n+ h: Math.round(this.tileSize.h * ratio)\n+ };\n+ if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n+ this.shiftColumn(true, tileSize);\n+ } else if (tlViewPort.x < -tileSize.w * buffer) {\n+ this.shiftColumn(false, tileSize);\n+ } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n+ this.shiftRow(true, tileSize);\n+ } else if (tlViewPort.y < -tileSize.h * buffer) {\n+ this.shiftRow(false, tileSize);\n+ } else {\n+ break;\n }\n }\n },\n \n /**\n- * Method: moveTo\n- * If layer is visible and Text has not been loaded, load Text. \n- * \n+ * Method: shiftRow\n+ * Shifty grid work\n+ *\n * Parameters:\n- * bounds - {Object} \n- * zoomChanged - {Object} \n- * minor - {Object} \n+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadText();\n+ shiftRow: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var rowIndex = prepend ? 0 : (grid.length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var rowSign = this.rowSign;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startrow += sign * rowSign;\n+\n+ var modelRow = grid[rowIndex];\n+ var row = grid[prepend ? 'pop' : 'shift']();\n+ for (var i = 0, len = row.length; i < len; i++) {\n+ var tile = row[i];\n+ var position = modelRow[i].position.clone();\n+ position.y += tileSize.h * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n }\n+ grid[prepend ? 'unshift' : 'push'](row);\n },\n \n /**\n- * Method: parseData\n+ * Method: shiftColumn\n+ * Shift grid work in the other dimension\n *\n * Parameters:\n- * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- parseData: function(ajaxRequest) {\n- var text = ajaxRequest.responseText;\n-\n- var options = {};\n-\n- OpenLayers.Util.extend(options, this.formatOptions);\n+ shiftColumn: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var colIndex = prepend ? 0 : (grid[0].length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startcol += sign;\n \n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject();\n+ for (var i = 0, len = grid.length; i < len; i++) {\n+ var row = grid[i];\n+ var position = row[colIndex].position.clone();\n+ var tile = row[prepend ? 'pop' : 'shift']();\n+ position.x += tileSize.w * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n+ row[prepend ? 'unshift' : 'push'](tile);\n }\n+ },\n \n- var parser = new OpenLayers.Format.Text(options);\n- var features = parser.read(text);\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n- var location;\n- var iconSize, iconOffset;\n-\n- location = new OpenLayers.LonLat(feature.geometry.x,\n- feature.geometry.y);\n-\n- if (feature.style.graphicWidth &&\n- feature.style.graphicHeight) {\n- iconSize = new OpenLayers.Size(\n- feature.style.graphicWidth,\n- feature.style.graphicHeight);\n- }\n-\n- // FIXME: At the moment, we only use this if we have an \n- // externalGraphic, because icon has no setOffset API Method.\n- /**\n- * FIXME FIRST!!\n- * The Text format does all sorts of parseFloating\n- * The result of a parseFloat for a bogus string is NaN. That\n- * means the three possible values here are undefined, NaN, or a\n- * number. The previous check was an identity check for null. This\n- * means it was failing for all undefined or NaN. A slightly better\n- * check is for undefined. An even better check is to see if the\n- * value is a number (see #1441).\n- */\n- if (feature.style.graphicXOffset !== undefined &&\n- feature.style.graphicYOffset !== undefined) {\n- iconOffset = new OpenLayers.Pixel(\n- feature.style.graphicXOffset,\n- feature.style.graphicYOffset);\n- }\n-\n- if (feature.style.externalGraphic != null) {\n- data.icon = new OpenLayers.Icon(feature.style.externalGraphic,\n- iconSize,\n- iconOffset);\n- } else {\n- data.icon = OpenLayers.Marker.defaultIcon();\n-\n- //allows for the case where the image url is not \n- // specified but the size is. use a default icon\n- // but change the size\n- if (iconSize != null) {\n- data.icon.setSize(iconSize);\n- }\n- }\n+ /**\n+ * Method: removeExcessTiles\n+ * When the size of the map or the buffer changes, we may need to\n+ * remove some excess rows and columns.\n+ * \n+ * Parameters:\n+ * rows - {Integer} Maximum number of rows we want our grid to have.\n+ * columns - {Integer} Maximum number of columns we want our grid to have.\n+ */\n+ removeExcessTiles: function(rows, columns) {\n+ var i, l;\n \n- if ((feature.attributes.title != null) &&\n- (feature.attributes.description != null)) {\n- data['popupContentHTML'] =\n- '<h2>' + feature.attributes.title + '</h2>' +\n- '<p>' + feature.attributes.description + '</p>';\n+ // remove extra rows\n+ while (this.grid.length > rows) {\n+ var row = this.grid.pop();\n+ for (i = 0, l = row.length; i < l; i++) {\n+ var tile = row[i];\n+ this.destroyTile(tile);\n }\n+ }\n \n- data['overflow'] = feature.attributes.overflow || \"auto\";\n-\n- var markerFeature = new OpenLayers.Feature(this, location, data);\n- this.features.push(markerFeature);\n- var marker = markerFeature.createMarker();\n- if ((feature.attributes.title != null) &&\n- (feature.attributes.description != null)) {\n- marker.events.register('click', markerFeature, this.markerClick);\n+ // remove extra columns\n+ for (i = 0, l = this.grid.length; i < l; i++) {\n+ while (this.grid[i].length > columns) {\n+ var row = this.grid[i];\n+ var tile = row.pop();\n+ this.destroyTile(tile);\n }\n- this.addMarker(marker);\n }\n- this.events.triggerEvent(\"loadend\");\n },\n \n /**\n- * Property: markerClick\n- * \n- * Parameters:\n- * evt - {Event} \n- *\n- * Context:\n- * - {<OpenLayers.Feature>}\n+ * Method: onMapResize\n+ * For singleTile layers, this will set a new tile size according to the\n+ * dimensions of the map pane.\n */\n- markerClick: function(evt) {\n- var sameMarkerClicked = (this == this.layer.selectedFeature);\n- this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- if (!sameMarkerClicked) {\n- this.layer.map.addPopup(this.createPopup());\n+ onMapResize: function() {\n+ if (this.singleTile) {\n+ this.clearGrid();\n+ this.setTileSize();\n }\n- OpenLayers.Event.stop(evt);\n },\n \n /**\n- * Method: clearFeatures\n+ * APIMethod: getTileBounds\n+ * Returns The tile bounds for a layer given a pixel location.\n+ *\n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n */\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy();\n- }\n- }\n+ getTileBounds: function(viewPortPx) {\n+ var maxExtent = this.maxExtent;\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = maxExtent.left + (tileMapWidth *\n+ Math.floor((mapPoint.lon -\n+ maxExtent.left) /\n+ tileMapWidth));\n+ var tileBottom = maxExtent.bottom + (tileMapHeight *\n+ Math.floor((mapPoint.lat -\n+ maxExtent.bottom) /\n+ tileMapHeight));\n+ return new OpenLayers.Bounds(tileLeft, tileBottom,\n+ tileLeft + tileMapWidth,\n+ tileBottom + tileMapHeight);\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Text\"\n+ CLASS_NAME: \"OpenLayers.Layer.Grid\"\n });\n /* ======================================================================\n- OpenLayers/Layer/Bing.js\n+ OpenLayers/TileManager.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Layer/XYZ.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes.js\n+ * @requires OpenLayers/BaseTypes/Element.js\n+ * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n-/** \n- * Class: OpenLayers.Layer.Bing\n- * Bing layer using direct tile access as provided by Bing Maps REST Services.\n- * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n- * information. Note: Terms of Service compliant use requires the map to be\n- * configured with an <OpenLayers.Control.Attribution> control and the\n- * attribution placed on or near the map.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n+/**\n+ * Class: OpenLayers.TileManager\n+ * Provides queueing of image requests and caching of image elements.\n+ *\n+ * Queueing avoids unnecessary image requests while changing zoom levels\n+ * quickly, and helps improve dragging performance on mobile devices that show\n+ * a lag in dragging when loading of new images starts. <zoomDelay> and\n+ * <moveDelay> are the configuration options to control this behavior.\n+ *\n+ * Caching avoids setting the src on image elements for images that have already\n+ * been used. Several maps can share a TileManager instance, in which case each\n+ * map gets its own tile queue, but all maps share the same tile cache.\n */\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+OpenLayers.TileManager = OpenLayers.Class({\n \n /**\n- * Property: key\n- * {String} API key for Bing maps, get your own key \n- * at http://bingmapsportal.com/ .\n+ * APIProperty: cacheSize\n+ * {Number} Number of image elements to keep referenced in this instance's\n+ * cache for fast reuse. Default is 256.\n */\n- key: null,\n+ cacheSize: 256,\n \n /**\n- * Property: serverResolutions\n- * {Array} the resolutions provided by the Bing servers.\n+ * APIProperty: tilesPerFrame\n+ * {Number} Number of queued tiles to load per frame (see <frameDelay>).\n+ * Default is 2.\n */\n- serverResolutions: [\n- 156543.03390625, 78271.516953125, 39135.7584765625,\n- 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n- 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n- 305.74811309814453, 152.87405654907226, 76.43702827453613,\n- 38.218514137268066, 19.109257068634033, 9.554628534317017,\n- 4.777314267158508, 2.388657133579254, 1.194328566789627,\n- 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n- 0.07464553542435169\n- ],\n+ tilesPerFrame: 2,\n \n /**\n- * Property: attributionTemplate\n- * {String}\n+ * APIProperty: frameDelay\n+ * {Number} Delay between tile loading frames (see <tilesPerFrame>) in\n+ * milliseconds. Default is 16.\n */\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n- '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n- '<img src=\"${logo}\" /></a></div>${copyrights}' +\n- '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n- 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n- 'Terms of Use</a></span>',\n+ frameDelay: 16,\n \n /**\n- * Property: metadata\n- * {Object} Metadata for this layer, as returned by the callback script\n+ * APIProperty: moveDelay\n+ * {Number} Delay in milliseconds after a map's move event before loading\n+ * tiles. Default is 100.\n */\n- metadata: null,\n+ moveDelay: 100,\n \n /**\n- * Property: protocolRegex\n- * {RegExp} Regular expression to match and replace http: in bing urls\n+ * APIProperty: zoomDelay\n+ * {Number} Delay in milliseconds after a map's zoomend event before loading\n+ * tiles. Default is 200.\n */\n- protocolRegex: /^http:/i,\n+ zoomDelay: 200,\n \n /**\n- * APIProperty: type\n- * {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used. Default is \"Road\".\n+ * Property: maps\n+ * {Array(<OpenLayers.Map>)} The maps to manage tiles on.\n */\n- type: \"Road\",\n+ maps: null,\n \n /**\n- * APIProperty: culture\n- * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n- * for the definition and the possible values. Default is \"en-US\".\n+ * Property: tileQueueId\n+ * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.\n */\n- culture: \"en-US\",\n+ tileQueueId: null,\n \n /**\n- * APIProperty: metadataParams\n- * {Object} Optional url parameters for the Get Imagery Metadata request\n- * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n+ * Property: tileQueue\n+ * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by\n+ * map id.\n */\n- metadataParams: null,\n+ tileQueue: null,\n \n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n+ /**\n+ * Property: tileCache\n+ * {Object} Cached image elements, keyed by URL.\n */\n- tileOptions: null,\n+ tileCache: null,\n \n- /** APIProperty: protocol\n- * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n- * Can be 'http:' 'https:' or ''\n- *\n- * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n- * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n- * this is undocumented and the Imagery Metadata API always returns HTTP\n- * urls.\n+ /**\n+ * Property: tileCacheIndex\n+ * {Array(String)} URLs of cached tiles. First entry is the least recently\n+ * used.\n+ */\n+ tileCacheIndex: null,\n+\n+ /** \n+ * Constructor: OpenLayers.TileManager\n+ * Constructor for a new <OpenLayers.TileManager> instance.\n+ * \n+ * Parameters:\n+ * options - {Object} Configuration for this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.maps = [];\n+ this.tileQueueId = {};\n+ this.tileQueue = {};\n+ this.tileCache = {};\n+ this.tileCacheIndex = [];\n+ },\n+\n+ /**\n+ * Method: addMap\n+ * Binds this instance to a map\n *\n- * Default is '', unless when executed from a file:/// uri, in which case\n- * it is 'http:'.\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n+ addMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n+ return;\n+ }\n+ this.maps.push(map);\n+ this.tileQueue[map.id] = [];\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.addLayer({\n+ layer: map.layers[i]\n+ });\n+ }\n+ map.events.on({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ });\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.Bing\n- * Create a new Bing layer.\n+ * Method: removeMap\n+ * Unbinds this instance from a map\n *\n- * Example:\n- * (code)\n- * var road = new OpenLayers.Layer.Bing({\n- * name: \"My Bing Aerial Layer\",\n- * type: \"Aerial\",\n- * key: \"my-api-key-here\",\n- * });\n- * (end)\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ removeMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n+ return;\n+ }\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ if (map.layers) {\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.removeLayer({\n+ layer: map.layers[i]\n+ });\n+ }\n+ }\n+ if (map.events) {\n+ map.events.un({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ });\n+ }\n+ delete this.tileQueue[map.id];\n+ delete this.tileQueueId[map.id];\n+ OpenLayers.Util.removeItem(this.maps, map);\n+ },\n+\n+ /**\n+ * Method: move\n+ * Handles the map's move event\n *\n * Parameters:\n- * options - {Object} Configuration properties for the layer.\n+ * evt - {Object} Listener argument\n+ */\n+ move: function(evt) {\n+ this.updateTimeout(evt.object, this.moveDelay, true);\n+ },\n+\n+ /**\n+ * Method: zoomEnd\n+ * Handles the map's zoomEnd event\n *\n- * Required configuration properties:\n- * key - {String} Bing Maps API key for your application. Get one at\n- * http://bingmapsportal.com/.\n- * type - {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used.\n+ * Parameters:\n+ * evt - {Object} Listener argument\n+ */\n+ zoomEnd: function(evt) {\n+ this.updateTimeout(evt.object, this.zoomDelay);\n+ },\n+\n+ /**\n+ * Method: changeLayer\n+ * Handles the map's changeLayer event\n *\n- * Any other documented layer properties can be provided in the config object.\n+ * Parameters:\n+ * evt - {Object} Listener argument\n */\n- initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n+ changeLayer: function(evt) {\n+ if (evt.property === 'visibility' || evt.property === 'params') {\n+ this.updateTimeout(evt.object, 0);\n+ }\n+ },\n \n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options.tileOptions);\n- this.loadMetadata();\n+ /**\n+ * Method: addLayer\n+ * Handles the map's addlayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n+ */\n+ addLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ layer.events.on({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.addTile({\n+ tile: tile\n+ });\n+ if (tile.url && !tile.imgDiv) {\n+ this.manageTileCache({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: loadMetadata\n+ * Method: removeLayer\n+ * Handles the map's preremovelayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n */\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- // link the processMetadata method to the global scope and bind it\n- // to this instance\n- window[this._callbackId] = OpenLayers.Function.bind(\n- OpenLayers.Layer.Bing.processMetadata, this\n- );\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n- this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script);\n+ removeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ this.clearTileQueue({\n+ object: layer\n+ });\n+ if (layer.events) {\n+ layer.events.un({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ }\n+ if (layer.grid) {\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.unloadTile({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: initLayer\n+ * Method: updateTimeout\n+ * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,\n+ * and schedules more queue processing after <frameDelay> if there are still\n+ * tiles in the queue.\n *\n- * Sets layer properties according to the metadata provided by the API\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} The map to update the timeout for\n+ * delay - {Number} The delay to apply\n+ * nice - {Boolean} If true, the timeout function will only be created if\n+ * the tilequeue is not empty. This is used by the move handler to\n+ * avoid impacts on dragging performance. For other events, the tile\n+ * queue may not be populated yet, so we need to set the timer\n+ * regardless of the queue size.\n */\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n+ updateTimeout: function(map, delay, nice) {\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ var tileQueue = this.tileQueue[map.id];\n+ if (!nice || tileQueue.length) {\n+ this.tileQueueId[map.id] = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.drawTilesFromQueue(map);\n+ if (tileQueue.length) {\n+ this.updateTimeout(map, this.frameDelay);\n+ }\n+ }, this), delay\n+ );\n }\n- this.addOptions({\n- maxResolution: Math.min(\n- this.serverResolutions[res.zoomMin],\n- this.maxResolution || Number.POSITIVE_INFINITY\n- ),\n- numZoomLevels: Math.min(\n- res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n- )\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw();\n+ },\n+\n+ /**\n+ * Method: addTile\n+ * Listener for the layer's addtile event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n+ */\n+ addTile: function(evt) {\n+ if (evt.tile instanceof OpenLayers.Tile.Image) {\n+ evt.tile.events.on({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ } else {\n+ // Layer has the wrong tile type, so don't handle it any longer\n+ this.removeLayer({\n+ layer: evt.tile.layer\n+ });\n }\n- this.updateAttribution();\n },\n \n /**\n- * Method: getURL\n+ * Method: unloadTile\n+ * Listener for the tile's unload event\n *\n- * Paramters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * Parameters:\n+ * evt - {Object} The listener argument\n */\n- getURL: function(bounds) {\n- if (!this.url) {\n- return;\n+ unloadTile: function(evt) {\n+ var tile = evt.object;\n+ tile.events.un({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n+ },\n+\n+ /**\n+ * Method: queueTileDraw\n+ * Adds a tile to the queue that will draw it.\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument of the tile's beforedraw event\n+ */\n+ queueTileDraw: function(evt) {\n+ var tile = evt.object;\n+ var queued = false;\n+ var layer = tile.layer;\n+ var url = layer.getURL(tile.bounds);\n+ var img = this.tileCache[url];\n+ if (img && img.className !== 'olTileImage') {\n+ // cached image no longer valid, e.g. because we're olTileReplacing\n+ delete this.tileCache[url];\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n+ img = null;\n }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = '0';\n- var mask = 1 << (i - 1);\n- if ((x & mask) != 0) {\n- digit++;\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++;\n+ // queue only if image with same url not cached already\n+ if (layer.url && (layer.async || !img)) {\n+ // add to queue only if not in queue already\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n+ tileQueue.push(tile);\n }\n- quadDigits.push(digit);\n+ queued = true;\n }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl('' + x + y + z, this.url);\n-\n- return OpenLayers.String.format(url, {\n- 'quadkey': quadKey\n- });\n+ return !queued;\n },\n \n /**\n- * Method: updateAttribution\n- * Updates the attribution according to the requirements outlined in\n- * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n+ * Method: drawTilesFromQueue\n+ * Draws tiles from the tileQueue, and unqueues the tiles\n */\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n- return;\n+ drawTilesFromQueue: function(map) {\n+ var tileQueue = this.tileQueue[map.id];\n+ var limit = this.tilesPerFrame;\n+ var animating = map.zoomTween && map.zoomTween.playing;\n+ while (!animating && tileQueue.length && limit) {\n+ tileQueue.shift().draw(true);\n+ --limit;\n }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(\n- this.map.getProjectionObject(),\n- new OpenLayers.Projection(\"EPSG:4326\")\n- );\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n- this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- // axis order provided is Y,X\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) &&\n- zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \";\n- }\n+ },\n+\n+ /**\n+ * Method: manageTileCache\n+ * Adds, updates, removes and fetches cache entries.\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument of the tile's beforeload event\n+ */\n+ manageTileCache: function(evt) {\n+ var tile = evt.object;\n+ var img = this.tileCache[tile.url];\n+ if (img) {\n+ // if image is on its layer's backbuffer, remove it from backbuffer\n+ if (img.parentNode &&\n+ OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n+ img.parentNode.removeChild(img);\n+ img.id = null;\n+ }\n+ // only use image from cache if it is not on a layer already\n+ if (!img.parentNode) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ tile.setImage(img);\n+ // LRU - move tile to the end of the array to mark it as the most\n+ // recently used\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n+ this.tileCacheIndex.push(tile.url);\n }\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- });\n },\n \n /**\n- * Method: setMap\n+ * Method: addToCache\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument for the tile's loadend event\n */\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution);\n+ addToCache: function(evt) {\n+ var tile = evt.object;\n+ if (!this.tileCache[tile.url]) {\n+ if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n+ if (this.tileCacheIndex.length >= this.cacheSize) {\n+ delete this.tileCache[this.tileCacheIndex[0]];\n+ this.tileCacheIndex.shift();\n+ }\n+ this.tileCache[tile.url] = tile.imgDiv;\n+ this.tileCacheIndex.push(tile.url);\n+ }\n+ }\n },\n \n /**\n- * APIMethod: clone\n- * \n+ * Method: clearTileQueue\n+ * Clears the tile queue from tiles of a specific layer\n+ *\n * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n+ * evt - {Object} Listener argument of the layer's retile event\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options);\n+ clearTileQueue: function(evt) {\n+ var layer = evt.object;\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ for (var i = tileQueue.length - 1; i >= 0; --i) {\n+ if (tileQueue[i].layer === layer) {\n+ tileQueue.splice(i, 1);\n+ }\n }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n },\n \n /**\n * Method: destroy\n */\n destroy: function() {\n- this.map &&\n- this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n- },\n+ for (var i = this.maps.length - 1; i >= 0; --i) {\n+ this.removeMap(this.maps[i]);\n+ }\n+ this.maps = null;\n+ this.tileQueue = null;\n+ this.tileQueueId = null;\n+ this.tileCache = null;\n+ this.tileCacheIndex = null;\n+ this._destroyed = true;\n+ }\n \n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n });\n-\n-/**\n- * Function: OpenLayers.Layer.Bing.processMetadata\n- * This function will be bound to an instance, linked to the global scope with\n- * an id, and called by the JSONP script returned by the API.\n- *\n- * Parameters:\n- * metadata - {Object} metadata as returned by the API\n- */\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined; // cannot delete from window in IE\n- delete this._callbackId;\n-};\n /* ======================================================================\n- OpenLayers/Tile/UTFGrid.js\n+ OpenLayers/Protocol.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Format/JSON.js\n- * @requires OpenLayers/Request.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Tile.UTFGrid\n- * Instances of OpenLayers.Tile.UTFGrid are used to manage \n- * UTFGrids. This is an unusual tile type in that it doesn't have a\n- * rendered image; only a 'hit grid' that can be used to \n- * look up feature attributes.\n- *\n- * See the <OpenLayers.Tile.UTFGrid> constructor for details on constructing a\n- * new instance.\n- *\n- * Inherits from:\n- * - <OpenLayers.Tile>\n+ * Class: OpenLayers.Protocol\n+ * Abstract vector layer protocol class. Not to be instantiated directly. Use\n+ * one of the protocol subclasses instead.\n */\n-OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n+OpenLayers.Protocol = OpenLayers.Class({\n \n- /** \n- * Property: url\n- * {String}\n- * The URL of the UTFGrid file being requested. Provided by the <getURL>\n- * method. \n+ /**\n+ * Property: format\n+ * {<OpenLayers.Format>} The format used by this protocol.\n */\n- url: null,\n+ format: null,\n \n /**\n- * Property: utfgridResolution\n- * {Number}\n- * Ratio of the pixel width to the width of a UTFGrid data point. If an \n- * entry in the grid represents a 4x4 block of pixels, the \n- * utfgridResolution would be 4. Default is 2.\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n */\n- utfgridResolution: 2,\n+ options: null,\n \n- /** \n- * Property: json\n- * {Object}\n- * Stores the parsed JSON tile data structure. \n+ /**\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the protocol can set autoDestroy to false\n+ * to fully control when the protocol is destroyed. Defaults to\n+ * true.\n */\n- json: null,\n+ autoDestroy: true,\n \n- /** \n- * Property: format\n- * {OpenLayers.Format.JSON}\n- * Parser instance used to parse JSON for cross browser support. The native\n- * JSON.parse method will be used where available (all except IE<8).\n+ /**\n+ * Property: defaultFilter\n+ * {<OpenLayers.Filter>} Optional default filter to read requests\n */\n- format: null,\n+ defaultFilter: null,\n \n- /** \n- * Constructor: OpenLayers.Tile.UTFGrid\n- * Constructor for a new <OpenLayers.Tile.UTFGrid> instance.\n- * \n+ /**\n+ * Constructor: OpenLayers.Protocol\n+ * Abstract class for vector protocols. Create instances of a subclass.\n+ *\n * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>} Deprecated. Remove me in 3.0.\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n- */\n-\n- /** \n- * APIMethod: destroy\n- * Clean up.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- destroy: function() {\n- this.clear();\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n+ initialize: function(options) {\n+ options = options || {};\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n },\n \n /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * In the case of UTFGrids, \"drawing\" it means fetching and\n- * parsing the json. \n- * \n- * Returns:\n- * {Boolean} Was a tile drawn?\n+ * Method: mergeWithDefaultFilter\n+ * Merge filter passed to the read method with the default one\n+ *\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>}\n */\n- draw: function() {\n- var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (drawn) {\n- if (this.isLoading) {\n- this.abortLoading();\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this.events.triggerEvent(\"reload\");\n- } else {\n- this.isLoading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.url = this.layer.getURL(this.bounds);\n-\n- if (this.layer.useJSONP) {\n- // Use JSONP method to avoid xbrowser policy\n- var ols = new OpenLayers.Protocol.Script({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- this.json = response.data;\n- },\n- scope: this\n- });\n- ols.read();\n- this.request = ols;\n- } else {\n- // Use standard XHR\n- this.request = OpenLayers.Request.GET({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- if (response.status === 200) {\n- this.parseData(response.responseText);\n- }\n- },\n- scope: this\n- });\n- }\n+ mergeWithDefaultFilter: function(filter) {\n+ var merged;\n+ if (filter && this.defaultFilter) {\n+ merged = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.defaultFilter, filter]\n+ });\n } else {\n- this.unload();\n+ merged = filter || this.defaultFilter || undefined;\n }\n- return drawn;\n+ return merged;\n },\n \n /**\n- * Method: abortLoading\n- * Cancel a pending request.\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- abortLoading: function() {\n- if (this.request) {\n- this.request.abort();\n- delete this.request;\n- }\n- this.isLoading = false;\n+ destroy: function() {\n+ this.options = null;\n+ this.format = null;\n },\n \n /**\n- * Method: getFeatureInfo\n- * Get feature information associated with a pixel offset. If the pixel\n- * offset corresponds to a feature, the returned object will have id\n- * and data properties. Otherwise, null will be returned.\n- * \n+ * APIMethod: read\n+ * Construct a request for reading new features.\n *\n * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n+ * options - {Object} Optional object for configuring the request.\n *\n * Returns:\n- * {Object} Object with feature id and data properties corresponding to the \n- * given pixel offset.\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- getFeatureInfo: function(i, j) {\n- var info = null;\n- if (this.json) {\n- var id = this.getFeatureId(i, j);\n- if (id !== null) {\n- info = {\n- id: id,\n- data: this.json.data[id]\n- };\n- }\n- }\n- return info;\n+ read: function(options) {\n+ options = options || {};\n+ options.filter = this.mergeWithDefaultFilter(options.filter);\n },\n \n+\n /**\n- * Method: getFeatureId\n- * Get the identifier for the feature associated with a pixel offset.\n+ * APIMethod: create\n+ * Construct a request for writing newly created features.\n *\n * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n *\n * Returns:\n- * {Object} The feature identifier corresponding to the given pixel offset.\n- * Returns null if pixel doesn't correspond to a feature.\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- getFeatureId: function(i, j) {\n- var id = null;\n- if (this.json) {\n- var resolution = this.utfgridResolution;\n- var row = Math.floor(j / resolution);\n- var col = Math.floor(i / resolution);\n- var charCode = this.json.grid[row].charCodeAt(col);\n- var index = this.indexFromCharCode(charCode);\n- var keys = this.json.keys;\n- if (!isNaN(index) && (index in keys)) {\n- id = keys[index];\n- }\n- }\n- return id;\n- },\n+ create: function() {},\n \n /**\n- * Method: indexFromCharCode\n- * Given a character code for one of the UTFGrid \"grid\" characters, \n- * resolve the integer index for the feature id in the UTFGrid \"keys\"\n- * array.\n+ * APIMethod: update\n+ * Construct a request updating modified features.\n *\n * Parameters:\n- * charCode - {Integer}\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n *\n * Returns:\n- * {Integer} Index for the feature id from the keys array.\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- indexFromCharCode: function(charCode) {\n- if (charCode >= 93) {\n- charCode--;\n- }\n- if (charCode >= 35) {\n- charCode--;\n- }\n- return charCode - 32;\n- },\n+ update: function() {},\n \n /**\n- * Method: parseData\n- * Parse the JSON from a request\n+ * APIMethod: delete\n+ * Construct a request deleting a removed feature.\n *\n * Parameters:\n- * str - {String} UTFGrid as a JSON string. \n- * \n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n * Returns:\n- * {Object} parsed javascript data\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- parseData: function(str) {\n- if (!this.format) {\n- this.format = new OpenLayers.Format.JSON();\n- }\n- this.json = this.format.read(str);\n- },\n+ \"delete\": function() {},\n \n- /** \n- * Method: clear\n- * Delete data stored with this tile.\n+ /**\n+ * APIMethod: commit\n+ * Go over the features and for each take action\n+ * based on the feature state. Possible actions are create,\n+ * update and delete.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})}\n+ * options - {Object} Object whose possible keys are \"create\", \"update\",\n+ * \"delete\", \"callback\" and \"scope\", the values referenced by the\n+ * first three are objects as passed to the \"create\", \"update\", and\n+ * \"delete\" methods, the value referenced by the \"callback\" key is\n+ * a function which is called when the commit operation is complete\n+ * using the scope referenced by the \"scope\" key.\n+ *\n+ * Returns:\n+ * {Array({<OpenLayers.Protocol.Response>})} An array of\n+ * <OpenLayers.Protocol.Response> objects.\n */\n- clear: function() {\n- this.json = null;\n- },\n+ commit: function() {},\n \n- CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n+ /**\n+ * Method: abort\n+ * Abort an ongoing request.\n+ *\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>}\n+ */\n+ abort: function(response) {},\n \n-});\n-/* ======================================================================\n- OpenLayers/Layer/UTFGrid.js\n- ====================================================================== */\n+ /**\n+ * Method: createCallback\n+ * Returns a function that applies the given public method with resp and\n+ * options arguments.\n+ *\n+ * Parameters:\n+ * method - {Function} The method to be applied by the callback.\n+ * response - {<OpenLayers.Protocol.Response>} The protocol response object.\n+ * options - {Object} Options sent to the protocol method\n+ */\n+ createCallback: function(method, response, options) {\n+ return OpenLayers.Function.bind(function() {\n+ method.apply(this, [response, options]);\n+ }, this);\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ CLASS_NAME: \"OpenLayers.Protocol\"\n+});\n \n /**\n- * @requires OpenLayers/Layer/XYZ.js\n- * @requires OpenLayers/Tile/UTFGrid.js\n- */\n-\n-/** \n- * Class: OpenLayers.Layer.UTFGrid\n- * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are \n- * essentially JSON-based ASCII art with attached attributes, they are not \n- * visibly rendered. In order to use them in the map, you must add a \n- * <OpenLayers.Control.UTFGrid> control as well.\n- *\n- * Example:\n- *\n- * (start code)\n- * var world_utfgrid = new OpenLayers.Layer.UTFGrid({\n- * url: \"/tiles/world_utfgrid/${z}/${x}/${y}.json\",\n- * utfgridResolution: 4,\n- * displayInLayerSwitcher: false\n- * );\n- * map.addLayer(world_utfgrid);\n- * \n- * var control = new OpenLayers.Control.UTFGrid({\n- * layers: [world_utfgrid],\n- * handlerMode: 'move',\n- * callback: function(dataLookup) {\n- * // do something with returned data\n- * }\n- * })\n- * (end code)\n- *\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n+ * Class: OpenLayers.Protocol.Response\n+ * Protocols return Response objects to their users.\n */\n-OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * APIProperty: isBaseLayer\n- * Default is false, as UTFGrids are designed to be a transparent overlay layer. \n- */\n- isBaseLayer: false,\n-\n+OpenLayers.Protocol.Response = OpenLayers.Class({\n /**\n- * APIProperty: projection\n- * {<OpenLayers.Projection>}\n- * Source projection for the UTFGrids. Default is \"EPSG:900913\".\n+ * Property: code\n+ * {Number} - OpenLayers.Protocol.Response.SUCCESS or\n+ * OpenLayers.Protocol.Response.FAILURE\n */\n- projection: new OpenLayers.Projection(\"EPSG:900913\"),\n+ code: null,\n \n /**\n- * Property: useJSONP\n- * {Boolean}\n- * Should we use a JSONP script approach instead of a standard AJAX call?\n- *\n- * Set to true for using utfgrids from another server. \n- * Avoids same-domain policy restrictions. \n- * Note that this only works if the server accepts \n- * the callback GET parameter and dynamically \n- * wraps the returned json in a function call.\n- * \n- * Default is false\n+ * Property: requestType\n+ * {String} The type of request this response corresponds to. Either\n+ * \"create\", \"read\", \"update\" or \"delete\".\n */\n- useJSONP: false,\n+ requestType: null,\n \n /**\n- * APIProperty: url\n- * {String}\n- * URL tempate for UTFGrid tiles. Include x, y, and z parameters.\n- * E.g. \"/tiles/${z}/${x}/${y}.json\"\n+ * Property: last\n+ * {Boolean} - true if this is the last response expected in a commit,\n+ * false otherwise, defaults to true.\n */\n+ last: true,\n \n /**\n- * APIProperty: utfgridResolution\n- * {Number}\n- * Ratio of the pixel width to the width of a UTFGrid data point. If an \n- * entry in the grid represents a 4x4 block of pixels, the \n- * utfgridResolution would be 4. Default is 2 (specified in \n- * <OpenLayers.Tile.UTFGrid>).\n+ * Property: features\n+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n+ * The features returned in the response by the server. Depending on the \n+ * protocol's read payload, either features or data will be populated.\n */\n+ features: null,\n \n /**\n- * Property: tileClass\n- * {<OpenLayers.Tile>} The tile class to use for this layer.\n- * Defaults is <OpenLayers.Tile.UTFGrid>.\n+ * Property: data\n+ * {Object}\n+ * The data returned in the response by the server. Depending on the \n+ * protocol's read payload, either features or data will be populated.\n */\n- tileClass: OpenLayers.Tile.UTFGrid,\n+ data: null,\n \n /**\n- * Constructor: OpenLayers.Layer.UTFGrid\n- * Create a new UTFGrid layer.\n- *\n- * Parameters:\n- * config - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * url - {String} The url template for UTFGrid tiles. See the <url> property.\n+ * Property: reqFeatures\n+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n+ * The features provided by the user and placed in the request by the\n+ * protocol.\n */\n- initialize: function(options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(\n- this, [options.name, options.url, {}, options]\n- );\n- this.tileOptions = OpenLayers.Util.extend({\n- utfgridResolution: this.utfgridResolution\n- }, this.tileOptions);\n- },\n+ reqFeatures: null,\n \n /**\n- * Method: createBackBuffer\n- * The UTFGrid cannot create a back buffer, so this method is overriden.\n+ * Property: priv\n */\n- createBackBuffer: function() {},\n+ priv: null,\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Only used by a subclass of this layer.\n- * \n- * Returns:\n- * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid\n+ * Property: error\n+ * {Object} The error object in case a service exception was encountered.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.UTFGrid(this.getOptions());\n- }\n-\n- // get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n- },\n+ error: null,\n \n /**\n- * APIProperty: getFeatureInfo\n- * Get details about a feature associated with a map location. The object\n- * returned will have id and data properties. If the given location\n- * doesn't correspond to a feature, null will be returned.\n+ * Constructor: OpenLayers.Protocol.Response\n *\n * Parameters:\n- * location - {<OpenLayers.LonLat>} map location\n- *\n- * Returns:\n- * {Object} Object representing the feature id and UTFGrid data \n- * corresponding to the given map location. Returns null if the given\n- * location doesn't hit a feature.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- getFeatureInfo: function(location) {\n- var info = null;\n- var tileInfo = this.getTileData(location);\n- if (tileInfo && tileInfo.tile) {\n- info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j);\n- }\n- return info;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * APIMethod: getFeatureId\n- * Get the identifier for the feature associated with a map location.\n- *\n- * Parameters:\n- * location - {<OpenLayers.LonLat>} map location\n+ * Method: success\n *\n * Returns:\n- * {String} The feature identifier corresponding to the given map location.\n- * Returns null if the location doesn't hit a feature.\n+ * {Boolean} - true on success, false otherwise\n */\n- getFeatureId: function(location) {\n- var id = null;\n- var info = this.getTileData(location);\n- if (info.tile) {\n- id = info.tile.getFeatureId(info.i, info.j);\n- }\n- return id;\n+ success: function() {\n+ return this.code > 0;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.UTFGrid\"\n+ CLASS_NAME: \"OpenLayers.Protocol.Response\"\n });\n+\n+OpenLayers.Protocol.Response.SUCCESS = 1;\n+OpenLayers.Protocol.Response.FAILURE = 0;\n /* ======================================================================\n- OpenLayers/Layer/WMS.js\n+ OpenLayers/Strategy.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Layer.WMS\n- * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n- * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Class: OpenLayers.Strategy\n+ * Abstract vector layer strategy class. Not to be instantiated directly. Use\n+ * one of the strategy subclasses instead.\n */\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n- */\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n- },\n+OpenLayers.Strategy = OpenLayers.Class({\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Default is true for WMS layer\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n */\n- isBaseLayer: true,\n+ layer: null,\n \n /**\n- * APIProperty: encodeBBOX\n- * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n- * but some services want it that way. Default false.\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n */\n- encodeBBOX: false,\n+ options: null,\n \n /** \n- * APIProperty: noMagic \n- * {Boolean} If true, the image format will not be automagicaly switched \n- * from image/jpeg to image/png or image/gif when using \n- * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n- * constructor. Default false. \n+ * Property: active \n+ * {Boolean} The control is active.\n */\n- noMagic: false,\n+ active: null,\n \n /**\n- * Property: yx\n- * {Object} Keys in this object are EPSG codes for which the axis order\n- * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n- * true as value. This is only relevant for WMS versions >= 1.3.0, and\n- * only if yx is not set in <OpenLayers.Projection.defaults> for the\n- * used projection.\n+ * Property: autoActivate\n+ * {Boolean} The creator of the strategy can set autoActivate to false\n+ * to fully control when the protocol is activated and deactivated.\n+ * Defaults to true.\n */\n- yx: {},\n+ autoActivate: true,\n \n /**\n- * Constructor: OpenLayers.Layer.WMS\n- * Create a new WMS layer object\n- *\n- * Examples:\n- *\n- * The code below creates a simple WMS layer using the image/jpeg format.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {layers: \"modis,global_mosaic\"});\n- * (end)\n- * Note the 3rd argument (params). Properties added to this object will be\n- * added to the WMS GetMap requests used for this layer's tiles. The only\n- * mandatory parameter is \"layers\". Other common WMS params include\n- * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n- * always be ignored. Instead, it will be derived from the baseLayer's or\n- * map's projection.\n- *\n- * The code below creates a transparent WMS layer with additional options.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {\n- * layers: \"modis,global_mosaic\",\n- * transparent: true\n- * }, {\n- * opacity: 0.5,\n- * singleTile: true\n- * });\n- * (end)\n- * Note that by default, a WMS layer is configured as baseLayer. Setting\n- * the \"transparent\" param to true will apply some magic (see <noMagic>).\n- * The default image format changes from image/jpeg to image/png, and the\n- * layer is not configured as baseLayer.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the WMS\n- * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n- * params - {Object} An object with key/value pairs representing the\n- * GetMap query string parameters and parameter values.\n- * options - {Object} Hashtable of extra options to tag onto the layer.\n- * These options include all properties listed above, plus the ones\n- * inherited from superclasses.\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the strategy can set autoDestroy to false\n+ * to fully control when the strategy is destroyed. Defaults to\n+ * true.\n */\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- //uppercase params\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\";\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n- );\n-\n-\n- //layer is transparent \n- if (!this.noMagic && this.params.TRANSPARENT &&\n- this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n-\n- // unless explicitly set in options, make layer an overlay\n- if ((options == null) || (!options.isBaseLayer)) {\n- this.isBaseLayer = false;\n- }\n-\n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n- \"image/png\";\n- }\n- }\n-\n- },\n+ autoDestroy: true,\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n+ * Constructor: OpenLayers.Strategy\n+ * Abstract class for vector strategies. Create instances of a subclass.\n *\n- * Returns:\n- * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ // set the active property here, so that user cannot override it\n+ this.active = false;\n },\n \n /**\n- * APIMethod: reverseAxisOrder\n- * Returns true if the axis order is reversed for the WMS version and\n- * projection of the layer.\n- * \n- * Returns:\n- * {Boolean} true if the axis order is reversed, false otherwise.\n+ * APIMethod: destroy\n+ * Clean up the strategy.\n */\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 &&\n- !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n- OpenLayers.Projection.defaults[projCode].yx));\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null;\n },\n \n /**\n- * Method: getURL\n- * Return a GetMap query string for this layer\n+ * Method: setLayer\n+ * Called to set the <layer> property.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as \n- * parameters.\n+ * layer - {<OpenLayers.Layer.Vector>}\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n-\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- // WMS 1.3 introduced axis order\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ?\n- bounds.toBBOX(null, reverseAxisOrder) :\n- bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString;\n+ setLayer: function(layer) {\n+ this.layer = layer;\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * Catch changeParams and uppercase the new params to be merged in\n- * before calling changeParams on the super class.\n- * \n- * Once params have been changed, the tiles will be reloaded with\n- * the new parameters.\n- * \n- * Parameters:\n- * newParams - {Object} Hashtable of new params to use\n+ * Method: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n- newArguments);\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true;\n+ }\n+ return false;\n },\n \n- /** \n- * APIMethod: getFullRequestString\n- * Combine the layer's url with its params and these newParams. \n- * \n- * Add the SRS parameter from projection -- this is probably\n- * more eloquently done via a setProjection() method, but this \n- * works for now and always.\n+ /**\n+ * Method: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n *\n- * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n * Returns:\n- * {String} \n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n */\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n- this.projection.getCode() :\n- mapProjection.getCode();\n- var value = (projectionCode == \"none\") ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value;\n- } else {\n- this.params.SRS = value;\n- }\n-\n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true;\n }\n-\n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n- this, arguments);\n+ return false;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n });\n /* ======================================================================\n- OpenLayers/Layer/KaMapCache.js\n+ OpenLayers/Popup.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Layer/KaMap.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n+\n /**\n- * Class: OpenLayers.Layer.KaMapCache\n- * \n- * This class is designed to talk directly to a web-accessible ka-Map\n- * cache generated by the precache2.php script.\n- * \n- * To create a a new KaMapCache layer, you must indicate also the \"i\" parameter\n- * (that will be used to calculate the file extension), and another special\n- * parameter, object names \"metaTileSize\", with \"h\" (height) and \"w\" (width)\n- * properties.\n- * \n- * // Create a new kaMapCache layer. \n- * var kamap_base = new OpenLayers.Layer.KaMapCache(\n- * \"Satellite\",\n- * \"http://www.example.org/web/acessible/cache\",\n- * {g: \"satellite\", map: \"world\", i: 'png24', metaTileSize: {w: 5, h: 5} }\n- * );\n- * \n- * // Create an kaMapCache overlay layer (using \"isBaseLayer: false\"). \n- * // Forces the output to be a \"gif\", using the \"i\" parameter.\n- * var kamap_overlay = new OpenLayers.Layer.KaMapCache(\n- * \"Streets\",\n- * \"http://www.example.org/web/acessible/cache\",\n- * {g: \"streets\", map: \"world\", i: \"gif\", metaTileSize: {w: 5, h: 5} },\n- * {isBaseLayer: false}\n- * );\n- *\n- * The cache URLs must look like: \n- * var/cache/World/50000/Group_Name/def/t-440320/l20480\n- * \n- * This means that the cache generated via tile.php will *not* work with\n- * this class, and should instead use the KaMap layer.\n+ * Class: OpenLayers.Popup\n+ * A popup is a small div that can opened and closed on the map.\n+ * Typically opened in response to clicking on a marker. \n+ * See <OpenLayers.Marker>. Popup's don't require their own\n+ * layer and are added the the map using the <OpenLayers.Map.addPopup>\n+ * method.\n *\n- * More information is available in Ticket #1518.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.KaMap>\n- * - <OpenLayers.Layer.Grid>\n+ * Example:\n+ * (code)\n+ * popup = new OpenLayers.Popup(\"chicken\", \n+ * new OpenLayers.LonLat(5,40),\n+ * new OpenLayers.Size(200,200),\n+ * \"example popup\",\n+ * true);\n+ * \n+ * map.addPopup(popup);\n+ * (end)\n */\n-OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {\n-\n- /**\n- * Constant: IMAGE_EXTENSIONS\n- * {Object} Simple hash map to convert format to extension.\n- */\n- IMAGE_EXTENSIONS: {\n- 'jpeg': 'jpg',\n- 'gif': 'gif',\n- 'png': 'png',\n- 'png8': 'png',\n- 'png24': 'png',\n- 'dithered': 'png'\n- },\n-\n- /**\n- * Constant: DEFAULT_FORMAT\n- * {Object} Simple hash map to convert format to extension.\n- */\n- DEFAULT_FORMAT: 'jpeg',\n-\n- /**\n- * Constructor: OpenLayers.Layer.KaMapCache\n- * \n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object} Parameters to be sent to the HTTP server in the\n- * query string for the tile. The format can be set via the 'i'\n- * parameter (defaults to jpg) , and the map should be set via \n- * the 'map' parameter. It has been reported that ka-Map may behave\n- * inconsistently if your format parameter does not match the format\n- * parameter configured in your config.php. (See ticket #327 for more\n- * information.)\n- * options - {Object} Additional options for the layer. Any of the \n- * APIProperties listed on this layer, and any layer types it\n- * extends, can be overridden through the options parameter. \n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);\n- this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT];\n- },\n-\n- /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n-\n- var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;\n- var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;\n-\n- var components = [\n- \"/\",\n- this.params.map,\n- \"/\",\n- scale,\n- \"/\",\n- this.params.g.replace(/\\s/g, '_'),\n- \"/def/t\",\n- metaY,\n- \"/l\",\n- metaX,\n- \"/t\",\n- pY,\n- \"l\",\n- pX,\n- \".\",\n- this.extension\n- ];\n+OpenLayers.Popup = OpenLayers.Class({\n \n- var url = this.url;\n+ /** \n+ * Property: events \n+ * {<OpenLayers.Events>} custom event manager \n+ */\n+ events: null,\n \n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(components.join(''), url);\n- }\n- return url + components.join(\"\");\n- },\n+ /** Property: id\n+ * {String} the unique identifier assigned to this popup.\n+ */\n+ id: \"\",\n \n- CLASS_NAME: \"OpenLayers.Layer.KaMapCache\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/WMTS.js\n- ====================================================================== */\n+ /** \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} the position of this popup on the map\n+ */\n+ lonlat: null,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /** \n+ * Property: div \n+ * {DOMElement} the div that contains this popup.\n+ */\n+ div: null,\n \n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n+ /** \n+ * Property: contentSize \n+ * {<OpenLayers.Size>} the width and height of the content.\n+ */\n+ contentSize: null,\n \n-/**\n- * Class: OpenLayers.Layer.WMTS\n- * Instances of the WMTS class allow viewing of tiles from a service that \n- * implements the OGC WMTS specification version 1.0.0.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>} the width and height of the popup.\n+ */\n+ size: null,\n \n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer will be considered a base layer. Default is true.\n+ /** \n+ * Property: contentHTML \n+ * {String} An HTML string for this popup to display.\n */\n- isBaseLayer: true,\n+ contentHTML: null,\n \n- /**\n- * Property: version\n- * {String} WMTS version. Default is \"1.0.0\".\n+ /** \n+ * Property: backgroundColor \n+ * {String} the background color used by the popup.\n */\n- version: \"1.0.0\",\n+ backgroundColor: \"\",\n \n- /**\n- * APIProperty: requestEncoding\n- * {String} Request encoding. Can be \"REST\" or \"KVP\". Default is \"KVP\".\n+ /** \n+ * Property: opacity \n+ * {float} the opacity of this popup (between 0.0 and 1.0)\n */\n- requestEncoding: \"KVP\",\n+ opacity: \"\",\n \n- /**\n- * APIProperty: url\n- * {String|Array(String)} The base URL or request URL template for the WMTS\n- * service. Must be provided. Array is only supported for base URLs, not\n- * for request URL templates. URL templates are only supported for\n- * REST <requestEncoding>.\n+ /** \n+ * Property: border \n+ * {String} the border size of the popup. (eg 2px)\n */\n- url: null,\n+ border: \"\",\n \n- /**\n- * APIProperty: layer\n- * {String} The layer identifier advertised by the WMTS service. Must be \n- * provided.\n+ /** \n+ * Property: contentDiv \n+ * {DOMElement} a reference to the element that holds the content of\n+ * the div.\n */\n- layer: null,\n+ contentDiv: null,\n \n /** \n- * APIProperty: matrixSet\n- * {String} One of the advertised matrix set identifiers. Must be provided.\n+ * Property: groupDiv \n+ * {DOMElement} First and only child of 'div'. The group Div contains the\n+ * 'contentDiv' and the 'closeDiv'.\n */\n- matrixSet: null,\n+ groupDiv: null,\n \n /** \n- * APIProperty: style\n- * {String} One of the advertised layer styles. Must be provided.\n+ * Property: closeDiv\n+ * {DOMElement} the optional closer image\n */\n- style: null,\n+ closeDiv: null,\n \n /** \n- * APIProperty: format\n- * {String} The image MIME type. Default is \"image/jpeg\".\n+ * APIProperty: autoSize\n+ * {Boolean} Resize the popup to auto-fit the contents.\n+ * Default is false.\n */\n- format: \"image/jpeg\",\n+ autoSize: false,\n \n /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map \n- * units. If the tile origin for each matrix in a set is different,\n- * the <matrixIds> should include a topLeftCorner property. If\n- * not provided, the tile origin will default to the top left corner\n- * of the layer <maxExtent>.\n+ * APIProperty: minSize\n+ * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.\n */\n- tileOrigin: null,\n+ minSize: null,\n \n /**\n- * APIProperty: tileFullExtent\n- * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,\n- * the layer's <maxExtent> property will be used.\n+ * APIProperty: maxSize\n+ * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.\n */\n- tileFullExtent: null,\n+ maxSize: null,\n \n- /**\n- * APIProperty: formatSuffix\n- * {String} For REST request encoding, an image format suffix must be \n- * included in the request. If not provided, the suffix will be derived\n- * from the <format> property.\n+ /** \n+ * Property: displayClass\n+ * {String} The CSS class of the popup.\n */\n- formatSuffix: null,\n+ displayClass: \"olPopup\",\n \n- /**\n- * APIProperty: matrixIds\n- * {Array} A list of tile matrix identifiers. If not provided, the matrix\n- * identifiers will be assumed to be integers corresponding to the \n- * map zoom level. If a list of strings is provided, each item should\n- * be the matrix identifier that corresponds to the map zoom level.\n- * Additionally, a list of objects can be provided. Each object should\n- * describe the matrix as presented in the WMTS capabilities. These\n- * objects should have the propertes shown below.\n+ /** \n+ * Property: contentDisplayClass\n+ * {String} The CSS class of the popup content div.\n+ */\n+ contentDisplayClass: \"olPopupContent\",\n+\n+ /** \n+ * Property: padding \n+ * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal \n+ * padding of the content div inside the popup. This was originally\n+ * confused with the css padding as specified in style.css's \n+ * 'olPopupContent' class. We would like to get rid of this altogether,\n+ * except that it does come in handy for the framed and anchoredbubble\n+ * popups, who need to maintain yet another barrier between their \n+ * content and the outer border of the popup itself. \n * \n- * Matrix properties:\n- * identifier - {String} The matrix identifier (required).\n- * scaleDenominator - {Number} The matrix scale denominator.\n- * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the \n- * matrix. Must be provided if different than the layer <tileOrigin>.\n- * tileWidth - {Number} The tile width for the matrix. Must be provided \n- * if different than the width given in the layer <tileSize>.\n- * tileHeight - {Number} The tile height for the matrix. Must be provided \n- * if different than the height given in the layer <tileSize>.\n+ * Note that in order to not break API, we must continue to support \n+ * this property being set as an integer. Really, though, we'd like to \n+ * have this specified as a Bounds object so that user can specify\n+ * distinct left, top, right, bottom paddings. With the 3.0 release\n+ * we can make this only a bounds.\n */\n- matrixIds: null,\n+ padding: 0,\n \n- /**\n- * APIProperty: dimensions\n- * {Array} For RESTful request encoding, extra dimensions may be specified.\n- * Items in this list should be property names in the <params> object.\n- * Values of extra dimensions will be determined from the corresponding\n- * values in the <params> object.\n+ /** \n+ * Property: disableFirefoxOverflowHack\n+ * {Boolean} The hack for overflow in Firefox causes all elements \n+ * to be re-drawn, which causes Flash elements to be \n+ * re-initialized, which is troublesome.\n+ * With this property the hack can be disabled.\n */\n- dimensions: null,\n+ disableFirefoxOverflowHack: false,\n \n /**\n- * APIProperty: params\n- * {Object} Extra parameters to include in tile requests. For KVP \n- * <requestEncoding>, these properties will be encoded in the request \n- * query string. For REST <requestEncoding>, these properties will\n- * become part of the request path, with order determined by the \n- * <dimensions> list.\n+ * Method: fixPadding\n+ * To be removed in 3.0, this function merely helps us to deal with the \n+ * case where the user may have set an integer value for padding, \n+ * instead of an <OpenLayers.Bounds> object.\n */\n- params: null,\n+ fixPadding: function() {\n+ if (typeof this.padding == \"number\") {\n+ this.padding = new OpenLayers.Bounds(\n+ this.padding, this.padding, this.padding, this.padding\n+ );\n+ }\n+ },\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Additionally, if this layer is to be used\n- * as an overlay and the cache has fewer zoom levels than the base\n- * layer, you can supply a negative zoomOffset. For example, if a\n- * map zoom level of 1 corresponds to your cache level zero, you would\n- * supply a -1 zoomOffset (and set the maxResolution of the layer\n- * appropriately). The zoomOffset value has no effect if complete\n- * matrix definitions (including scaleDenominator) are supplied in\n- * the <matrixIds> property. Defaults to 0 (no zoom offset).\n+ * APIProperty: panMapIfOutOfView\n+ * {Boolean} When drawn, pan map such that the entire popup is visible in\n+ * the current viewport (if necessary).\n+ * Default is false.\n */\n- zoomOffset: 0,\n+ panMapIfOutOfView: false,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * APIProperty: keepInMap \n+ * {Boolean} If panMapIfOutOfView is false, and this property is true, \n+ * contrain the popup such that it always fits in the available map\n+ * space. By default, this is not set on the base class. If you are\n+ * creating popups that are near map edges and not allowing pannning,\n+ * and especially if you have a popup which has a\n+ * fixedRelativePosition, setting this to false may be a smart thing to\n+ * do. Subclasses may want to override this setting.\n+ * \n+ * Default is false.\n */\n- serverResolutions: null,\n+ keepInMap: false,\n \n /**\n- * Property: formatSuffixMap\n- * {Object} a map between WMTS 'format' request parameter and tile image file suffix\n+ * APIProperty: closeOnMove\n+ * {Boolean} When map pans, close the popup.\n+ * Default is false.\n */\n- formatSuffixMap: {\n- \"image/png\": \"png\",\n- \"image/png8\": \"png\",\n- \"image/png24\": \"png\",\n- \"image/png32\": \"png\",\n- \"png\": \"png\",\n- \"image/jpeg\": \"jpg\",\n- \"image/jpg\": \"jpg\",\n- \"jpeg\": \"jpg\",\n- \"jpg\": \"jpg\"\n- },\n+ closeOnMove: false,\n \n- /**\n- * Property: matrix\n- * {Object} Matrix definition for the current map resolution. Updated by\n- * the <updateMatrixProperties> method.\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map\n */\n- matrix: null,\n+ map: null,\n \n- /**\n- * Constructor: OpenLayers.Layer.WMTS\n- * Create a new WMTS layer.\n- *\n- * Example:\n- * (code)\n- * var wmts = new OpenLayers.Layer.WMTS({\n- * name: \"My WMTS Layer\",\n- * url: \"http://example.com/wmts\", \n- * layer: \"layer_id\",\n- * style: \"default\",\n- * matrixSet: \"matrix_id\"\n- * });\n- * (end)\n- *\n- * Parameters:\n- * config - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * url - {String} The base url for the service. See the <url> property.\n- * layer - {String} The layer identifier. See the <layer> property.\n- * style - {String} The layer style identifier. See the <style> property.\n- * matrixSet - {String} The tile matrix set identifier. See the <matrixSet>\n- * property.\n- *\n- * Any other documented layer properties can be provided in the config object.\n+ /** \n+ * Constructor: OpenLayers.Popup\n+ * Create a popup.\n+ * \n+ * Parameters: \n+ * id - {String} a unqiue identifier for this popup. If null is passed\n+ * an identifier will be automatically generated. \n+ * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will\n+ * be shown.\n+ * contentSize - {<OpenLayers.Size>} The size of the content.\n+ * contentHTML - {String} An HTML string to display inside the \n+ * popup.\n+ * closeBox - {Boolean} Whether to display a close box inside\n+ * the popup.\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n */\n- initialize: function(config) {\n+ initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n+ if (id == null) {\n+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ }\n \n- // confirm required properties are supplied\n- var required = {\n- url: true,\n- layer: true,\n- style: true,\n- matrixSet: true\n- };\n- for (var prop in required) {\n- if (!(prop in config)) {\n- throw new Error(\"Missing property '\" + prop + \"' in layer configuration.\");\n- }\n+ this.id = id;\n+ this.lonlat = lonlat;\n+\n+ this.contentSize = (contentSize != null) ? contentSize :\n+ new OpenLayers.Size(\n+ OpenLayers.Popup.WIDTH,\n+ OpenLayers.Popup.HEIGHT);\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n }\n+ this.backgroundColor = OpenLayers.Popup.COLOR;\n+ this.opacity = OpenLayers.Popup.OPACITY;\n+ this.border = OpenLayers.Popup.BORDER;\n \n- config.params = OpenLayers.Util.upperCaseObject(config.params);\n- var args = [config.name, config.url, config.params, config];\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);\n+ this.div = OpenLayers.Util.createDiv(this.id, null, null,\n+ null, null, null, \"hidden\");\n+ this.div.className = this.displayClass;\n \n+ var groupDivId = this.id + \"_GroupDiv\";\n+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n+ null, \"relative\", null,\n+ \"hidden\");\n \n- // determine format suffix (for REST)\n- if (!this.formatSuffix) {\n- this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split(\"/\").pop();\n- }\n+ var id = this.div.id + \"_contentDiv\";\n+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n+ null, \"relative\");\n+ this.contentDiv.className = this.contentDisplayClass;\n+ this.groupDiv.appendChild(this.contentDiv);\n+ this.div.appendChild(this.groupDiv);\n \n- // expand matrixIds (may be array of string or array of object)\n- if (this.matrixIds) {\n- var len = this.matrixIds.length;\n- if (len && typeof this.matrixIds[0] === \"string\") {\n- var ids = this.matrixIds;\n- this.matrixIds = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- this.matrixIds[i] = {\n- identifier: ids[i]\n- };\n- }\n- }\n+ if (closeBox) {\n+ this.addCloseBox(closeBoxCallback);\n }\n \n+ this.registerEvents();\n },\n \n- /**\n- * Method: setMap\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- setMap: function() {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ destroy: function() {\n+\n+ this.id = null;\n+ this.lonlat = null;\n+ this.size = null;\n+ this.contentHTML = null;\n+\n+ this.backgroundColor = null;\n+ this.opacity = null;\n+ this.border = null;\n+\n+ if (this.closeOnMove && this.map) {\n+ this.map.events.unregister(\"movestart\", this, this.hide);\n+ }\n+\n+ this.events.destroy();\n+ this.events = null;\n+\n+ if (this.closeDiv) {\n+ OpenLayers.Event.stopObservingElement(this.closeDiv);\n+ this.groupDiv.removeChild(this.closeDiv);\n+ }\n+ this.closeDiv = null;\n+\n+ this.div.removeChild(this.groupDiv);\n+ this.groupDiv = null;\n+\n+ if (this.map != null) {\n+ this.map.removePopup(this);\n+ }\n+ this.map = null;\n+ this.div = null;\n+\n+ this.autoSize = null;\n+ this.minSize = null;\n+ this.maxSize = null;\n+ this.padding = null;\n+ this.panMapIfOutOfView = null;\n },\n \n- /**\n- * Method: updateMatrixProperties\n- * Called when map resolution changes to update matrix related properties.\n+ /** \n+ * Method: draw\n+ * Constructs the elements that make up the popup.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>} the position the popup in pixels.\n+ * \n+ * Returns:\n+ * {DOMElement} Reference to a div that contains the drawn popup\n */\n- updateMatrixProperties: function() {\n- this.matrix = this.getMatrix();\n- if (this.matrix) {\n- if (this.matrix.topLeftCorner) {\n- this.tileOrigin = this.matrix.topLeftCorner;\n- }\n- if (this.matrix.tileWidth && this.matrix.tileHeight) {\n- this.tileSize = new OpenLayers.Size(\n- this.matrix.tileWidth, this.matrix.tileHeight\n- );\n+ draw: function(px) {\n+ if (px == null) {\n+ if ((this.lonlat != null) && (this.map != null)) {\n+ px = this.map.getLayerPxFromLonLat(this.lonlat);\n }\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(\n- this.maxExtent.left, this.maxExtent.top\n+ }\n+\n+ // this assumes that this.map already exists, which is okay because \n+ // this.draw is only called once the popup has been added to the map.\n+ if (this.closeOnMove) {\n+ this.map.events.register(\"movestart\", this, this.hide);\n+ }\n+\n+ //listen to movestart, moveend to disable overflow (FF bug)\n+ if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n+ this.map.events.register(\"movestart\", this, function() {\n+ var style = document.defaultView.getComputedStyle(\n+ this.contentDiv, null\n );\n- }\n- if (!this.tileFullExtent) {\n- this.tileFullExtent = this.maxExtent;\n+ var currentOverflow = style.getPropertyValue(\"overflow\");\n+ if (currentOverflow != \"hidden\") {\n+ this.contentDiv._oldOverflow = currentOverflow;\n+ this.contentDiv.style.overflow = \"hidden\";\n+ }\n+ });\n+ this.map.events.register(\"moveend\", this, function() {\n+ var oldOverflow = this.contentDiv._oldOverflow;\n+ if (oldOverflow) {\n+ this.contentDiv.style.overflow = oldOverflow;\n+ this.contentDiv._oldOverflow = null;\n+ }\n+ });\n+ }\n+\n+ this.moveTo(px);\n+ if (!this.autoSize && !this.size) {\n+ this.setSize(this.contentSize);\n+ }\n+ this.setBackgroundColor();\n+ this.setOpacity();\n+ this.setBorder();\n+ this.setContentHTML();\n+\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n+ }\n+\n+ return this.div;\n+ },\n+\n+ /** \n+ * Method: updatePosition\n+ * if the popup has a lonlat and its map members set, \n+ * then have it move itself to its proper position\n+ */\n+ updatePosition: function() {\n+ if ((this.lonlat) && (this.map)) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ if (px) {\n+ this.moveTo(px);\n }\n }\n },\n \n /**\n * Method: moveTo\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n+ * px - {<OpenLayers.Pixel>} the top and left position of the popup div. \n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- if (zoomChanged || !this.matrix) {\n- this.updateMatrixProperties();\n+ moveTo: function(px) {\n+ if ((px != null) && (this.div != null)) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\";\n }\n- return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);\n },\n \n /**\n- * APIMethod: clone\n- * \n- * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>\n+ * Method: visible\n+ *\n+ * Returns: \n+ * {Boolean} Boolean indicating whether or not the popup is visible\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMTS(this.options);\n- }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n+ visible: function() {\n+ return OpenLayers.Element.visible(this.div);\n },\n \n /**\n- * Method: getIdentifier\n- * Get the current index in the matrixIds array.\n+ * Method: toggle\n+ * Toggles visibility of the popup.\n */\n- getIdentifier: function() {\n- return this.getServerZoom();\n+ toggle: function() {\n+ if (this.visible()) {\n+ this.hide();\n+ } else {\n+ this.show();\n+ }\n },\n \n /**\n- * Method: getMatrix\n- * Get the appropriate matrix definition for the current map resolution.\n+ * Method: show\n+ * Makes the popup visible.\n */\n- getMatrix: function() {\n- var matrix;\n- if (!this.matrixIds || this.matrixIds.length === 0) {\n- matrix = {\n- identifier: this.getIdentifier()\n- };\n- } else {\n- // get appropriate matrix given the map scale if possible\n- if (\"scaleDenominator\" in this.matrixIds[0]) {\n- // scale denominator calculation based on WMTS spec\n- var denom =\n- OpenLayers.METERS_PER_INCH *\n- OpenLayers.INCHES_PER_UNIT[this.units] *\n- this.getServerResolution() / 0.28E-3;\n- var diff = Number.POSITIVE_INFINITY;\n- var delta;\n- for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {\n- delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));\n- if (delta < diff) {\n- diff = delta;\n- matrix = this.matrixIds[i];\n- }\n- }\n- } else {\n- // fall back on zoom as index\n- matrix = this.matrixIds[this.getIdentifier()];\n- }\n+ show: function() {\n+ this.div.style.display = '';\n+\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n }\n- return matrix;\n },\n \n- /** \n- * Method: getTileInfo\n- * Get tile information for a given location at the current map resolution.\n+ /**\n+ * Method: hide\n+ * Makes the popup invisible.\n+ */\n+ hide: function() {\n+ this.div.style.display = 'none';\n+ },\n+\n+ /**\n+ * Method: setSize\n+ * Used to adjust the size of the popup. \n *\n * Parameters:\n- * loc - {<OpenLayers.LonLat} A location in map coordinates.\n- *\n- * Returns:\n- * {Object} An object with \"col\", \"row\", \"i\", and \"j\" properties. The col\n- * and row values are zero based tile indexes from the top left. The\n- * i and j values are the number of pixels to the left and top \n- * (respectively) of the given location within the target tile.\n+ * contentSize - {<OpenLayers.Size>} the new size for the popup's \n+ * contents div (in pixels).\n */\n- getTileInfo: function(loc) {\n- var res = this.getServerResolution();\n+ setSize: function(contentSize) {\n+ this.size = contentSize.clone();\n \n- var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);\n- var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);\n+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n+ // must add that to the desired \"size\". \n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n \n- var col = Math.floor(fx);\n- var row = Math.floor(fy);\n+ // take into account the popup's 'padding' property\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n \n- return {\n- col: col,\n- row: row,\n- i: Math.floor((fx - col) * this.tileSize.w),\n- j: Math.floor((fy - row) * this.tileSize.h)\n- };\n+ // make extra space for the close div\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right;\n+ }\n+\n+ //increase size of the main popup div to take into account the \n+ // users's desired padding and close div. \n+ this.size.w += wPadding;\n+ this.size.h += hPadding;\n+\n+ //now if our browser is IE, we need to actually make the contents \n+ // div itself bigger to take its own padding into effect. this makes \n+ // me want to shoot someone, but so it goes.\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.contentSize.w +=\n+ contentDivPadding.left + contentDivPadding.right;\n+ this.contentSize.h +=\n+ contentDivPadding.bottom + contentDivPadding.top;\n+ }\n+\n+ if (this.div != null) {\n+ this.div.style.width = this.size.w + \"px\";\n+ this.div.style.height = this.size.h + \"px\";\n+ }\n+ if (this.contentDiv != null) {\n+ this.contentDiv.style.width = contentSize.w + \"px\";\n+ this.contentDiv.style.height = contentSize.h + \"px\";\n+ }\n },\n \n /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {String} A URL for the tile corresponding to the given bounds.\n+ * APIMethod: updateSize\n+ * Auto size the popup so that it precisely fits its contents (as \n+ * determined by this.contentDiv.innerHTML). Popup size will, of\n+ * course, be limited by the available space on the current map\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var url = \"\";\n- if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {\n+ updateSize: function() {\n \n- var center = bounds.getCenterLonLat();\n- var info = this.getTileInfo(center);\n- var matrixId = this.matrix.identifier;\n- var dimensions = this.dimensions,\n- params;\n+ // determine actual render dimensions of the contents by putting its\n+ // contents into a fake contentDiv (for the CSS) and then measuring it\n+ var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" +\n+ this.contentDiv.innerHTML +\n+ \"</div>\";\n \n- if (OpenLayers.Util.isArray(this.url)) {\n- url = this.selectUrl([\n- this.version, this.style, this.matrixSet,\n- this.matrix.identifier, info.row, info.col\n- ].join(\",\"), this.url);\n- } else {\n- url = this.url;\n+ var containerElement = (this.map) ? this.map.div : document.body;\n+ var realSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, null, {\n+ displayClass: this.displayClass,\n+ containerElement: containerElement\n }\n+ );\n \n- if (this.requestEncoding.toUpperCase() === \"REST\") {\n- params = this.params;\n- if (url.indexOf(\"{\") !== -1) {\n- var template = url.replace(/\\{/g, \"${\");\n- var context = {\n- // spec does not make clear if capital S or not\n- style: this.style,\n- Style: this.style,\n- TileMatrixSet: this.matrixSet,\n- TileMatrix: this.matrix.identifier,\n- TileRow: info.row,\n- TileCol: info.col\n- };\n- if (dimensions) {\n- var dimension, i;\n- for (i = dimensions.length - 1; i >= 0; --i) {\n- dimension = dimensions[i];\n- context[dimension] = params[dimension.toUpperCase()];\n- }\n- }\n- url = OpenLayers.String.format(template, context);\n- } else {\n- // include 'version', 'layer' and 'style' in tile resource url\n- var path = this.version + \"/\" + this.layer + \"/\" + this.style + \"/\";\n+ // is the \"real\" size of the div is safe to display in our map?\n+ var safeSize = this.getSafeContentSize(realSize);\n \n- // append optional dimension path elements\n- if (dimensions) {\n- for (var i = 0; i < dimensions.length; i++) {\n- if (params[dimensions[i]]) {\n- path = path + params[dimensions[i]] + \"/\";\n- }\n- }\n- }\n+ var newSize = null;\n+ if (safeSize.equals(realSize)) {\n+ //real size of content is small enough to fit on the map, \n+ // so we use real size.\n+ newSize = realSize;\n \n- // append other required path elements\n- path = path + this.matrixSet + \"/\" + this.matrix.identifier +\n- \"/\" + info.row + \"/\" + info.col + \".\" + this.formatSuffix;\n+ } else {\n \n- if (!url.match(/\\/$/)) {\n- url = url + \"/\";\n+ // make a new 'size' object with the clipped dimensions \n+ // set or null if not clipped.\n+ var fixedSize = {\n+ w: (safeSize.w < realSize.w) ? safeSize.w : null,\n+ h: (safeSize.h < realSize.h) ? safeSize.h : null\n+ };\n+\n+ if (fixedSize.w && fixedSize.h) {\n+ //content is too big in both directions, so we will use \n+ // max popup size (safeSize), knowing well that it will \n+ // overflow both ways. \n+ newSize = safeSize;\n+ } else {\n+ //content is clipped in only one direction, so we need to \n+ // run getRenderedDimensions() again with a fixed dimension\n+ var clippedSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, fixedSize, {\n+ displayClass: this.contentDisplayClass,\n+ containerElement: containerElement\n }\n- url = url + path;\n- }\n- } else if (this.requestEncoding.toUpperCase() === \"KVP\") {\n+ );\n \n- // assemble all required parameters\n- params = {\n- SERVICE: \"WMTS\",\n- REQUEST: \"GetTile\",\n- VERSION: this.version,\n- LAYER: this.layer,\n- STYLE: this.style,\n- TILEMATRIXSET: this.matrixSet,\n- TILEMATRIX: this.matrix.identifier,\n- TILEROW: info.row,\n- TILECOL: info.col,\n- FORMAT: this.format\n- };\n- url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);\n+ //if the clipped size is still the same as the safeSize, \n+ // that means that our content must be fixed in the \n+ // offending direction. If overflow is 'auto', this means \n+ // we are going to have a scrollbar for sure, so we must \n+ // adjust for that.\n+ //\n+ var currentOverflow = OpenLayers.Element.getStyle(\n+ this.contentDiv, \"overflow\"\n+ );\n+ if ((currentOverflow != \"hidden\") &&\n+ (clippedSize.equals(safeSize))) {\n+ var scrollBar = OpenLayers.Util.getScrollbarWidth();\n+ if (fixedSize.w) {\n+ clippedSize.h += scrollBar;\n+ } else {\n+ clippedSize.w += scrollBar;\n+ }\n+ }\n \n+ newSize = this.getSafeContentSize(clippedSize);\n }\n }\n- return url;\n+ this.setSize(newSize);\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * Extend the existing layer <params> with new properties. Tiles will be\n- * reloaded with updated params in the request.\n- * \n+ * Method: setBackgroundColor\n+ * Sets the background color of the popup.\n+ *\n * Parameters:\n- * newParams - {Object} Properties to extend to existing <params>.\n+ * color - {String} the background color. eg \"#FFBBBB\"\n */\n- mergeNewParams: function(newParams) {\n- if (this.requestEncoding.toUpperCase() === \"KVP\") {\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(\n- this, [OpenLayers.Util.upperCaseObject(newParams)]\n- );\n+ setBackgroundColor: function(color) {\n+ if (color != undefined) {\n+ this.backgroundColor = color;\n }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.WMTS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/MapServer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n \n-/**\n- * Class: OpenLayers.Layer.MapServer\n- * Instances of OpenLayers.Layer.MapServer are used to display\n- * data from a MapServer CGI instance.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ if (this.div != null) {\n+ this.div.style.backgroundColor = this.backgroundColor;\n+ }\n+ },\n \n /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n+ * Method: setOpacity\n+ * Sets the opacity of the popup.\n+ * \n+ * Parameters:\n+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n */\n- DEFAULT_PARAMS: {\n- mode: \"map\",\n- map_imagetype: \"png\"\n+ setOpacity: function(opacity) {\n+ if (opacity != undefined) {\n+ this.opacity = opacity;\n+ }\n+\n+ if (this.div != null) {\n+ // for Mozilla and Safari\n+ this.div.style.opacity = this.opacity;\n+\n+ // for IE\n+ this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n+ }\n },\n \n /**\n- * Constructor: OpenLayers.Layer.MapServer\n- * Create a new MapServer layer object\n+ * Method: setBorder\n+ * Sets the border style of the popup.\n *\n * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the MapServer CGI\n- * (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)\n- * params - {Object} An object with key/value pairs representing the\n- * GetMap query string parameters and parameter values.\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * border - {String} The border style value. eg 2px \n */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n-\n- this.params = OpenLayers.Util.applyDefaults(\n- this.params, this.DEFAULT_PARAMS\n- );\n+ setBorder: function(border) {\n+ if (border != undefined) {\n+ this.border = border;\n+ }\n \n- // unless explicitly set in options, if the layer is transparent, \n- // it will be an overlay\n- if (options == null || options.isBaseLayer == null) {\n- this.isBaseLayer = ((this.params.transparent != \"true\") &&\n- (this.params.transparent != true));\n+ if (this.div != null) {\n+ this.div.style.border = this.border;\n }\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n+ * Method: setContentHTML\n+ * Allows the user to set the HTML content of the popup.\n *\n- * Returns:\n- * {<OpenLayers.Layer.MapServer>} An exact clone of this layer\n+ * Parameters:\n+ * contentHTML - {String} HTML for the div.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.MapServer(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n+ setContentHTML: function(contentHTML) {\n+\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n- // copy/set any non-init, non-simple values here\n+ if ((this.contentDiv != null) &&\n+ (this.contentHTML != null) &&\n+ (this.contentHTML != this.contentDiv.innerHTML)) {\n+\n+ this.contentDiv.innerHTML = this.contentHTML;\n+\n+ if (this.autoSize) {\n+\n+ //if popup has images, listen for when they finish\n+ // loading and resize accordingly\n+ this.registerImageListeners();\n+\n+ //auto size the popup to its current contents\n+ this.updateSize();\n+ }\n+ }\n \n- return obj;\n },\n \n /**\n- * Method: getURL\n- * Return a query string for this layer\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox \n- * for the request\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also \n- * the passed-in bounds and appropriate tile size specified \n- * as parameters.\n+ * Method: registerImageListeners\n+ * Called when an image contained by the popup loaded. this function\n+ * updates the popup size, then unregisters the image load listener.\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- // Make a list, so that getFullRequestString uses literal \",\" \n- var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top];\n+ registerImageListeners: function() {\n \n- var imageSize = this.getImageSize();\n+ // As the images load, this function will call updateSize() to \n+ // resize the popup to fit the content div (which presumably is now\n+ // bigger than when the image was not loaded).\n+ // \n+ // If the 'panMapIfOutOfView' property is set, we will pan the newly\n+ // resized popup back into view.\n+ // \n+ // Note that this function, when called, will have 'popup' and \n+ // 'img' properties in the context.\n+ //\n+ var onImgLoad = function() {\n+ if (this.popup.id === null) { // this.popup has been destroyed!\n+ return;\n+ }\n+ this.popup.updateSize();\n \n- // make lists, so that literal ','s are used \n- var url = this.getFullRequestString({\n- mapext: extent,\n- imgext: extent,\n- map_size: [imageSize.w, imageSize.h],\n- imgx: imageSize.w / 2,\n- imgy: imageSize.h / 2,\n- imgxy: [imageSize.w, imageSize.h]\n- });\n+ if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n+ this.popup.panIntoView();\n+ }\n \n- return url;\n+ OpenLayers.Event.stopObserving(\n+ this.img, \"load\", this.img._onImgLoad\n+ );\n+\n+ };\n+\n+ //cycle through the images and if their size is 0x0, that means that \n+ // they haven't been loaded yet, so we attach the listener, which \n+ // will fire when the images finish loading and will resize the \n+ // popup accordingly to its new size.\n+ var images = this.contentDiv.getElementsByTagName(\"img\");\n+ for (var i = 0, len = images.length; i < len; i++) {\n+ var img = images[i];\n+ if (img.width == 0 || img.height == 0) {\n+\n+ var context = {\n+ 'popup': this,\n+ 'img': img\n+ };\n+\n+ //expando this function to the image itself before registering\n+ // it. This way we can easily and properly unregister it.\n+ img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n+\n+ OpenLayers.Event.observe(img, 'load', img._onImgLoad);\n+ }\n+ }\n },\n \n- /** \n- * Method: getFullRequestString\n- * combine the layer's url with its params and these newParams. \n- * \n+ /**\n+ * APIMethod: getSafeContentSize\n+ * \n * Parameters:\n- * newParams - {Object} New parameters that should be added to the \n- * request string.\n- * altUrl - {String} (optional) Replace the URL in the full request \n- * string with the provided URL.\n+ * size - {<OpenLayers.Size>} Desired size to make the popup.\n * \n- * Returns: \n- * {String} A string with the layer's url and parameters embedded in it.\n+ * Returns:\n+ * {<OpenLayers.Size>} A size to make the popup which is neither smaller\n+ * than the specified minimum size, nor bigger than the maximum \n+ * size (which is calculated relative to the size of the viewport).\n */\n- getFullRequestString: function(newParams, altUrl) {\n- // use layer's url unless altUrl passed in\n- var url = (altUrl == null) ? this.url : altUrl;\n+ getSafeContentSize: function(size) {\n \n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ var safeContentSize = size.clone();\n \n- // if url is not a string, it should be an array of strings, \n- // in which case we will deterministically select one of them in \n- // order to evenly distribute requests to different urls.\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url);\n+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n+ // must add that to the desired \"size\". \n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n+\n+ // take into account the popup's 'padding' property\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n+\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right;\n }\n \n- // ignore parameters that are already in the url search string\n- var urlParams = OpenLayers.Util.upperCaseObject(\n- OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n- }\n+ // prevent the popup from being smaller than a specified minimal size\n+ if (this.minSize) {\n+ safeContentSize.w = Math.max(safeContentSize.w,\n+ (this.minSize.w - wPadding));\n+ safeContentSize.h = Math.max(safeContentSize.h,\n+ (this.minSize.h - hPadding));\n }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- // requestString always starts with url\n- var requestString = url;\n+ // prevent the popup from being bigger than a specified maximum size\n+ if (this.maxSize) {\n+ safeContentSize.w = Math.min(safeContentSize.w,\n+ (this.maxSize.w - wPadding));\n+ safeContentSize.h = Math.min(safeContentSize.h,\n+ (this.maxSize.h - hPadding));\n+ }\n \n- // MapServer needs '+' seperating things like bounds/height/width.\n- // Since typically this is URL encoded, we use a slight hack: we\n- // depend on the list-like functionality of getParameterString to\n- // leave ',' only in the case of list items (since otherwise it is\n- // encoded) then do a regular expression replace on the , characters\n- // to '+'\n+ //make sure the desired size to set doesn't result in a popup that \n+ // is bigger than the map's viewport.\n //\n- paramsString = paramsString.replace(/,/g, \"+\");\n+ if (this.map && this.map.size) {\n \n- if (paramsString != \"\") {\n- var lastServerChar = url.charAt(url.length - 1);\n- if ((lastServerChar == \"&\") || (lastServerChar == \"?\")) {\n- requestString += paramsString;\n- } else {\n- if (url.indexOf('?') == -1) {\n- //serverPath has no ? -- add one\n- requestString += '?' + paramsString;\n- } else {\n- //serverPath contains ?, so must already have paramsString at the end\n- requestString += '&' + paramsString;\n+ var extraX = 0,\n+ extraY = 0;\n+ if (this.keepInMap && !this.panMapIfOutOfView) {\n+ var px = this.map.getPixelFromLonLat(this.lonlat);\n+ switch (this.relativePosition) {\n+ case \"tr\":\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"tl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"bl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = px.y;\n+ break;\n+ case \"br\":\n+ extraX = px.x;\n+ extraY = px.y;\n+ break;\n+ default:\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n }\n }\n- }\n- return requestString;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.MapServer\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/ArcGIS93Rest.js\n- ====================================================================== */\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ var maxY = this.map.size.h -\n+ this.map.paddingForPopups.top -\n+ this.map.paddingForPopups.bottom -\n+ hPadding - extraY;\n \n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n+ var maxX = this.map.size.w -\n+ this.map.paddingForPopups.left -\n+ this.map.paddingForPopups.right -\n+ wPadding - extraX;\n \n-/**\n- * Class: OpenLayers.Layer.ArcGIS93Rest\n- * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from\n- * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API.\n- * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest>\n- * constructor. More detail on the REST API is available at\n- * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ;\n- * specifically, the URL provided to this layer should be an export service\n- * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ safeContentSize.w = Math.min(safeContentSize.w, maxX);\n+ safeContentSize.h = Math.min(safeContentSize.h, maxY);\n+ }\n \n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n- */\n- DEFAULT_PARAMS: {\n- format: \"png\"\n+ return safeContentSize;\n },\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Default is true for ArcGIS93Rest layer\n- */\n- isBaseLayer: true,\n-\n-\n- /**\n- * Constructor: OpenLayers.Layer.ArcGIS93Rest\n- * Create a new ArcGIS93Rest layer object.\n- *\n- * Example:\n- * (code)\n- * var arcims = new OpenLayers.Layer.ArcGIS93Rest(\"MyName\",\n- * \"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export\", \n- * {\n- * layers: \"0,1,2\"\n- * });\n- * (end)\n+ * Method: getContentDivPadding\n+ * Glorious, oh glorious hack in order to determine the css 'padding' of \n+ * the contentDiv. IE/Opera return null here unless we actually add the \n+ * popup's main 'div' element (which contains contentDiv) to the DOM. \n+ * So we make it invisible and then add it to the document temporarily. \n *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the ArcGIS server REST service\n- * options - {Object} An object with key/value pairs representing the\n- * options and option values.\n+ * Once we've taken the padding readings we need, we then remove it \n+ * from the DOM (it will actually get added to the DOM in \n+ * Map.js's addPopup)\n *\n- * Valid Options:\n- * format - {String} MIME type of desired image type.\n- * layers - {String} Comma-separated list of layers to display.\n- * srs - {String} Projection ID.\n+ * Returns:\n+ * {<OpenLayers.Bounds>}\n */\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- //uppercase params\n- params = OpenLayers.Util.upperCaseObject(params);\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n- );\n+ getContentDivPadding: function() {\n \n- //layer is transparent \n- if (this.params.TRANSPARENT &&\n- this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ //use cached value if we have it\n+ var contentDivPadding = this._contentDivPadding;\n+ if (!contentDivPadding) {\n \n- // unless explicitly set in options, make layer an overlay\n- if ((options == null) || (!options.isBaseLayer)) {\n- this.isBaseLayer = false;\n+ if (this.div.parentNode == null) {\n+ //make the div invisible and add it to the page \n+ this.div.style.display = \"none\";\n+ document.body.appendChild(this.div);\n }\n \n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.params.FORMAT == \"jpg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"gif\" :\n- \"png\";\n+ //read the padding settings from css, put them in an OL.Bounds \n+ contentDivPadding = new OpenLayers.Bounds(\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"),\n+ OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\")\n+ );\n+\n+ //cache the value\n+ this._contentDivPadding = contentDivPadding;\n+\n+ if (this.div.parentNode == document.body) {\n+ //remove the div from the page and make it visible again\n+ document.body.removeChild(this.div);\n+ this.div.style.display = \"\";\n }\n }\n+ return contentDivPadding;\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer\n+ * Method: addCloseBox\n+ * \n+ * Parameters:\n+ * callback - {Function} The callback to be called when the close button\n+ * is clicked.\n */\n- clone: function(obj) {\n+ addCloseBox: function(callback) {\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGIS93Rest(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n+ this.closeDiv = OpenLayers.Util.createDiv(\n+ this.id + \"_close\", null, {\n+ w: 17,\n+ h: 17\n+ }\n+ );\n+ this.closeDiv.className = \"olPopupCloseBox\";\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ // use the content div's css padding to determine if we should\n+ // padd the close div\n+ var contentDivPadding = this.getContentDivPadding();\n \n- // copy/set any non-init, non-simple values here\n+ this.closeDiv.style.right = contentDivPadding.right + \"px\";\n+ this.closeDiv.style.top = contentDivPadding.top + \"px\";\n+ this.groupDiv.appendChild(this.closeDiv);\n \n- return obj;\n+ var closePopup = callback || function(e) {\n+ this.hide();\n+ OpenLayers.Event.stop(e);\n+ };\n+ OpenLayers.Event.observe(this.closeDiv, \"touchend\",\n+ OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ OpenLayers.Event.observe(this.closeDiv, \"click\",\n+ OpenLayers.Function.bindAsEventListener(closePopup, this));\n },\n \n-\n /**\n- * Method: getURL\n- * Return an image url this layer.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the map image's url.\n+ * Method: panIntoView\n+ * Pans the map such that the popup is totaly viewable (if necessary)\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n+ panIntoView: function() {\n \n- // ArcGIS Server only wants the numeric portion of the projection ID.\n- var projWords = this.projection.getCode().split(\":\");\n- var srid = projWords[projWords.length - 1];\n+ var mapSize = this.map.getSize();\n \n- var imageSize = this.getImageSize();\n- var newParams = {\n- 'BBOX': bounds.toBBOX(),\n- 'SIZE': imageSize.w + \",\" + imageSize.h,\n- // We always want image, the other options were json, image with a whole lotta html around it, etc.\n- 'F': \"image\",\n- 'BBOXSR': srid,\n- 'IMAGESR': srid\n- };\n+ //start with the top left corner of the popup, in px, \n+ // relative to the viewport\n+ var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(\n+ parseInt(this.div.style.left),\n+ parseInt(this.div.style.top)\n+ ));\n+ var newTL = origTL.clone();\n \n- // Now add the filter parameters.\n- if (this.layerDefs) {\n- var layerDefStrList = [];\n- var layerID;\n- for (layerID in this.layerDefs) {\n- if (this.layerDefs.hasOwnProperty(layerID)) {\n- if (this.layerDefs[layerID]) {\n- layerDefStrList.push(layerID);\n- layerDefStrList.push(\":\");\n- layerDefStrList.push(this.layerDefs[layerID]);\n- layerDefStrList.push(\";\");\n- }\n- }\n- }\n- if (layerDefStrList.length > 0) {\n- newParams['LAYERDEFS'] = layerDefStrList.join(\"\");\n- }\n+ //new left (compare to margins, using this.size to calculate right)\n+ if (origTL.x < this.map.paddingForPopups.left) {\n+ newTL.x = this.map.paddingForPopups.left;\n+ } else\n+ if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {\n+ newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;\n }\n- var requestString = this.getFullRequestString(newParams);\n- return requestString;\n+\n+ //new top (compare to margins, using this.size to calculate bottom)\n+ if (origTL.y < this.map.paddingForPopups.top) {\n+ newTL.y = this.map.paddingForPopups.top;\n+ } else\n+ if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {\n+ newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;\n+ }\n+\n+ var dx = origTL.x - newTL.x;\n+ var dy = origTL.y - newTL.y;\n+\n+ this.map.pan(dx, dy);\n },\n \n- /**\n- * Method: setLayerFilter\n- * addTile creates a tile, initializes it, and adds it to the layer div. \n+ /** \n+ * Method: registerEvents\n+ * Registers events on the popup.\n *\n- * Parameters:\n- * id - {String} The id of the layer to which the filter applies.\n- * queryDef - {String} A sql-ish query filter, for more detail see the ESRI\n- * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html\n+ * Do this in a separate function so that subclasses can \n+ * choose to override it if they wish to deal differently\n+ * with mouse events\n+ * \n+ * Note in the following handler functions that some special\n+ * care is needed to deal correctly with mousing and popups. \n+ * \n+ * Because the user might select the zoom-rectangle option and\n+ * then drag it over a popup, we need a safe way to allow the\n+ * mousemove and mouseup events to pass through the popup when\n+ * they are initiated from outside. The same procedure is needed for\n+ * touchmove and touchend events.\n+ * \n+ * Otherwise, we want to essentially kill the event propagation\n+ * for all other events, though we have to do so carefully, \n+ * without disabling basic html functionality, like clicking on \n+ * hyperlinks or drag-selecting text.\n */\n- setLayerFilter: function(id, queryDef) {\n- if (!this.layerDefs) {\n- this.layerDefs = {};\n+ registerEvents: function() {\n+ this.events = new OpenLayers.Events(this, this.div, null, true);\n+\n+ function onTouchstart(evt) {\n+ OpenLayers.Event.stop(evt, true);\n }\n- if (queryDef) {\n- this.layerDefs[id] = queryDef;\n- } else {\n- delete this.layerDefs[id];\n+ this.events.on({\n+ \"mousedown\": this.onmousedown,\n+ \"mousemove\": this.onmousemove,\n+ \"mouseup\": this.onmouseup,\n+ \"click\": this.onclick,\n+ \"mouseout\": this.onmouseout,\n+ \"dblclick\": this.ondblclick,\n+ \"touchstart\": onTouchstart,\n+ scope: this\n+ });\n+\n+ },\n+\n+ /** \n+ * Method: onmousedown \n+ * When mouse goes down within the popup, make a note of\n+ * it locally, and then do not propagate the mousedown \n+ * (but do so safely so that user can select text inside)\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmousedown: function(evt) {\n+ this.mousedown = true;\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ /** \n+ * Method: onmousemove\n+ * If the drag was started within the popup, then \n+ * do not propagate the mousemove (but do so safely\n+ * so that user can select text inside)\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onmousemove: function(evt) {\n+ if (this.mousedown) {\n+ OpenLayers.Event.stop(evt, true);\n }\n },\n \n- /**\n- * Method: clearLayerFilter\n- * Clears layer filters, either from a specific layer,\n- * or all of them.\n- *\n+ /** \n+ * Method: onmouseup\n+ * When mouse comes up within the popup, after going down \n+ * in it, reset the flag, and then (once again) do not \n+ * propagate the event, but do so safely so that user can \n+ * select text inside\n+ * \n * Parameters:\n- * id - {String} The id of the layer from which to remove any\n- * filter. If unspecified/blank, all filters\n- * will be removed.\n+ * evt - {Event} \n */\n- clearLayerFilter: function(id) {\n- if (id) {\n- delete this.layerDefs[id];\n- } else {\n- delete this.layerDefs;\n+ onmouseup: function(evt) {\n+ if (this.mousedown) {\n+ this.mousedown = false;\n+ OpenLayers.Event.stop(evt, true);\n }\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * Catch changeParams and uppercase the new params to be merged in\n- * before calling changeParams on the super class.\n+ * Method: onclick\n+ * Ignore clicks, but allowing default browser handling\n * \n- * Once params have been changed, the tiles will be reloaded with\n- * the new parameters.\n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ onclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ /** \n+ * Method: onmouseout\n+ * When mouse goes out of the popup set the flag to false so that\n+ * if they let go and then drag back in, we won't be confused.\n * \n * Parameters:\n- * newParams - {Object} Hashtable of new params to use\n+ * evt - {Event} \n */\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n- newArguments);\n+ onmouseout: function(evt) {\n+ this.mousedown = false;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.ArcGIS93Rest\"\n+ /** \n+ * Method: ondblclick\n+ * Ignore double-clicks, but allowing default browser handling\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ ondblclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup\"\n });\n+\n+OpenLayers.Popup.WIDTH = 200;\n+OpenLayers.Popup.HEIGHT = 200;\n+OpenLayers.Popup.COLOR = \"white\";\n+OpenLayers.Popup.OPACITY = 1;\n+OpenLayers.Popup.BORDER = \"0px\";\n /* ======================================================================\n- OpenLayers/Layer/Zoomify.js\n+ OpenLayers/Spherical.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/*\n- * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online\n- * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic.\n+/**\n+ * @requires OpenLayers/SingleFile.js\n+ */\n+\n+/**\n+ * Namespace: Spherical\n+ * The OpenLayers.Spherical namespace includes utility functions for\n+ * calculations on the basis of a spherical earth (ignoring ellipsoidal\n+ * effects), which is accurate enough for most purposes.\n+ *\n+ * Relevant links:\n+ * * http://www.movable-type.co.uk/scripts/latlong.html\n+ * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical\n */\n \n+OpenLayers.Spherical = OpenLayers.Spherical || {};\n+\n+OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n \n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * APIFunction: computeDistanceBetween\n+ * Computes the distance between two LonLats.\n+ *\n+ * Parameters:\n+ * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n+ * JavaScript literal with lon lat properties.\n+ * radius - {Float} The radius. Optional. Defaults to 6378137 meters.\n+ *\n+ * Returns:\n+ * {Float} The distance in meters.\n */\n+OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n+ var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n+ var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n+ var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n+ var a = sinHalfDeltaLat * sinHalfDeltaLat +\n+ sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n+ return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n+};\n+\n \n /**\n- * Class: OpenLayers.Layer.Zoomify\n+ * APIFunction: computeHeading\n+ * Computes the heading from one LonLat to another LonLat.\n *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Parameters:\n+ * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n+ * JavaScript literal with lon lat properties.\n+ *\n+ * Returns:\n+ * {Float} The heading in degrees.\n */\n-OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Spherical.computeHeading = function(from, to) {\n+ var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n+ var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) -\n+ Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n+ return 180 * Math.atan2(y, x) / Math.PI;\n+};\n+/* ======================================================================\n+ OpenLayers/Renderer.js\n+ ====================================================================== */\n \n- /**\n- * Property: size\n- * {<OpenLayers.Size>} The Zoomify image size in pixels.\n- */\n- size: null,\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean}\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Renderer \n+ * This is the base class for all renderers.\n+ *\n+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n+ * It is largely composed of virtual functions that are to be implemented\n+ * in technology-specific subclasses, but there is some generic code too.\n+ * \n+ * The functions that *are* implemented here merely deal with the maintenance\n+ * of the size and extent variables, as well as the cached 'resolution' \n+ * value. \n+ * \n+ * A note to the user that all subclasses should use getResolution() instead\n+ * of directly accessing this.resolution in order to correctly use the \n+ * cacheing system.\n+ *\n+ */\n+OpenLayers.Renderer = OpenLayers.Class({\n+\n+ /** \n+ * Property: container\n+ * {DOMElement} \n */\n- isBaseLayer: true,\n+ container: null,\n \n /**\n- * Property: standardTileSize\n- * {Integer} The size of a standard (non-border) square tile in pixels.\n+ * Property: root\n+ * {DOMElement}\n */\n- standardTileSize: 256,\n+ root: null,\n \n /** \n- * Property: tileOriginCorner\n- * {String} This layer uses top-left as tile origin\n- **/\n- tileOriginCorner: \"tl\",\n+ * Property: extent\n+ * {<OpenLayers.Bounds>}\n+ */\n+ extent: null,\n \n /**\n- * Property: numberOfTiers\n- * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels)\n- * - filled during Zoomify pyramid initialization.\n+ * Property: locked\n+ * {Boolean} If the renderer is currently in a state where many things\n+ * are changing, the 'locked' property is set to true. This means \n+ * that renderers can expect at least one more drawFeature event to be\n+ * called with the 'locked' property set to 'true': In some renderers,\n+ * this might make sense to use as a 'only update local information'\n+ * flag. \n */\n- numberOfTiers: 0,\n+ locked: false,\n+\n+ /** \n+ * Property: size\n+ * {<OpenLayers.Size>} \n+ */\n+ size: null,\n \n /**\n- * Property: tileCountUpToTier\n- * {Array(Integer)} Number of tiles up to the given tier of pyramid.\n- * - filled during Zoomify pyramid initialization.\n+ * Property: resolution\n+ * {Float} cache of current map resolution\n */\n- tileCountUpToTier: null,\n+ resolution: null,\n \n /**\n- * Property: tierSizeInTiles\n- * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid.\n- * - filled during Zoomify pyramid initialization.\n+ * Property: map \n+ * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n */\n- tierSizeInTiles: null,\n+ map: null,\n \n /**\n- * Property: tierImageSize\n- * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier.\n- * - filled during Zoomify pyramid initialization.\n+ * Property: featureDx\n+ * {Number} Feature offset in x direction. Will be calculated for and\n+ * applied to the current feature while rendering (see\n+ * <calculateFeatureDx>).\n */\n- tierImageSize: null,\n+ featureDx: 0,\n \n /**\n- * Constructor: OpenLayers.Layer.Zoomify\n+ * Constructor: OpenLayers.Renderer \n *\n * Parameters:\n- * name - {String} A name for the layer.\n- * url - {String} - Relative or absolute path to the image or more\n- * precisly to the TileGroup[X] directories root.\n- * Flash plugin use the variable name \"zoomifyImagePath\" for this.\n- * size - {<OpenLayers.Size>} The size (in pixels) of the image.\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * containerID - {<String>} \n+ * options - {Object} options for this renderer. See sublcasses for\n+ * supported options.\n */\n- initialize: function(name, url, size, options) {\n+ initialize: function(containerID, options) {\n+ this.container = OpenLayers.Util.getElement(containerID);\n+ OpenLayers.Util.extend(this, options);\n+ },\n \n- // initilize the Zoomify pyramid for given size\n- this.initializeZoomify(size);\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.container = null;\n+ this.extent = null;\n+ this.size = null;\n+ this.resolution = null;\n+ this.map = null;\n+ },\n \n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name, url, size, {},\n- options\n- ]);\n+ /**\n+ * APIMethod: supported\n+ * This should be overridden by specific subclasses\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the renderer class\n+ */\n+ supported: function() {\n+ return false;\n },\n \n /**\n- * Method: initializeZoomify\n- * It generates constants for all tiers of the Zoomify pyramid\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ * We nullify the resolution cache (this.resolution) if resolutionChanged\n+ * is set to true - this way it will be re-computed on the next\n+ * getResolution() request.\n *\n * Parameters:\n- * size - {<OpenLayers.Size>} The size of the image in pixels\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n *\n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n */\n- initializeZoomify: function(size) {\n-\n- var imageSize = size.clone();\n- this.size = size.clone();\n- var tiles = new OpenLayers.Size(\n- Math.ceil(imageSize.w / this.standardTileSize),\n- Math.ceil(imageSize.h / this.standardTileSize)\n- );\n-\n- this.tierSizeInTiles = [tiles];\n- this.tierImageSize = [imageSize];\n-\n- while (imageSize.w > this.standardTileSize ||\n- imageSize.h > this.standardTileSize) {\n-\n- imageSize = new OpenLayers.Size(\n- Math.floor(imageSize.w / 2),\n- Math.floor(imageSize.h / 2)\n- );\n- tiles = new OpenLayers.Size(\n- Math.ceil(imageSize.w / this.standardTileSize),\n- Math.ceil(imageSize.h / this.standardTileSize)\n- );\n- this.tierSizeInTiles.push(tiles);\n- this.tierImageSize.push(imageSize);\n- }\n-\n- this.tierSizeInTiles.reverse();\n- this.tierImageSize.reverse();\n-\n- this.numberOfTiers = this.tierSizeInTiles.length;\n- var resolutions = [1];\n- this.tileCountUpToTier = [0];\n- for (var i = 1; i < this.numberOfTiers; i++) {\n- resolutions.unshift(Math.pow(2, i));\n- this.tileCountUpToTier.push(\n- this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h +\n- this.tileCountUpToTier[i - 1]\n- );\n+ setExtent: function(extent, resolutionChanged) {\n+ this.extent = extent.clone();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio);\n+ this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n }\n- if (!this.serverResolutions) {\n- this.serverResolutions = resolutions;\n+ if (resolutionChanged) {\n+ this.resolution = null;\n }\n+ return true;\n },\n \n /**\n- * APIMethod:destroy\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ * \n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ *\n+ * Parameters:\n+ * size - {<OpenLayers.Size>} \n */\n- destroy: function() {\n- // for now, nothing special to do here.\n- OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);\n-\n- // Remove from memory the Zoomify pyramid - is that enough?\n- this.tileCountUpToTier.length = 0;\n- this.tierSizeInTiles.length = 0;\n- this.tierImageSize.length = 0;\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ this.resolution = null;\n+ },\n \n+ /** \n+ * Method: getResolution\n+ * Uses cached copy of resolution if available to minimize computing\n+ * \n+ * Returns:\n+ * {Float} The current map's resolution\n+ */\n+ getResolution: function() {\n+ this.resolution = this.resolution || this.map.getResolution();\n+ return this.resolution;\n },\n \n /**\n- * APIMethod: clone\n+ * Method: drawFeature\n+ * Draw the feature. The optional style argument can be used\n+ * to override the feature's own style. This method should only\n+ * be called from layer.drawFeature().\n *\n * Parameters:\n- * obj - {Object}\n- *\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {<Object>}\n+ * \n * Returns:\n- * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify>\n+ * {Boolean} true if the feature has been drawn completely, false if not,\n+ * undefined if the feature had no geometry\n */\n- clone: function(obj) {\n+ drawFeature: function(feature, style) {\n+ if (style == null) {\n+ style = feature.style;\n+ }\n+ if (feature.geometry) {\n+ var bounds = feature.geometry.getBounds();\n+ if (bounds) {\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n+ if (!bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ })) {\n+ style = {\n+ display: \"none\"\n+ };\n+ } else {\n+ this.calculateFeatureDx(bounds, worldBounds);\n+ }\n+ var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n+ if (style.display != \"none\" && style.label && rendered !== false) {\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.Zoomify(this.name,\n- this.url,\n- this.size,\n- this.options);\n+ var location = feature.geometry.getCentroid();\n+ if (style.labelXOffset || style.labelYOffset) {\n+ var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n+ var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n+ var res = this.getResolution();\n+ location.move(xOffset * res, yOffset * res);\n+ }\n+ this.drawText(feature.id, style, location);\n+ } else {\n+ this.removeText(feature.id);\n+ }\n+ return rendered;\n+ }\n }\n+ },\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ /**\n+ * Method: calculateFeatureDx\n+ * {Number} Calculates the feature offset in x direction. Looking at the\n+ * center of the feature bounds and the renderer extent, we calculate how\n+ * many world widths the two are away from each other. This distance is\n+ * used to shift the feature as close as possible to the center of the\n+ * current enderer extent, which ensures that the feature is visible in the\n+ * current viewport.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n+ * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n+ */\n+ calculateFeatureDx: function(bounds, worldBounds) {\n+ this.featureDx = 0;\n+ if (worldBounds) {\n+ var worldWidth = worldBounds.getWidth(),\n+ rendererCenterX = (this.extent.left + this.extent.right) / 2,\n+ featureCenterX = (bounds.left + bounds.right) / 2,\n+ worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n+ this.featureDx = worldsAway * worldWidth;\n+ }\n+ },\n \n- // copy/set any non-init, non-simple values here\n+ /** \n+ * Method: drawGeometry\n+ * \n+ * Draw a geometry. This should only be called from the renderer itself.\n+ * Use layer.drawFeature() from outside the renderer.\n+ * virtual function\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * style - {Object} \n+ * featureId - {<String>} \n+ */\n+ drawGeometry: function(geometry, style, featureId) {},\n \n- return obj;\n- },\n+ /**\n+ * Method: drawText\n+ * Function for drawing text labels.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ * style -\n+ * location - {<OpenLayers.Geometry.Point>}\n+ */\n+ drawText: function(featureId, style, location) {},\n \n /**\n- * Method: getURL\n- *\n+ * Method: removeText\n+ * Function for removing text labels.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ */\n+ removeText: function(featureId) {},\n+\n+ /**\n+ * Method: clear\n+ * Clear all vectors from the renderer.\n+ * virtual function.\n+ */\n+ clear: function() {},\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * Returns a feature id from an event on the renderer. \n+ * How this happens is specific to the renderer. This should be\n+ * called from layer.getFeatureFromEvent().\n+ * Virtual function.\n+ * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * evt - {<OpenLayers.Event>} \n *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n+ * {String} A feature id or undefined.\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n+ getFeatureIdFromEvent: function(evt) {},\n \n- var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];\n- var path = \"TileGroup\" + Math.floor((tileIndex) / 256) +\n- \"/\" + z + \"-\" + x + \"-\" + y + \".jpg\";\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n+ /**\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features\n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ eraseFeatures: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ var feature = features[i];\n+ this.eraseGeometry(feature.geometry, feature.id);\n+ this.removeText(feature.id);\n }\n- return url + path;\n },\n \n /**\n- * Method: getImageSize\n- * getImageSize returns size for a particular tile. If bounds are given as\n- * first argument, size is calculated (bottom-right tiles are non square).\n- *\n+ * Method: eraseGeometry\n+ * Remove a geometry from the renderer (by id).\n+ * virtual function.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * featureId - {String}\n */\n- getImageSize: function() {\n- if (arguments.length > 0) {\n- var bounds = this.adjustBounds(arguments[0]);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n- var w = this.standardTileSize;\n- var h = this.standardTileSize;\n- if (x == this.tierSizeInTiles[z].w - 1) {\n- var w = this.tierImageSize[z].w % this.standardTileSize;\n- }\n- if (y == this.tierSizeInTiles[z].h - 1) {\n- var h = this.tierImageSize[z].h % this.standardTileSize;\n- }\n- return (new OpenLayers.Size(w, h));\n- } else {\n- return this.tileSize;\n- }\n+ eraseGeometry: function(geometry, featureId) {},\n+\n+ /**\n+ * Method: moveRoot\n+ * moves this renderer's root to a (different) renderer.\n+ * To be implemented by subclasses that require a common renderer root for\n+ * feature selection.\n+ * \n+ * Parameters:\n+ * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ */\n+ moveRoot: function(renderer) {},\n+\n+ /**\n+ * Method: getRenderLayerId\n+ * Gets the layer that this renderer's output appears on. If moveRoot was\n+ * used, this will be different from the id of the layer containing the\n+ * features rendered by this renderer.\n+ * \n+ * Returns:\n+ * {String} the id of the output layer.\n+ */\n+ getRenderLayerId: function() {\n+ return this.container.id;\n },\n \n /**\n- * APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin\n- * (if we don't have one.)\n- *\n+ * Method: applyDefaultSymbolizer\n+ * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * symbolizer - {Object}\n+ * \n+ * Returns:\n+ * {Object}\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n- this.map.maxExtent.top);\n+ applyDefaultSymbolizer: function(symbolizer) {\n+ var result = OpenLayers.Util.extend({},\n+ OpenLayers.Renderer.defaultSymbolizer);\n+ if (symbolizer.stroke === false) {\n+ delete result.strokeWidth;\n+ delete result.strokeColor;\n+ }\n+ if (symbolizer.fill === false) {\n+ delete result.fillColor;\n+ }\n+ OpenLayers.Util.extend(result, symbolizer);\n+ return result;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Zoomify\"\n+ CLASS_NAME: \"OpenLayers.Renderer\"\n });\n+\n+/**\n+ * Constant: OpenLayers.Renderer.defaultSymbolizer\n+ * {Object} Properties from this symbolizer will be applied to symbolizers\n+ * with missing properties. This can also be used to set a global\n+ * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n+ * following code before rendering any vector features:\n+ * (code)\n+ * OpenLayers.Renderer.defaultSymbolizer = {\n+ * fillColor: \"#808080\",\n+ * fillOpacity: 1,\n+ * strokeColor: \"#000000\",\n+ * strokeOpacity: 1,\n+ * strokeWidth: 1,\n+ * pointRadius: 3,\n+ * graphicName: \"square\"\n+ * };\n+ * (end)\n+ */\n+OpenLayers.Renderer.defaultSymbolizer = {\n+ fillColor: \"#000000\",\n+ strokeColor: \"#000000\",\n+ strokeWidth: 2,\n+ fillOpacity: 1,\n+ strokeOpacity: 1,\n+ pointRadius: 0,\n+ labelAlign: 'cm'\n+};\n+\n+\n+\n+/**\n+ * Constant: OpenLayers.Renderer.symbol\n+ * Coordinate arrays for well known (named) symbols.\n+ */\n+OpenLayers.Renderer.symbol = {\n+ \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n+ 303, 215, 231, 161, 321, 161, 350, 75\n+ ],\n+ \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n+ 4, 0\n+ ],\n+ \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n+ \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n+ \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n+};\n /* ======================================================================\n- OpenLayers/Layer/Google/v3.js\n+ OpenLayers/Handler.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Google.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n */\n \n /**\n- * Constant: OpenLayers.Layer.Google.v3\n- * \n- * Mixin providing functionality specific to the Google Maps API v3.\n- * \n- * To use this layer, you must include the GMaps v3 API in your html.\n- * \n- * Note that this layer configures the google.maps.map object with the\n- * \"disableDefaultUI\" option set to true. Using UI controls that the Google\n- * Maps API provides is not supported by the OpenLayers API.\n+ * Class: OpenLayers.Handler\n+ * Base class to construct a higher-level handler for event sequences. All\n+ * handlers have activate and deactivate methods. In addition, they have\n+ * methods named like browser events. When a handler is activated, any\n+ * additional methods named like a browser event is registered as a\n+ * listener for the corresponding event. When a handler is deactivated,\n+ * those same methods are unregistered as event listeners.\n+ *\n+ * Handlers also typically have a callbacks object with keys named like\n+ * the abstracted events or event sequences that they are in charge of\n+ * handling. The controls that wrap handlers define the methods that\n+ * correspond to these abstract events - so instead of listening for\n+ * individual browser events, they only listen for the abstract events\n+ * defined by the handler.\n+ * \n+ * Handlers are created by controls, which ultimately have the responsibility\n+ * of making changes to the the state of the application. Handlers\n+ * themselves may make temporary changes, but in general are expected to\n+ * return the application in the same state that they found it.\n */\n-OpenLayers.Layer.Google.v3 = {\n+OpenLayers.Handler = OpenLayers.Class({\n \n /**\n- * Constant: DEFAULTS\n- * {Object} It is not recommended to change the properties set here. Note\n- * that Google.v3 layers only work when sphericalMercator is set to true.\n- * \n+ * Property: id\n+ * {String}\n+ */\n+ id: null,\n+\n+ /**\n+ * APIProperty: control\n+ * {<OpenLayers.Control>}. The control that initialized this handler. The\n+ * control is assumed to have a valid map property - that map is used\n+ * in the handler's own setMap method.\n+ */\n+ control: null,\n+\n+ /**\n+ * Property: map\n+ * {<OpenLayers.Map>}\n+ */\n+ map: null,\n+\n+ /**\n+ * APIProperty: keyMask\n+ * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n+ * constants to construct a keyMask. The keyMask is used by\n+ * <checkModifiers>. If the keyMask matches the combination of keys\n+ * down on an event, checkModifiers returns true.\n+ *\n+ * Example:\n * (code)\n- * {\n- * sphericalMercator: true,\n- * projection: \"EPSG:900913\"\n- * }\n+ * // handler only responds if the Shift key is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n+ *\n+ * // handler only responds if Ctrl-Shift is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n+ * OpenLayers.Handler.MOD_CTRL;\n * (end)\n */\n- DEFAULTS: {\n- sphericalMercator: true,\n- projection: \"EPSG:900913\"\n- },\n+ keyMask: null,\n \n /**\n- * APIProperty: animationEnabled\n- * {Boolean} If set to true, the transition between zoom levels will be\n- * animated (if supported by the GMaps API for the device used). Set to\n- * false to match the zooming experience of other layer types. Default\n- * is true. Note that the GMaps API does not give us control over zoom\n- * animation, so if set to false, when zooming, this will make the\n- * layer temporarily invisible, wait until GMaps reports the map being\n- * idle, and make it visible again. The result will be a blank layer\n- * for a few moments while zooming.\n+ * Property: active\n+ * {Boolean}\n */\n- animationEnabled: true,\n+ active: false,\n \n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners.\n+ /**\n+ * Property: evt\n+ * {Event} This property references the last event handled by the handler.\n+ * Note that this property is not part of the stable API. Use of the\n+ * evt property should be restricted to controls in the library\n+ * or other applications that are willing to update with changes to\n+ * the OpenLayers code.\n */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = google.maps.MapTypeId.ROADMAP;\n- }\n- var mapObject;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n- // create GMap\n- var center = this.map.getCenter();\n- var container = document.createElement('div');\n- container.className = \"olForeignContainer\";\n- container.style.width = '100%';\n- container.style.height = '100%';\n- mapObject = new google.maps.Map(container, {\n- center: center ?\n- new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n- zoom: this.map.getZoom() || 0,\n- mapTypeId: this.type,\n- disableDefaultUI: true,\n- keyboardShortcuts: false,\n- draggable: false,\n- disableDoubleClickZoom: true,\n- scrollwheel: false,\n- streetViewControl: false\n- });\n- var googleControl = document.createElement('div');\n- googleControl.style.width = '100%';\n- googleControl.style.height = '100%';\n- mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n-\n- // cache elements for use by any other google layers added to\n- // this same map\n- cache = {\n- googleControl: googleControl,\n- mapObject: mapObject,\n- count: 1\n- };\n- OpenLayers.Layer.Google.cache[this.map.id] = cache;\n- }\n- this.mapObject = mapObject;\n- this.setGMapVisibility(this.visibility);\n- },\n+ evt: null,\n \n /**\n- * APIMethod: onMapResize\n+ * Property: touch\n+ * {Boolean} Indicates the support of touch events. When touch events are \n+ * started touch will be true and all mouse related listeners will do \n+ * nothing.\n */\n- onMapResize: function() {\n- if (this.visibility) {\n- google.maps.event.trigger(this.mapObject, \"resize\");\n- }\n- },\n+ touch: false,\n \n /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n- * \n+ * Constructor: OpenLayers.Handler\n+ * Construct a handler.\n+ *\n * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n+ * control - {<OpenLayers.Control>} The control that initialized this\n+ * handler. The control is assumed to have a valid map property; that\n+ * map is used in the handler's own setMap method. If a map property\n+ * is present in the options argument it will be used instead.\n+ * callbacks - {Object} An object whose properties correspond to abstracted\n+ * events or sequences of browser events. The values for these\n+ * properties are functions defined by the control that get called by\n+ * the handler.\n+ * options - {Object} An optional object whose properties will be set on\n+ * the handler.\n */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- var map = this.map;\n- if (cache) {\n- var type = this.type;\n- var layers = map.layers;\n- var layer;\n- for (var i = layers.length - 1; i >= 0; --i) {\n- layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.Google &&\n- layer.visibility === true && layer.inRange === true) {\n- type = layer.type;\n- visible = true;\n- break;\n- }\n- }\n- var container = this.mapObject.getDiv();\n- if (visible === true) {\n- if (container.parentNode !== map.div) {\n- if (!cache.rendered) {\n- var me = this;\n- google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {\n- cache.rendered = true;\n- me.setGMapVisibility(me.getVisibility());\n- me.moveTo(me.map.getCenter());\n- });\n- } else {\n- map.div.appendChild(container);\n- cache.googleControl.appendChild(map.viewPortDiv);\n- google.maps.event.trigger(this.mapObject, 'resize');\n- }\n- }\n- this.mapObject.setMapTypeId(type);\n- } else if (cache.googleControl.hasChildNodes()) {\n- map.div.appendChild(map.viewPortDiv);\n- map.div.removeChild(container);\n- }\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.control = control;\n+ this.callbacks = callbacks;\n+\n+ var map = this.map || control.map;\n+ if (map) {\n+ this.setMap(map);\n }\n+\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n /**\n- * Method: getMapContainer\n- * \n- * Returns:\n- * {DOMElement} the GMap container's div\n+ * Method: setMap\n */\n- getMapContainer: function() {\n- return this.mapObject.getDiv();\n+ setMap: function(map) {\n+ this.map = map;\n },\n \n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n- * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n- * \n+ * Method: checkModifiers\n+ * Check the keyMask on the handler. If no <keyMask> is set, this always\n+ * returns true. If a <keyMask> is set and it matches the combination\n+ * of keys down on an event, this returns true.\n+ *\n * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n+ * {Boolean} The keyMask matches the keys down on an event.\n */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new google.maps.LatLngBounds(\n- new google.maps.LatLng(sw.lat, sw.lon),\n- new google.maps.LatLng(ne.lat, ne.lon)\n- );\n+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true;\n }\n- return moBounds;\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n-\n+ /* calculate the keyboard modifier mask for this event */\n+ var keyModifiers =\n+ (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n+ (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n+ (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n+ (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n \n- // LonLat - Pixel Translation\n+ /* if it differs from the handler object's key mask,\n+ bail out of the event handler */\n+ return (keyModifiers == this.keyMask);\n+ },\n \n /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n+ * APIMethod: activate\n+ * Turn on the handler. Returns false if the handler was already active.\n * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n+ * Returns: \n+ * {Boolean} The handler was activated.\n */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- var size = this.map.getSize();\n- var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n- var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n- var res = this.map.getResolution();\n-\n- var delta_x = moPixel.x - (size.w / 2);\n- var delta_y = moPixel.y - (size.h / 2);\n-\n- var lonlat = new OpenLayers.LonLat(\n- lon + delta_x * res,\n- lat - delta_y * res\n- );\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n+ activate: function() {\n+ if (this.active) {\n+ return false;\n }\n- return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);\n+ // register for event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.register(events[i], this[events[i]]);\n+ }\n+ }\n+ this.active = true;\n+ return true;\n },\n \n /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n+ * APIMethod: deactivate\n+ * Turn off the handler. Returns false if the handler was already inactive.\n * \n * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n+ * {Boolean} The handler was deactivated.\n */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- var res = this.map.getResolution();\n- var extent = this.map.getExtent();\n- return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)),\n- (1 / res * (extent.top - lat)));\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false;\n+ }\n+ // unregister event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n+ }\n+ }\n+ this.touch = false;\n+ this.active = false;\n+ return true;\n },\n \n-\n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n- * \n- * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n+ /**\n+ * Method: startTouch\n+ * Start touch events, this method must be called by subclasses in \n+ * \"touchstart\" method. When touch events are started <touch> will be\n+ * true and all mouse related listeners will do nothing.\n */\n- setMapObjectCenter: function(center, zoom) {\n- if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n- var mapContainer = this.getMapContainer();\n- google.maps.event.addListenerOnce(\n- this.mapObject,\n- \"idle\",\n- function() {\n- mapContainer.style.visibility = \"\";\n+ startTouch: function() {\n+ if (!this.touch) {\n+ this.touch = true;\n+ var events = [\n+ \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n+ \"mouseout\"\n+ ];\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n }\n- );\n- mapContainer.style.visibility = \"hidden\";\n+ }\n }\n- this.mapObject.setOptions({\n- center: center,\n- zoom: zoom\n- });\n },\n \n-\n- // Bounds\n-\n- /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n- * \n+ /**\n+ * Method: callback\n+ * Trigger the control's named callback with the given arguments\n+ *\n * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n+ * name - {String} The key for the callback that is one of the properties\n+ * of the handler's callbacks object.\n+ * args - {Array(*)} An array of arguments (any type) with which to call \n+ * the callback (defined by the control).\n */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args);\n+ }\n },\n \n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n+ /**\n+ * Method: register\n+ * register an event on the map\n+ */\n+ register: function(name, method) {\n+ // TODO: deal with registerPriority in 3.0\n+ this.map.events.registerPriority(name, this, method);\n+ this.map.events.registerPriority(name, this, this.setEvent);\n+ },\n \n- // LonLat\n+ /**\n+ * Method: unregister\n+ * unregister an event from the map\n+ */\n+ unregister: function(name, method) {\n+ this.map.events.unregister(name, this, method);\n+ this.map.events.unregister(name, this, this.setEvent);\n+ },\n \n /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n- * \n+ * Method: setEvent\n+ * With each registered browser event, the handler sets its own evt\n+ * property. This property can be accessed by controls if needed\n+ * to get more information about the event that the handler is\n+ * processing.\n+ *\n+ * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n+ * and meta cannot be checked with the keyboard handler). For a\n+ * control to determine which modifier keys are associated with the\n+ * event that a handler is currently processing, it should access\n+ * (code)handler.evt.altKey || handler.evt.shiftKey ||\n+ * handler.evt.ctrlKey || handler.evt.metaKey(end).\n+ *\n * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n- * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n+ * evt - {Event} The browser event.\n */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new google.maps.LatLng(lat, lon);\n- }\n- return gLatLng;\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true;\n },\n \n- // Pixel\n-\n /**\n- * APIMethod: getMapObjectPixelFromXY\n- * \n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n- * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n+ * Method: destroy\n+ * Deconstruct the handler.\n */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new google.maps.Point(x, y);\n- }\n+ destroy: function() {\n+ // unregister event listeners\n+ this.deactivate();\n+ // eliminate circular references\n+ this.control = this.map = null;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_NONE\n+ * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n+ */\n+OpenLayers.Handler.MOD_NONE = 0;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_SHIFT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n+ */\n+OpenLayers.Handler.MOD_SHIFT = 1;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_CTRL\n+ * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n+ */\n+OpenLayers.Handler.MOD_CTRL = 2;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_ALT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n+ */\n+OpenLayers.Handler.MOD_ALT = 4;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_META\n+ * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n+ */\n+OpenLayers.Handler.MOD_META = 8;\n+\n \n-};\n /* ======================================================================\n- OpenLayers/Layer/Vector/RootContainer.js\n+ OpenLayers/Icon.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/Vector.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Vector.RootContainer\n- * A special layer type to combine multiple vector layers inside a single\n- * renderer root container. This class is not supposed to be instantiated\n- * from user space, it is a helper class for controls that require event\n- * processing for multiple vector layers.\n+ * Class: OpenLayers.Icon\n+ * \n+ * The icon represents a graphical icon on the screen. Typically used in\n+ * conjunction with a <OpenLayers.Marker> to represent markers on a screen.\n *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n+ * An icon has a url, size and position. It also contains an offset which \n+ * allows the center point to be represented correctly. This can be\n+ * provided either as a fixed offset or a function provided to calculate\n+ * the desired offset. \n+ * \n */\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+OpenLayers.Icon = OpenLayers.Class({\n \n- /**\n- * Property: displayInLayerSwitcher\n- * Set to false for this layer type\n+ /** \n+ * Property: url \n+ * {String} image url\n */\n- displayInLayerSwitcher: false,\n+ url: null,\n \n- /**\n- * APIProperty: layers\n- * Layers that are attached to this container. Required config option.\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>|Object} An OpenLayers.Size or\n+ * an object with a 'w' and 'h' properties.\n */\n- layers: null,\n+ size: null,\n \n- /**\n- * Constructor: OpenLayers.Layer.Vector.RootContainer\n- * Create a new root container for multiple vector layer. This constructor\n- * is not supposed to be used from user space, it is only to be used by\n- * controls that need feature selection across multiple vector layers.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- * \n- * Required options properties:\n- * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n- * container\n+ /** \n+ * Property: offset \n+ * {<OpenLayers.Pixel>|Object} distance in pixels to offset the\n+ * image when being rendered. An OpenLayers.Pixel or an object\n+ * with a 'x' and 'y' properties.\n+ */\n+ offset: null,\n+\n+ /** \n+ * Property: calculateOffset \n+ * {Function} Function to calculate the offset (based on the size)\n+ */\n+ calculateOffset: null,\n+\n+ /** \n+ * Property: imageDiv \n+ * {DOMElement} \n+ */\n+ imageDiv: null,\n+\n+ /** \n+ * Property: px \n+ * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object\n+ * with a 'x' and 'y' properties.\n+ */\n+ px: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Icon\n+ * Creates an icon, which is an image tag in a div. \n *\n- * Returns:\n- * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n- * container\n+ * url - {String} \n+ * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an\n+ * object with a 'w' and 'h'\n+ * properties.\n+ * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n+ * object with a 'x' and 'y'\n+ * properties.\n+ * calculateOffset - {Function} \n */\n+ initialize: function(url, size, offset, calculateOffset) {\n+ this.url = url;\n+ this.size = size || {\n+ w: 20,\n+ h: 20\n+ };\n+ this.offset = offset || {\n+ x: -(this.size.w / 2),\n+ y: -(this.size.h / 2)\n+ };\n+ this.calculateOffset = calculateOffset;\n \n- /**\n- * Method: display\n+ var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n+ this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);\n+ },\n+\n+ /** \n+ * Method: destroy\n+ * Nullify references and remove event listeners to prevent circular \n+ * references and memory leaks\n */\n- display: function() {},\n+ destroy: function() {\n+ // erase any drawn elements\n+ this.erase();\n+\n+ OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n+ this.imageDiv.innerHTML = \"\";\n+ this.imageDiv = null;\n+ },\n+\n+ /** \n+ * Method: clone\n+ * \n+ * Returns:\n+ * {<OpenLayers.Icon>} A fresh copy of the icon.\n+ */\n+ clone: function() {\n+ return new OpenLayers.Icon(this.url,\n+ this.size,\n+ this.offset,\n+ this.calculateOffset);\n+ },\n \n /**\n- * Method: getFeatureFromEvent\n- * walk through the layers to find the feature returned by the event\n+ * Method: setSize\n * \n * Parameters:\n- * evt - {Object} event object with a feature property\n- * \n- * Returns:\n- * {<OpenLayers.Feature.Vector>}\n+ * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or\n+ * an object with a 'w' and 'h' properties.\n */\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature;\n- }\n+ setSize: function(size) {\n+ if (size != null) {\n+ this.size = size;\n }\n+ this.draw();\n },\n \n /**\n- * Method: setMap\n+ * Method: setUrl\n * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * url - {String} \n */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer);\n+ setUrl: function(url) {\n+ if (url != null) {\n+ this.url = url;\n+ }\n+ this.draw();\n },\n \n- /**\n- * Method: removeMap\n+ /** \n+ * Method: draw\n+ * Move the div to the given pixel.\n * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n+ * object with a 'x' and 'y' properties.\n+ * \n+ * Returns:\n+ * {DOMElement} A new DOM Image of this icon set at the location passed-in\n */\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n+ draw: function(px) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,\n+ null,\n+ null,\n+ this.size,\n+ this.url,\n+ \"absolute\");\n+ this.moveTo(px);\n+ return this.imageDiv;\n },\n \n- /**\n- * Method: collectRoots\n- * Collects the root nodes of all layers this control is configured with\n- * and moveswien the nodes to this control's layer\n+ /** \n+ * Method: erase\n+ * Erase the underlying image element.\n */\n- collectRoots: function() {\n- var layer;\n- // walk through all map layers, because we want to keep the order\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer);\n- }\n+ erase: function() {\n+ if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n+ OpenLayers.Element.remove(this.imageDiv);\n }\n },\n \n+ /** \n+ * Method: setOpacity\n+ * Change the icon's opacity\n+ *\n+ * Parameters:\n+ * opacity - {float} \n+ */\n+ setOpacity: function(opacity) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,\n+ null, null, null, null, opacity);\n+\n+ },\n+\n /**\n- * Method: resetRoots\n- * Resets the root nodes back into the layers they belong to.\n+ * Method: moveTo\n+ * move icon to passed in px.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n+ * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n */\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer);\n+ moveTo: function(px) {\n+ //if no px passed in, use stored location\n+ if (px != null) {\n+ this.px = px;\n+ }\n+\n+ if (this.imageDiv != null) {\n+ if (this.px == null) {\n+ this.display(false);\n+ } else {\n+ if (this.calculateOffset) {\n+ this.offset = this.calculateOffset(this.size);\n+ }\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n+ x: this.px.x + this.offset.x,\n+ y: this.px.y + this.offset.y\n+ });\n }\n }\n },\n \n+ /** \n+ * Method: display\n+ * Hide or show the icon\n+ *\n+ * Parameters:\n+ * display - {Boolean} \n+ */\n+ display: function(display) {\n+ this.imageDiv.style.display = (display) ? \"\" : \"none\";\n+ },\n+\n+\n /**\n- * Method: handleChangeLayer\n- * Event handler for the map's changelayer event. We need to rebuild\n- * this container's layer dom if order of one of its layers changes.\n- * This handler is added with the setMap method, and removed with the\n- * removeMap method.\n+ * APIMethod: isDrawn\n * \n- * Parameters:\n- * evt - {Object}\n+ * Returns:\n+ * {Boolean} Whether or not the icon is drawn.\n */\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" &&\n- OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots();\n- }\n+ isDrawn: function() {\n+ // nodeType 11 for ie, whose nodes *always* have a parentNode\n+ // (of type document fragment)\n+ var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&\n+ (this.imageDiv.parentNode.nodeType != 11));\n+\n+ return isDrawn;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n+ CLASS_NAME: \"OpenLayers.Icon\"\n });\n /* ======================================================================\n- OpenLayers/Popup/Anchored.js\n+ OpenLayers/Marker.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Popup.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Icon.js\n */\n \n /**\n- * Class: OpenLayers.Popup.Anchored\n- * \n- * Inherits from:\n- * - <OpenLayers.Popup>\n+ * Class: OpenLayers.Marker\n+ * Instances of OpenLayers.Marker are a combination of a \n+ * <OpenLayers.LonLat> and an <OpenLayers.Icon>. \n+ *\n+ * Markers are generally added to a special layer called\n+ * <OpenLayers.Layer.Markers>.\n+ *\n+ * Example:\n+ * (code)\n+ * var markers = new OpenLayers.Layer.Markers( \"Markers\" );\n+ * map.addLayer(markers);\n+ *\n+ * var size = new OpenLayers.Size(21,25);\n+ * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);\n+ * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);\n+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));\n+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));\n+ *\n+ * (end)\n+ *\n+ * Note that if you pass an icon into the Marker constructor, it will take\n+ * that icon and use it. This means that you should not share icons between\n+ * markers -- you use them once, but you should clone() for any additional\n+ * markers using that same icon.\n */\n-OpenLayers.Popup.Anchored =\n- OpenLayers.Class(OpenLayers.Popup, {\n+OpenLayers.Marker = OpenLayers.Class({\n \n- /** \n- * Property: relativePosition\n- * {String} Relative position of the popup (\"br\", \"tr\", \"tl\" or \"bl\").\n- */\n- relativePosition: null,\n+ /** \n+ * Property: icon \n+ * {<OpenLayers.Icon>} The icon used by this marker.\n+ */\n+ icon: null,\n \n- /**\n- * APIProperty: keepInMap \n- * {Boolean} If panMapIfOutOfView is false, and this property is true, \n- * contrain the popup such that it always fits in the available map\n- * space. By default, this is set. If you are creating popups that are\n- * near map edges and not allowing pannning, and especially if you have\n- * a popup which has a fixedRelativePosition, setting this to false may\n- * be a smart thing to do.\n- * \n- * For anchored popups, default is true, since subclasses will\n- * usually want this functionality.\n- */\n- keepInMap: true,\n+ /** \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} location of object\n+ */\n+ lonlat: null,\n \n- /**\n- * Property: anchor\n- * {Object} Object to which we'll anchor the popup. Must expose a \n- * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).\n- */\n- anchor: null,\n+ /** \n+ * Property: events \n+ * {<OpenLayers.Events>} the event handler.\n+ */\n+ events: null,\n \n- /** \n- * Constructor: OpenLayers.Popup.Anchored\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> \n- * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n- var newArguments = [\n- id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback\n- ];\n- OpenLayers.Popup.prototype.initialize.apply(this, newArguments);\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} the map this marker is attached to\n+ */\n+ map: null,\n \n- this.anchor = (anchor != null) ? anchor :\n- {\n- size: new OpenLayers.Size(0, 0),\n- offset: new OpenLayers.Pixel(0, 0)\n- };\n- },\n+ /** \n+ * Constructor: OpenLayers.Marker\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>} the position of this marker\n+ * icon - {<OpenLayers.Icon>} the icon for this marker\n+ */\n+ initialize: function(lonlat, icon) {\n+ this.lonlat = lonlat;\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.anchor = null;\n- this.relativePosition = null;\n+ var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();\n+ if (this.icon == null) {\n+ this.icon = newIcon;\n+ } else {\n+ this.icon.url = newIcon.url;\n+ this.icon.size = newIcon.size;\n+ this.icon.offset = newIcon.offset;\n+ this.icon.calculateOffset = newIcon.calculateOffset;\n+ }\n+ this.events = new OpenLayers.Events(this, this.icon.imageDiv);\n+ },\n \n- OpenLayers.Popup.prototype.destroy.apply(this, arguments);\n- },\n+ /**\n+ * APIMethod: destroy\n+ * Destroy the marker. You must first remove the marker from any \n+ * layer which it has been added to, or you will get buggy behavior.\n+ * (This can not be done within the marker since the marker does not\n+ * know which layer it is attached to.)\n+ */\n+ destroy: function() {\n+ // erase any drawn features\n+ this.erase();\n \n- /**\n- * APIMethod: show\n- * Overridden from Popup since user might hide popup and then show() it \n- * in a new location (meaning we might want to update the relative\n- * position on the show)\n- */\n- show: function() {\n- this.updatePosition();\n- OpenLayers.Popup.prototype.show.apply(this, arguments);\n- },\n+ this.map = null;\n \n- /**\n- * Method: moveTo\n- * Since the popup is moving to a new px, it might need also to be moved\n- * relative to where the marker is. We first calculate the new \n- * relativePosition, and then we calculate the new px where we will \n- * put the popup, based on the new relative position. \n- * \n- * If the relativePosition has changed, we must also call \n- * updateRelativePosition() to make any visual changes to the popup \n- * which are associated with putting it in a new relativePosition.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- */\n- moveTo: function(px) {\n- var oldRelativePosition = this.relativePosition;\n- this.relativePosition = this.calculateRelativePosition(px);\n+ this.events.destroy();\n+ this.events = null;\n \n- OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));\n+ if (this.icon != null) {\n+ this.icon.destroy();\n+ this.icon = null;\n+ }\n+ },\n \n- //if this move has caused the popup to change its relative position, \n- // we need to make the appropriate cosmetic changes.\n- if (this.relativePosition != oldRelativePosition) {\n- this.updateRelativePosition();\n- }\n- },\n+ /** \n+ * Method: draw\n+ * Calls draw on the icon, and returns that output.\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {DOMElement} A new DOM Image with this marker's icon set at the \n+ * location passed-in\n+ */\n+ draw: function(px) {\n+ return this.icon.draw(px);\n+ },\n \n- /**\n- * APIMethod: setSize\n- * \n- * Parameters:\n- * contentSize - {<OpenLayers.Size>} the new size for the popup's \n- * contents div (in pixels).\n- */\n- setSize: function(contentSize) {\n- OpenLayers.Popup.prototype.setSize.apply(this, arguments);\n+ /** \n+ * Method: erase\n+ * Erases any drawn elements for this marker.\n+ */\n+ erase: function() {\n+ if (this.icon != null) {\n+ this.icon.erase();\n+ }\n+ },\n \n- if ((this.lonlat) && (this.map)) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- this.moveTo(px);\n- }\n- },\n+ /**\n+ * Method: moveTo\n+ * Move the marker to the new location.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n+ * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n+ */\n+ moveTo: function(px) {\n+ if ((px != null) && (this.icon != null)) {\n+ this.icon.moveTo(px);\n+ }\n+ this.lonlat = this.map.getLonLatFromLayerPx(px);\n+ },\n \n- /** \n- * Method: calculateRelativePosition\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {String} The relative position (\"br\" \"tr\" \"tl\" \"bl\") at which the popup\n- * should be placed.\n- */\n- calculateRelativePosition: function(px) {\n- var lonlat = this.map.getLonLatFromLayerPx(px);\n+ /**\n+ * APIMethod: isDrawn\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the marker is drawn.\n+ */\n+ isDrawn: function() {\n+ var isDrawn = (this.icon && this.icon.isDrawn());\n+ return isDrawn;\n+ },\n \n- var extent = this.map.getExtent();\n- var quadrant = extent.determineQuadrant(lonlat);\n+ /**\n+ * Method: onScreen\n+ *\n+ * Returns:\n+ * {Boolean} Whether or not the marker is currently visible on screen.\n+ */\n+ onScreen: function() {\n \n- return OpenLayers.Bounds.oppositeQuadrant(quadrant);\n- },\n+ var onScreen = false;\n+ if (this.map) {\n+ var screenBounds = this.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat);\n+ }\n+ return onScreen;\n+ },\n \n- /**\n- * Method: updateRelativePosition\n- * The popup has been moved to a new relative location, so we may want to \n- * make some cosmetic adjustments to it. \n- * \n- * Note that in the classic Anchored popup, there is nothing to do \n- * here, since the popup looks exactly the same in all four positions.\n- * Subclasses such as Framed, however, will want to do something\n- * special here.\n- */\n- updateRelativePosition: function() {\n- //to be overridden by subclasses\n- },\n+ /**\n+ * Method: inflate\n+ * Englarges the markers icon by the specified ratio.\n+ *\n+ * Parameters:\n+ * inflate - {float} the ratio to enlarge the marker by (passing 2\n+ * will double the size).\n+ */\n+ inflate: function(inflate) {\n+ if (this.icon) {\n+ this.icon.setSize({\n+ w: this.icon.size.w * inflate,\n+ h: this.icon.size.h * inflate\n+ });\n+ }\n+ },\n \n- /** \n- * Method: calculateNewPx\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {<OpenLayers.Pixel>} The the new px position of the popup on the screen\n- * relative to the passed-in px.\n- */\n- calculateNewPx: function(px) {\n- var newPx = px.offset(this.anchor.offset);\n+ /** \n+ * Method: setOpacity\n+ * Change the opacity of the marker by changin the opacity of \n+ * its icon\n+ * \n+ * Parameters:\n+ * opacity - {float} Specified as fraction (0.4, etc)\n+ */\n+ setOpacity: function(opacity) {\n+ this.icon.setOpacity(opacity);\n+ },\n \n- //use contentSize if size is not already set\n- var size = this.size || this.contentSize;\n+ /**\n+ * Method: setUrl\n+ * Change URL of the Icon Image.\n+ * \n+ * url - {String} \n+ */\n+ setUrl: function(url) {\n+ this.icon.setUrl(url);\n+ },\n \n- var top = (this.relativePosition.charAt(0) == 't');\n- newPx.y += (top) ? -size.h : this.anchor.size.h;\n+ /** \n+ * Method: display\n+ * Hide or show the icon\n+ * \n+ * display - {Boolean} \n+ */\n+ display: function(display) {\n+ this.icon.display(display);\n+ },\n \n- var left = (this.relativePosition.charAt(1) == 'l');\n- newPx.x += (left) ? -size.w : this.anchor.size.w;\n+ CLASS_NAME: \"OpenLayers.Marker\"\n+});\n \n- return newPx;\n- },\n \n- CLASS_NAME: \"OpenLayers.Popup.Anchored\"\n+/**\n+ * Function: defaultIcon\n+ * Creates a default <OpenLayers.Icon>.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker\n+ */\n+OpenLayers.Marker.defaultIcon = function() {\n+ return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n+ w: 21,\n+ h: 25\n+ }, {\n+ x: -10.5,\n+ y: -25\n });\n+};\n+\n+\n /* ======================================================================\n- OpenLayers/Popup/Framed.js\n+ OpenLayers/Format/WPSDescribeProcess.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Popup/Anchored.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n */\n \n /**\n- * Class: OpenLayers.Popup.Framed\n- * \n+ * Class: OpenLayers.Format.WPSDescribeProcess\n+ * Read WPS DescribeProcess responses. \n+ *\n * Inherits from:\n- * - <OpenLayers.Popup.Anchored>\n+ * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Popup.Framed =\n- OpenLayers.Class(OpenLayers.Popup.Anchored, {\n-\n- /**\n- * Property: imageSrc\n- * {String} location of the image to be used as the popup frame\n- */\n- imageSrc: null,\n-\n- /**\n- * Property: imageSize\n- * {<OpenLayers.Size>} Size (measured in pixels) of the image located\n- * by the 'imageSrc' property.\n- */\n- imageSize: null,\n-\n- /**\n- * APIProperty: isAlphaImage\n- * {Boolean} The image has some alpha and thus needs to use the alpha \n- * image hack. Note that setting this to true will have no noticeable\n- * effect in FF or IE7 browsers, but will all but crush the ie6 \n- * browser. \n- * Default is false.\n- */\n- isAlphaImage: false,\n+OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n \n /**\n- * Property: positionBlocks\n- * {Object} Hash of different position blocks (Object/Hashs). Each block \n- * will be keyed by a two-character 'relativePosition' \n- * code string (ie \"tl\", \"tr\", \"bl\", \"br\"). Block properties are \n- * 'offset', 'padding' (self-explanatory), and finally the 'blocks'\n- * parameter, which is an array of the block objects. \n- * \n- * Each block object must have 'size', 'anchor', and 'position' \n- * properties.\n- * \n- * Note that positionBlocks should never be modified at runtime.\n+ * Constant: VERSION\n+ * {String} 1.0.0\n */\n- positionBlocks: null,\n+ VERSION: \"1.0.0\",\n \n /**\n- * Property: blocks\n- * {Array[Object]} Array of objects, each of which is one \"block\" of the \n- * popup. Each block has a 'div' and an 'image' property, both of \n- * which are DOMElements, and the latter of which is appended to the \n- * former. These are reused as the popup goes changing positions for\n- * great economy and elegance.\n- */\n- blocks: null,\n-\n- /** \n- * APIProperty: fixedRelativePosition\n- * {Boolean} We want the framed popup to work dynamically placed relative\n- * to its anchor but also in just one fixed position. A well designed\n- * framed popup will have the pixels and logic to display itself in \n- * any of the four relative positions, but (understandably), this will\n- * not be the case for all of them. By setting this property to 'true', \n- * framed popup will not recalculate for the best placement each time\n- * it's open, but will always open the same way. \n- * Note that if this is set to true, it is generally advisable to also\n- * set the 'panIntoView' property to true so that the popup can be \n- * scrolled into view (since it will often be offscreen on open)\n- * Default is false.\n- */\n- fixedRelativePosition: false,\n-\n- /** \n- * Constructor: OpenLayers.Popup.Framed\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object to which we'll anchor the popup. Must expose \n- * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n- * (Note that this is generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n-\n- OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);\n-\n- if (this.fixedRelativePosition) {\n- //based on our decided relativePostion, set the current padding\n- // this keeps us from getting into trouble \n- this.updateRelativePosition();\n-\n- //make calculateRelativePosition always return the specified\n- // fixed position.\n- this.calculateRelativePosition = function(px) {\n- return this.relativePosition;\n- };\n- }\n-\n- this.contentDiv.style.position = \"absolute\";\n- this.contentDiv.style.zIndex = 1;\n-\n- if (closeBox) {\n- this.closeDiv.style.zIndex = 1;\n- }\n-\n- this.groupDiv.style.position = \"absolute\";\n- this.groupDiv.style.top = \"0px\";\n- this.groupDiv.style.left = \"0px\";\n- this.groupDiv.style.height = \"100%\";\n- this.groupDiv.style.width = \"100%\";\n- },\n-\n- /** \n- * APIMethod: destroy\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n */\n- destroy: function() {\n- this.imageSrc = null;\n- this.imageSize = null;\n- this.isAlphaImage = null;\n-\n- this.fixedRelativePosition = false;\n- this.positionBlocks = null;\n-\n- //remove our blocks\n- for (var i = 0; i < this.blocks.length; i++) {\n- var block = this.blocks[i];\n-\n- if (block.image) {\n- block.div.removeChild(block.image);\n- }\n- block.image = null;\n-\n- if (block.div) {\n- this.groupDiv.removeChild(block.div);\n- }\n- block.div = null;\n- }\n- this.blocks = null;\n-\n- OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);\n+ namespaces: {\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n },\n \n /**\n- * APIMethod: setBackgroundColor\n+ * Property: schemaLocation\n+ * {String} Schema location\n */\n- setBackgroundColor: function(color) {\n- //does nothing since the framed popup's entire scheme is based on a \n- // an image -- changing the background color makes no sense. \n- },\n+ schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n \n /**\n- * APIMethod: setBorder\n+ * Property: defaultPrefix\n */\n- setBorder: function() {\n- //does nothing since the framed popup's entire scheme is based on a \n- // an image -- changing the popup's border makes no sense. \n- },\n+ defaultPrefix: \"wps\",\n \n /**\n- * Method: setOpacity\n- * Sets the opacity of the popup.\n- * \n- * Parameters:\n- * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n */\n- setOpacity: function(opacity) {\n- //does nothing since we suppose that we'll never apply an opacity\n- // to a framed popup\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n \n /**\n- * APIMethod: setSize\n- * Overridden here, because we need to update the blocks whenever the size\n- * of the popup has changed.\n- * \n+ * Constructor: OpenLayers.Format.WPSDescribeProcess\n+ *\n * Parameters:\n- * contentSize - {<OpenLayers.Size>} the new size for the popup's \n- * contents div (in pixels).\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n */\n- setSize: function(contentSize) {\n- OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);\n-\n- this.updateBlocks();\n- },\n \n /**\n- * Method: updateRelativePosition\n- * When the relative position changes, we need to set the new padding \n- * BBOX on the popup, reposition the close div, and update the blocks.\n- */\n- updateRelativePosition: function() {\n-\n- //update the padding\n- this.padding = this.positionBlocks[this.relativePosition].padding;\n-\n- //update the position of our close box to new padding\n- if (this.closeDiv) {\n- // use the content div's css padding to determine if we should\n- // padd the close div\n- var contentDivPadding = this.getContentDivPadding();\n-\n- this.closeDiv.style.right = contentDivPadding.right +\n- this.padding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top +\n- this.padding.top + \"px\";\n- }\n-\n- this.updateBlocks();\n- },\n-\n- /** \n- * Method: calculateNewPx\n- * Besides the standard offset as determined by the Anchored class, our \n- * Framed popups have a special 'offset' property for each of their \n- * positions, which is used to offset the popup relative to its anchor.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n+ * APIMethod: read\n+ * Parse a WPS DescribeProcess and return an object with its information.\n * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n * Returns:\n- * {<OpenLayers.Pixel>} The the new px position of the popup on the screen\n- * relative to the passed-in px.\n- */\n- calculateNewPx: function(px) {\n- var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(\n- this, arguments\n- );\n-\n- newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);\n-\n- return newPx;\n- },\n-\n- /**\n- * Method: createBlocks\n+ * {Object}\n */\n- createBlocks: function() {\n- this.blocks = [];\n-\n- //since all positions contain the same number of blocks, we can \n- // just pick the first position and use its blocks array to create\n- // our blocks array\n- var firstPosition = null;\n- for (var key in this.positionBlocks) {\n- firstPosition = key;\n- break;\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n-\n- var position = this.positionBlocks[firstPosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n-\n- var block = {};\n- this.blocks.push(block);\n-\n- var divId = this.id + '_FrameDecorationDiv_' + i;\n- block.div = OpenLayers.Util.createDiv(divId,\n- null, null, null, \"absolute\", null, \"hidden\", null\n- );\n-\n- var imgId = this.id + '_FrameDecorationImg_' + i;\n- var imageCreator =\n- (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv :\n- OpenLayers.Util.createImage;\n-\n- block.image = imageCreator(imgId,\n- null, this.imageSize, this.imageSrc,\n- \"absolute\", null, null, null\n- );\n-\n- block.div.appendChild(block.image);\n- this.groupDiv.appendChild(block.div);\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n }\n+ var info = {};\n+ this.readNode(data, info);\n+ return info;\n },\n \n /**\n- * Method: updateBlocks\n- * Internal method, called on initialize and when the popup's relative\n- * position has changed. This function takes care of re-positioning\n- * the popup's blocks in their appropropriate places.\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n */\n- updateBlocks: function() {\n- if (!this.blocks) {\n- this.createBlocks();\n- }\n-\n- if (this.size && this.relativePosition) {\n- var position = this.positionBlocks[this.relativePosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n-\n- var positionBlock = position.blocks[i];\n- var block = this.blocks[i];\n-\n- // adjust sizes\n- var l = positionBlock.anchor.left;\n- var b = positionBlock.anchor.bottom;\n- var r = positionBlock.anchor.right;\n- var t = positionBlock.anchor.top;\n-\n- //note that we use the isNaN() test here because if the \n- // size object is initialized with a \"auto\" parameter, the \n- // size constructor calls parseFloat() on the string, \n- // which will turn it into NaN\n- //\n- var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) :\n- positionBlock.size.w;\n-\n- var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) :\n- positionBlock.size.h;\n-\n- block.div.style.width = (w < 0 ? 0 : w) + 'px';\n- block.div.style.height = (h < 0 ? 0 : h) + 'px';\n-\n- block.div.style.left = (l != null) ? l + 'px' : '';\n- block.div.style.bottom = (b != null) ? b + 'px' : '';\n- block.div.style.right = (r != null) ? r + 'px' : '';\n- block.div.style.top = (t != null) ? t + 'px' : '';\n-\n- block.image.style.left = positionBlock.position.x + 'px';\n- block.image.style.top = positionBlock.position.y + 'px';\n+ readers: {\n+ \"wps\": {\n+ \"ProcessDescriptions\": function(node, obj) {\n+ obj.processDescriptions = {};\n+ this.readChildNodes(node, obj.processDescriptions);\n+ },\n+ \"ProcessDescription\": function(node, processDescriptions) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var processDescription = {\n+ processVersion: processVersion,\n+ statusSupported: (node.getAttribute(\"statusSupported\") === \"true\"),\n+ storeSupported: (node.getAttribute(\"storeSupported\") === \"true\")\n+ };\n+ this.readChildNodes(node, processDescription);\n+ processDescriptions[processDescription.identifier] = processDescription;\n+ },\n+ \"DataInputs\": function(node, processDescription) {\n+ processDescription.dataInputs = [];\n+ this.readChildNodes(node, processDescription.dataInputs);\n+ },\n+ \"ProcessOutputs\": function(node, processDescription) {\n+ processDescription.processOutputs = [];\n+ this.readChildNodes(node, processDescription.processOutputs);\n+ },\n+ \"Output\": function(node, processOutputs) {\n+ var output = {};\n+ this.readChildNodes(node, output);\n+ processOutputs.push(output);\n+ },\n+ \"ComplexOutput\": function(node, output) {\n+ output.complexOutput = {};\n+ this.readChildNodes(node, output.complexOutput);\n+ },\n+ \"LiteralOutput\": function(node, output) {\n+ output.literalOutput = {};\n+ this.readChildNodes(node, output.literalOutput);\n+ },\n+ \"Input\": function(node, dataInputs) {\n+ var input = {\n+ maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n+ minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n+ };\n+ this.readChildNodes(node, input);\n+ dataInputs.push(input);\n+ },\n+ \"BoundingBoxData\": function(node, input) {\n+ input.boundingBoxData = {};\n+ this.readChildNodes(node, input.boundingBoxData);\n+ },\n+ \"CRS\": function(node, obj) {\n+ if (!obj.CRSs) {\n+ obj.CRSs = {};\n+ }\n+ obj.CRSs[this.getChildValue(node)] = true;\n+ },\n+ \"LiteralData\": function(node, input) {\n+ input.literalData = {};\n+ this.readChildNodes(node, input.literalData);\n+ },\n+ \"ComplexData\": function(node, input) {\n+ input.complexData = {};\n+ this.readChildNodes(node, input.complexData);\n+ },\n+ \"Default\": function(node, complexData) {\n+ complexData[\"default\"] = {};\n+ this.readChildNodes(node, complexData[\"default\"]);\n+ },\n+ \"Supported\": function(node, complexData) {\n+ complexData[\"supported\"] = {};\n+ this.readChildNodes(node, complexData[\"supported\"]);\n+ },\n+ \"Format\": function(node, obj) {\n+ var format = {};\n+ this.readChildNodes(node, format);\n+ if (!obj.formats) {\n+ obj.formats = {};\n+ }\n+ obj.formats[format.mimeType] = true;\n+ },\n+ \"MimeType\": function(node, format) {\n+ format.mimeType = this.getChildValue(node);\n }\n-\n- this.contentDiv.style.left = this.padding.left + \"px\";\n- this.contentDiv.style.top = this.padding.top + \"px\";\n- }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n \n- CLASS_NAME: \"OpenLayers.Popup.Framed\"\n+ CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n+\n });\n /* ======================================================================\n- OpenLayers/Popup/FramedCloud.js\n+ OpenLayers/WPSClient.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Popup/Framed.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes/Bounds.js\n- * @requires OpenLayers/BaseTypes/Pixel.js\n- * @requires OpenLayers/BaseTypes/Size.js\n+ * @requires OpenLayers/SingleFile.js\n */\n \n /**\n- * Class: OpenLayers.Popup.FramedCloud\n- * \n- * Inherits from: \n- * - <OpenLayers.Popup.Framed>\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/WPSProcess.js\n+ * @requires OpenLayers/Format/WPSDescribeProcess.js\n+ * @requires OpenLayers/Request.js\n */\n-OpenLayers.Popup.FramedCloud =\n- OpenLayers.Class(OpenLayers.Popup.Framed, {\n \n- /** \n- * Property: contentDisplayClass\n- * {String} The CSS class of the popup content div.\n- */\n- contentDisplayClass: \"olFramedCloudPopupContent\",\n+/**\n+ * Class: OpenLayers.WPSClient\n+ * High level API for interaction with Web Processing Services (WPS).\n+ * An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess>\n+ * instances for servers known to the WPSClient. The WPSClient also caches\n+ * DescribeProcess responses to reduce the number of requests sent to servers\n+ * when processes are created.\n+ */\n+OpenLayers.WPSClient = OpenLayers.Class({\n \n- /**\n- * APIProperty: autoSize\n- * {Boolean} Framed Cloud is autosizing by default.\n- */\n- autoSize: true,\n+ /**\n+ * Property: servers\n+ * {Object} Service metadata, keyed by a local identifier.\n+ *\n+ * Properties:\n+ * url - {String} the url of the server\n+ * version - {String} WPS version of the server\n+ * processDescription - {Object} Cache of raw DescribeProcess\n+ * responses, keyed by process identifier.\n+ */\n+ servers: null,\n \n- /**\n- * APIProperty: panMapIfOutOfView\n- * {Boolean} Framed Cloud does pan into view by default.\n- */\n- panMapIfOutOfView: true,\n+ /**\n+ * Property: version\n+ * {String} The default WPS version to use if none is configured. Default\n+ * is '1.0.0'.\n+ */\n+ version: '1.0.0',\n \n- /**\n- * APIProperty: imageSize\n- * {<OpenLayers.Size>}\n- */\n- imageSize: new OpenLayers.Size(1276, 736),\n+ /**\n+ * Property: lazy\n+ * {Boolean} Should the DescribeProcess be deferred until a process is\n+ * fully configured? Default is false.\n+ */\n+ lazy: false,\n \n- /**\n- * APIProperty: isAlphaImage\n- * {Boolean} The FramedCloud does not use an alpha image (in honor of the \n- * good ie6 folk out there)\n- */\n- isAlphaImage: false,\n+ /**\n+ * Property: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Supported event types:\n+ * describeprocess - Fires when the process description is available.\n+ * Listeners receive an object with a 'raw' property holding the raw\n+ * DescribeProcess response, and an 'identifier' property holding the\n+ * process identifier of the described process.\n+ */\n+ events: null,\n \n- /** \n- * APIProperty: fixedRelativePosition\n- * {Boolean} The Framed Cloud popup works in just one fixed position.\n- */\n- fixedRelativePosition: false,\n+ /**\n+ * Constructor: OpenLayers.WPSClient\n+ *\n+ * Parameters:\n+ * options - {Object} Object whose properties will be set on the instance.\n+ *\n+ * Avaliable options:\n+ * servers - {Object} Mandatory. Service metadata, keyed by a local\n+ * identifier. Can either be a string with the service url or an\n+ * object literal with additional metadata:\n+ *\n+ * (code)\n+ * servers: {\n+ * local: '/geoserver/wps'\n+ * }, {\n+ * opengeo: {\n+ * url: 'http://demo.opengeo.org/geoserver/wps',\n+ * version: '1.0.0'\n+ * }\n+ * }\n+ * (end)\n+ *\n+ * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be\n+ * requested until a process is fully configured. Default is false.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ this.servers = {};\n+ for (var s in options.servers) {\n+ this.servers[s] = typeof options.servers[s] == 'string' ? {\n+ url: options.servers[s],\n+ version: this.version,\n+ processDescription: {}\n+ } : options.servers[s];\n+ }\n+ },\n \n- /**\n- * Property: positionBlocks\n- * {Object} Hash of differen position blocks, keyed by relativePosition\n- * two-character code string (ie \"tl\", \"tr\", \"bl\", \"br\")\n- */\n- positionBlocks: {\n- \"tl\": {\n- 'offset': new OpenLayers.Pixel(44, 0),\n- 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 18),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -632)\n- }, { // stem\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(0, -688)\n- }]\n- },\n- \"tr\": {\n- 'offset': new OpenLayers.Pixel(-45, 0),\n- 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 19),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -631)\n- }, { // stem\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(0, 0, null, null),\n- position: new OpenLayers.Pixel(-215, -687)\n- }]\n- },\n- \"bl\": {\n- 'offset': new OpenLayers.Pixel(45, 0),\n- 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, { // stem\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(null, null, 0, 0),\n- position: new OpenLayers.Pixel(-101, -674)\n- }]\n- },\n- \"br\": {\n- 'offset': new OpenLayers.Pixel(-44, 0),\n- 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, { // stem\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(0, null, null, 0),\n- position: new OpenLayers.Pixel(-311, -674)\n- }]\n+ /**\n+ * APIMethod: execute\n+ * Shortcut to execute a process with a single function call. This is\n+ * equivalent to using <getProcess> and then calling execute on the\n+ * process.\n+ *\n+ * Parameters:\n+ * options - {Object} Options for the execute operation.\n+ *\n+ * Available options:\n+ * server - {String} Mandatory. One of the local identifiers of the\n+ * configured servers.\n+ * process - {String} Mandatory. A process identifier known to the\n+ * server.\n+ * inputs - {Object} The inputs for the process, keyed by input identifier.\n+ * For spatial data inputs, the value of an input is usually an\n+ * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of\n+ * geometries or features.\n+ * output - {String} The identifier of an output to parse. Optional. If not\n+ * provided, the first output will be parsed.\n+ * success - {Function} Callback to call when the process is complete.\n+ * This function is called with an outputs object as argument, which\n+ * will have a property with the identifier of the requested output\n+ * (e.g. 'result'). For processes that generate spatial output, the\n+ * value will either be a single <OpenLayers.Feature.Vector> or an\n+ * array of features.\n+ * scope - {Object} Optional scope for the success callback.\n+ */\n+ execute: function(options) {\n+ var process = this.getProcess(options.server, options.process);\n+ process.execute({\n+ inputs: options.inputs,\n+ success: options.success,\n+ scope: options.scope\n+ });\n+ },\n+\n+ /**\n+ * APIMethod: getProcess\n+ * Creates an <OpenLayers.WPSProcess>.\n+ *\n+ * Parameters:\n+ * serverID - {String} Local identifier from the servers that this instance\n+ * was constructed with.\n+ * processID - {String} Process identifier known to the server.\n+ *\n+ * Returns:\n+ * {<OpenLayers.WPSProcess>}\n+ */\n+ getProcess: function(serverID, processID) {\n+ var process = new OpenLayers.WPSProcess({\n+ client: this,\n+ server: serverID,\n+ identifier: processID\n+ });\n+ if (!this.lazy) {\n+ process.describe();\n+ }\n+ return process;\n+ },\n+\n+ /**\n+ * Method: describeProcess\n+ *\n+ * Parameters:\n+ * serverID - {String} Identifier of the server\n+ * processID - {String} Identifier of the requested process\n+ * callback - {Function} Callback to call when the description is available\n+ * scope - {Object} Optional execution scope for the callback function\n+ */\n+ describeProcess: function(serverID, processID, callback, scope) {\n+ var server = this.servers[serverID];\n+ if (!server.processDescription[processID]) {\n+ if (!(processID in server.processDescription)) {\n+ // set to null so we know a describeFeature request is pending\n+ server.processDescription[processID] = null;\n+ OpenLayers.Request.GET({\n+ url: server.url,\n+ params: {\n+ SERVICE: 'WPS',\n+ VERSION: server.version,\n+ REQUEST: 'DescribeProcess',\n+ IDENTIFIER: processID\n+ },\n+ success: function(response) {\n+ server.processDescription[processID] = response.responseText;\n+ this.events.triggerEvent('describeprocess', {\n+ identifier: processID,\n+ raw: response.responseText\n+ });\n+ },\n+ scope: this\n+ });\n+ } else {\n+ // pending request\n+ this.events.register('describeprocess', this, function describe(evt) {\n+ if (evt.identifier === processID) {\n+ this.events.unregister('describeprocess', this, describe);\n+ callback.call(scope, evt.raw);\n+ }\n+ });\n }\n- },\n-\n- /**\n- * APIProperty: minSize\n- * {<OpenLayers.Size>}\n- */\n- minSize: new OpenLayers.Size(105, 10),\n-\n- /**\n- * APIProperty: maxSize\n- * {<OpenLayers.Size>}\n- */\n- maxSize: new OpenLayers.Size(1200, 660),\n+ } else {\n+ window.setTimeout(function() {\n+ callback.call(scope, server.processDescription[processID]);\n+ }, 0);\n+ }\n+ },\n \n- /** \n- * Constructor: OpenLayers.Popup.FramedCloud\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object to which we'll anchor the popup. Must expose \n- * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n- * (Note that this is generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ this.events.destroy();\n+ this.events = null;\n+ this.servers = null;\n+ },\n \n- this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');\n- OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n- this.contentDiv.className = this.contentDisplayClass;\n- },\n+ CLASS_NAME: 'OpenLayers.WPSClient'\n \n- CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n- });\n+});\n /* ======================================================================\n- OpenLayers/Format/WFSCapabilities.js\n+ OpenLayers/Format/GPX.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Format.WFSCapabilities\n- * Read WFS Capabilities.\n- * \n+ * Class: OpenLayers.Format.GPX\n+ * Read/write GPX parser. Create a new instance with the \n+ * <OpenLayers.Format.GPX> constructor.\n+ *\n * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n+ * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+\n+ /** \n+ * APIProperty: defaultDesc\n+ * {String} Default description for the waypoints/tracks in the case\n+ * where the feature has no \"description\" attribute.\n+ * Default is \"No description available\".\n+ */\n+ defaultDesc: \"No description available\",\n \n /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ * APIProperty: extractWaypoints\n+ * {Boolean} Extract waypoints from GPX. (default: true)\n */\n- defaultVersion: \"1.1.0\",\n+ extractWaypoints: true,\n \n /**\n- * Constructor: OpenLayers.Format.WFSCapabilities\n- * Create a new parser for WFS capabilities.\n+ * APIProperty: extractTracks\n+ * {Boolean} Extract tracks from GPX. (default: true)\n+ */\n+ extractTracks: true,\n+\n+ /**\n+ * APIProperty: extractRoutes\n+ * {Boolean} Extract routes from GPX. (default: true)\n+ */\n+ extractRoutes: true,\n+\n+ /**\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract feature attributes from GPX. (default: true)\n+ * NOTE: Attributes as part of extensions to the GPX standard may not\n+ * be extracted.\n+ */\n+ extractAttributes: true,\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ gpx: \"http://www.topografix.com/GPX/1/1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location. Defaults to\n+ * \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\"\n+ */\n+ schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n+\n+ /**\n+ * APIProperty: creator\n+ * {String} The creator attribute to be added to the written GPX files.\n+ * Defaults to \"OpenLayers\"\n+ */\n+ creator: \"OpenLayers\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.GPX\n+ * Create a new parser for GPX.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n+ initialize: function(options) {\n+ // GPX coordinates are always in longlat WGS84\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n \n /**\n * APIMethod: read\n- * Read capabilities data from a string, and return a list of layers. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n+ * Return a list of features from a GPX doc\n+ *\n+ * Parameters:\n+ * doc - {Element} \n *\n * Returns:\n- * {Array} List of named layers.\n+ * Array({<OpenLayers.Feature.Vector>})\n */\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ }\n+ var features = [];\n \n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n+ if (this.extractTracks) {\n+ var tracks = doc.getElementsByTagName(\"trk\");\n+ for (var i = 0, len = tracks.length; i < len; i++) {\n+ // Attributes are only in trk nodes, not trkseg nodes\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(tracks[i]);\n+ }\n \n-});\n-/* ======================================================================\n- OpenLayers/Format/WPSCapabilities.js\n- ====================================================================== */\n+ var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n+ for (var j = 0, seglen = segs.length; j < seglen; j++) {\n+ // We don't yet support extraction of trkpt attributes\n+ // All trksegs of a trk get that trk's attributes\n+ var track = this.extractSegment(segs[j], \"trkpt\");\n+ features.push(new OpenLayers.Feature.Vector(track, attrs));\n+ }\n+ }\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ if (this.extractRoutes) {\n+ var routes = doc.getElementsByTagName(\"rte\");\n+ for (var k = 0, klen = routes.length; k < klen; k++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(routes[k]);\n+ }\n+ var route = this.extractSegment(routes[k], \"rtept\");\n+ features.push(new OpenLayers.Feature.Vector(route, attrs));\n+ }\n+ }\n \n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n+ if (this.extractWaypoints) {\n+ var waypoints = doc.getElementsByTagName(\"wpt\");\n+ for (var l = 0, len = waypoints.length; l < len; l++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(waypoints[l]);\n+ }\n+ var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n+ features.push(new OpenLayers.Feature.Vector(wpt, attrs));\n+ }\n+ }\n \n-/**\n- * Class: OpenLayers.Format.WPSCapabilities\n- * Read WPS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ if (this.internalProjection && this.externalProjection) {\n+ for (var g = 0, featLength = features.length; g < featLength; g++) {\n+ features[g].geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ }\n+\n+ return features;\n+ },\n \n /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n+ * Method: extractSegment\n+ *\n+ * Parameters:\n+ * segment - {DOMElement} a trkseg or rte node to parse\n+ * segmentType - {String} nodeName of waypoints that form the line\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.LineString>} A linestring geometry\n */\n- defaultVersion: \"1.0.0\",\n+ extractSegment: function(segment, segmentType) {\n+ var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n+ var point_features = [];\n+ for (var i = 0, len = points.length; i < len; i++) {\n+ point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")));\n+ }\n+ return new OpenLayers.Geometry.LineString(point_features);\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.WPSCapabilities\n- * Create a new parser for WPS Capabilities.\n+ * Method: parseAttributes\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An attributes object.\n */\n+ parseAttributes: function(node) {\n+ // node is either a wpt, trk or rte\n+ // attributes are children of the form <attr>value</attr>\n+ var attributes = {};\n+ var attrNode = node.firstChild,\n+ value, name;\n+ while (attrNode) {\n+ if (attrNode.nodeType == 1 && attrNode.firstChild) {\n+ value = attrNode.firstChild;\n+ if (value.nodeType == 3 || value.nodeType == 4) {\n+ name = (attrNode.prefix) ?\n+ attrNode.nodeName.split(\":\")[1] :\n+ attrNode.nodeName;\n+ if (name != \"trkseg\" && name != \"rtept\") {\n+ attributes[name] = value.nodeValue;\n+ }\n+ }\n+ }\n+ attrNode = attrNode.nextSibling;\n+ }\n+ return attributes;\n+ },\n \n /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return information about\n- * the service.\n+ * APIMethod: write\n+ * Accepts Feature Collection, and returns a string. \n * \n * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n+ * metadata - {Object} A key/value pairs object to build a metadata node to\n+ * add to the gpx. Supported keys are 'name', 'desc', 'author'.\n+ */\n+ write: function(features, metadata) {\n+ features = OpenLayers.Util.isArray(features) ?\n+ features : [features];\n+ var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n+ gpx.setAttribute(\"version\", \"1.1\");\n+ gpx.setAttribute(\"creator\", this.creator);\n+ this.setAttributes(gpx, {\n+ \"xsi:schemaLocation\": this.schemaLocation\n+ });\n+\n+ if (metadata && typeof metadata == 'object') {\n+ gpx.appendChild(this.buildMetadataNode(metadata));\n+ }\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ gpx.appendChild(this.buildFeatureNode(features[i]));\n+ }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);\n+ },\n+\n+ /**\n+ * Method: buildMetadataNode\n+ * Creates a \"metadata\" node.\n *\n * Returns:\n- * {Object} Info about the WPS\n+ * {DOMElement}\n */\n+ buildMetadataNode: function(metadata) {\n+ var types = ['name', 'desc', 'author'],\n+ node = this.createElementNS(this.namespaces.gpx, 'metadata');\n+ for (var i = 0; i < types.length; i++) {\n+ var type = types[i];\n+ if (metadata[type]) {\n+ var n = this.createElementNS(this.namespaces.gpx, type);\n+ n.appendChild(this.createTextNode(metadata[type]));\n+ node.appendChild(n);\n+ }\n+ }\n+ return node;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n+ /**\n+ * Method: buildFeatureNode\n+ * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {DOMElement} - The created node, either a 'wpt' or a 'trk'.\n+ */\n+ buildFeatureNode: function(feature) {\n+ var geometry = feature.geometry;\n+ geometry = geometry.clone();\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n+ }\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ var wpt = this.buildWptNode(geometry);\n+ this.appendAttributesNode(wpt, feature);\n+ return wpt;\n+ } else {\n+ var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n+ this.appendAttributesNode(trkNode, feature);\n+ var trkSegNodes = this.buildTrkSegNode(geometry);\n+ trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?\n+ trkSegNodes : [trkSegNodes];\n+ for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n+ trkNode.appendChild(trkSegNodes[i]);\n+ }\n+ return trkNode;\n+ }\n+ },\n+\n+ /**\n+ * Method: buildTrkSegNode\n+ * Builds trkseg node(s) given a geometry\n+ *\n+ * Parameters:\n+ * trknode\n+ * geometry - {<OpenLayers.Geometry>}\n+ */\n+ buildTrkSegNode: function(geometry) {\n+ var node,\n+ i,\n+ len,\n+ point,\n+ nodes;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n+ geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ point = geometry.components[i];\n+ node.appendChild(this.buildTrkPtNode(point));\n+ }\n+ return node;\n+ } else {\n+ nodes = [];\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ nodes.push(this.buildTrkSegNode(geometry.components[i]));\n+ }\n+ return nodes;\n+ }\n+ },\n+\n+ /**\n+ * Method: buildTrkPtNode\n+ * Builds a trkpt node given a point \n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns:\n+ * {DOMElement} A trkpt node\n+ */\n+ buildTrkPtNode: function(point) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n+ node.setAttribute(\"lon\", point.x);\n+ node.setAttribute(\"lat\", point.y);\n+ return node;\n+ },\n+\n+ /**\n+ * Method: buildWptNode\n+ * Builds a wpt node given a point\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns:\n+ * {DOMElement} A wpt node\n+ */\n+ buildWptNode: function(geometry) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n+ node.setAttribute(\"lon\", geometry.x);\n+ node.setAttribute(\"lat\", geometry.y);\n+ return node;\n+ },\n+\n+ /**\n+ * Method: appendAttributesNode\n+ * Adds some attributes node.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} the node to append the attribute nodes to.\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ appendAttributesNode: function(node, feature) {\n+ var name = this.createElementNS(this.namespaces.gpx, 'name');\n+ name.appendChild(this.createTextNode(\n+ feature.attributes.name || feature.id));\n+ node.appendChild(name);\n+ var desc = this.createElementNS(this.namespaces.gpx, 'desc');\n+ desc.appendChild(this.createTextNode(\n+ feature.attributes.description || this.defaultDesc));\n+ node.appendChild(desc);\n+ // TBD - deal with remaining (non name/description) attributes.\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Format.GPX\"\n });\n /* ======================================================================\n- OpenLayers/Format/Atom.js\n+ OpenLayers/Format/EncodedPolyline.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/GML/v3.js\n+ * @requires OpenLayers/Format.js\n * @requires OpenLayers/Feature/Vector.js\n */\n \n /**\n- * Class: OpenLayers.Format.Atom\n- * Read/write Atom feeds. Create a new instance with the\n- * <OpenLayers.Format.AtomFeed> constructor.\n+ * Class: OpenLayers.Format.EncodedPolyline\n+ * Class for reading and writing encoded polylines. Create a new instance\n+ * with the <OpenLayers.Format.EncodedPolyline> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * - <OpenLayers.Format>\n */\n-OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n \n /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs. Properties\n- * of this object should not be set individually. Read-only. All\n- * XML subclasses should have their own namespaces object. Use\n- * <setNamespace> to add or set a namespace alias after construction.\n+ * APIProperty: geometryType\n+ * {String} Geometry type to output. One of: linestring (default),\n+ * linearring, point, multipoint or polygon. If the geometryType is\n+ * point, only the first point of the string is returned.\n */\n- namespaces: {\n- atom: \"http://www.w3.org/2005/Atom\",\n- georss: \"http://www.georss.org/georss\"\n- },\n+ geometryType: \"linestring\",\n \n /**\n- * APIProperty: feedTitle\n- * {String} Atom feed elements require a title. Default is \"untitled\".\n+ * Constructor: OpenLayers.Format.EncodedPolyline\n+ * Create a new parser for encoded polylines\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.\n */\n- feedTitle: \"untitled\",\n+ initialize: function(options) {\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n+ },\n \n /**\n- * APIProperty: defaultEntryTitle\n- * {String} Atom entry elements require a title. In cases where one is\n- * not provided in the feature attributes, this will be used. Default\n- * is \"untitled\".\n+ * APIMethod: read\n+ * Deserialize an encoded polyline string and return a vector feature.\n+ *\n+ * Parameters:\n+ * encoded - {String} An encoded polyline string\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A vector feature with a linestring.\n */\n- defaultEntryTitle: \"untitled\",\n+ read: function(encoded) {\n+ var geomType;\n+ if (this.geometryType == \"linestring\")\n+ geomType = OpenLayers.Geometry.LineString;\n+ else if (this.geometryType == \"linearring\")\n+ geomType = OpenLayers.Geometry.LinearRing;\n+ else if (this.geometryType == \"multipoint\")\n+ geomType = OpenLayers.Geometry.MultiPoint;\n+ else if (this.geometryType != \"point\" && this.geometryType != \"polygon\")\n+ return null;\n \n- /**\n- * Property: gmlParse\n- * {Object} GML Format object for parsing features\n- * Non-API and only created if necessary\n- */\n- gmlParser: null,\n+ var flatPoints = this.decodeDeltas(encoded, 2);\n+ var flatPointsLength = flatPoints.length;\n \n- /**\n- * APIProperty: xy\n- * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)\n- * For GeoRSS the default is (y,x), therefore: false\n- */\n- xy: false,\n+ var pointGeometries = [];\n+ for (var i = 0; i + 1 < flatPointsLength;) {\n+ var y = flatPoints[i++],\n+ x = flatPoints[i++];\n+ pointGeometries.push(new OpenLayers.Geometry.Point(x, y));\n+ }\n+\n+\n+ if (this.geometryType == \"point\")\n+ return new OpenLayers.Feature.Vector(\n+ pointGeometries[0]\n+ );\n+\n+ if (this.geometryType == \"polygon\")\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Polygon([\n+ new OpenLayers.Geometry.LinearRing(pointGeometries)\n+ ])\n+ );\n+\n+ return new OpenLayers.Feature.Vector(\n+ new geomType(pointGeometries)\n+ );\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.AtomEntry\n- * Create a new parser for Atom.\n+ * APIMethod: decode\n+ * Deserialize an encoded string and return an array of n-dimensional\n+ * points.\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * encoded - {String} An encoded string\n+ * dims - {int} The dimension of the points that are returned\n+ *\n+ * Returns:\n+ * {Array(Array(int))} An array containing n-dimensional arrays of\n+ * coordinates.\n */\n+ decode: function(encoded, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = this.decodeDeltas(encoded, dims, factor);\n+ var flatPointsLength = flatPoints.length;\n+\n+ var points = [];\n+ for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n+ var point = [];\n+\n+ for (var dim = 0; dim < dims; ++dim) {\n+ point.push(flatPoints[i++])\n+ }\n+\n+ points.push(point);\n+ }\n+\n+ return points;\n+ },\n \n /**\n- * APIMethod: read\n- * Return a list of features from an Atom feed or entry document.\n- \n+ * APIMethod: write\n+ * Serialize a feature or array of features into a WKT string.\n+ *\n * Parameters:\n- * doc - {Element} or {String}\n+ * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of\n+ * features\n *\n * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n+ * {String} The WKT string representation of the input geometries\n */\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ write: function(features) {\n+ var feature;\n+ if (features.constructor == Array)\n+ feature = features[0];\n+ else\n+ feature = features;\n+\n+ var geometry = feature.geometry;\n+ var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n+\n+ var pointGeometries;\n+ if (type == \"point\")\n+ pointGeometries = new Array(geometry);\n+ else if (type == \"linestring\" ||\n+ type == \"linearring\" ||\n+ type == \"multipoint\")\n+ pointGeometries = geometry.components;\n+ else if (type == \"polygon\")\n+ pointGeometries = geometry.components[0].components;\n+ else\n+ return null;\n+\n+ var flatPoints = [];\n+\n+ var pointGeometriesLength = pointGeometries.length;\n+ for (var i = 0; i < pointGeometriesLength; ++i) {\n+ var pointGeometry = pointGeometries[i];\n+ flatPoints.push(pointGeometry.y);\n+ flatPoints.push(pointGeometry.x);\n }\n- return this.parseFeatures(doc);\n+\n+ return this.encodeDeltas(flatPoints, 2);\n },\n \n /**\n- * APIMethod: write\n- * Serialize or more feature nodes to Atom documents.\n+ * APIMethod: encode\n+ * Serialize an array of n-dimensional points and return an encoded string\n *\n * Parameters:\n- * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>})\n+ * points - {Array(Array(int))} An array containing n-dimensional\n+ * arrays of coordinates\n+ * dims - {int} The dimension of the points that should be read\n *\n * Returns:\n- * {String} an Atom entry document if passed one feature node, or a feed\n- * document if passed an array of feature nodes.\n+ * {String} An encoded string\n */\n- write: function(features) {\n- var doc;\n- if (OpenLayers.Util.isArray(features)) {\n- doc = this.createElementNSPlus(\"atom:feed\");\n- doc.appendChild(\n- this.createElementNSPlus(\"atom:title\", {\n- value: this.feedTitle\n- })\n- );\n- for (var i = 0, ii = features.length; i < ii; i++) {\n- doc.appendChild(this.buildEntryNode(features[i]));\n+ encode: function(points, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = [];\n+\n+ var pointsLength = points.length;\n+ for (var i = 0; i < pointsLength; ++i) {\n+ var point = points[i];\n+\n+ for (var dim = 0; dim < dims; ++dim) {\n+ flatPoints.push(point[dim]);\n }\n- } else {\n- doc = this.buildEntryNode(features);\n }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [doc]);\n+\n+ return this.encodeDeltas(flatPoints, dims, factor);\n },\n \n /**\n- * Method: buildContentNode\n+ * APIMethod: encodeDeltas\n+ * Encode a list of n-dimensional points and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n *\n * Parameters:\n- * content - {Object}\n+ * numbers - {Array.<number>} A list of n-dimensional points.\n+ * dimension - {number} The dimension of the points in the list.\n+ * opt_factor - {number=} The factor by which the numbers will be\n+ * multiplied. The remaining decimal places will get rounded away.\n *\n * Returns:\n- * {DOMElement} an Atom content node.\n- *\n- * TODO: types other than text.\n+ * {string} The encoded string.\n */\n- buildContentNode: function(content) {\n- var node = this.createElementNSPlus(\"atom:content\", {\n- attributes: {\n- type: content.type || null\n- }\n- });\n- if (content.src) {\n- node.setAttribute(\"src\", content.src);\n- } else {\n- if (content.type == \"text\" || content.type == null) {\n- node.appendChild(\n- this.createTextNode(content.value)\n- );\n- } else if (content.type == \"html\") {\n- if (typeof content.value != \"string\") {\n- throw \"HTML content must be in form of an escaped string\";\n- }\n- node.appendChild(\n- this.createTextNode(content.value)\n- );\n- } else if (content.type == \"xhtml\") {\n- node.appendChild(content.value);\n- } else if (content.type == \"xhtml\" ||\n- content.type.match(/(\\+|\\/)xml$/)) {\n- node.appendChild(content.value);\n- } else { // MUST be a valid Base64 encoding\n- node.appendChild(\n- this.createTextNode(content.value)\n- );\n+ encodeDeltas: function(numbers, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n+\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0;\n+ }\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ var num = numbers[i];\n+ var delta = num - lastNumbers[d];\n+ lastNumbers[d] = num;\n+\n+ numbers[i] = delta;\n }\n }\n- return node;\n+\n+ return this.encodeFloats(numbers, factor);\n },\n \n+\n /**\n- * Method: buildEntryNode\n- * Build an Atom entry node from a feature object.\n+ * APIMethod: decodeDeltas\n+ * Decode a list of n-dimensional points from an encoded string\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n+ * encoded - {string} An encoded string.\n+ * dimension - {number} The dimension of the points in the encoded string.\n+ * opt_factor - {number=} The factor by which the resulting numbers will\n+ * be divided.\n *\n * Returns:\n- * {DOMElement} an Atom entry node.\n- *\n- * These entries are geared for publication using AtomPub.\n- *\n- * TODO: support extension elements\n+ * {Array.<number>} A list of n-dimensional points.\n */\n- buildEntryNode: function(feature) {\n- var attrib = feature.attributes;\n- var atomAttrib = attrib.atom || {};\n- var entryNode = this.createElementNSPlus(\"atom:entry\");\n+ decodeDeltas: function(encoded, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n \n- // atom:author\n- if (atomAttrib.authors) {\n- var authors = OpenLayers.Util.isArray(atomAttrib.authors) ?\n- atomAttrib.authors : [atomAttrib.authors];\n- for (var i = 0, ii = authors.length; i < ii; i++) {\n- entryNode.appendChild(\n- this.buildPersonConstructNode(\n- \"author\", authors[i]\n- )\n- );\n- }\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0;\n }\n \n- // atom:category\n- if (atomAttrib.categories) {\n- var categories = OpenLayers.Util.isArray(atomAttrib.categories) ?\n- atomAttrib.categories : [atomAttrib.categories];\n- var category;\n- for (var i = 0, ii = categories.length; i < ii; i++) {\n- category = categories[i];\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:category\", {\n- attributes: {\n- term: category.term,\n- scheme: category.scheme || null,\n- label: category.label || null\n- }\n- })\n- );\n+ var numbers = this.decodeFloats(encoded, factor);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ lastNumbers[d] += numbers[i];\n+\n+ numbers[i] = lastNumbers[d];\n }\n }\n \n- // atom:content\n- if (atomAttrib.content) {\n- entryNode.appendChild(this.buildContentNode(atomAttrib.content));\n- }\n+ return numbers;\n+ },\n \n- // atom:contributor\n- if (atomAttrib.contributors) {\n- var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ?\n- atomAttrib.contributors : [atomAttrib.contributors];\n- for (var i = 0, ii = contributors.length; i < ii; i++) {\n- entryNode.appendChild(\n- this.buildPersonConstructNode(\n- \"contributor\",\n- contributors[i]\n- )\n- );\n- }\n+\n+ /**\n+ * APIMethod: encodeFloats\n+ * Encode a list of floating point numbers and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of floating point numbers.\n+ * opt_factor - {number=} The factor by which the numbers will be\n+ * multiplied. The remaining decimal places will get rounded away.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeFloats: function(numbers, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] = Math.round(numbers[i] * factor);\n }\n \n- // atom:id\n- if (feature.fid) {\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:id\", {\n- value: feature.fid\n- })\n- );\n+ return this.encodeSignedIntegers(numbers);\n+ },\n+\n+\n+ /**\n+ * APIMethod: decodeFloats\n+ * Decode a list of floating point numbers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ * opt_factor - {number=} The factor by which the result will be divided.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of floating point numbers.\n+ */\n+ decodeFloats: function(encoded, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+\n+ var numbers = this.decodeSignedIntegers(encoded);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] /= factor;\n }\n \n- // atom:link\n- if (atomAttrib.links) {\n- var links = OpenLayers.Util.isArray(atomAttrib.links) ?\n- atomAttrib.links : [atomAttrib.links];\n- var link;\n- for (var i = 0, ii = links.length; i < ii; i++) {\n- link = links[i];\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:link\", {\n- attributes: {\n- href: link.href,\n- rel: link.rel || null,\n- type: link.type || null,\n- hreflang: link.hreflang || null,\n- title: link.title || null,\n- length: link.length || null\n- }\n- })\n- );\n+ return numbers;\n+ },\n+\n+\n+ /**\n+ * APIMethod: encodeSignedIntegers\n+ * Encode a list of signed integers and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of signed integers.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeSignedIntegers: function(numbers) {\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~(signedNum);\n }\n- }\n \n- // atom:published\n- if (atomAttrib.published) {\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:published\", {\n- value: atomAttrib.published\n- })\n- );\n+ numbers[i] = signedNum;\n }\n \n- // atom:rights\n- if (atomAttrib.rights) {\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:rights\", {\n- value: atomAttrib.rights\n- })\n- );\n- }\n+ return this.encodeUnsignedIntegers(numbers);\n+ },\n \n- // atom:source not implemented\n \n- // atom:summary\n- if (atomAttrib.summary || attrib.description) {\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:summary\", {\n- value: atomAttrib.summary || attrib.description\n- })\n- );\n+ /**\n+ * APIMethod: decodeSignedIntegers\n+ * Decode a list of signed integers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of signed integers.\n+ */\n+ decodeSignedIntegers: function(encoded) {\n+ var numbers = this.decodeUnsignedIntegers(encoded);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+ numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);\n }\n \n- // atom:title\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:title\", {\n- value: atomAttrib.title || attrib.title || this.defaultEntryTitle\n- })\n- );\n+ return numbers;\n+ },\n \n- // atom:updated\n- if (atomAttrib.updated) {\n- entryNode.appendChild(\n- this.createElementNSPlus(\"atom:updated\", {\n- value: atomAttrib.updated\n- })\n- );\n- }\n \n- // georss:where\n- if (feature.geometry) {\n- var whereNode = this.createElementNSPlus(\"georss:where\");\n- whereNode.appendChild(\n- this.buildGeometryNode(feature.geometry)\n- );\n- entryNode.appendChild(whereNode);\n+ /**\n+ * APIMethod: encodeUnsignedIntegers\n+ * Encode a list of unsigned integers and return an encoded string\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of unsigned integers.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeUnsignedIntegers: function(numbers) {\n+ var encoded = '';\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ encoded += this.encodeUnsignedInteger(numbers[i]);\n }\n \n- return entryNode;\n+ return encoded;\n },\n \n+\n /**\n- * Method: initGmlParser\n- * Creates a GML parser.\n+ * APIMethod: decodeUnsignedIntegers\n+ * Decode a list of unsigned integers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of unsigned integers.\n */\n- initGmlParser: function() {\n- this.gmlParser = new OpenLayers.Format.GML.v3({\n- xy: this.xy,\n- featureNS: \"http://example.com#feature\",\n- internalProjection: this.internalProjection,\n- externalProjection: this.externalProjection\n- });\n+ decodeUnsignedIntegers: function(encoded) {\n+ var numbers = [];\n+\n+ var current = 0;\n+ var shift = 0;\n+\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n+\n+ current |= (b & 0x1f) << shift;\n+\n+ if (b < 0x20) {\n+ numbers.push(current);\n+ current = 0;\n+ shift = 0;\n+ } else {\n+ shift += 5;\n+ }\n+ }\n+\n+ return numbers;\n },\n \n+\n /**\n- * Method: buildGeometryNode\n- * builds a GeoRSS node with a given geometry\n+ * Method: encodeFloat\n+ * Encode one single floating point number and return an encoded string\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n+ * num - {number} Floating point number that should be encoded.\n+ * opt_factor - {number=} The factor by which num will be multiplied.\n+ * The remaining decimal places will get rounded away.\n *\n * Returns:\n- * {DOMElement} A gml node.\n+ * {string} The encoded string.\n */\n- buildGeometryNode: function(geometry) {\n- if (!this.gmlParser) {\n- this.initGmlParser();\n- }\n- var node = this.gmlParser.writeNode(\"feature:_geometry\", geometry);\n- return node.firstChild;\n+ encodeFloat: function(num, opt_factor) {\n+ num = Math.round(num * (opt_factor || 1e5));\n+ return this.encodeSignedInteger(num);\n },\n \n+\n /**\n- * Method: buildPersonConstructNode\n+ * Method: decodeFloat\n+ * Decode one single floating point number from an encoded string\n *\n * Parameters:\n- * name - {String}\n- * value - {Object}\n+ * encoded - {string} An encoded string.\n+ * opt_factor - {number=} The factor by which the result will be divided.\n *\n * Returns:\n- * {DOMElement} an Atom person construct node.\n+ * {number} The decoded floating point number.\n+ */\n+ decodeFloat: function(encoded, opt_factor) {\n+ var result = this.decodeSignedInteger(encoded);\n+ return result / (opt_factor || 1e5);\n+ },\n+\n+\n+ /**\n+ * Method: encodeSignedInteger\n+ * Encode one single signed integer and return an encoded string\n *\n- * Example:\n- * >>> buildPersonConstructNode(\"author\", {name: \"John Smith\"})\n- * {<author><name>John Smith</name></author>}\n+ * Parameters:\n+ * num - {number} Signed integer that should be encoded.\n *\n- * TODO: how to specify extension elements? Add to the oNames array?\n+ * Returns:\n+ * {string} The encoded string.\n */\n- buildPersonConstructNode: function(name, value) {\n- var oNames = [\"uri\", \"email\"];\n- var personNode = this.createElementNSPlus(\"atom:\" + name);\n- personNode.appendChild(\n- this.createElementNSPlus(\"atom:name\", {\n- value: value.name\n- })\n- );\n- for (var i = 0, ii = oNames.length; i < ii; i++) {\n- if (value[oNames[i]]) {\n- personNode.appendChild(\n- this.createElementNSPlus(\"atom:\" + oNames[i], {\n- value: value[oNames[i]]\n- })\n- );\n- }\n+ encodeSignedInteger: function(num) {\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~(signedNum);\n }\n- return personNode;\n+\n+ return this.encodeUnsignedInteger(signedNum);\n },\n \n+\n /**\n- * Method: getFirstChildValue\n+ * Method: decodeSignedInteger\n+ * Decode one single signed integer from an encoded string\n *\n * Parameters:\n- * node - {DOMElement}\n- * nsuri - {String} Child node namespace uri (\"*\" for any).\n- * name - {String} Child node name.\n- * def - {String} Optional string default to return if no child found.\n+ * encoded - {string} An encoded string.\n *\n * Returns:\n- * {String} The value of the first child with the given tag name. Returns\n- * default value or empty string if none found.\n+ * {number} The decoded signed integer.\n */\n- getFirstChildValue: function(node, nsuri, name, def) {\n- var value;\n- var nodes = this.getElementsByTagNameNS(node, nsuri, name);\n- if (nodes && nodes.length > 0) {\n- value = this.getChildValue(nodes[0], def);\n- } else {\n- value = def;\n+ decodeSignedInteger: function(encoded) {\n+ var result = this.decodeUnsignedInteger(encoded);\n+ return ((result & 1) ? ~(result >> 1) : (result >> 1));\n+ },\n+\n+\n+ /**\n+ * Method: encodeUnsignedInteger\n+ * Encode one single unsigned integer and return an encoded string\n+ *\n+ * Parameters:\n+ * num - {number} Unsigned integer that should be encoded.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeUnsignedInteger: function(num) {\n+ var value, encoded = '';\n+ while (num >= 0x20) {\n+ value = (0x20 | (num & 0x1f)) + 63;\n+ encoded += (String.fromCharCode(value));\n+ num >>= 5;\n }\n- return value;\n+ value = num + 63;\n+ encoded += (String.fromCharCode(value));\n+ return encoded;\n },\n \n+\n /**\n- * Method: parseFeature\n- * Parse feature from an Atom entry node..\n+ * Method: decodeUnsignedInteger\n+ * Decode one single unsigned integer from an encoded string\n *\n * Parameters:\n- * node - {DOMElement} An Atom entry or feed node.\n+ * encoded - {string} An encoded string.\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>}\n+ * {number} The decoded unsigned integer.\n */\n- parseFeature: function(node) {\n- var atomAttrib = {};\n- var value = null;\n- var nodes = null;\n- var attval = null;\n- var atomns = this.namespaces.atom;\n+ decodeUnsignedInteger: function(encoded) {\n+ var result = 0;\n+ var shift = 0;\n \n- // atomAuthor*\n- this.parsePersonConstructs(node, \"author\", atomAttrib);\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n \n- // atomCategory*\n- nodes = this.getElementsByTagNameNS(node, atomns, \"category\");\n- if (nodes.length > 0) {\n- atomAttrib.categories = [];\n- }\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- value = {};\n- value.term = nodes[i].getAttribute(\"term\");\n- attval = nodes[i].getAttribute(\"scheme\");\n- if (attval) {\n- value.scheme = attval;\n- }\n- attval = nodes[i].getAttribute(\"label\");\n- if (attval) {\n- value.label = attval;\n- }\n- atomAttrib.categories.push(value);\n+ result |= (b & 0x1f) << shift;\n+\n+ if (b < 0x20)\n+ break;\n+\n+ shift += 5;\n }\n \n- // atomContent?\n- nodes = this.getElementsByTagNameNS(node, atomns, \"content\");\n- if (nodes.length > 0) {\n- value = {};\n- attval = nodes[0].getAttribute(\"type\");\n- if (attval) {\n- value.type = attval;\n- }\n- attval = nodes[0].getAttribute(\"src\");\n- if (attval) {\n- value.src = attval;\n+ return result;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/Context.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.Context\n+ * Base class for both Format.WMC and Format.OWSContext\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * Property: layerOptions\n+ * {Object} Default options for layers created by the parser. These\n+ * options are overridden by the options which are read from the\n+ * capabilities document.\n+ */\n+ layerOptions: null,\n+\n+ /**\n+ * Property: layerParams\n+ * {Object} Default parameters for layers created by the parser. This\n+ * can be used e.g. to override DEFAULT_PARAMS for \n+ * OpenLayers.Layer.WMS.\n+ */\n+ layerParams: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.Context\n+ * Create a new parser for Context documents.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read Context data from a string, and return an object with map\n+ * properties and a list of layers.\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ * options - {Object} The options object must contain a map property. If\n+ * the map property is a string, it must be the id of a dom element\n+ * where the new map will be placed. If the map property is an\n+ * <OpenLayers.Map>, the layers from the context document will be added\n+ * to the map.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} A map based on the context.\n+ */\n+ read: function(data, options) {\n+ var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,\n+ arguments);\n+ var map;\n+ if (options && options.map) {\n+ this.context = context;\n+ if (options.map instanceof OpenLayers.Map) {\n+ map = this.mergeContextToMap(context, options.map);\n } else {\n- if (value.type == \"text\" ||\n- value.type == \"html\" ||\n- value.type == null) {\n- value.value = this.getFirstChildValue(\n- node,\n- atomns,\n- \"content\",\n- null\n- );\n- } else if (value.type == \"xhtml\" ||\n- value.type.match(/(\\+|\\/)xml$/)) {\n- value.value = this.getChildEl(nodes[0]);\n- } else { // MUST be base64 encoded\n- value.value = this.getFirstChildValue(\n- node,\n- atomns,\n- \"content\",\n- null\n- );\n+ var mapOptions = options.map;\n+ if (OpenLayers.Util.isElement(mapOptions) ||\n+ typeof mapOptions == \"string\") {\n+ // we assume mapOptions references a div\n+ // element\n+ mapOptions = {\n+ div: mapOptions\n+ };\n }\n- atomAttrib.content = value;\n+ map = this.contextToMap(context, mapOptions);\n }\n+ } else {\n+ // not documented as part of the API, provided as a non-API option\n+ map = context;\n }\n+ return map;\n+ },\n \n- // atomContributor*\n- this.parsePersonConstructs(node, \"contributor\", atomAttrib);\n-\n- // atomId\n- atomAttrib.id = this.getFirstChildValue(node, atomns, \"id\", null);\n+ /**\n+ * Method: getLayerFromContext\n+ * Create a WMS layer from a layerContext object.\n+ *\n+ * Parameters:\n+ * layerContext - {Object} An object representing a WMS layer.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} A WMS layer.\n+ */\n+ getLayerFromContext: function(layerContext) {\n+ var i, len;\n+ // fill initial options object from layerContext\n+ var options = {\n+ queryable: layerContext.queryable, //keep queryable for api compatibility\n+ visibility: layerContext.visibility,\n+ maxExtent: layerContext.maxExtent,\n+ metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n+ styles: layerContext.styles,\n+ formats: layerContext.formats,\n+ \"abstract\": layerContext[\"abstract\"],\n+ dataURL: layerContext.dataURL\n+ }),\n+ numZoomLevels: layerContext.numZoomLevels,\n+ units: layerContext.units,\n+ isBaseLayer: layerContext.isBaseLayer,\n+ opacity: layerContext.opacity,\n+ displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n+ singleTile: layerContext.singleTile,\n+ tileSize: (layerContext.tileSize) ?\n+ new OpenLayers.Size(\n+ layerContext.tileSize.width,\n+ layerContext.tileSize.height\n+ ) : undefined,\n+ minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n+ maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n+ srs: layerContext.srs,\n+ dimensions: layerContext.dimensions,\n+ metadataURL: layerContext.metadataURL\n+ };\n+ if (this.layerOptions) {\n+ OpenLayers.Util.applyDefaults(options, this.layerOptions);\n+ }\n \n- // atomLink*\n- nodes = this.getElementsByTagNameNS(node, atomns, \"link\");\n- if (nodes.length > 0) {\n- atomAttrib.links = new Array(nodes.length);\n+ var params = {\n+ layers: layerContext.name,\n+ transparent: layerContext.transparent,\n+ version: layerContext.version\n+ };\n+ if (layerContext.formats && layerContext.formats.length > 0) {\n+ // set default value for params if current attribute is not positionned\n+ params.format = layerContext.formats[0].value;\n+ for (i = 0, len = layerContext.formats.length; i < len; i++) {\n+ var format = layerContext.formats[i];\n+ if (format.current == true) {\n+ params.format = format.value;\n+ break;\n+ }\n+ }\n }\n- var oAtts = [\"rel\", \"type\", \"hreflang\", \"title\", \"length\"];\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- value = {};\n- value.href = nodes[i].getAttribute(\"href\");\n- for (var j = 0, jj = oAtts.length; j < jj; j++) {\n- attval = nodes[i].getAttribute(oAtts[j]);\n- if (attval) {\n- value[oAtts[j]] = attval;\n+ if (layerContext.styles && layerContext.styles.length > 0) {\n+ for (i = 0, len = layerContext.styles.length; i < len; i++) {\n+ var style = layerContext.styles[i];\n+ if (style.current == true) {\n+ // three style types to consider\n+ // 1) linked SLD\n+ // 2) inline SLD\n+ // 3) named style\n+ if (style.href) {\n+ params.sld = style.href;\n+ } else if (style.body) {\n+ params.sld_body = style.body;\n+ } else {\n+ params.styles = style.name;\n+ }\n+ break;\n }\n }\n- atomAttrib.links[i] = value;\n+ }\n+ if (this.layerParams) {\n+ OpenLayers.Util.applyDefaults(params, this.layerParams);\n }\n \n- // atomPublished?\n- value = this.getFirstChildValue(node, atomns, \"published\", null);\n- if (value) {\n- atomAttrib.published = value;\n+ var layer = null;\n+ var service = layerContext.service;\n+ if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n+ options.strategies = [new OpenLayers.Strategy.BBOX()];\n+ options.protocol = new OpenLayers.Protocol.WFS({\n+ url: layerContext.url,\n+ // since we do not know featureNS, let the protocol\n+ // determine it automagically using featurePrefix\n+ featurePrefix: layerContext.name.split(\":\")[0],\n+ featureType: layerContext.name.split(\":\").pop()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n+ // use a vector layer with an HTTP Protcol and a Fixed strategy\n+ options.strategies = [new OpenLayers.Strategy.Fixed()];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.KML()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n+ // use a vector layer with a HTTP Protocol and a Fixed strategy\n+ options.strategies = [new OpenLayers.Strategy.Fixed()];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.GML()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (layerContext.features) {\n+ // inline GML or KML features\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ layer.addFeatures(layerContext.features);\n+ } else if (layerContext.categoryLayer !== true) {\n+ layer = new OpenLayers.Layer.WMS(\n+ layerContext.title || layerContext.name,\n+ layerContext.url,\n+ params,\n+ options\n+ );\n }\n+ return layer;\n+ },\n \n- // atomRights?\n- value = this.getFirstChildValue(node, atomns, \"rights\", null);\n- if (value) {\n- atomAttrib.rights = value;\n+ /**\n+ * Method: getLayersFromContext\n+ * Create an array of layers from an array of layerContext objects.\n+ *\n+ * Parameters:\n+ * layersContext - {Array(Object)} An array of objects representing layers.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Layer>)} An array of layers.\n+ */\n+ getLayersFromContext: function(layersContext) {\n+ var layers = [];\n+ for (var i = 0, len = layersContext.length; i < len; i++) {\n+ var layer = this.getLayerFromContext(layersContext[i]);\n+ if (layer !== null) {\n+ layers.push(layer);\n+ }\n }\n+ return layers;\n+ },\n \n- // atomSource? -- not implemented\n+ /**\n+ * Method: contextToMap\n+ * Create a map given a context object.\n+ *\n+ * Parameters:\n+ * context - {Object} The context object.\n+ * options - {Object} Default map options.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} A map based on the context object.\n+ */\n+ contextToMap: function(context, options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ maxExtent: context.maxExtent,\n+ projection: context.projection,\n+ units: context.units\n+ }, options);\n \n- // atomSummary?\n- value = this.getFirstChildValue(node, atomns, \"summary\", null);\n- if (value) {\n- atomAttrib.summary = value;\n+ if (options.maxExtent) {\n+ options.maxResolution =\n+ options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;\n }\n \n- // atomTitle\n- atomAttrib.title = this.getFirstChildValue(\n- node, atomns, \"title\", null\n- );\n+ var metadata = {\n+ contactInformation: context.contactInformation,\n+ \"abstract\": context[\"abstract\"],\n+ keywords: context.keywords,\n+ logo: context.logo,\n+ descriptionURL: context.descriptionURL\n+ };\n \n- // atomUpdated\n- atomAttrib.updated = this.getFirstChildValue(\n- node, atomns, \"updated\", null\n+ options.metadata = metadata;\n+\n+ var map = new OpenLayers.Map(options);\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ map.setCenter(\n+ context.bounds.getCenterLonLat(),\n+ map.getZoomForExtent(context.bounds, true)\n );\n+ return map;\n+ },\n \n- var featureAttrib = {\n- title: atomAttrib.title,\n- description: atomAttrib.summary,\n- atom: atomAttrib\n- };\n- var geometry = this.parseLocations(node)[0];\n- var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);\n- feature.fid = atomAttrib.id;\n- return feature;\n+ /**\n+ * Method: mergeContextToMap\n+ * Add layers from a context object to a map.\n+ *\n+ * Parameters:\n+ * context - {Object} The context object.\n+ * map - {<OpenLayers.Map>} The map.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} The same map with layers added.\n+ */\n+ mergeContextToMap: function(context, map) {\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ return map;\n },\n \n /**\n- * Method: parseFeatures\n- * Return features from an Atom entry or feed.\n+ * APIMethod: write\n+ * Write a context document given a map.\n *\n * Parameters:\n- * node - {DOMElement} An Atom entry or feed node.\n+ * obj - {<OpenLayers.Map> | Object} A map or context object.\n+ * options - {Object} Optional configuration object.\n *\n * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n+ * {String} A context document string.\n */\n- parseFeatures: function(node) {\n- var features = [];\n- var entries = this.getElementsByTagNameNS(\n- node, this.namespaces.atom, \"entry\"\n- );\n- if (entries.length == 0) {\n- entries = [node];\n+ write: function(obj, options) {\n+ obj = this.toContext(obj);\n+ return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,\n+ arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.Context\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Format.Context.serviceTypes\n+ * Enumeration for service types\n+ */\n+OpenLayers.Format.Context.serviceTypes = {\n+ \"WMS\": \"urn:ogc:serviceType:WMS\",\n+ \"WFS\": \"urn:ogc:serviceType:WFS\",\n+ \"WCS\": \"urn:ogc:serviceType:WCS\",\n+ \"GML\": \"urn:ogc:serviceType:GML\",\n+ \"SLD\": \"urn:ogc:serviceType:SLD\",\n+ \"FES\": \"urn:ogc:serviceType:FES\",\n+ \"KML\": \"urn:ogc:serviceType:KML\"\n+};\n+/* ======================================================================\n+ OpenLayers/Format/OWSContext.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/Context.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.OWSContext\n+ * Read and write OWS Context documents. OWS Context documents are a \n+ * preliminary OGC (Open Geospatial Consortium) standard for storing the \n+ * state of a web mapping application. In a way it is the successor to\n+ * Web Map Context (WMC), since it is more generic and more types of layers\n+ * can be stored. Also, nesting of layers is supported since version 0.3.1.\n+ * For more information see: http://www.ogcnetwork.net/context\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.Context>\n+ */\n+OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"0.3.1\".\n+ */\n+ defaultVersion: \"0.3.1\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.OWSContext\n+ * Create a new parser for OWS Context documents.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Method: getVersion\n+ * Returns the version to use. Subclasses can override this function\n+ * if a different version detection is needed.\n+ *\n+ * Parameters:\n+ * root - {DOMElement}\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} The version to use.\n+ */\n+ getVersion: function(root, options) {\n+ var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(\n+ this, arguments);\n+ // 0.3.1 is backwards compatible with 0.3.0\n+ if (version === \"0.3.0\") {\n+ version = this.defaultVersion;\n }\n- for (var i = 0, ii = entries.length; i < ii; i++) {\n- features.push(this.parseFeature(entries[i]));\n+ return version;\n+ },\n+\n+ /**\n+ * Method: toContext\n+ * Create a context object free from layer given a map or a\n+ * context object.\n+ *\n+ * Parameters:\n+ * obj - {<OpenLayers.Map> | Object} The map or context.\n+ *\n+ * Returns:\n+ * {Object} A context object.\n+ */\n+ toContext: function(obj) {\n+ var context = {};\n+ if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n+ context.bounds = obj.getExtent();\n+ context.maxExtent = obj.maxExtent;\n+ context.projection = obj.projection;\n+ context.size = obj.getSize();\n+ context.layers = obj.layers;\n }\n- return features;\n+ return context;\n },\n \n+ CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/XLS.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.XLS\n+ * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS>\n+ * constructor. Currently only implemented for Location Utility Services, more\n+ * specifically only for Geocoding. No support for Reverse Geocoding as yet.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n /**\n- * Method: parseLocations\n- * Parse the locations from an Atom entry or feed.\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * APIProperty: stringifyOutput\n+ * {Boolean} If true, write will return a string otherwise a DOMElement.\n+ * Default is true.\n+ */\n+ stringifyOutput: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.XLS\n+ * Create a new parser for XLS.\n *\n * Parameters:\n- * node - {DOMElement} An Atom entry or feed node.\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: write\n+ * Write out an XLS request.\n+ *\n+ * Parameters:\n+ * request - {Object} An object representing the LUS request.\n+ * options - {Object} Optional configuration object.\n *\n * Returns:\n- * Array({<OpenLayers.Geometry>})\n+ * {String} An XLS document string.\n */\n- parseLocations: function(node) {\n- var georssns = this.namespaces.georss;\n \n- var locations = {\n- components: []\n- };\n- var where = this.getElementsByTagNameNS(node, georssns, \"where\");\n- if (where && where.length > 0) {\n- if (!this.gmlParser) {\n- this.initGmlParser();\n- }\n- for (var i = 0, ii = where.length; i < ii; i++) {\n- this.gmlParser.readChildNodes(where[i], locations);\n- }\n- }\n+ /**\n+ * APIMethod: read\n+ * Read an XLS doc and return an object representing the result.\n+ *\n+ * Parameters:\n+ * data - {String | DOMElement} Data to read.\n+ * options - {Object} Options for the reader.\n+ *\n+ * Returns:\n+ * {Object} An object representing the GeocodeResponse.\n+ */\n \n- var components = locations.components;\n- var point = this.getElementsByTagNameNS(node, georssns, \"point\");\n- if (point && point.length > 0) {\n- for (var i = 0, ii = point.length; i < ii; i++) {\n- var xy = OpenLayers.String.trim(\n- point[i].firstChild.nodeValue\n- ).split(/\\s+/);\n- if (xy.length != 2) {\n- xy = OpenLayers.String.trim(\n- point[i].firstChild.nodeValue\n- ).split(/\\s*,\\s*/);\n- }\n- components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]));\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Format.XLS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/Text.js\n+ ====================================================================== */\n \n- var line = this.getElementsByTagNameNS(node, georssns, \"line\");\n- if (line && line.length > 0) {\n- var coords;\n- var p;\n- var points;\n- for (var i = 0, ii = line.length; i < ii; i++) {\n- coords = OpenLayers.String.trim(\n- line[i].firstChild.nodeValue\n- ).split(/\\s+/);\n- points = [];\n- for (var j = 0, jj = coords.length; j < jj; j += 2) {\n- p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n- points.push(p);\n- }\n- components.push(\n- new OpenLayers.Geometry.LineString(points)\n- );\n- }\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- var polygon = this.getElementsByTagNameNS(node, georssns, \"polygon\");\n- if (polygon && polygon.length > 0) {\n- var coords;\n- var p;\n- var points;\n- for (var i = 0, ii = polygon.length; i < ii; i++) {\n- coords = OpenLayers.String.trim(\n- polygon[i].firstChild.nodeValue\n- ).split(/\\s+/);\n- points = [];\n- for (var j = 0, jj = coords.length; j < jj; j += 2) {\n- p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n- points.push(p);\n- }\n- components.push(\n- new OpenLayers.Geometry.Polygon(\n- [new OpenLayers.Geometry.LinearRing(points)]\n- )\n- );\n- }\n- }\n+/**\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ */\n \n- if (this.internalProjection && this.externalProjection) {\n- for (var i = 0, ii = components.length; i < ii; i++) {\n- if (components[i]) {\n- components[i].transform(\n- this.externalProjection,\n- this.internalProjection\n- );\n- }\n- }\n+/**\n+ * Class: OpenLayers.Format.Text\n+ * Read Text format. Create a new instance with the <OpenLayers.Format.Text>\n+ * constructor. This reads text which is formatted like CSV text, using\n+ * tabs as the seperator by default. It provides parsing of data originally\n+ * used in the MapViewerService, described on the wiki. This Format is used\n+ * by the <OpenLayers.Layer.Text> class.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * APIProperty: defaultStyle\n+ * defaultStyle allows one to control the default styling of the features.\n+ * It should be a symbolizer hash. By default, this is set to match the\n+ * Layer.Text behavior, which is to use the default OpenLayers Icon.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * APIProperty: extractStyles\n+ * set to true to extract styles from the TSV files, using information\n+ * from the image or icon, iconSize and iconOffset fields. This will result\n+ * in features with a symbolizer (style) property set, using the\n+ * default symbolizer specified in <defaultStyle>. Set to false if you\n+ * wish to use a styleMap or OpenLayers.Style options to style your\n+ * layer instead.\n+ */\n+ extractStyles: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.Text\n+ * Create a new parser for TSV Text.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ options = options || {};\n+\n+ if (options.extractStyles !== false) {\n+ options.defaultStyle = {\n+ 'externalGraphic': OpenLayers.Util.getImageLocation(\"marker.png\"),\n+ 'graphicWidth': 21,\n+ 'graphicHeight': 25,\n+ 'graphicXOffset': -10.5,\n+ 'graphicYOffset': -12.5\n+ };\n }\n \n- return components;\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n },\n \n /**\n- * Method: parsePersonConstruct\n- * Parse Atom person constructs from an Atom entry node.\n- *\n+ * APIMethod: read\n+ * Return a list of features from a Tab Seperated Values text string.\n+ * \n * Parameters:\n- * node - {DOMElement} An Atom entry or feed node.\n- * name - {String} Construcy name (\"author\" or \"contributor\")\n- * data = {Object} Object in which to put parsed persons.\n+ * text - {String} \n *\n * Returns:\n- * An {Object}.\n+ * Array({<OpenLayers.Feature.Vector>})\n */\n- parsePersonConstructs: function(node, name, data) {\n- var persons = [];\n- var atomns = this.namespaces.atom;\n- var nodes = this.getElementsByTagNameNS(node, atomns, name);\n- var oAtts = [\"uri\", \"email\"];\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- var value = {};\n- value.name = this.getFirstChildValue(\n- nodes[i],\n- atomns,\n- \"name\",\n- null\n- );\n- for (var j = 0, jj = oAtts.length; j < jj; j++) {\n- var attval = this.getFirstChildValue(\n- nodes[i],\n- atomns,\n- oAtts[j],\n- null);\n- if (attval) {\n- value[oAtts[j]] = attval;\n+ read: function(text) {\n+ var lines = text.split('\\n');\n+ var columns;\n+ var features = [];\n+ // length - 1 to allow for trailing new line\n+ for (var lcv = 0; lcv < (lines.length - 1); lcv++) {\n+ var currLine = lines[lcv].replace(/^\\s*/, '').replace(/\\s*$/, '');\n+\n+ if (currLine.charAt(0) != '#') {\n+ /* not a comment */\n+\n+ if (!columns) {\n+ //First line is columns\n+ columns = currLine.split('\\t');\n+ } else {\n+ var vals = currLine.split('\\t');\n+ var geometry = new OpenLayers.Geometry.Point(0, 0);\n+ var attributes = {};\n+ var style = this.defaultStyle ?\n+ OpenLayers.Util.applyDefaults({}, this.defaultStyle) :\n+ null;\n+ var icon, iconSize, iconOffset, overflow;\n+ var set = false;\n+ for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n+ if (vals[valIndex]) {\n+ if (columns[valIndex] == 'point') {\n+ var coords = vals[valIndex].split(',');\n+ geometry.y = parseFloat(coords[0]);\n+ geometry.x = parseFloat(coords[1]);\n+ set = true;\n+ } else if (columns[valIndex] == 'lat') {\n+ geometry.y = parseFloat(vals[valIndex]);\n+ set = true;\n+ } else if (columns[valIndex] == 'lon') {\n+ geometry.x = parseFloat(vals[valIndex]);\n+ set = true;\n+ } else if (columns[valIndex] == 'title')\n+ attributes['title'] = vals[valIndex];\n+ else if (columns[valIndex] == 'image' ||\n+ columns[valIndex] == 'icon' && style) {\n+ style['externalGraphic'] = vals[valIndex];\n+ } else if (columns[valIndex] == 'iconSize' && style) {\n+ var size = vals[valIndex].split(',');\n+ style['graphicWidth'] = parseFloat(size[0]);\n+ style['graphicHeight'] = parseFloat(size[1]);\n+ } else if (columns[valIndex] == 'iconOffset' && style) {\n+ var offset = vals[valIndex].split(',');\n+ style['graphicXOffset'] = parseFloat(offset[0]);\n+ style['graphicYOffset'] = parseFloat(offset[1]);\n+ } else if (columns[valIndex] == 'description') {\n+ attributes['description'] = vals[valIndex];\n+ } else if (columns[valIndex] == 'overflow') {\n+ attributes['overflow'] = vals[valIndex];\n+ } else {\n+ // For StyleMap filtering, allow additional\n+ // columns to be stored as attributes.\n+ attributes[columns[valIndex]] = vals[valIndex];\n+ }\n+ }\n+ }\n+ if (set) {\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n+ features.push(feature);\n+ }\n }\n }\n- persons.push(value);\n- }\n- if (persons.length > 0) {\n- data[name + \"s\"] = persons;\n }\n+ return features;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.Atom\"\n+ CLASS_NAME: \"OpenLayers.Format.Text\"\n });\n /* ======================================================================\n OpenLayers/Format/SOSCapabilities.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -45315,14 +34620,201 @@\n * {Object} Info about the SOS\n */\n \n CLASS_NAME: \"OpenLayers.Format.SOSCapabilities\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/QueryStringFilter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Filter/Spatial.js\n+ * @requires OpenLayers/Filter/Comparison.js\n+ * @requires OpenLayers/Filter/Logical.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.QueryStringFilter\n+ * Parser for reading a query string and creating a simple filter.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.QueryStringFilter = (function() {\n+\n+ /** \n+ * Map the OpenLayers.Filter.Comparison types to the operation strings of \n+ * the protocol.\n+ */\n+ var cmpToStr = {};\n+ cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n+ cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n+\n+ /**\n+ * Function: regex2value\n+ * Convert the value from a regular expression string to a LIKE/ILIKE\n+ * string known to the web service.\n+ *\n+ * Parameters:\n+ * value - {String} The regex string.\n+ *\n+ * Returns:\n+ * {String} The converted string.\n+ */\n+ function regex2value(value) {\n+\n+ // highly sensitive!! Do not change this without running the\n+ // Protocol/HTTP.html unit tests\n+\n+ // convert % to \\%\n+ value = value.replace(/%/g, \"\\\\%\");\n+\n+ // convert \\\\. to \\\\_ (\\\\.* occurences converted later)\n+ value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n+ return $1 ? $0 : \"\\\\\\\\_\";\n+ });\n+\n+ // convert \\\\.* to \\\\%\n+ value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n+\n+ // convert . to _ (\\. and .* occurences converted later)\n+ value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n+ return $1 || $2 ? $0 : \"_\";\n+ });\n+\n+ // convert .* to % (\\.* occurnces converted later)\n+ value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"%\";\n+ });\n+\n+ // convert \\. to .\n+ value = value.replace(/\\\\\\./g, \".\");\n+\n+ // replace \\* with * (watching out for \\\\*)\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"*\";\n+ });\n+\n+ return value;\n+ }\n+\n+ return OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * Property: wildcarded.\n+ * {Boolean} If true percent signs are added around values\n+ * read from LIKE filters, for example if the protocol\n+ * read method is passed a LIKE filter whose property\n+ * is \"foo\" and whose value is \"bar\" the string\n+ * \"foo__ilike=%bar%\" will be sent in the query string;\n+ * defaults to false.\n+ */\n+ wildcarded: false,\n+\n+ /**\n+ * APIProperty: srsInBBOX\n+ * {Boolean} Include the SRS identifier in BBOX query string parameter. \n+ * Default is false. If true and the layer has a projection object set,\n+ * any BBOX filter will be serialized with a fifth item identifying the\n+ * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n+ */\n+ srsInBBOX: false,\n+\n+ /**\n+ * APIMethod: write\n+ * Serialize an <OpenLayers.Filter> objects using the \"simple\" filter syntax for \n+ * query string parameters. This function must be called as a method of\n+ * a protocol instance.\n+ *\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} filter to convert.\n+ * params - {Object} The parameters object.\n+ *\n+ * Returns:\n+ * {Object} The resulting parameters object.\n+ */\n+ write: function(filter, params) {\n+ params = params || {};\n+ var className = filter.CLASS_NAME;\n+ var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n+ switch (filterType) {\n+ case \"Spatial\":\n+ switch (filter.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ params.bbox = filter.value.toArray();\n+ if (this.srsInBBOX && filter.projection) {\n+ params.bbox.push(filter.projection.getCode());\n+ }\n+ break;\n+ case OpenLayers.Filter.Spatial.DWITHIN:\n+ params.tolerance = filter.distance;\n+ // no break here\n+ case OpenLayers.Filter.Spatial.WITHIN:\n+ params.lon = filter.value.x;\n+ params.lat = filter.value.y;\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\n+ \"Unknown spatial filter type \" + filter.type);\n+ }\n+ break;\n+ case \"Comparison\":\n+ var op = cmpToStr[filter.type];\n+ if (op !== undefined) {\n+ var value = filter.value;\n+ if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n+ value = regex2value(value);\n+ if (this.wildcarded) {\n+ value = \"%\" + value + \"%\";\n+ }\n+ }\n+ params[filter.property + \"__\" + op] = value;\n+ params.queryable = params.queryable || [];\n+ params.queryable.push(filter.property);\n+ } else {\n+ OpenLayers.Console.warn(\n+ \"Unknown comparison filter type \" + filter.type);\n+ }\n+ break;\n+ case \"Logical\":\n+ if (filter.type === OpenLayers.Filter.Logical.AND) {\n+ for (var i = 0, len = filter.filters.length; i < len; i++) {\n+ params = this.write(filter.filters[i], params);\n+ }\n+ } else {\n+ OpenLayers.Console.warn(\n+ \"Unsupported logical filter type \" + filter.type);\n+ }\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\"Unknown filter type \" + filterType);\n+ }\n+ return params;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n+\n+ });\n+\n+\n+})();\n+/* ======================================================================\n OpenLayers/Format/SOSGetFeatureOfInterest.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -45519,71 +35011,14 @@\n }\n },\n \n CLASS_NAME: \"OpenLayers.Format.SOSGetFeatureOfInterest\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WMSDescribeLayer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSDescribeLayer\n- * Read SLD WMS DescribeLayer response\n- * DescribeLayer is meant to couple WMS to WFS and WCS\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.1\".\n- */\n- defaultVersion: \"1.1.1\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSDescribeLayer\n- * Create a new parser for WMS DescribeLayer responses.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read DescribeLayer data from a string, and return the response. \n- * The OGC currently defines 2 formats which are allowed for output,\n- * so we need to parse these 2 types\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} Array of {<LayerDescription>} objects which have:\n- * - {String} owsType: WFS/WCS\n- * - {String} owsURL: the online resource\n- * - {String} typeName: the name of the typename on the service\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n-\n-});\n-/* ======================================================================\n OpenLayers/Format/SOSGetObservation.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -45893,359 +35328,2292 @@\n \"timePosition\": function(options) {\n var node = this.createElementNSPlus(\"gml:timePosition\", {\n value: options.value\n });\n return node;\n }\n }\n- },\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.SOSGetObservation\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WMSGetFeatureInfo.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSGetFeatureInfo\n+ * Class to read GetFeatureInfo responses from Web Mapping Services\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * APIProperty: layerIdentifier\n+ * {String} All xml nodes containing this search criteria will populate an\n+ * internal array of layer nodes.\n+ */\n+ layerIdentifier: '_layer',\n+\n+ /**\n+ * APIProperty: featureIdentifier\n+ * {String} All xml nodes containing this search criteria will populate an \n+ * internal array of feature nodes for each layer node found.\n+ */\n+ featureIdentifier: '_feature',\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Property: gmlFormat\n+ * {<OpenLayers.Format.GML>} internal GML format for parsing geometries\n+ * in msGMLOutput\n+ */\n+ gmlFormat: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSGetFeatureInfo\n+ * Create a new parser for WMS GetFeatureInfo responses\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read WMS GetFeatureInfo data from a string, and return an array of features\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} An array of features.\n+ */\n+ read: function(data) {\n+ var result;\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ var root = data.documentElement;\n+ if (root) {\n+ var scope = this;\n+ var read = this[\"read_\" + root.nodeName];\n+ if (read) {\n+ result = read.call(this, root);\n+ } else {\n+ // fall-back to GML since this is a common output format for WMS\n+ // GetFeatureInfo responses\n+ result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);\n+ }\n+ } else {\n+ result = data;\n+ }\n+ return result;\n+ },\n+\n+\n+ /**\n+ * Method: read_msGMLOutput\n+ * Parse msGMLOutput nodes.\n+ *\n+ * Parameters:\n+ * data - {DOMElement}\n+ *\n+ * Returns:\n+ * {Array}\n+ */\n+ read_msGMLOutput: function(data) {\n+ var response = [];\n+ var layerNodes = this.getSiblingNodesByTagCriteria(data,\n+ this.layerIdentifier);\n+ if (layerNodes) {\n+ for (var i = 0, len = layerNodes.length; i < len; ++i) {\n+ var node = layerNodes[i];\n+ var layerName = node.nodeName;\n+ if (node.prefix) {\n+ layerName = layerName.split(':')[1];\n+ }\n+ var layerName = layerName.replace(this.layerIdentifier, '');\n+ var featureNodes = this.getSiblingNodesByTagCriteria(node,\n+ this.featureIdentifier);\n+ if (featureNodes) {\n+ for (var j = 0; j < featureNodes.length; j++) {\n+ var featureNode = featureNodes[j];\n+ var geomInfo = this.parseGeometry(featureNode);\n+ var attributes = this.parseAttributes(featureNode);\n+ var feature = new OpenLayers.Feature.Vector(geomInfo.geometry,\n+ attributes, null);\n+ feature.bounds = geomInfo.bounds;\n+ feature.type = layerName;\n+ response.push(feature);\n+ }\n+ }\n+ }\n+ }\n+ return response;\n+ },\n+\n+ /**\n+ * Method: read_FeatureInfoResponse\n+ * Parse FeatureInfoResponse nodes.\n+ *\n+ * Parameters:\n+ * data - {DOMElement}\n+ *\n+ * Returns:\n+ * {Array}\n+ */\n+ read_FeatureInfoResponse: function(data) {\n+ var response = [];\n+ var featureNodes = this.getElementsByTagNameNS(data, '*',\n+ 'FIELDS');\n+\n+ for (var i = 0, len = featureNodes.length; i < len; i++) {\n+ var featureNode = featureNodes[i];\n+ var geom = null;\n+\n+ // attributes can be actual attributes on the FIELDS tag, \n+ // or FIELD children\n+ var attributes = {};\n+ var j;\n+ var jlen = featureNode.attributes.length;\n+ if (jlen > 0) {\n+ for (j = 0; j < jlen; j++) {\n+ var attribute = featureNode.attributes[j];\n+ attributes[attribute.nodeName] = attribute.nodeValue;\n+ }\n+ } else {\n+ var nodes = featureNode.childNodes;\n+ for (j = 0, jlen = nodes.length; j < jlen; ++j) {\n+ var node = nodes[j];\n+ if (node.nodeType != 3) {\n+ attributes[node.getAttribute(\"name\")] =\n+ node.getAttribute(\"value\");\n+ }\n+ }\n+ }\n+\n+ response.push(\n+ new OpenLayers.Feature.Vector(geom, attributes, null)\n+ );\n+ }\n+ return response;\n+ },\n+\n+ /**\n+ * Method: getSiblingNodesByTagCriteria\n+ * Recursively searches passed xml node and all it's descendant levels for \n+ * nodes whose tagName contains the passed search string. This returns an \n+ * array of all sibling nodes which match the criteria from the highest \n+ * hierarchial level from which a match is found.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} An xml node\n+ * criteria - {String} Search string which will match some part of a tagName \n+ * \n+ * Returns:\n+ * Array({DOMElement}) An array of sibling xml nodes\n+ */\n+ getSiblingNodesByTagCriteria: function(node, criteria) {\n+ var nodes = [];\n+ var children, tagName, n, matchNodes, child;\n+ if (node && node.hasChildNodes()) {\n+ children = node.childNodes;\n+ n = children.length;\n+\n+ for (var k = 0; k < n; k++) {\n+ child = children[k];\n+ while (child && child.nodeType != 1) {\n+ child = child.nextSibling;\n+ k++;\n+ }\n+ tagName = (child ? child.nodeName : '');\n+ if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {\n+ nodes.push(child);\n+ } else {\n+ matchNodes = this.getSiblingNodesByTagCriteria(\n+ child, criteria);\n+\n+ if (matchNodes.length > 0) {\n+ (nodes.length == 0) ?\n+ nodes = matchNodes: nodes.push(matchNodes);\n+ }\n+ }\n+ }\n+\n+ }\n+ return nodes;\n+ },\n+\n+ /**\n+ * Method: parseAttributes\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An attributes object.\n+ * \n+ * Notes:\n+ * Assumes that attributes are direct child xml nodes of the passed node\n+ * and contain only a single text node. \n+ */\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+ if (node.nodeType == 1) {\n+ var children = node.childNodes;\n+ var n = children.length;\n+ for (var i = 0; i < n; ++i) {\n+ var child = children[i];\n+ if (child.nodeType == 1) {\n+ var grandchildren = child.childNodes;\n+ var name = (child.prefix) ?\n+ child.nodeName.split(\":\")[1] : child.nodeName;\n+ if (grandchildren.length == 0) {\n+ attributes[name] = null;\n+ } else if (grandchildren.length == 1) {\n+ var grandchild = grandchildren[0];\n+ if (grandchild.nodeType == 3 ||\n+ grandchild.nodeType == 4) {\n+ var value = grandchild.nodeValue.replace(\n+ this.regExes.trimSpace, \"\");\n+ attributes[name] = value;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return attributes;\n+ },\n+\n+ /**\n+ * Method: parseGeometry\n+ * Parse the geometry and the feature bounds out of the node using \n+ * Format.GML\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An object containing the geometry and the feature bounds\n+ */\n+ parseGeometry: function(node) {\n+ // we need to use the old Format.GML parser since we do not know the \n+ // geometry name\n+ if (!this.gmlFormat) {\n+ this.gmlFormat = new OpenLayers.Format.GML();\n+ }\n+ var feature = this.gmlFormat.parseFeature(node);\n+ var geometry, bounds = null;\n+ if (feature) {\n+ geometry = feature.geometry && feature.geometry.clone();\n+ bounds = feature.bounds && feature.bounds.clone();\n+ feature.destroy();\n+ }\n+ return {\n+ geometry: geometry,\n+ bounds: bounds\n+ };\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WPSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSCapabilities\n+ * Read WPS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n+ */\n+ defaultVersion: \"1.0.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSCapabilities\n+ * Create a new parser for WPS Capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return information about\n+ * the service.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Info about the WPS\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WFSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFSCapabilities\n+ * Read WFS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFSCapabilities\n+ * Create a new parser for WFS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of layers. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named layers.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WMSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSCapabilities\n+ * Read WMS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.1\".\n+ */\n+ defaultVersion: \"1.1.1\",\n+\n+ /**\n+ * APIProperty: profile\n+ * {String} If provided, use a custom profile.\n+ *\n+ * Currently supported profiles:\n+ * - WMSC - parses vendor specific capabilities for WMS-C.\n+ */\n+ profile: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSCapabilities\n+ * Create a new parser for WMS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of layers. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named layers.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WCSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WCSCapabilities\n+ * Read WCS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WCSCapabilities\n+ * Create a new parser for WCS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of coverages. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named coverages.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/ArcXML.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/MultiPolygon.js\n+ * @requires OpenLayers/Geometry/LinearRing.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.ArcXML\n+ * Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML>\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: fontStyleKeys\n+ * {Array} List of keys used in font styling.\n+ */\n+ fontStyleKeys: [\n+ 'antialiasing', 'blockout', 'font', 'fontcolor', 'fontsize', 'fontstyle',\n+ 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency'\n+ ],\n+\n+ /**\n+ * Property: request\n+ * A get_image request destined for an ArcIMS server.\n+ */\n+ request: null,\n+\n+ /**\n+ * Property: response\n+ * A parsed response from an ArcIMS server.\n+ */\n+ response: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.ArcXML\n+ * Create a new parser/writer for ArcXML. Create an instance of this class\n+ * to begin authoring a request to an ArcIMS service. This is used\n+ * primarily by the ArcIMS layer, but could be used to do other wild\n+ * stuff, like geocoding.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ this.request = new OpenLayers.Format.ArcXML.Request();\n+ this.response = new OpenLayers.Format.ArcXML.Response();\n+\n+ if (options) {\n+ if (options.requesttype == \"feature\") {\n+ this.request.get_image = null;\n+\n+ var qry = this.request.get_feature.query;\n+ this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);\n+ this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);\n+\n+ if (options.polygon) {\n+ qry.isspatial = true;\n+ qry.spatialfilter.polygon = options.polygon;\n+ } else if (options.envelope) {\n+ qry.isspatial = true;\n+ qry.spatialfilter.envelope = {\n+ minx: 0,\n+ miny: 0,\n+ maxx: 0,\n+ maxy: 0\n+ };\n+ this.parseEnvelope(qry.spatialfilter.envelope, options.envelope);\n+ }\n+ } else if (options.requesttype == \"image\") {\n+ this.request.get_feature = null;\n+\n+ var props = this.request.get_image.properties;\n+ this.parseEnvelope(props.envelope, options.envelope);\n+\n+ this.addLayers(props.layerlist, options.layers);\n+ this.addImageSize(props.imagesize, options.tileSize);\n+ this.addCoordSys(props.featurecoordsys, options.featureCoordSys);\n+ this.addCoordSys(props.filtercoordsys, options.filterCoordSys);\n+ } else {\n+ // if an arcxml object is being created with no request type, it is\n+ // probably going to consume a response, so do not throw an error if\n+ // the requesttype is not defined\n+ this.request = null;\n+ }\n+ }\n+\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * Method: parseEnvelope\n+ * Parse an array of coordinates into an ArcXML envelope structure.\n+ *\n+ * Parameters:\n+ * env - {Object} An envelope object that will contain the parsed coordinates.\n+ * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ]\n+ */\n+ parseEnvelope: function(env, arr) {\n+ if (arr && arr.length == 4) {\n+ env.minx = arr[0];\n+ env.miny = arr[1];\n+ env.maxx = arr[2];\n+ env.maxy = arr[3];\n+ }\n+ },\n+\n+ /** \n+ * Method: addLayers\n+ * Add a collection of layers to another collection of layers. Each layer in the list is tuple of\n+ * { id, visible }. These layer collections represent the \n+ * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML\n+ *\n+ * TODO: Add support for dynamic layer rendering.\n+ *\n+ * Parameters:\n+ * ll - {Array({id,visible})} A list of layer definitions.\n+ * lyrs - {Array({id,visible})} A list of layer definitions.\n+ */\n+ addLayers: function(ll, lyrs) {\n+ for (var lind = 0, len = lyrs.length; lind < len; lind++) {\n+ ll.push(lyrs[lind]);\n+ }\n+ },\n+\n+ /**\n+ * Method: addImageSize\n+ * Set the size of the requested image.\n+ *\n+ * Parameters:\n+ * imsize - {Object} An ArcXML imagesize object.\n+ * olsize - {<OpenLayers.Size>} The image size to set.\n+ */\n+ addImageSize: function(imsize, olsize) {\n+ if (olsize !== null) {\n+ imsize.width = olsize.w;\n+ imsize.height = olsize.h;\n+ imsize.printwidth = olsize.w;\n+ imsize.printheight = olsize.h;\n+ }\n+ },\n+\n+ /**\n+ * Method: addCoordSys\n+ * Add the coordinate system information to an object. The object may be \n+ *\n+ * Parameters:\n+ * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure.\n+ * fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or \n+ * {featurecoordsys} A projection representation. If it's a {String}, \n+ * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} \n+ * AND Proj4js is available, the projection number and name are extracted \n+ * from there. If it's a filter or feature ArcXML structure, it is copied.\n+ */\n+ addCoordSys: function(featOrFilt, fsys) {\n+ if (typeof fsys == \"string\") {\n+ featOrFilt.id = parseInt(fsys);\n+ featOrFilt.string = fsys;\n+ }\n+ // is this a proj4js instance?\n+ else if (typeof fsys == \"object\" && fsys.proj !== null) {\n+ featOrFilt.id = fsys.proj.srsProjNumber;\n+ featOrFilt.string = fsys.proj.srsCode;\n+ } else {\n+ featOrFilt = fsys;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: iserror\n+ * Check to see if the response from the server was an error.\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse. If nothing is supplied,\n+ * the current response is examined.\n+ *\n+ * Returns:\n+ * {Boolean} true if the response was an error.\n+ */\n+ iserror: function(data) {\n+ var ret = null;\n+\n+ if (!data) {\n+ ret = (this.response.error !== '');\n+ } else {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ var errorNodes = data.documentElement.getElementsByTagName(\"ERROR\");\n+ ret = (errorNodes !== null && errorNodes.length > 0);\n+ }\n+\n+ return ret;\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read data from a string, and return an response. \n+ * \n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response\n+ * data may change in the future. \n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+\n+ var arcNode = null;\n+ if (data && data.documentElement) {\n+ if (data.documentElement.nodeName == \"ARCXML\") {\n+ arcNode = data.documentElement;\n+ } else {\n+ arcNode = data.documentElement.getElementsByTagName(\"ARCXML\")[0];\n+ }\n+ }\n+\n+ // in Safari, arcNode will be there but will have a child named \n+ // parsererror\n+ if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') {\n+ var error, source;\n+ try {\n+ error = data.firstChild.nodeValue;\n+ source = data.firstChild.childNodes[1].firstChild.nodeValue;\n+ } catch (err) {\n+ // pass\n+ }\n+ throw {\n+ message: \"Error parsing the ArcXML request\",\n+ error: error,\n+ source: source\n+ };\n+ }\n+\n+ var response = this.parseResponse(arcNode);\n+ return response;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Generate an ArcXml document string for sending to an ArcIMS server. \n+ * \n+ * Returns:\n+ * {String} A string representing the ArcXML document request.\n+ */\n+ write: function(request) {\n+ if (!request) {\n+ request = this.request;\n+ }\n+ var root = this.createElementNS(\"\", \"ARCXML\");\n+ root.setAttribute(\"version\", \"1.1\");\n+\n+ var reqElem = this.createElementNS(\"\", \"REQUEST\");\n+\n+ if (request.get_image != null) {\n+ var getElem = this.createElementNS(\"\", \"GET_IMAGE\");\n+ reqElem.appendChild(getElem);\n+\n+ var propElem = this.createElementNS(\"\", \"PROPERTIES\");\n+ getElem.appendChild(propElem);\n+\n+ var props = request.get_image.properties;\n+ if (props.featurecoordsys != null) {\n+ var feat = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n+ propElem.appendChild(feat);\n+\n+ if (props.featurecoordsys.id === 0) {\n+ feat.setAttribute(\"string\", props.featurecoordsys['string']);\n+ } else {\n+ feat.setAttribute(\"id\", props.featurecoordsys.id);\n+ }\n+ }\n+\n+ if (props.filtercoordsys != null) {\n+ var filt = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n+ propElem.appendChild(filt);\n+\n+ if (props.filtercoordsys.id === 0) {\n+ filt.setAttribute(\"string\", props.filtercoordsys.string);\n+ } else {\n+ filt.setAttribute(\"id\", props.filtercoordsys.id);\n+ }\n+ }\n+\n+ if (props.envelope != null) {\n+ var env = this.createElementNS(\"\", \"ENVELOPE\");\n+ propElem.appendChild(env);\n+\n+ env.setAttribute(\"minx\", props.envelope.minx);\n+ env.setAttribute(\"miny\", props.envelope.miny);\n+ env.setAttribute(\"maxx\", props.envelope.maxx);\n+ env.setAttribute(\"maxy\", props.envelope.maxy);\n+ }\n+\n+ var imagesz = this.createElementNS(\"\", \"IMAGESIZE\");\n+ propElem.appendChild(imagesz);\n+\n+ imagesz.setAttribute(\"height\", props.imagesize.height);\n+ imagesz.setAttribute(\"width\", props.imagesize.width);\n+\n+ if (props.imagesize.height != props.imagesize.printheight ||\n+ props.imagesize.width != props.imagesize.printwidth) {\n+ imagesz.setAttribute(\"printheight\", props.imagesize.printheight);\n+ imagesz.setArrtibute(\"printwidth\", props.imagesize.printwidth);\n+ }\n+\n+ if (props.background != null) {\n+ var backgrnd = this.createElementNS(\"\", \"BACKGROUND\");\n+ propElem.appendChild(backgrnd);\n+\n+ backgrnd.setAttribute(\"color\",\n+ props.background.color.r + \",\" +\n+ props.background.color.g + \",\" +\n+ props.background.color.b);\n+\n+ if (props.background.transcolor !== null) {\n+ backgrnd.setAttribute(\"transcolor\",\n+ props.background.transcolor.r + \",\" +\n+ props.background.transcolor.g + \",\" +\n+ props.background.transcolor.b);\n+ }\n+ }\n+\n+ if (props.layerlist != null && props.layerlist.length > 0) {\n+ var layerlst = this.createElementNS(\"\", \"LAYERLIST\");\n+ propElem.appendChild(layerlst);\n+\n+ for (var ld = 0; ld < props.layerlist.length; ld++) {\n+ var ldef = this.createElementNS(\"\", \"LAYERDEF\");\n+ layerlst.appendChild(ldef);\n+\n+ ldef.setAttribute(\"id\", props.layerlist[ld].id);\n+ ldef.setAttribute(\"visible\", props.layerlist[ld].visible);\n+\n+ if (typeof props.layerlist[ld].query == \"object\") {\n+ var query = props.layerlist[ld].query;\n+\n+ if (query.where.length < 0) {\n+ continue;\n+ }\n+\n+ var queryElem = null;\n+ if (typeof query.spatialfilter == \"boolean\" && query.spatialfilter) {\n+ // handle spatial filter madness\n+ queryElem = this.createElementNS(\"\", \"SPATIALQUERY\");\n+ } else {\n+ queryElem = this.createElementNS(\"\", \"QUERY\");\n+ }\n+\n+ queryElem.setAttribute(\"where\", query.where);\n+\n+ if (typeof query.accuracy == \"number\" && query.accuracy > 0) {\n+ queryElem.setAttribute(\"accuracy\", query.accuracy);\n+ }\n+ if (typeof query.featurelimit == \"number\" && query.featurelimit < 2000) {\n+ queryElem.setAttribute(\"featurelimit\", query.featurelimit);\n+ }\n+ if (typeof query.subfields == \"string\" && query.subfields != \"#ALL#\") {\n+ queryElem.setAttribute(\"subfields\", query.subfields);\n+ }\n+ if (typeof query.joinexpression == \"string\" && query.joinexpression.length > 0) {\n+ queryElem.setAttribute(\"joinexpression\", query.joinexpression);\n+ }\n+ if (typeof query.jointables == \"string\" && query.jointables.length > 0) {\n+ queryElem.setAttribute(\"jointables\", query.jointables);\n+ }\n+\n+ ldef.appendChild(queryElem);\n+ }\n+\n+ if (typeof props.layerlist[ld].renderer == \"object\") {\n+ this.addRenderer(ldef, props.layerlist[ld].renderer);\n+ }\n+ }\n+ }\n+ } else if (request.get_feature != null) {\n+ var getElem = this.createElementNS(\"\", \"GET_FEATURES\");\n+ getElem.setAttribute(\"outputmode\", \"newxml\");\n+ getElem.setAttribute(\"checkesc\", \"true\");\n+\n+ if (request.get_feature.geometry) {\n+ getElem.setAttribute(\"geometry\", request.get_feature.geometry);\n+ } else {\n+ getElem.setAttribute(\"geometry\", \"false\");\n+ }\n+\n+ if (request.get_feature.compact) {\n+ getElem.setAttribute(\"compact\", request.get_feature.compact);\n+ }\n+\n+ if (request.get_feature.featurelimit == \"number\") {\n+ getElem.setAttribute(\"featurelimit\", request.get_feature.featurelimit);\n+ }\n+\n+ getElem.setAttribute(\"globalenvelope\", \"true\");\n+ reqElem.appendChild(getElem);\n+\n+ if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {\n+ var lyrElem = this.createElementNS(\"\", \"LAYER\");\n+ lyrElem.setAttribute(\"id\", request.get_feature.layer);\n+ getElem.appendChild(lyrElem);\n+ }\n+\n+ var fquery = request.get_feature.query;\n+ if (fquery != null) {\n+ var qElem = null;\n+ if (fquery.isspatial) {\n+ qElem = this.createElementNS(\"\", \"SPATIALQUERY\");\n+ } else {\n+ qElem = this.createElementNS(\"\", \"QUERY\");\n+ }\n+ getElem.appendChild(qElem);\n+\n+ if (typeof fquery.accuracy == \"number\") {\n+ qElem.setAttribute(\"accuracy\", fquery.accuracy);\n+ }\n+ //qElem.setAttribute(\"featurelimit\", \"5\");\n+\n+ if (fquery.featurecoordsys != null) {\n+ var fcsElem1 = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n+\n+ if (fquery.featurecoordsys.id == 0) {\n+ fcsElem1.setAttribute(\"string\", fquery.featurecoordsys.string);\n+ } else {\n+ fcsElem1.setAttribute(\"id\", fquery.featurecoordsys.id);\n+ }\n+ qElem.appendChild(fcsElem1);\n+ }\n+\n+ if (fquery.filtercoordsys != null) {\n+ var fcsElem2 = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n+\n+ if (fquery.filtercoordsys.id === 0) {\n+ fcsElem2.setAttribute(\"string\", fquery.filtercoordsys.string);\n+ } else {\n+ fcsElem2.setAttribute(\"id\", fquery.filtercoordsys.id);\n+ }\n+ qElem.appendChild(fcsElem2);\n+ }\n+\n+ if (fquery.buffer > 0) {\n+ var bufElem = this.createElementNS(\"\", \"BUFFER\");\n+ bufElem.setAttribute(\"distance\", fquery.buffer);\n+ qElem.appendChild(bufElem);\n+ }\n+\n+ if (fquery.isspatial) {\n+ var spfElem = this.createElementNS(\"\", \"SPATIALFILTER\");\n+ spfElem.setAttribute(\"relation\", fquery.spatialfilter.relation);\n+ qElem.appendChild(spfElem);\n+\n+ if (fquery.spatialfilter.envelope) {\n+ var envElem = this.createElementNS(\"\", \"ENVELOPE\");\n+ envElem.setAttribute(\"minx\", fquery.spatialfilter.envelope.minx);\n+ envElem.setAttribute(\"miny\", fquery.spatialfilter.envelope.miny);\n+ envElem.setAttribute(\"maxx\", fquery.spatialfilter.envelope.maxx);\n+ envElem.setAttribute(\"maxy\", fquery.spatialfilter.envelope.maxy);\n+ spfElem.appendChild(envElem);\n+ } else if (typeof fquery.spatialfilter.polygon == \"object\") {\n+ spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon));\n+ }\n+ }\n+\n+ if (fquery.where != null && fquery.where.length > 0) {\n+ qElem.setAttribute(\"where\", fquery.where);\n+ }\n+ }\n+ }\n+\n+ root.appendChild(reqElem);\n+\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root]);\n+ },\n+\n+\n+ addGroupRenderer: function(ldef, toprenderer) {\n+ var topRelem = this.createElementNS(\"\", \"GROUPRENDERER\");\n+ ldef.appendChild(topRelem);\n+\n+ for (var rind = 0; rind < toprenderer.length; rind++) {\n+ var renderer = toprenderer[rind];\n+ this.addRenderer(topRelem, renderer);\n+ }\n+ },\n+\n+\n+ addRenderer: function(topRelem, renderer) {\n+ if (OpenLayers.Util.isArray(renderer)) {\n+ this.addGroupRenderer(topRelem, renderer);\n+ } else {\n+ var renderElem = this.createElementNS(\"\", renderer.type.toUpperCase() + \"RENDERER\");\n+ topRelem.appendChild(renderElem);\n+\n+ if (renderElem.tagName == \"VALUEMAPRENDERER\") {\n+ this.addValueMapRenderer(renderElem, renderer);\n+ } else if (renderElem.tagName == \"VALUEMAPLABELRENDERER\") {\n+ this.addValueMapLabelRenderer(renderElem, renderer);\n+ } else if (renderElem.tagName == \"SIMPLELABELRENDERER\") {\n+ this.addSimpleLabelRenderer(renderElem, renderer);\n+ } else if (renderElem.tagName == \"SCALEDEPENDENTRENDERER\") {\n+ this.addScaleDependentRenderer(renderElem, renderer);\n+ }\n+ }\n+ },\n+\n+\n+ addScaleDependentRenderer: function(renderElem, renderer) {\n+ if (typeof renderer.lower == \"string\" || typeof renderer.lower == \"number\") {\n+ renderElem.setAttribute(\"lower\", renderer.lower);\n+ }\n+ if (typeof renderer.upper == \"string\" || typeof renderer.upper == \"number\") {\n+ renderElem.setAttribute(\"upper\", renderer.upper);\n+ }\n+\n+ this.addRenderer(renderElem, renderer.renderer);\n+ },\n+\n+\n+ addValueMapLabelRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n+ renderElem.setAttribute(\"labelfield\", renderer.labelfield);\n+\n+ if (typeof renderer.exacts == \"object\") {\n+ for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n+ var exact = renderer.exacts[ext];\n+\n+ var eelem = this.createElementNS(\"\", \"EXACT\");\n+\n+ if (typeof exact.value == \"string\") {\n+ eelem.setAttribute(\"value\", exact.value);\n+ }\n+ if (typeof exact.label == \"string\") {\n+ eelem.setAttribute(\"label\", exact.label);\n+ }\n+ if (typeof exact.method == \"string\") {\n+ eelem.setAttribute(\"method\", exact.method);\n+ }\n+\n+ renderElem.appendChild(eelem);\n+\n+ if (typeof exact.symbol == \"object\") {\n+ var selem = null;\n+\n+ if (exact.symbol.type == \"text\") {\n+ selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n+ }\n+\n+ if (selem != null) {\n+ var keys = this.fontStyleKeys;\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (exact.symbol[key]) {\n+ selem.setAttribute(key, exact.symbol[key]);\n+ }\n+ }\n+ eelem.appendChild(selem);\n+ }\n+ }\n+ } // for each exact\n+ }\n+ },\n+\n+ addValueMapRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n+\n+ if (typeof renderer.ranges == \"object\") {\n+ for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) {\n+ var range = renderer.ranges[rng];\n+\n+ var relem = this.createElementNS(\"\", \"RANGE\");\n+ relem.setAttribute(\"lower\", range.lower);\n+ relem.setAttribute(\"upper\", range.upper);\n+\n+ renderElem.appendChild(relem);\n+\n+ if (typeof range.symbol == \"object\") {\n+ var selem = null;\n+\n+ if (range.symbol.type == \"simplepolygon\") {\n+ selem = this.createElementNS(\"\", \"SIMPLEPOLYGONSYMBOL\");\n+ }\n+\n+ if (selem != null) {\n+ if (typeof range.symbol.boundarycolor == \"string\") {\n+ selem.setAttribute(\"boundarycolor\", range.symbol.boundarycolor);\n+ }\n+ if (typeof range.symbol.fillcolor == \"string\") {\n+ selem.setAttribute(\"fillcolor\", range.symbol.fillcolor);\n+ }\n+ if (typeof range.symbol.filltransparency == \"number\") {\n+ selem.setAttribute(\"filltransparency\", range.symbol.filltransparency);\n+ }\n+ relem.appendChild(selem);\n+ }\n+ }\n+ } // for each range\n+ } else if (typeof renderer.exacts == \"object\") {\n+ for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n+ var exact = renderer.exacts[ext];\n+\n+ var eelem = this.createElementNS(\"\", \"EXACT\");\n+ if (typeof exact.value == \"string\") {\n+ eelem.setAttribute(\"value\", exact.value);\n+ }\n+ if (typeof exact.label == \"string\") {\n+ eelem.setAttribute(\"label\", exact.label);\n+ }\n+ if (typeof exact.method == \"string\") {\n+ eelem.setAttribute(\"method\", exact.method);\n+ }\n+\n+ renderElem.appendChild(eelem);\n+\n+ if (typeof exact.symbol == \"object\") {\n+ var selem = null;\n+\n+ if (exact.symbol.type == \"simplemarker\") {\n+ selem = this.createElementNS(\"\", \"SIMPLEMARKERSYMBOL\");\n+ }\n+\n+ if (selem != null) {\n+ if (typeof exact.symbol.antialiasing == \"string\") {\n+ selem.setAttribute(\"antialiasing\", exact.symbol.antialiasing);\n+ }\n+ if (typeof exact.symbol.color == \"string\") {\n+ selem.setAttribute(\"color\", exact.symbol.color);\n+ }\n+ if (typeof exact.symbol.outline == \"string\") {\n+ selem.setAttribute(\"outline\", exact.symbol.outline);\n+ }\n+ if (typeof exact.symbol.overlap == \"string\") {\n+ selem.setAttribute(\"overlap\", exact.symbol.overlap);\n+ }\n+ if (typeof exact.symbol.shadow == \"string\") {\n+ selem.setAttribute(\"shadow\", exact.symbol.shadow);\n+ }\n+ if (typeof exact.symbol.transparency == \"number\") {\n+ selem.setAttribute(\"transparency\", exact.symbol.transparency);\n+ }\n+ //if (typeof exact.symbol.type == \"string\")\n+ // selem.setAttribute(\"type\", exact.symbol.type);\n+ if (typeof exact.symbol.usecentroid == \"string\") {\n+ selem.setAttribute(\"usecentroid\", exact.symbol.usecentroid);\n+ }\n+ if (typeof exact.symbol.width == \"number\") {\n+ selem.setAttribute(\"width\", exact.symbol.width);\n+ }\n+\n+ eelem.appendChild(selem);\n+ }\n+ }\n+ } // for each exact\n+ }\n+ },\n+\n+\n+ addSimpleLabelRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"field\", renderer.field);\n+ var keys = ['featureweight', 'howmanylabels', 'labelbufferratio',\n+ 'labelpriorities', 'labelweight', 'linelabelposition',\n+ 'rotationalangles'\n+ ];\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (renderer[key]) {\n+ renderElem.setAttribute(key, renderer[key]);\n+ }\n+ }\n+\n+ if (renderer.symbol.type == \"text\") {\n+ var symbol = renderer.symbol;\n+ var selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n+ renderElem.appendChild(selem);\n+\n+ var keys = this.fontStyleKeys;\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (symbol[key]) {\n+ selem.setAttribute(key, renderer[key]);\n+ }\n+ }\n+ }\n+ },\n+\n+ writePolygonGeometry: function(polygon) {\n+ if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {\n+ throw {\n+ message: 'Cannot write polygon geometry to ArcXML with an ' +\n+ polygon.CLASS_NAME + ' object.',\n+ geometry: polygon\n+ };\n+ }\n+\n+ var polyElem = this.createElementNS(\"\", \"POLYGON\");\n+\n+ for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) {\n+ var ring = polygon.components[ln];\n+ var ringElem = this.createElementNS(\"\", \"RING\");\n+\n+ for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) {\n+ var point = ring.components[rn];\n+ var pointElem = this.createElementNS(\"\", \"POINT\");\n+\n+ pointElem.setAttribute(\"x\", point.x);\n+ pointElem.setAttribute(\"y\", point.y);\n+\n+ ringElem.appendChild(pointElem);\n+ }\n+\n+ polyElem.appendChild(ringElem);\n+ }\n+\n+ return polyElem;\n+ },\n+\n+ /**\n+ * Method: parseResponse\n+ * Take an ArcXML response, and parse in into this object's internal properties.\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} The ArcXML response, as either a string or the\n+ * top level DOMElement of the response.\n+ */\n+ parseResponse: function(data) {\n+ if (typeof data == \"string\") {\n+ var newData = new OpenLayers.Format.XML();\n+ data = newData.read(data);\n+ }\n+ var response = new OpenLayers.Format.ArcXML.Response();\n+\n+ var errorNode = data.getElementsByTagName(\"ERROR\");\n+\n+ if (errorNode != null && errorNode.length > 0) {\n+ response.error = this.getChildValue(errorNode, \"Unknown error.\");\n+ } else {\n+ var responseNode = data.getElementsByTagName(\"RESPONSE\");\n+\n+ if (responseNode == null || responseNode.length == 0) {\n+ response.error = \"No RESPONSE tag found in ArcXML response.\";\n+ return response;\n+ }\n+\n+ var rtype = responseNode[0].firstChild.nodeName;\n+ if (rtype == \"#text\") {\n+ rtype = responseNode[0].firstChild.nextSibling.nodeName;\n+ }\n+\n+ if (rtype == \"IMAGE\") {\n+ var envelopeNode = data.getElementsByTagName(\"ENVELOPE\");\n+ var outputNode = data.getElementsByTagName(\"OUTPUT\");\n+\n+ if (envelopeNode == null || envelopeNode.length == 0) {\n+ response.error = \"No ENVELOPE tag found in ArcXML response.\";\n+ } else if (outputNode == null || outputNode.length == 0) {\n+ response.error = \"No OUTPUT tag found in ArcXML response.\";\n+ } else {\n+ var envAttr = this.parseAttributes(envelopeNode[0]);\n+ var outputAttr = this.parseAttributes(outputNode[0]);\n+\n+ if (typeof outputAttr.type == \"string\") {\n+ response.image = {\n+ envelope: envAttr,\n+ output: {\n+ type: outputAttr.type,\n+ data: this.getChildValue(outputNode[0])\n+ }\n+ };\n+ } else {\n+ response.image = {\n+ envelope: envAttr,\n+ output: outputAttr\n+ };\n+ }\n+ }\n+ } else if (rtype == \"FEATURES\") {\n+ var features = responseNode[0].getElementsByTagName(\"FEATURES\");\n+\n+ // get the feature count\n+ var featureCount = features[0].getElementsByTagName(\"FEATURECOUNT\");\n+ response.features.featurecount = featureCount[0].getAttribute(\"count\");\n+\n+ if (response.features.featurecount > 0) {\n+ // get the feature envelope\n+ var envelope = features[0].getElementsByTagName(\"ENVELOPE\");\n+ response.features.envelope = this.parseAttributes(envelope[0], typeof(0));\n+\n+ // get the field values per feature\n+ var featureList = features[0].getElementsByTagName(\"FEATURE\");\n+ for (var fn = 0; fn < featureList.length; fn++) {\n+ var feature = new OpenLayers.Feature.Vector();\n+ var fields = featureList[fn].getElementsByTagName(\"FIELD\");\n+\n+ for (var fdn = 0; fdn < fields.length; fdn++) {\n+ var fieldName = fields[fdn].getAttribute(\"name\");\n+ var fieldValue = fields[fdn].getAttribute(\"value\");\n+ feature.attributes[fieldName] = fieldValue;\n+ }\n+\n+ var geom = featureList[fn].getElementsByTagName(\"POLYGON\");\n+\n+ if (geom.length > 0) {\n+ // if there is a polygon, create an openlayers polygon, and assign\n+ // it to the .geometry property of the feature\n+ var ring = geom[0].getElementsByTagName(\"RING\");\n+\n+ var polys = [];\n+ for (var rn = 0; rn < ring.length; rn++) {\n+ var linearRings = [];\n+ linearRings.push(this.parsePointGeometry(ring[rn]));\n+\n+ var holes = ring[rn].getElementsByTagName(\"HOLE\");\n+ for (var hn = 0; hn < holes.length; hn++) {\n+ linearRings.push(this.parsePointGeometry(holes[hn]));\n+ }\n+ holes = null;\n+ polys.push(new OpenLayers.Geometry.Polygon(linearRings));\n+ linearRings = null;\n+ }\n+ ring = null;\n+\n+ if (polys.length == 1) {\n+ feature.geometry = polys[0];\n+ } else {\n+ feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys);\n+ }\n+ }\n+\n+ response.features.feature.push(feature);\n+ }\n+ }\n+ } else {\n+ response.error = \"Unidentified response type.\";\n+ }\n+ }\n+ return response;\n+ },\n+\n+\n+ /**\n+ * Method: parseAttributes\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>} An element to parse attributes from.\n+ *\n+ * Returns:\n+ * {Object} An attributes object, with properties set to attribute values.\n+ */\n+ parseAttributes: function(node, type) {\n+ var attributes = {};\n+ for (var attr = 0; attr < node.attributes.length; attr++) {\n+ if (type == \"number\") {\n+ attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue);\n+ } else {\n+ attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue;\n+ }\n+ }\n+ return attributes;\n+ },\n+\n+\n+ /**\n+ * Method: parsePointGeometry\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points.\n+ */\n+ parsePointGeometry: function(node) {\n+ var ringPoints = [];\n+ var coords = node.getElementsByTagName(\"COORDS\");\n+\n+ if (coords.length > 0) {\n+ // if coords is present, it's the only coords item\n+ var coordArr = this.getChildValue(coords[0]);\n+ coordArr = coordArr.split(/;/);\n+ for (var cn = 0; cn < coordArr.length; cn++) {\n+ var coordItems = coordArr[cn].split(/ /);\n+ ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1]));\n+ }\n+ coords = null;\n+ } else {\n+ var point = node.getElementsByTagName(\"POINT\");\n+ if (point.length > 0) {\n+ for (var pn = 0; pn < point.length; pn++) {\n+ ringPoints.push(\n+ new OpenLayers.Geometry.Point(\n+ parseFloat(point[pn].getAttribute(\"x\")),\n+ parseFloat(point[pn].getAttribute(\"y\"))\n+ )\n+ );\n+ }\n+ }\n+ point = null;\n+ }\n+\n+ return new OpenLayers.Geometry.LinearRing(ringPoints);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML\"\n+});\n+\n+OpenLayers.Format.ArcXML.Request = OpenLayers.Class({\n+ initialize: function(params) {\n+ var defaults = {\n+ get_image: {\n+ properties: {\n+ background: null,\n+ /*{ \n+ color: { r:255, g:255, b:255 },\n+ transcolor: null\n+ },*/\n+ draw: true,\n+ envelope: {\n+ minx: 0,\n+ miny: 0,\n+ maxx: 0,\n+ maxy: 0\n+ },\n+ featurecoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ filtercoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ imagesize: {\n+ height: 0,\n+ width: 0,\n+ dpi: 96,\n+ printheight: 0,\n+ printwidth: 0,\n+ scalesymbols: false\n+ },\n+ layerlist: [],\n+ /* no support for legends */\n+ output: {\n+ baseurl: \"\",\n+ legendbaseurl: \"\",\n+ legendname: \"\",\n+ legendpath: \"\",\n+ legendurl: \"\",\n+ name: \"\",\n+ path: \"\",\n+ type: \"jpg\",\n+ url: \"\"\n+ }\n+ }\n+ },\n+\n+ get_feature: {\n+ layer: \"\",\n+ query: {\n+ isspatial: false,\n+ featurecoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ filtercoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ buffer: 0,\n+ where: \"\",\n+ spatialfilter: {\n+ relation: \"envelope_intersection\",\n+ envelope: null\n+ }\n+ }\n+ },\n+\n+ environment: {\n+ separators: {\n+ cs: \" \",\n+ ts: \";\"\n+ }\n+ },\n+\n+ layer: [],\n+ workspaces: []\n+ };\n+\n+ return OpenLayers.Util.extend(this, defaults);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML.Request\"\n+});\n+\n+OpenLayers.Format.ArcXML.Response = OpenLayers.Class({\n+ initialize: function(params) {\n+ var defaults = {\n+ image: {\n+ envelope: null,\n+ output: ''\n+ },\n+\n+ features: {\n+ featurecount: 0,\n+ envelope: null,\n+ feature: []\n+ },\n+\n+ error: ''\n+ };\n+\n+ return OpenLayers.Util.extend(this, defaults);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML.Response\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/Atom.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/GML/v3.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.Atom\n+ * Read/write Atom feeds. Create a new instance with the\n+ * <OpenLayers.Format.AtomFeed> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs. Properties\n+ * of this object should not be set individually. Read-only. All\n+ * XML subclasses should have their own namespaces object. Use\n+ * <setNamespace> to add or set a namespace alias after construction.\n+ */\n+ namespaces: {\n+ atom: \"http://www.w3.org/2005/Atom\",\n+ georss: \"http://www.georss.org/georss\"\n+ },\n+\n+ /**\n+ * APIProperty: feedTitle\n+ * {String} Atom feed elements require a title. Default is \"untitled\".\n+ */\n+ feedTitle: \"untitled\",\n+\n+ /**\n+ * APIProperty: defaultEntryTitle\n+ * {String} Atom entry elements require a title. In cases where one is\n+ * not provided in the feature attributes, this will be used. Default\n+ * is \"untitled\".\n+ */\n+ defaultEntryTitle: \"untitled\",\n+\n+ /**\n+ * Property: gmlParse\n+ * {Object} GML Format object for parsing features\n+ * Non-API and only created if necessary\n+ */\n+ gmlParser: null,\n+\n+ /**\n+ * APIProperty: xy\n+ * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)\n+ * For GeoRSS the default is (y,x), therefore: false\n+ */\n+ xy: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.AtomEntry\n+ * Create a new parser for Atom.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Return a list of features from an Atom feed or entry document.\n+ \n+ * Parameters:\n+ * doc - {Element} or {String}\n+ *\n+ * Returns:\n+ * Array({<OpenLayers.Feature.Vector>})\n+ */\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ }\n+ return this.parseFeatures(doc);\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Serialize or more feature nodes to Atom documents.\n+ *\n+ * Parameters:\n+ * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>})\n+ *\n+ * Returns:\n+ * {String} an Atom entry document if passed one feature node, or a feed\n+ * document if passed an array of feature nodes.\n+ */\n+ write: function(features) {\n+ var doc;\n+ if (OpenLayers.Util.isArray(features)) {\n+ doc = this.createElementNSPlus(\"atom:feed\");\n+ doc.appendChild(\n+ this.createElementNSPlus(\"atom:title\", {\n+ value: this.feedTitle\n+ })\n+ );\n+ for (var i = 0, ii = features.length; i < ii; i++) {\n+ doc.appendChild(this.buildEntryNode(features[i]));\n+ }\n+ } else {\n+ doc = this.buildEntryNode(features);\n+ }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [doc]);\n+ },\n+\n+ /**\n+ * Method: buildContentNode\n+ *\n+ * Parameters:\n+ * content - {Object}\n+ *\n+ * Returns:\n+ * {DOMElement} an Atom content node.\n+ *\n+ * TODO: types other than text.\n+ */\n+ buildContentNode: function(content) {\n+ var node = this.createElementNSPlus(\"atom:content\", {\n+ attributes: {\n+ type: content.type || null\n+ }\n+ });\n+ if (content.src) {\n+ node.setAttribute(\"src\", content.src);\n+ } else {\n+ if (content.type == \"text\" || content.type == null) {\n+ node.appendChild(\n+ this.createTextNode(content.value)\n+ );\n+ } else if (content.type == \"html\") {\n+ if (typeof content.value != \"string\") {\n+ throw \"HTML content must be in form of an escaped string\";\n+ }\n+ node.appendChild(\n+ this.createTextNode(content.value)\n+ );\n+ } else if (content.type == \"xhtml\") {\n+ node.appendChild(content.value);\n+ } else if (content.type == \"xhtml\" ||\n+ content.type.match(/(\\+|\\/)xml$/)) {\n+ node.appendChild(content.value);\n+ } else { // MUST be a valid Base64 encoding\n+ node.appendChild(\n+ this.createTextNode(content.value)\n+ );\n+ }\n+ }\n+ return node;\n+ },\n+\n+ /**\n+ * Method: buildEntryNode\n+ * Build an Atom entry node from a feature object.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {DOMElement} an Atom entry node.\n+ *\n+ * These entries are geared for publication using AtomPub.\n+ *\n+ * TODO: support extension elements\n+ */\n+ buildEntryNode: function(feature) {\n+ var attrib = feature.attributes;\n+ var atomAttrib = attrib.atom || {};\n+ var entryNode = this.createElementNSPlus(\"atom:entry\");\n+\n+ // atom:author\n+ if (atomAttrib.authors) {\n+ var authors = OpenLayers.Util.isArray(atomAttrib.authors) ?\n+ atomAttrib.authors : [atomAttrib.authors];\n+ for (var i = 0, ii = authors.length; i < ii; i++) {\n+ entryNode.appendChild(\n+ this.buildPersonConstructNode(\n+ \"author\", authors[i]\n+ )\n+ );\n+ }\n+ }\n+\n+ // atom:category\n+ if (atomAttrib.categories) {\n+ var categories = OpenLayers.Util.isArray(atomAttrib.categories) ?\n+ atomAttrib.categories : [atomAttrib.categories];\n+ var category;\n+ for (var i = 0, ii = categories.length; i < ii; i++) {\n+ category = categories[i];\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:category\", {\n+ attributes: {\n+ term: category.term,\n+ scheme: category.scheme || null,\n+ label: category.label || null\n+ }\n+ })\n+ );\n+ }\n+ }\n+\n+ // atom:content\n+ if (atomAttrib.content) {\n+ entryNode.appendChild(this.buildContentNode(atomAttrib.content));\n+ }\n+\n+ // atom:contributor\n+ if (atomAttrib.contributors) {\n+ var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ?\n+ atomAttrib.contributors : [atomAttrib.contributors];\n+ for (var i = 0, ii = contributors.length; i < ii; i++) {\n+ entryNode.appendChild(\n+ this.buildPersonConstructNode(\n+ \"contributor\",\n+ contributors[i]\n+ )\n+ );\n+ }\n+ }\n+\n+ // atom:id\n+ if (feature.fid) {\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:id\", {\n+ value: feature.fid\n+ })\n+ );\n+ }\n+\n+ // atom:link\n+ if (atomAttrib.links) {\n+ var links = OpenLayers.Util.isArray(atomAttrib.links) ?\n+ atomAttrib.links : [atomAttrib.links];\n+ var link;\n+ for (var i = 0, ii = links.length; i < ii; i++) {\n+ link = links[i];\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:link\", {\n+ attributes: {\n+ href: link.href,\n+ rel: link.rel || null,\n+ type: link.type || null,\n+ hreflang: link.hreflang || null,\n+ title: link.title || null,\n+ length: link.length || null\n+ }\n+ })\n+ );\n+ }\n+ }\n+\n+ // atom:published\n+ if (atomAttrib.published) {\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:published\", {\n+ value: atomAttrib.published\n+ })\n+ );\n+ }\n+\n+ // atom:rights\n+ if (atomAttrib.rights) {\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:rights\", {\n+ value: atomAttrib.rights\n+ })\n+ );\n+ }\n \n- CLASS_NAME: \"OpenLayers.Format.SOSGetObservation\"\n+ // atom:source not implemented\n \n-});\n-/* ======================================================================\n- OpenLayers/Format/Context.js\n- ====================================================================== */\n+ // atom:summary\n+ if (atomAttrib.summary || attrib.description) {\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:summary\", {\n+ value: atomAttrib.summary || attrib.description\n+ })\n+ );\n+ }\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // atom:title\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:title\", {\n+ value: atomAttrib.title || attrib.title || this.defaultEntryTitle\n+ })\n+ );\n \n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n+ // atom:updated\n+ if (atomAttrib.updated) {\n+ entryNode.appendChild(\n+ this.createElementNSPlus(\"atom:updated\", {\n+ value: atomAttrib.updated\n+ })\n+ );\n+ }\n \n-/**\n- * Class: OpenLayers.Format.Context\n- * Base class for both Format.WMC and Format.OWSContext\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ // georss:where\n+ if (feature.geometry) {\n+ var whereNode = this.createElementNSPlus(\"georss:where\");\n+ whereNode.appendChild(\n+ this.buildGeometryNode(feature.geometry)\n+ );\n+ entryNode.appendChild(whereNode);\n+ }\n+\n+ return entryNode;\n+ },\n \n /**\n- * Property: layerOptions\n- * {Object} Default options for layers created by the parser. These\n- * options are overridden by the options which are read from the\n- * capabilities document.\n+ * Method: initGmlParser\n+ * Creates a GML parser.\n */\n- layerOptions: null,\n+ initGmlParser: function() {\n+ this.gmlParser = new OpenLayers.Format.GML.v3({\n+ xy: this.xy,\n+ featureNS: \"http://example.com#feature\",\n+ internalProjection: this.internalProjection,\n+ externalProjection: this.externalProjection\n+ });\n+ },\n \n /**\n- * Property: layerParams\n- * {Object} Default parameters for layers created by the parser. This\n- * can be used e.g. to override DEFAULT_PARAMS for \n- * OpenLayers.Layer.WMS.\n+ * Method: buildGeometryNode\n+ * builds a GeoRSS node with a given geometry\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {DOMElement} A gml node.\n */\n- layerParams: null,\n+ buildGeometryNode: function(geometry) {\n+ if (!this.gmlParser) {\n+ this.initGmlParser();\n+ }\n+ var node = this.gmlParser.writeNode(\"feature:_geometry\", geometry);\n+ return node.firstChild;\n+ },\n \n /**\n- * Constructor: OpenLayers.Format.Context\n- * Create a new parser for Context documents.\n+ * Method: buildPersonConstructNode\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * name - {String}\n+ * value - {Object}\n+ *\n+ * Returns:\n+ * {DOMElement} an Atom person construct node.\n+ *\n+ * Example:\n+ * >>> buildPersonConstructNode(\"author\", {name: \"John Smith\"})\n+ * {<author><name>John Smith</name></author>}\n+ *\n+ * TODO: how to specify extension elements? Add to the oNames array?\n */\n+ buildPersonConstructNode: function(name, value) {\n+ var oNames = [\"uri\", \"email\"];\n+ var personNode = this.createElementNSPlus(\"atom:\" + name);\n+ personNode.appendChild(\n+ this.createElementNSPlus(\"atom:name\", {\n+ value: value.name\n+ })\n+ );\n+ for (var i = 0, ii = oNames.length; i < ii; i++) {\n+ if (value[oNames[i]]) {\n+ personNode.appendChild(\n+ this.createElementNSPlus(\"atom:\" + oNames[i], {\n+ value: value[oNames[i]]\n+ })\n+ );\n+ }\n+ }\n+ return personNode;\n+ },\n \n /**\n- * APIMethod: read\n- * Read Context data from a string, and return an object with map\n- * properties and a list of layers.\n+ * Method: getFirstChildValue\n *\n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- * options - {Object} The options object must contain a map property. If\n- * the map property is a string, it must be the id of a dom element\n- * where the new map will be placed. If the map property is an\n- * <OpenLayers.Map>, the layers from the context document will be added\n- * to the map.\n+ * node - {DOMElement}\n+ * nsuri - {String} Child node namespace uri (\"*\" for any).\n+ * name - {String} Child node name.\n+ * def - {String} Optional string default to return if no child found.\n *\n * Returns:\n- * {<OpenLayers.Map>} A map based on the context.\n+ * {String} The value of the first child with the given tag name. Returns\n+ * default value or empty string if none found.\n */\n- read: function(data, options) {\n- var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,\n- arguments);\n- var map;\n- if (options && options.map) {\n- this.context = context;\n- if (options.map instanceof OpenLayers.Map) {\n- map = this.mergeContextToMap(context, options.map);\n- } else {\n- var mapOptions = options.map;\n- if (OpenLayers.Util.isElement(mapOptions) ||\n- typeof mapOptions == \"string\") {\n- // we assume mapOptions references a div\n- // element\n- mapOptions = {\n- div: mapOptions\n- };\n- }\n- map = this.contextToMap(context, mapOptions);\n- }\n+ getFirstChildValue: function(node, nsuri, name, def) {\n+ var value;\n+ var nodes = this.getElementsByTagNameNS(node, nsuri, name);\n+ if (nodes && nodes.length > 0) {\n+ value = this.getChildValue(nodes[0], def);\n } else {\n- // not documented as part of the API, provided as a non-API option\n- map = context;\n+ value = def;\n }\n- return map;\n+ return value;\n },\n \n /**\n- * Method: getLayerFromContext\n- * Create a WMS layer from a layerContext object.\n+ * Method: parseFeature\n+ * Parse feature from an Atom entry node..\n *\n * Parameters:\n- * layerContext - {Object} An object representing a WMS layer.\n+ * node - {DOMElement} An Atom entry or feed node.\n *\n * Returns:\n- * {<OpenLayers.Layer.WMS>} A WMS layer.\n+ * {<OpenLayers.Feature.Vector>}\n */\n- getLayerFromContext: function(layerContext) {\n- var i, len;\n- // fill initial options object from layerContext\n- var options = {\n- queryable: layerContext.queryable, //keep queryable for api compatibility\n- visibility: layerContext.visibility,\n- maxExtent: layerContext.maxExtent,\n- metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n- styles: layerContext.styles,\n- formats: layerContext.formats,\n- \"abstract\": layerContext[\"abstract\"],\n- dataURL: layerContext.dataURL\n- }),\n- numZoomLevels: layerContext.numZoomLevels,\n- units: layerContext.units,\n- isBaseLayer: layerContext.isBaseLayer,\n- opacity: layerContext.opacity,\n- displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n- singleTile: layerContext.singleTile,\n- tileSize: (layerContext.tileSize) ?\n- new OpenLayers.Size(\n- layerContext.tileSize.width,\n- layerContext.tileSize.height\n- ) : undefined,\n- minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n- maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n- srs: layerContext.srs,\n- dimensions: layerContext.dimensions,\n- metadataURL: layerContext.metadataURL\n- };\n- if (this.layerOptions) {\n- OpenLayers.Util.applyDefaults(options, this.layerOptions);\n+ parseFeature: function(node) {\n+ var atomAttrib = {};\n+ var value = null;\n+ var nodes = null;\n+ var attval = null;\n+ var atomns = this.namespaces.atom;\n+\n+ // atomAuthor*\n+ this.parsePersonConstructs(node, \"author\", atomAttrib);\n+\n+ // atomCategory*\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"category\");\n+ if (nodes.length > 0) {\n+ atomAttrib.categories = [];\n+ }\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ value = {};\n+ value.term = nodes[i].getAttribute(\"term\");\n+ attval = nodes[i].getAttribute(\"scheme\");\n+ if (attval) {\n+ value.scheme = attval;\n+ }\n+ attval = nodes[i].getAttribute(\"label\");\n+ if (attval) {\n+ value.label = attval;\n+ }\n+ atomAttrib.categories.push(value);\n }\n \n- var params = {\n- layers: layerContext.name,\n- transparent: layerContext.transparent,\n- version: layerContext.version\n- };\n- if (layerContext.formats && layerContext.formats.length > 0) {\n- // set default value for params if current attribute is not positionned\n- params.format = layerContext.formats[0].value;\n- for (i = 0, len = layerContext.formats.length; i < len; i++) {\n- var format = layerContext.formats[i];\n- if (format.current == true) {\n- params.format = format.value;\n- break;\n+ // atomContent?\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"content\");\n+ if (nodes.length > 0) {\n+ value = {};\n+ attval = nodes[0].getAttribute(\"type\");\n+ if (attval) {\n+ value.type = attval;\n+ }\n+ attval = nodes[0].getAttribute(\"src\");\n+ if (attval) {\n+ value.src = attval;\n+ } else {\n+ if (value.type == \"text\" ||\n+ value.type == \"html\" ||\n+ value.type == null) {\n+ value.value = this.getFirstChildValue(\n+ node,\n+ atomns,\n+ \"content\",\n+ null\n+ );\n+ } else if (value.type == \"xhtml\" ||\n+ value.type.match(/(\\+|\\/)xml$/)) {\n+ value.value = this.getChildEl(nodes[0]);\n+ } else { // MUST be base64 encoded\n+ value.value = this.getFirstChildValue(\n+ node,\n+ atomns,\n+ \"content\",\n+ null\n+ );\n }\n+ atomAttrib.content = value;\n }\n }\n- if (layerContext.styles && layerContext.styles.length > 0) {\n- for (i = 0, len = layerContext.styles.length; i < len; i++) {\n- var style = layerContext.styles[i];\n- if (style.current == true) {\n- // three style types to consider\n- // 1) linked SLD\n- // 2) inline SLD\n- // 3) named style\n- if (style.href) {\n- params.sld = style.href;\n- } else if (style.body) {\n- params.sld_body = style.body;\n- } else {\n- params.styles = style.name;\n- }\n- break;\n+\n+ // atomContributor*\n+ this.parsePersonConstructs(node, \"contributor\", atomAttrib);\n+\n+ // atomId\n+ atomAttrib.id = this.getFirstChildValue(node, atomns, \"id\", null);\n+\n+ // atomLink*\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"link\");\n+ if (nodes.length > 0) {\n+ atomAttrib.links = new Array(nodes.length);\n+ }\n+ var oAtts = [\"rel\", \"type\", \"hreflang\", \"title\", \"length\"];\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ value = {};\n+ value.href = nodes[i].getAttribute(\"href\");\n+ for (var j = 0, jj = oAtts.length; j < jj; j++) {\n+ attval = nodes[i].getAttribute(oAtts[j]);\n+ if (attval) {\n+ value[oAtts[j]] = attval;\n }\n }\n+ atomAttrib.links[i] = value;\n }\n- if (this.layerParams) {\n- OpenLayers.Util.applyDefaults(params, this.layerParams);\n+\n+ // atomPublished?\n+ value = this.getFirstChildValue(node, atomns, \"published\", null);\n+ if (value) {\n+ atomAttrib.published = value;\n }\n \n- var layer = null;\n- var service = layerContext.service;\n- if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n- options.strategies = [new OpenLayers.Strategy.BBOX()];\n- options.protocol = new OpenLayers.Protocol.WFS({\n- url: layerContext.url,\n- // since we do not know featureNS, let the protocol\n- // determine it automagically using featurePrefix\n- featurePrefix: layerContext.name.split(\":\")[0],\n- featureType: layerContext.name.split(\":\").pop()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n- // use a vector layer with an HTTP Protcol and a Fixed strategy\n- options.strategies = [new OpenLayers.Strategy.Fixed()];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.KML()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n- // use a vector layer with a HTTP Protocol and a Fixed strategy\n- options.strategies = [new OpenLayers.Strategy.Fixed()];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.GML()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (layerContext.features) {\n- // inline GML or KML features\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- layer.addFeatures(layerContext.features);\n- } else if (layerContext.categoryLayer !== true) {\n- layer = new OpenLayers.Layer.WMS(\n- layerContext.title || layerContext.name,\n- layerContext.url,\n- params,\n- options\n- );\n+ // atomRights?\n+ value = this.getFirstChildValue(node, atomns, \"rights\", null);\n+ if (value) {\n+ atomAttrib.rights = value;\n }\n- return layer;\n+\n+ // atomSource? -- not implemented\n+\n+ // atomSummary?\n+ value = this.getFirstChildValue(node, atomns, \"summary\", null);\n+ if (value) {\n+ atomAttrib.summary = value;\n+ }\n+\n+ // atomTitle\n+ atomAttrib.title = this.getFirstChildValue(\n+ node, atomns, \"title\", null\n+ );\n+\n+ // atomUpdated\n+ atomAttrib.updated = this.getFirstChildValue(\n+ node, atomns, \"updated\", null\n+ );\n+\n+ var featureAttrib = {\n+ title: atomAttrib.title,\n+ description: atomAttrib.summary,\n+ atom: atomAttrib\n+ };\n+ var geometry = this.parseLocations(node)[0];\n+ var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);\n+ feature.fid = atomAttrib.id;\n+ return feature;\n },\n \n /**\n- * Method: getLayersFromContext\n- * Create an array of layers from an array of layerContext objects.\n+ * Method: parseFeatures\n+ * Return features from an Atom entry or feed.\n *\n * Parameters:\n- * layersContext - {Array(Object)} An array of objects representing layers.\n+ * node - {DOMElement} An Atom entry or feed node.\n *\n * Returns:\n- * {Array(<OpenLayers.Layer>)} An array of layers.\n+ * Array({<OpenLayers.Feature.Vector>})\n */\n- getLayersFromContext: function(layersContext) {\n- var layers = [];\n- for (var i = 0, len = layersContext.length; i < len; i++) {\n- var layer = this.getLayerFromContext(layersContext[i]);\n- if (layer !== null) {\n- layers.push(layer);\n- }\n+ parseFeatures: function(node) {\n+ var features = [];\n+ var entries = this.getElementsByTagNameNS(\n+ node, this.namespaces.atom, \"entry\"\n+ );\n+ if (entries.length == 0) {\n+ entries = [node];\n }\n- return layers;\n+ for (var i = 0, ii = entries.length; i < ii; i++) {\n+ features.push(this.parseFeature(entries[i]));\n+ }\n+ return features;\n },\n \n /**\n- * Method: contextToMap\n- * Create a map given a context object.\n+ * Method: parseLocations\n+ * Parse the locations from an Atom entry or feed.\n *\n * Parameters:\n- * context - {Object} The context object.\n- * options - {Object} Default map options.\n+ * node - {DOMElement} An Atom entry or feed node.\n *\n * Returns:\n- * {<OpenLayers.Map>} A map based on the context object.\n+ * Array({<OpenLayers.Geometry>})\n */\n- contextToMap: function(context, options) {\n- options = OpenLayers.Util.applyDefaults({\n- maxExtent: context.maxExtent,\n- projection: context.projection,\n- units: context.units\n- }, options);\n+ parseLocations: function(node) {\n+ var georssns = this.namespaces.georss;\n \n- if (options.maxExtent) {\n- options.maxResolution =\n- options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;\n+ var locations = {\n+ components: []\n+ };\n+ var where = this.getElementsByTagNameNS(node, georssns, \"where\");\n+ if (where && where.length > 0) {\n+ if (!this.gmlParser) {\n+ this.initGmlParser();\n+ }\n+ for (var i = 0, ii = where.length; i < ii; i++) {\n+ this.gmlParser.readChildNodes(where[i], locations);\n+ }\n }\n \n- var metadata = {\n- contactInformation: context.contactInformation,\n- \"abstract\": context[\"abstract\"],\n- keywords: context.keywords,\n- logo: context.logo,\n- descriptionURL: context.descriptionURL\n- };\n+ var components = locations.components;\n+ var point = this.getElementsByTagNameNS(node, georssns, \"point\");\n+ if (point && point.length > 0) {\n+ for (var i = 0, ii = point.length; i < ii; i++) {\n+ var xy = OpenLayers.String.trim(\n+ point[i].firstChild.nodeValue\n+ ).split(/\\s+/);\n+ if (xy.length != 2) {\n+ xy = OpenLayers.String.trim(\n+ point[i].firstChild.nodeValue\n+ ).split(/\\s*,\\s*/);\n+ }\n+ components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]));\n+ }\n+ }\n \n- options.metadata = metadata;\n+ var line = this.getElementsByTagNameNS(node, georssns, \"line\");\n+ if (line && line.length > 0) {\n+ var coords;\n+ var p;\n+ var points;\n+ for (var i = 0, ii = line.length; i < ii; i++) {\n+ coords = OpenLayers.String.trim(\n+ line[i].firstChild.nodeValue\n+ ).split(/\\s+/);\n+ points = [];\n+ for (var j = 0, jj = coords.length; j < jj; j += 2) {\n+ p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n+ points.push(p);\n+ }\n+ components.push(\n+ new OpenLayers.Geometry.LineString(points)\n+ );\n+ }\n+ }\n \n- var map = new OpenLayers.Map(options);\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- map.setCenter(\n- context.bounds.getCenterLonLat(),\n- map.getZoomForExtent(context.bounds, true)\n- );\n- return map;\n- },\n+ var polygon = this.getElementsByTagNameNS(node, georssns, \"polygon\");\n+ if (polygon && polygon.length > 0) {\n+ var coords;\n+ var p;\n+ var points;\n+ for (var i = 0, ii = polygon.length; i < ii; i++) {\n+ coords = OpenLayers.String.trim(\n+ polygon[i].firstChild.nodeValue\n+ ).split(/\\s+/);\n+ points = [];\n+ for (var j = 0, jj = coords.length; j < jj; j += 2) {\n+ p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n+ points.push(p);\n+ }\n+ components.push(\n+ new OpenLayers.Geometry.Polygon(\n+ [new OpenLayers.Geometry.LinearRing(points)]\n+ )\n+ );\n+ }\n+ }\n \n- /**\n- * Method: mergeContextToMap\n- * Add layers from a context object to a map.\n- *\n- * Parameters:\n- * context - {Object} The context object.\n- * map - {<OpenLayers.Map>} The map.\n- *\n- * Returns:\n- * {<OpenLayers.Map>} The same map with layers added.\n- */\n- mergeContextToMap: function(context, map) {\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- return map;\n+ if (this.internalProjection && this.externalProjection) {\n+ for (var i = 0, ii = components.length; i < ii; i++) {\n+ if (components[i]) {\n+ components[i].transform(\n+ this.externalProjection,\n+ this.internalProjection\n+ );\n+ }\n+ }\n+ }\n+\n+ return components;\n },\n \n /**\n- * APIMethod: write\n- * Write a context document given a map.\n+ * Method: parsePersonConstruct\n+ * Parse Atom person constructs from an Atom entry node.\n *\n * Parameters:\n- * obj - {<OpenLayers.Map> | Object} A map or context object.\n- * options - {Object} Optional configuration object.\n+ * node - {DOMElement} An Atom entry or feed node.\n+ * name - {String} Construcy name (\"author\" or \"contributor\")\n+ * data = {Object} Object in which to put parsed persons.\n *\n * Returns:\n- * {String} A context document string.\n+ * An {Object}.\n */\n- write: function(obj, options) {\n- obj = this.toContext(obj);\n- return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,\n- arguments);\n+ parsePersonConstructs: function(node, name, data) {\n+ var persons = [];\n+ var atomns = this.namespaces.atom;\n+ var nodes = this.getElementsByTagNameNS(node, atomns, name);\n+ var oAtts = [\"uri\", \"email\"];\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ var value = {};\n+ value.name = this.getFirstChildValue(\n+ nodes[i],\n+ atomns,\n+ \"name\",\n+ null\n+ );\n+ for (var j = 0, jj = oAtts.length; j < jj; j++) {\n+ var attval = this.getFirstChildValue(\n+ nodes[i],\n+ atomns,\n+ oAtts[j],\n+ null);\n+ if (attval) {\n+ value[oAtts[j]] = attval;\n+ }\n+ }\n+ persons.push(value);\n+ }\n+ if (persons.length > 0) {\n+ data[name + \"s\"] = persons;\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Format.Context\"\n+ CLASS_NAME: \"OpenLayers.Format.Atom\"\n });\n-\n-/**\n- * Constant: OpenLayers.Format.Context.serviceTypes\n- * Enumeration for service types\n- */\n-OpenLayers.Format.Context.serviceTypes = {\n- \"WMS\": \"urn:ogc:serviceType:WMS\",\n- \"WFS\": \"urn:ogc:serviceType:WFS\",\n- \"WCS\": \"urn:ogc:serviceType:WCS\",\n- \"GML\": \"urn:ogc:serviceType:GML\",\n- \"SLD\": \"urn:ogc:serviceType:SLD\",\n- \"FES\": \"urn:ogc:serviceType:FES\",\n- \"KML\": \"urn:ogc:serviceType:KML\"\n-};\n /* ======================================================================\n OpenLayers/Format/WMC.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -46901,3154 +38269,243 @@\n }\n }\n },\n \n CLASS_NAME: \"OpenLayers.Format.OSM\"\n });\n /* ======================================================================\n- OpenLayers/Format/OWSContext.js\n+ OpenLayers/Format/WMTSCapabilities.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/Context.js\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n */\n \n /**\n- * Class: OpenLayers.Format.OWSContext\n- * Read and write OWS Context documents. OWS Context documents are a \n- * preliminary OGC (Open Geospatial Consortium) standard for storing the \n- * state of a web mapping application. In a way it is the successor to\n- * Web Map Context (WMC), since it is more generic and more types of layers\n- * can be stored. Also, nesting of layers is supported since version 0.3.1.\n- * For more information see: http://www.ogcnetwork.net/context\n+ * Class: OpenLayers.Format.WMTSCapabilities\n+ * Read WMTS Capabilities.\n *\n * Inherits from:\n- * - <OpenLayers.Format.Context>\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n */\n-OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n+OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n \n /**\n * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"0.3.1\".\n- */\n- defaultVersion: \"0.3.1\",\n-\n- /**\n- * Constructor: OpenLayers.Format.OWSContext\n- * Create a new parser for OWS Context documents.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Method: getVersion\n- * Returns the version to use. Subclasses can override this function\n- * if a different version detection is needed.\n- *\n- * Parameters:\n- * root - {DOMElement}\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {String} The version to use.\n- */\n- getVersion: function(root, options) {\n- var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(\n- this, arguments);\n- // 0.3.1 is backwards compatible with 0.3.0\n- if (version === \"0.3.0\") {\n- version = this.defaultVersion;\n- }\n- return version;\n- },\n-\n- /**\n- * Method: toContext\n- * Create a context object free from layer given a map or a\n- * context object.\n- *\n- * Parameters:\n- * obj - {<OpenLayers.Map> | Object} The map or context.\n- *\n- * Returns:\n- * {Object} A context object.\n- */\n- toContext: function(obj) {\n- var context = {};\n- if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n- context.bounds = obj.getExtent();\n- context.maxExtent = obj.maxExtent;\n- context.projection = obj.projection;\n- context.size = obj.getSize();\n- context.layers = obj.layers;\n- }\n- return context;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/EncodedPolyline.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.EncodedPolyline\n- * Class for reading and writing encoded polylines. Create a new instance\n- * with the <OpenLayers.Format.EncodedPolyline> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * APIProperty: geometryType\n- * {String} Geometry type to output. One of: linestring (default),\n- * linearring, point, multipoint or polygon. If the geometryType is\n- * point, only the first point of the string is returned.\n- */\n- geometryType: \"linestring\",\n-\n- /**\n- * Constructor: OpenLayers.Format.EncodedPolyline\n- * Create a new parser for encoded polylines\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance\n- *\n- * Returns:\n- * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Deserialize an encoded polyline string and return a vector feature.\n- *\n- * Parameters:\n- * encoded - {String} An encoded polyline string\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A vector feature with a linestring.\n- */\n- read: function(encoded) {\n- var geomType;\n- if (this.geometryType == \"linestring\")\n- geomType = OpenLayers.Geometry.LineString;\n- else if (this.geometryType == \"linearring\")\n- geomType = OpenLayers.Geometry.LinearRing;\n- else if (this.geometryType == \"multipoint\")\n- geomType = OpenLayers.Geometry.MultiPoint;\n- else if (this.geometryType != \"point\" && this.geometryType != \"polygon\")\n- return null;\n-\n- var flatPoints = this.decodeDeltas(encoded, 2);\n- var flatPointsLength = flatPoints.length;\n-\n- var pointGeometries = [];\n- for (var i = 0; i + 1 < flatPointsLength;) {\n- var y = flatPoints[i++],\n- x = flatPoints[i++];\n- pointGeometries.push(new OpenLayers.Geometry.Point(x, y));\n- }\n-\n-\n- if (this.geometryType == \"point\")\n- return new OpenLayers.Feature.Vector(\n- pointGeometries[0]\n- );\n-\n- if (this.geometryType == \"polygon\")\n- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Polygon([\n- new OpenLayers.Geometry.LinearRing(pointGeometries)\n- ])\n- );\n-\n- return new OpenLayers.Feature.Vector(\n- new geomType(pointGeometries)\n- );\n- },\n-\n- /**\n- * APIMethod: decode\n- * Deserialize an encoded string and return an array of n-dimensional\n- * points.\n- *\n- * Parameters:\n- * encoded - {String} An encoded string\n- * dims - {int} The dimension of the points that are returned\n- *\n- * Returns:\n- * {Array(Array(int))} An array containing n-dimensional arrays of\n- * coordinates.\n- */\n- decode: function(encoded, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = this.decodeDeltas(encoded, dims, factor);\n- var flatPointsLength = flatPoints.length;\n-\n- var points = [];\n- for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n- var point = [];\n-\n- for (var dim = 0; dim < dims; ++dim) {\n- point.push(flatPoints[i++])\n- }\n-\n- points.push(point);\n- }\n-\n- return points;\n- },\n-\n- /**\n- * APIMethod: write\n- * Serialize a feature or array of features into a WKT string.\n- *\n- * Parameters:\n- * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of\n- * features\n- *\n- * Returns:\n- * {String} The WKT string representation of the input geometries\n- */\n- write: function(features) {\n- var feature;\n- if (features.constructor == Array)\n- feature = features[0];\n- else\n- feature = features;\n-\n- var geometry = feature.geometry;\n- var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n-\n- var pointGeometries;\n- if (type == \"point\")\n- pointGeometries = new Array(geometry);\n- else if (type == \"linestring\" ||\n- type == \"linearring\" ||\n- type == \"multipoint\")\n- pointGeometries = geometry.components;\n- else if (type == \"polygon\")\n- pointGeometries = geometry.components[0].components;\n- else\n- return null;\n-\n- var flatPoints = [];\n-\n- var pointGeometriesLength = pointGeometries.length;\n- for (var i = 0; i < pointGeometriesLength; ++i) {\n- var pointGeometry = pointGeometries[i];\n- flatPoints.push(pointGeometry.y);\n- flatPoints.push(pointGeometry.x);\n- }\n-\n- return this.encodeDeltas(flatPoints, 2);\n- },\n-\n- /**\n- * APIMethod: encode\n- * Serialize an array of n-dimensional points and return an encoded string\n- *\n- * Parameters:\n- * points - {Array(Array(int))} An array containing n-dimensional\n- * arrays of coordinates\n- * dims - {int} The dimension of the points that should be read\n- *\n- * Returns:\n- * {String} An encoded string\n- */\n- encode: function(points, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = [];\n-\n- var pointsLength = points.length;\n- for (var i = 0; i < pointsLength; ++i) {\n- var point = points[i];\n-\n- for (var dim = 0; dim < dims; ++dim) {\n- flatPoints.push(point[dim]);\n- }\n- }\n-\n- return this.encodeDeltas(flatPoints, dims, factor);\n- },\n-\n- /**\n- * APIMethod: encodeDeltas\n- * Encode a list of n-dimensional points and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of n-dimensional points.\n- * dimension - {number} The dimension of the points in the list.\n- * opt_factor - {number=} The factor by which the numbers will be\n- * multiplied. The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeDeltas: function(numbers, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n-\n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0;\n- }\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- var num = numbers[i];\n- var delta = num - lastNumbers[d];\n- lastNumbers[d] = num;\n-\n- numbers[i] = delta;\n- }\n- }\n-\n- return this.encodeFloats(numbers, factor);\n- },\n-\n-\n- /**\n- * APIMethod: decodeDeltas\n- * Decode a list of n-dimensional points from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- * dimension - {number} The dimension of the points in the encoded string.\n- * opt_factor - {number=} The factor by which the resulting numbers will\n- * be divided.\n- *\n- * Returns:\n- * {Array.<number>} A list of n-dimensional points.\n- */\n- decodeDeltas: function(encoded, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n-\n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0;\n- }\n-\n- var numbers = this.decodeFloats(encoded, factor);\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- lastNumbers[d] += numbers[i];\n-\n- numbers[i] = lastNumbers[d];\n- }\n- }\n-\n- return numbers;\n- },\n-\n-\n- /**\n- * APIMethod: encodeFloats\n- * Encode a list of floating point numbers and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of floating point numbers.\n- * opt_factor - {number=} The factor by which the numbers will be\n- * multiplied. The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeFloats: function(numbers, opt_factor) {\n- var factor = opt_factor || 1e5;\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] = Math.round(numbers[i] * factor);\n- }\n-\n- return this.encodeSignedIntegers(numbers);\n- },\n-\n-\n- /**\n- * APIMethod: decodeFloats\n- * Decode a list of floating point numbers from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- * opt_factor - {number=} The factor by which the result will be divided.\n- *\n- * Returns:\n- * {Array.<number>} A list of floating point numbers.\n- */\n- decodeFloats: function(encoded, opt_factor) {\n- var factor = opt_factor || 1e5;\n-\n- var numbers = this.decodeSignedIntegers(encoded);\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] /= factor;\n- }\n-\n- return numbers;\n- },\n-\n-\n- /**\n- * APIMethod: encodeSignedIntegers\n- * Encode a list of signed integers and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of signed integers.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeSignedIntegers: function(numbers) {\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n-\n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~(signedNum);\n- }\n-\n- numbers[i] = signedNum;\n- }\n-\n- return this.encodeUnsignedIntegers(numbers);\n- },\n-\n-\n- /**\n- * APIMethod: decodeSignedIntegers\n- * Decode a list of signed integers from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {Array.<number>} A list of signed integers.\n- */\n- decodeSignedIntegers: function(encoded) {\n- var numbers = this.decodeUnsignedIntegers(encoded);\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n- numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);\n- }\n-\n- return numbers;\n- },\n-\n-\n- /**\n- * APIMethod: encodeUnsignedIntegers\n- * Encode a list of unsigned integers and return an encoded string\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of unsigned integers.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeUnsignedIntegers: function(numbers) {\n- var encoded = '';\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- encoded += this.encodeUnsignedInteger(numbers[i]);\n- }\n-\n- return encoded;\n- },\n-\n-\n- /**\n- * APIMethod: decodeUnsignedIntegers\n- * Decode a list of unsigned integers from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {Array.<number>} A list of unsigned integers.\n- */\n- decodeUnsignedIntegers: function(encoded) {\n- var numbers = [];\n-\n- var current = 0;\n- var shift = 0;\n-\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n-\n- current |= (b & 0x1f) << shift;\n-\n- if (b < 0x20) {\n- numbers.push(current);\n- current = 0;\n- shift = 0;\n- } else {\n- shift += 5;\n- }\n- }\n-\n- return numbers;\n- },\n-\n-\n- /**\n- * Method: encodeFloat\n- * Encode one single floating point number and return an encoded string\n- *\n- * Parameters:\n- * num - {number} Floating point number that should be encoded.\n- * opt_factor - {number=} The factor by which num will be multiplied.\n- * The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeFloat: function(num, opt_factor) {\n- num = Math.round(num * (opt_factor || 1e5));\n- return this.encodeSignedInteger(num);\n- },\n-\n-\n- /**\n- * Method: decodeFloat\n- * Decode one single floating point number from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- * opt_factor - {number=} The factor by which the result will be divided.\n- *\n- * Returns:\n- * {number} The decoded floating point number.\n- */\n- decodeFloat: function(encoded, opt_factor) {\n- var result = this.decodeSignedInteger(encoded);\n- return result / (opt_factor || 1e5);\n- },\n-\n-\n- /**\n- * Method: encodeSignedInteger\n- * Encode one single signed integer and return an encoded string\n- *\n- * Parameters:\n- * num - {number} Signed integer that should be encoded.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeSignedInteger: function(num) {\n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~(signedNum);\n- }\n-\n- return this.encodeUnsignedInteger(signedNum);\n- },\n-\n-\n- /**\n- * Method: decodeSignedInteger\n- * Decode one single signed integer from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {number} The decoded signed integer.\n- */\n- decodeSignedInteger: function(encoded) {\n- var result = this.decodeUnsignedInteger(encoded);\n- return ((result & 1) ? ~(result >> 1) : (result >> 1));\n- },\n-\n-\n- /**\n- * Method: encodeUnsignedInteger\n- * Encode one single unsigned integer and return an encoded string\n- *\n- * Parameters:\n- * num - {number} Unsigned integer that should be encoded.\n- *\n- * Returns:\n- * {string} The encoded string.\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n */\n- encodeUnsignedInteger: function(num) {\n- var value, encoded = '';\n- while (num >= 0x20) {\n- value = (0x20 | (num & 0x1f)) + 63;\n- encoded += (String.fromCharCode(value));\n- num >>= 5;\n- }\n- value = num + 63;\n- encoded += (String.fromCharCode(value));\n- return encoded;\n- },\n-\n+ defaultVersion: \"1.0.0\",\n \n /**\n- * Method: decodeUnsignedInteger\n- * Decode one single unsigned integer from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n+ * APIProperty: yx\n+ * {Object} Members in the yx object are used to determine if a CRS URN\n+ * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n+ * and values are boolean. By default, the following CRS URN are\n+ * assumed to correspond to a CRS with y,x axis order:\n *\n- * Returns:\n- * {number} The decoded unsigned integer.\n- */\n- decodeUnsignedInteger: function(encoded) {\n- var result = 0;\n- var shift = 0;\n-\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n-\n- result |= (b & 0x1f) << shift;\n-\n- if (b < 0x20)\n- break;\n-\n- shift += 5;\n- }\n-\n- return result;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMSGetFeatureInfo.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSGetFeatureInfo\n- * Class to read GetFeatureInfo responses from Web Mapping Services\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * APIProperty: layerIdentifier\n- * {String} All xml nodes containing this search criteria will populate an\n- * internal array of layer nodes.\n- */\n- layerIdentifier: '_layer',\n-\n- /**\n- * APIProperty: featureIdentifier\n- * {String} All xml nodes containing this search criteria will populate an \n- * internal array of feature nodes for each layer node found.\n- */\n- featureIdentifier: '_feature',\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n+ * * urn:ogc:def:crs:EPSG::4326\n */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n+ yx: {\n+ \"urn:ogc:def:crs:EPSG::4326\": true\n },\n \n /**\n- * Property: gmlFormat\n- * {<OpenLayers.Format.GML>} internal GML format for parsing geometries\n- * in msGMLOutput\n- */\n- gmlFormat: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSGetFeatureInfo\n- * Create a new parser for WMS GetFeatureInfo responses\n+ * Constructor: OpenLayers.Format.WMTSCapabilities\n+ * Create a new parser for WMTS capabilities.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n \n /**\n * APIMethod: read\n- * Read WMS GetFeatureInfo data from a string, and return an array of features\n+ * Read capabilities data from a string, and return information about\n+ * the service (offering and observedProperty mostly).\n *\n * Parameters:\n * data - {String} or {DOMElement} data to read/parse.\n *\n * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} An array of features.\n- */\n- read: function(data) {\n- var result;\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- var root = data.documentElement;\n- if (root) {\n- var scope = this;\n- var read = this[\"read_\" + root.nodeName];\n- if (read) {\n- result = read.call(this, root);\n- } else {\n- // fall-back to GML since this is a common output format for WMS\n- // GetFeatureInfo responses\n- result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);\n- }\n- } else {\n- result = data;\n- }\n- return result;\n- },\n-\n-\n- /**\n- * Method: read_msGMLOutput\n- * Parse msGMLOutput nodes.\n- *\n- * Parameters:\n- * data - {DOMElement}\n- *\n- * Returns:\n- * {Array}\n- */\n- read_msGMLOutput: function(data) {\n- var response = [];\n- var layerNodes = this.getSiblingNodesByTagCriteria(data,\n- this.layerIdentifier);\n- if (layerNodes) {\n- for (var i = 0, len = layerNodes.length; i < len; ++i) {\n- var node = layerNodes[i];\n- var layerName = node.nodeName;\n- if (node.prefix) {\n- layerName = layerName.split(':')[1];\n- }\n- var layerName = layerName.replace(this.layerIdentifier, '');\n- var featureNodes = this.getSiblingNodesByTagCriteria(node,\n- this.featureIdentifier);\n- if (featureNodes) {\n- for (var j = 0; j < featureNodes.length; j++) {\n- var featureNode = featureNodes[j];\n- var geomInfo = this.parseGeometry(featureNode);\n- var attributes = this.parseAttributes(featureNode);\n- var feature = new OpenLayers.Feature.Vector(geomInfo.geometry,\n- attributes, null);\n- feature.bounds = geomInfo.bounds;\n- feature.type = layerName;\n- response.push(feature);\n- }\n- }\n- }\n- }\n- return response;\n- },\n-\n- /**\n- * Method: read_FeatureInfoResponse\n- * Parse FeatureInfoResponse nodes.\n- *\n- * Parameters:\n- * data - {DOMElement}\n- *\n- * Returns:\n- * {Array}\n- */\n- read_FeatureInfoResponse: function(data) {\n- var response = [];\n- var featureNodes = this.getElementsByTagNameNS(data, '*',\n- 'FIELDS');\n-\n- for (var i = 0, len = featureNodes.length; i < len; i++) {\n- var featureNode = featureNodes[i];\n- var geom = null;\n-\n- // attributes can be actual attributes on the FIELDS tag, \n- // or FIELD children\n- var attributes = {};\n- var j;\n- var jlen = featureNode.attributes.length;\n- if (jlen > 0) {\n- for (j = 0; j < jlen; j++) {\n- var attribute = featureNode.attributes[j];\n- attributes[attribute.nodeName] = attribute.nodeValue;\n- }\n- } else {\n- var nodes = featureNode.childNodes;\n- for (j = 0, jlen = nodes.length; j < jlen; ++j) {\n- var node = nodes[j];\n- if (node.nodeType != 3) {\n- attributes[node.getAttribute(\"name\")] =\n- node.getAttribute(\"value\");\n- }\n- }\n- }\n-\n- response.push(\n- new OpenLayers.Feature.Vector(geom, attributes, null)\n- );\n- }\n- return response;\n- },\n-\n- /**\n- * Method: getSiblingNodesByTagCriteria\n- * Recursively searches passed xml node and all it's descendant levels for \n- * nodes whose tagName contains the passed search string. This returns an \n- * array of all sibling nodes which match the criteria from the highest \n- * hierarchial level from which a match is found.\n- * \n- * Parameters:\n- * node - {DOMElement} An xml node\n- * criteria - {String} Search string which will match some part of a tagName \n- * \n- * Returns:\n- * Array({DOMElement}) An array of sibling xml nodes\n- */\n- getSiblingNodesByTagCriteria: function(node, criteria) {\n- var nodes = [];\n- var children, tagName, n, matchNodes, child;\n- if (node && node.hasChildNodes()) {\n- children = node.childNodes;\n- n = children.length;\n-\n- for (var k = 0; k < n; k++) {\n- child = children[k];\n- while (child && child.nodeType != 1) {\n- child = child.nextSibling;\n- k++;\n- }\n- tagName = (child ? child.nodeName : '');\n- if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {\n- nodes.push(child);\n- } else {\n- matchNodes = this.getSiblingNodesByTagCriteria(\n- child, criteria);\n-\n- if (matchNodes.length > 0) {\n- (nodes.length == 0) ?\n- nodes = matchNodes: nodes.push(matchNodes);\n- }\n- }\n- }\n-\n- }\n- return nodes;\n- },\n-\n- /**\n- * Method: parseAttributes\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An attributes object.\n- * \n- * Notes:\n- * Assumes that attributes are direct child xml nodes of the passed node\n- * and contain only a single text node. \n- */\n- parseAttributes: function(node) {\n- var attributes = {};\n- if (node.nodeType == 1) {\n- var children = node.childNodes;\n- var n = children.length;\n- for (var i = 0; i < n; ++i) {\n- var child = children[i];\n- if (child.nodeType == 1) {\n- var grandchildren = child.childNodes;\n- var name = (child.prefix) ?\n- child.nodeName.split(\":\")[1] : child.nodeName;\n- if (grandchildren.length == 0) {\n- attributes[name] = null;\n- } else if (grandchildren.length == 1) {\n- var grandchild = grandchildren[0];\n- if (grandchild.nodeType == 3 ||\n- grandchild.nodeType == 4) {\n- var value = grandchild.nodeValue.replace(\n- this.regExes.trimSpace, \"\");\n- attributes[name] = value;\n- }\n- }\n- }\n- }\n- }\n- return attributes;\n- },\n-\n- /**\n- * Method: parseGeometry\n- * Parse the geometry and the feature bounds out of the node using \n- * Format.GML\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An object containing the geometry and the feature bounds\n- */\n- parseGeometry: function(node) {\n- // we need to use the old Format.GML parser since we do not know the \n- // geometry name\n- if (!this.gmlFormat) {\n- this.gmlFormat = new OpenLayers.Format.GML();\n- }\n- var feature = this.gmlFormat.parseFeature(node);\n- var geometry, bounds = null;\n- if (feature) {\n- geometry = feature.geometry && feature.geometry.clone();\n- bounds = feature.bounds && feature.bounds.clone();\n- feature.destroy();\n- }\n- return {\n- geometry: geometry,\n- bounds: bounds\n- };\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/CSWGetDomain.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CSWGetDomain\n- * Default version is 2.0.2.\n- *\n- * Returns:\n- * {<OpenLayers.Format>} A CSWGetDomain format of the given version.\n- */\n-OpenLayers.Format.CSWGetDomain = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Format.CSWGetDomain.DEFAULTS\n- );\n- var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetDomain version: \" + options.version;\n- }\n- return new cls(options);\n-};\n-\n-/**\n- * Constant: DEFAULTS\n- * {Object} Default properties for the CSWGetDomain format.\n- */\n-OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n- \"version\": \"2.0.2\"\n-};\n-/* ======================================================================\n- OpenLayers/Format/QueryStringFilter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Format.js\n- * @requires OpenLayers/Filter/Spatial.js\n- * @requires OpenLayers/Filter/Comparison.js\n- * @requires OpenLayers/Filter/Logical.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.QueryStringFilter\n- * Parser for reading a query string and creating a simple filter.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.QueryStringFilter = (function() {\n-\n- /** \n- * Map the OpenLayers.Filter.Comparison types to the operation strings of \n- * the protocol.\n+ * {Object} Info about the WMTS Capabilities\n */\n- var cmpToStr = {};\n- cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n- cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n- cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n \n /**\n- * Function: regex2value\n- * Convert the value from a regular expression string to a LIKE/ILIKE\n- * string known to the web service.\n+ * APIMethod: createLayer\n+ * Create a WMTS layer given a capabilities object.\n *\n * Parameters:\n- * value - {String} The regex string.\n- *\n- * Returns:\n- * {String} The converted string.\n- */\n- function regex2value(value) {\n-\n- // highly sensitive!! Do not change this without running the\n- // Protocol/HTTP.html unit tests\n-\n- // convert % to \\%\n- value = value.replace(/%/g, \"\\\\%\");\n-\n- // convert \\\\. to \\\\_ (\\\\.* occurences converted later)\n- value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n- return $1 ? $0 : \"\\\\\\\\_\";\n- });\n-\n- // convert \\\\.* to \\\\%\n- value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n-\n- // convert . to _ (\\. and .* occurences converted later)\n- value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n- return $1 || $2 ? $0 : \"_\";\n- });\n-\n- // convert .* to % (\\.* occurnces converted later)\n- value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n- return $1 ? $0 : \"%\";\n- });\n-\n- // convert \\. to .\n- value = value.replace(/\\\\\\./g, \".\");\n-\n- // replace \\* with * (watching out for \\\\*)\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"*\";\n- });\n-\n- return value;\n- }\n-\n- return OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * Property: wildcarded.\n- * {Boolean} If true percent signs are added around values\n- * read from LIKE filters, for example if the protocol\n- * read method is passed a LIKE filter whose property\n- * is \"foo\" and whose value is \"bar\" the string\n- * \"foo__ilike=%bar%\" will be sent in the query string;\n- * defaults to false.\n- */\n- wildcarded: false,\n-\n- /**\n- * APIProperty: srsInBBOX\n- * {Boolean} Include the SRS identifier in BBOX query string parameter. \n- * Default is false. If true and the layer has a projection object set,\n- * any BBOX filter will be serialized with a fifth item identifying the\n- * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n- */\n- srsInBBOX: false,\n-\n- /**\n- * APIMethod: write\n- * Serialize an <OpenLayers.Filter> objects using the \"simple\" filter syntax for \n- * query string parameters. This function must be called as a method of\n- * a protocol instance.\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>} filter to convert.\n- * params - {Object} The parameters object.\n- *\n- * Returns:\n- * {Object} The resulting parameters object.\n- */\n- write: function(filter, params) {\n- params = params || {};\n- var className = filter.CLASS_NAME;\n- var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n- switch (filterType) {\n- case \"Spatial\":\n- switch (filter.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- params.bbox = filter.value.toArray();\n- if (this.srsInBBOX && filter.projection) {\n- params.bbox.push(filter.projection.getCode());\n- }\n- break;\n- case OpenLayers.Filter.Spatial.DWITHIN:\n- params.tolerance = filter.distance;\n- // no break here\n- case OpenLayers.Filter.Spatial.WITHIN:\n- params.lon = filter.value.x;\n- params.lat = filter.value.y;\n- break;\n- default:\n- OpenLayers.Console.warn(\n- \"Unknown spatial filter type \" + filter.type);\n- }\n- break;\n- case \"Comparison\":\n- var op = cmpToStr[filter.type];\n- if (op !== undefined) {\n- var value = filter.value;\n- if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n- value = regex2value(value);\n- if (this.wildcarded) {\n- value = \"%\" + value + \"%\";\n- }\n- }\n- params[filter.property + \"__\" + op] = value;\n- params.queryable = params.queryable || [];\n- params.queryable.push(filter.property);\n- } else {\n- OpenLayers.Console.warn(\n- \"Unknown comparison filter type \" + filter.type);\n- }\n- break;\n- case \"Logical\":\n- if (filter.type === OpenLayers.Filter.Logical.AND) {\n- for (var i = 0, len = filter.filters.length; i < len; i++) {\n- params = this.write(filter.filters[i], params);\n- }\n- } else {\n- OpenLayers.Console.warn(\n- \"Unsupported logical filter type \" + filter.type);\n- }\n- break;\n- default:\n- OpenLayers.Console.warn(\"Unknown filter type \" + filterType);\n- }\n- return params;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n-\n- });\n-\n-\n-})();\n-/* ======================================================================\n- OpenLayers/Format/KML.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Date.js\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.KML\n- * Read/Write KML. Create a new instance with the <OpenLayers.Format.KML>\n- * constructor. \n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- kml: \"http://www.opengis.net/kml/2.2\",\n- gx: \"http://www.google.com/kml/ext/2.2\"\n- },\n-\n- /**\n- * APIProperty: kmlns\n- * {String} KML Namespace to use. Defaults to 2.0 namespace.\n- */\n- kmlns: \"http://earth.google.com/kml/2.0\",\n-\n- /** \n- * APIProperty: placemarksDesc\n- * {String} Name of the placemarks. Default is \"No description available\".\n- */\n- placemarksDesc: \"No description available\",\n-\n- /** \n- * APIProperty: foldersName\n- * {String} Name of the folders. Default is \"OpenLayers export\".\n- * If set to null, no name element will be created.\n- */\n- foldersName: \"OpenLayers export\",\n-\n- /** \n- * APIProperty: foldersDesc\n- * {String} Description of the folders. Default is \"Exported on [date].\"\n- * If set to null, no description element will be created.\n- */\n- foldersDesc: \"Exported on \" + new Date(),\n-\n- /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract attributes from KML. Default is true.\n- * Extracting styleUrls requires this to be set to true\n- * Note that currently only Data and SimpleData \n- * elements are handled.\n- */\n- extractAttributes: true,\n-\n- /**\n- * APIProperty: kvpAttributes\n- * {Boolean} Only used if extractAttributes is true.\n- * If set to true, attributes will be simple\n- * key-value pairs, compatible with other formats,\n- * Any displayName elements will be ignored.\n- * If set to false, attributes will be objects,\n- * retaining any displayName elements, but not\n- * compatible with other formats. Any CDATA in\n- * displayName will be read in as a string value.\n- * Default is false.\n- */\n- kvpAttributes: false,\n-\n- /**\n- * Property: extractStyles\n- * {Boolean} Extract styles from KML. Default is false.\n- * Extracting styleUrls also requires extractAttributes to be\n- * set to true\n- */\n- extractStyles: false,\n-\n- /**\n- * APIProperty: extractTracks\n- * {Boolean} Extract gx:Track elements from Placemark elements. Default\n- * is false. If true, features will be generated for all points in\n- * all gx:Track elements. Features will have a when (Date) attribute\n- * based on when elements in the track. If tracks include angle\n- * elements, features will have heading, tilt, and roll attributes.\n- * If track point coordinates have three values, features will have\n- * an altitude attribute with the third coordinate value.\n- */\n- extractTracks: false,\n-\n- /**\n- * APIProperty: trackAttributes\n- * {Array} If <extractTracks> is true, points within gx:Track elements will \n- * be parsed as features with when, heading, tilt, and roll attributes.\n- * Any additional attribute names can be provided in <trackAttributes>.\n- */\n- trackAttributes: null,\n-\n- /**\n- * Property: internalns\n- * {String} KML Namespace to use -- defaults to the namespace of the\n- * Placemark node being parsed, but falls back to kmlns. \n- */\n- internalns: null,\n-\n- /**\n- * Property: features\n- * {Array} Array of features\n- * \n- */\n- features: null,\n-\n- /**\n- * Property: styles\n- * {Object} Storage of style objects\n- * \n- */\n- styles: null,\n-\n- /**\n- * Property: styleBaseUrl\n- * {String}\n- */\n- styleBaseUrl: \"\",\n-\n- /**\n- * Property: fetched\n- * {Object} Storage of KML URLs that have been fetched before\n- * in order to prevent reloading them.\n- */\n- fetched: null,\n-\n- /**\n- * APIProperty: maxDepth\n- * {Integer} Maximum depth for recursive loading external KML URLs \n- * Defaults to 0: do no external fetching\n- */\n- maxDepth: 0,\n-\n- /**\n- * Constructor: OpenLayers.Format.KML\n- * Create a new parser for KML.\n+ * capabilities - {Object} The object returned from a <read> call to this\n+ * format.\n+ * config - {Object} Configuration properties for the layer. Defaults for\n+ * the layer will apply if not provided.\n *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- // compile regular expressions once instead of every time they are used\n- this.regExes = {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g),\n- kmlColor: (/(\\w{2})(\\w{2})(\\w{2})(\\w{2})/),\n- kmlIconPalette: (/root:\\/\\/icons\\/palette-(\\d+)(\\.\\w+)/),\n- straightBracket: (/\\$\\[(.*?)\\]/g)\n- };\n- // KML coordinates are always in longlat WGS84\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n-\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Read data from a string, and return a list of features. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n+ * Required config properties:\n+ * layer - {String} The layer identifier.\n *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} List of features.\n- */\n- read: function(data) {\n- this.features = [];\n- this.styles = {};\n- this.fetched = {};\n-\n- // Set default options \n- var options = {\n- depth: 0,\n- styleBaseUrl: this.styleBaseUrl\n- };\n-\n- return this.parseData(data, options);\n- },\n-\n- /**\n- * Method: parseData\n- * Read data from a string, and return a list of features. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- * options - {Object} Hash of options\n+ * Optional config properties:\n+ * matrixSet - {String} The matrix set identifier, required if there is \n+ * more than one matrix set in the layer capabilities.\n+ * style - {String} The name of the style\n+ * format - {String} Image format for the layer. Default is the first\n+ * format returned in the GetCapabilities response.\n+ * param - {Object} The dimensions values eg: {\"Year\": \"2012\"}\n *\n * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} List of features.\n- */\n- parseData: function(data, options) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n-\n- // Loop throught the following node types in this order and\n- // process the nodes found \n- var types = [\"Link\", \"NetworkLink\", \"Style\", \"StyleMap\", \"Placemark\"];\n- for (var i = 0, len = types.length; i < len; ++i) {\n- var type = types[i];\n-\n- var nodes = this.getElementsByTagNameNS(data, \"*\", type);\n-\n- // skip to next type if no nodes are found\n- if (nodes.length == 0) {\n- continue;\n- }\n-\n- switch (type.toLowerCase()) {\n-\n- // Fetch external links \n- case \"link\":\n- case \"networklink\":\n- this.parseLinks(nodes, options);\n- break;\n-\n- // parse style information\n- case \"style\":\n- if (this.extractStyles) {\n- this.parseStyles(nodes, options);\n- }\n- break;\n- case \"stylemap\":\n- if (this.extractStyles) {\n- this.parseStyleMaps(nodes, options);\n- }\n- break;\n-\n- // parse features\n- case \"placemark\":\n- this.parseFeatures(nodes, options);\n- break;\n- }\n- }\n-\n- return this.features;\n- },\n-\n- /**\n- * Method: parseLinks\n- * Finds URLs of linked KML documents and fetches them\n- * \n- * Parameters: \n- * nodes - {Array} of {DOMElement} data to read/parse.\n- * options - {Object} Hash of options\n- * \n- */\n- parseLinks: function(nodes, options) {\n-\n- // Fetch external links <NetworkLink> and <Link>\n- // Don't do anything if we have reached our maximum depth for recursion\n- if (options.depth >= this.maxDepth) {\n- return false;\n- }\n-\n- // increase depth\n- var newOptions = OpenLayers.Util.extend({}, options);\n- newOptions.depth++;\n-\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var href = this.parseProperty(nodes[i], \"*\", \"href\");\n- if (href && !this.fetched[href]) {\n- this.fetched[href] = true; // prevent reloading the same urls\n- var data = this.fetchLink(href);\n- if (data) {\n- this.parseData(data, newOptions);\n- }\n- }\n- }\n-\n- },\n-\n- /**\n- * Method: fetchLink\n- * Fetches a URL and returns the result\n- * \n- * Parameters: \n- * href - {String} url to be fetched\n- * \n- */\n- fetchLink: function(href) {\n- var request = OpenLayers.Request.GET({\n- url: href,\n- async: false\n- });\n- if (request) {\n- return request.responseText;\n- }\n- },\n-\n- /**\n- * Method: parseStyles\n- * Parses <Style> nodes\n- * \n- * Parameters: \n- * nodes - {Array} of {DOMElement} data to read/parse.\n- * options - {Object} Hash of options\n- * \n+ * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an\n+ * error if an incomplete config is provided. Returns undefined if no\n+ * layer could be created with the provided config.\n */\n- parseStyles: function(nodes, options) {\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var style = this.parseStyle(nodes[i]);\n- if (style) {\n- var styleName = (options.styleBaseUrl || \"\") + \"#\" + style.id;\n-\n- this.styles[styleName] = style;\n- }\n- }\n- },\n+ createLayer: function(capabilities, config) {\n+ var layer;\n \n- /**\n- * Method: parseKmlColor\n- * Parses a kml color (in 'aabbggrr' format) and returns the corresponding \n- * color and opacity or null if the color is invalid.\n- *\n- * Parameters: \n- * kmlColor - {String} a kml formated color\n- *\n- * Returns:\n- * {Object}\n- */\n- parseKmlColor: function(kmlColor) {\n- var color = null;\n- if (kmlColor) {\n- var matches = kmlColor.match(this.regExes.kmlColor);\n- if (matches) {\n- color = {\n- color: '#' + matches[4] + matches[3] + matches[2],\n- opacity: parseInt(matches[1], 16) / 255\n- };\n- }\n+ // confirm required properties are supplied in config\n+ if (!('layer' in config)) {\n+ throw new Error(\"Missing property 'layer' in configuration.\");\n }\n- return color;\n- },\n-\n- /**\n- * Method: parseStyle\n- * Parses the children of a <Style> node and builds the style hash\n- * accordingly\n- * \n- * Parameters: \n- * node - {DOMElement} <Style> node\n- * \n- */\n- parseStyle: function(node) {\n- var style = {};\n-\n- var types = [\"LineStyle\", \"PolyStyle\", \"IconStyle\", \"BalloonStyle\",\n- \"LabelStyle\"\n- ];\n- var type, styleTypeNode, nodeList, geometry, parser;\n- for (var i = 0, len = types.length; i < len; ++i) {\n- type = types[i];\n- styleTypeNode = this.getElementsByTagNameNS(node, \"*\", type)[0];\n- if (!styleTypeNode) {\n- continue;\n- }\n-\n- // only deal with first geometry of this type\n- switch (type.toLowerCase()) {\n- case \"linestyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"strokeColor\"] = color.color;\n- style[\"strokeOpacity\"] = color.opacity;\n- }\n-\n- var width = this.parseProperty(styleTypeNode, \"*\", \"width\");\n- if (width) {\n- style[\"strokeWidth\"] = width;\n- }\n- break;\n-\n- case \"polystyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"fillOpacity\"] = color.opacity;\n- style[\"fillColor\"] = color.color;\n- }\n- // Check if fill is disabled\n- var fill = this.parseProperty(styleTypeNode, \"*\", \"fill\");\n- if (fill == \"0\") {\n- style[\"fillColor\"] = \"none\";\n- }\n- // Check if outline is disabled\n- var outline = this.parseProperty(styleTypeNode, \"*\", \"outline\");\n- if (outline == \"0\") {\n- style[\"strokeWidth\"] = \"0\";\n- }\n-\n- break;\n-\n- case \"iconstyle\":\n- // set scale\n- var scale = parseFloat(this.parseProperty(styleTypeNode,\n- \"*\", \"scale\") || 1);\n-\n- // set default width and height of icon\n- var width = 32 * scale;\n- var height = 32 * scale;\n-\n- var iconNode = this.getElementsByTagNameNS(styleTypeNode,\n- \"*\",\n- \"Icon\")[0];\n- if (iconNode) {\n- var href = this.parseProperty(iconNode, \"*\", \"href\");\n- if (href) {\n-\n- var w = this.parseProperty(iconNode, \"*\", \"w\");\n- var h = this.parseProperty(iconNode, \"*\", \"h\");\n-\n- // Settings for Google specific icons that are 64x64\n- // We set the width and height to 64 and halve the\n- // scale to prevent icons from being too big\n- var google = \"http://maps.google.com/mapfiles/kml\";\n- if (OpenLayers.String.startsWith(\n- href, google) && !w && !h) {\n- w = 64;\n- h = 64;\n- scale = scale / 2;\n- }\n-\n- // if only dimension is defined, make sure the\n- // other one has the same value\n- w = w || h;\n- h = h || w;\n-\n- if (w) {\n- width = parseInt(w) * scale;\n- }\n-\n- if (h) {\n- height = parseInt(h) * scale;\n- }\n-\n- // support for internal icons \n- // (/root://icons/palette-x.png)\n- // x and y tell the position on the palette:\n- // - in pixels\n- // - starting from the left bottom\n- // We translate that to a position in the list \n- // and request the appropriate icon from the \n- // google maps website\n- var matches = href.match(this.regExes.kmlIconPalette);\n- if (matches) {\n- var palette = matches[1];\n- var file_extension = matches[2];\n-\n- var x = this.parseProperty(iconNode, \"*\", \"x\");\n- var y = this.parseProperty(iconNode, \"*\", \"y\");\n-\n- var posX = x ? x / 32 : 0;\n- var posY = y ? (7 - y / 32) : 7;\n-\n- var pos = posY * 8 + posX;\n- href = \"http://maps.google.com/mapfiles/kml/pal\" +\n- palette + \"/icon\" + pos + file_extension;\n- }\n-\n- style[\"graphicOpacity\"] = 1; // fully opaque\n- style[\"externalGraphic\"] = href;\n- }\n-\n- }\n-\n-\n- // hotSpots define the offset for an Icon\n- var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,\n- \"*\",\n- \"hotSpot\")[0];\n- if (hotSpotNode) {\n- var x = parseFloat(hotSpotNode.getAttribute(\"x\"));\n- var y = parseFloat(hotSpotNode.getAttribute(\"y\"));\n \n- var xUnits = hotSpotNode.getAttribute(\"xunits\");\n- if (xUnits == \"pixels\") {\n- style[\"graphicXOffset\"] = -x * scale;\n- } else if (xUnits == \"insetPixels\") {\n- style[\"graphicXOffset\"] = -width + (x * scale);\n- } else if (xUnits == \"fraction\") {\n- style[\"graphicXOffset\"] = -width * x;\n- }\n-\n- var yUnits = hotSpotNode.getAttribute(\"yunits\");\n- if (yUnits == \"pixels\") {\n- style[\"graphicYOffset\"] = -height + (y * scale) + 1;\n- } else if (yUnits == \"insetPixels\") {\n- style[\"graphicYOffset\"] = -(y * scale) + 1;\n- } else if (yUnits == \"fraction\") {\n- style[\"graphicYOffset\"] = -height * (1 - y) + 1;\n- }\n- }\n-\n- style[\"graphicWidth\"] = width;\n- style[\"graphicHeight\"] = height;\n- break;\n-\n- case \"balloonstyle\":\n- var balloonStyle = OpenLayers.Util.getXmlNodeValue(\n- styleTypeNode);\n- if (balloonStyle) {\n- style[\"balloonStyle\"] = balloonStyle.replace(\n- this.regExes.straightBracket, \"${$1}\");\n- }\n- break;\n- case \"labelstyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"fontColor\"] = color.color;\n- style[\"fontOpacity\"] = color.opacity;\n- }\n- break;\n+ var contents = capabilities.contents;\n \n- default:\n+ // find the layer definition with the given identifier\n+ var layers = contents.layers;\n+ var layerDef;\n+ for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n+ if (contents.layers[i].identifier === config.layer) {\n+ layerDef = contents.layers[i];\n+ break;\n }\n }\n-\n- // Some polygons have no line color, so we use the fillColor for that\n- if (!style[\"strokeColor\"] && style[\"fillColor\"]) {\n- style[\"strokeColor\"] = style[\"fillColor\"];\n- }\n-\n- var id = node.getAttribute(\"id\");\n- if (id && style) {\n- style.id = id;\n+ if (!layerDef) {\n+ throw new Error(\"Layer not found\");\n }\n \n- return style;\n- },\n-\n- /**\n- * Method: parseStyleMaps\n- * Parses <StyleMap> nodes, but only uses the 'normal' key\n- * \n- * Parameters: \n- * nodes - {Array} of {DOMElement} data to read/parse.\n- * options - {Object} Hash of options\n- * \n- */\n- parseStyleMaps: function(nodes, options) {\n- // Only the default or \"normal\" part of the StyleMap is processed now\n- // To do the select or \"highlight\" bit, we'd need to change lots more\n-\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var node = nodes[i];\n- var pairs = this.getElementsByTagNameNS(node, \"*\",\n- \"Pair\");\n-\n- var id = node.getAttribute(\"id\");\n- for (var j = 0, jlen = pairs.length; j < jlen; j++) {\n- var pair = pairs[j];\n- // Use the shortcut in the SLD format to quickly retrieve the \n- // value of a node. Maybe it's good to have a method in \n- // Format.XML to do this\n- var key = this.parseProperty(pair, \"*\", \"key\");\n- var styleUrl = this.parseProperty(pair, \"*\", \"styleUrl\");\n-\n- if (styleUrl && key == \"normal\") {\n- this.styles[(options.styleBaseUrl || \"\") + \"#\" + id] =\n- this.styles[(options.styleBaseUrl || \"\") + styleUrl];\n- }\n-\n- // TODO: implement the \"select\" part\n- //if (styleUrl && key == \"highlight\") {\n- //}\n-\n- }\n+ var format = config.format;\n+ if (!format && layerDef.formats && layerDef.formats.length) {\n+ format = layerDef.formats[0];\n }\n \n- },\n-\n-\n- /**\n- * Method: parseFeatures\n- * Loop through all Placemark nodes and parse them.\n- * Will create a list of features\n- * \n- * Parameters: \n- * nodes - {Array} of {DOMElement} data to read/parse.\n- * options - {Object} Hash of options\n- * \n- */\n- parseFeatures: function(nodes, options) {\n- var features = [];\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var featureNode = nodes[i];\n- var feature = this.parseFeature.apply(this, [featureNode]);\n- if (feature) {\n-\n- // Create reference to styleUrl \n- if (this.extractStyles && feature.attributes &&\n- feature.attributes.styleUrl) {\n- feature.style = this.getStyle(feature.attributes.styleUrl, options);\n- }\n-\n- if (this.extractStyles) {\n- // Make sure that <Style> nodes within a placemark are \n- // processed as well\n- var inlineStyleNode = this.getElementsByTagNameNS(featureNode,\n- \"*\",\n- \"Style\")[0];\n- if (inlineStyleNode) {\n- var inlineStyle = this.parseStyle(inlineStyleNode);\n- if (inlineStyle) {\n- feature.style = OpenLayers.Util.extend(\n- feature.style, inlineStyle\n- );\n- }\n- }\n- }\n-\n- // check if gx:Track elements should be parsed\n- if (this.extractTracks) {\n- var tracks = this.getElementsByTagNameNS(\n- featureNode, this.namespaces.gx, \"Track\"\n- );\n- if (tracks && tracks.length > 0) {\n- var track = tracks[0];\n- var container = {\n- features: [],\n- feature: feature\n- };\n- this.readNode(track, container);\n- if (container.features.length > 0) {\n- features.push.apply(features, container.features);\n- }\n- }\n- } else {\n- // add feature to list of features\n- features.push(feature);\n- }\n- } else {\n- throw \"Bad Placemark: \" + i;\n- }\n+ // find the matrixSet definition\n+ var matrixSet;\n+ if (config.matrixSet) {\n+ matrixSet = contents.tileMatrixSets[config.matrixSet];\n+ } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n+ matrixSet = contents.tileMatrixSets[\n+ layerDef.tileMatrixSetLinks[0].tileMatrixSet];\n }\n-\n- // add new features to existing feature list\n- this.features = this.features.concat(features);\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"kml\": {\n- \"when\": function(node, container) {\n- container.whens.push(OpenLayers.Date.parse(\n- this.getChildValue(node)\n- ));\n- },\n- \"_trackPointAttribute\": function(node, container) {\n- var name = node.nodeName.split(\":\").pop();\n- container.attributes[name].push(this.getChildValue(node));\n- }\n- },\n- \"gx\": {\n- \"Track\": function(node, container) {\n- var obj = {\n- whens: [],\n- points: [],\n- angles: []\n- };\n- if (this.trackAttributes) {\n- var name;\n- obj.attributes = {};\n- for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) {\n- name = this.trackAttributes[i];\n- obj.attributes[name] = [];\n- if (!(name in this.readers.kml)) {\n- this.readers.kml[name] = this.readers.kml._trackPointAttribute;\n- }\n- }\n- }\n- this.readChildNodes(node, obj);\n- if (obj.whens.length !== obj.points.length) {\n- throw new Error(\"gx:Track with unequal number of when (\" +\n- obj.whens.length + \") and gx:coord (\" +\n- obj.points.length + \") elements.\");\n- }\n- var hasAngles = obj.angles.length > 0;\n- if (hasAngles && obj.whens.length !== obj.angles.length) {\n- throw new Error(\"gx:Track with unequal number of when (\" +\n- obj.whens.length + \") and gx:angles (\" +\n- obj.angles.length + \") elements.\");\n- }\n- var feature, point, angles;\n- for (var i = 0, ii = obj.whens.length; i < ii; ++i) {\n- feature = container.feature.clone();\n- feature.fid = container.feature.fid || container.feature.id;\n- point = obj.points[i];\n- feature.geometry = point;\n- if (\"z\" in point) {\n- feature.attributes.altitude = point.z;\n- }\n- if (this.internalProjection && this.externalProjection) {\n- feature.geometry.transform(\n- this.externalProjection, this.internalProjection\n- );\n- }\n- if (this.trackAttributes) {\n- for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) {\n- var name = this.trackAttributes[j];\n- feature.attributes[name] = obj.attributes[name][i];\n- }\n- }\n- feature.attributes.when = obj.whens[i];\n- feature.attributes.trackId = container.feature.id;\n- if (hasAngles) {\n- angles = obj.angles[i];\n- feature.attributes.heading = parseFloat(angles[0]);\n- feature.attributes.tilt = parseFloat(angles[1]);\n- feature.attributes.roll = parseFloat(angles[2]);\n- }\n- container.features.push(feature);\n- }\n- },\n- \"coord\": function(node, container) {\n- var str = this.getChildValue(node);\n- var coords = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n- var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);\n- if (coords.length > 2) {\n- point.z = parseFloat(coords[2]);\n- }\n- container.points.push(point);\n- },\n- \"angles\": function(node, container) {\n- var str = this.getChildValue(node);\n- var parts = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n- container.angles.push(parts);\n- }\n+ if (!matrixSet) {\n+ throw new Error(\"matrixSet not found\");\n }\n- },\n \n- /**\n- * Method: parseFeature\n- * This function is the core of the KML parsing code in OpenLayers.\n- * It creates the geometries that are then attached to the returned\n- * feature, and calls parseAttributes() to get attribute data out.\n- *\n- * Parameters:\n- * node - {DOMElement}\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A vector feature.\n- */\n- parseFeature: function(node) {\n- // only accept one geometry per feature - look for highest \"order\"\n- var order = [\"MultiGeometry\", \"Polygon\", \"LineString\", \"Point\"];\n- var type, nodeList, geometry, parser;\n- for (var i = 0, len = order.length; i < len; ++i) {\n- type = order[i];\n- this.internalns = node.namespaceURI ?\n- node.namespaceURI : this.kmlns;\n- nodeList = this.getElementsByTagNameNS(node,\n- this.internalns, type);\n- if (nodeList.length > 0) {\n- // only deal with first geometry of this type\n- var parser = this.parseGeometry[type.toLowerCase()];\n- if (parser) {\n- geometry = parser.apply(this, [nodeList[0]]);\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- } else {\n- throw new TypeError(\"Unsupported geometry type: \" + type);\n- }\n- // stop looking for different geometry types\n+ // get the default style for the layer\n+ var style;\n+ for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n+ style = layerDef.styles[i];\n+ if (style.isDefault) {\n break;\n }\n }\n \n- // construct feature (optionally with attributes)\n- var attributes;\n- if (this.extractAttributes) {\n- attributes = this.parseAttributes(node);\n- }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n-\n- var fid = node.getAttribute(\"id\") || node.getAttribute(\"name\");\n- if (fid != null) {\n- feature.fid = fid;\n- }\n-\n- return feature;\n- },\n-\n- /**\n- * Method: getStyle\n- * Retrieves a style from a style hash using styleUrl as the key\n- * If the styleUrl doesn't exist yet, we try to fetch it \n- * Internet\n- * \n- * Parameters: \n- * styleUrl - {String} URL of style\n- * options - {Object} Hash of options \n- *\n- * Returns:\n- * {Object} - (reference to) Style hash\n- */\n- getStyle: function(styleUrl, options) {\n-\n- var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);\n-\n- var newOptions = OpenLayers.Util.extend({}, options);\n- newOptions.depth++;\n- newOptions.styleBaseUrl = styleBaseUrl;\n-\n- // Fetch remote Style URLs (if not fetched before) \n- if (!this.styles[styleUrl] &&\n- !OpenLayers.String.startsWith(styleUrl, \"#\") &&\n- newOptions.depth <= this.maxDepth &&\n- !this.fetched[styleBaseUrl]) {\n-\n- var data = this.fetchLink(styleBaseUrl);\n- if (data) {\n- this.parseData(data, newOptions);\n- }\n-\n- }\n-\n- // return requested style\n- var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);\n- return style;\n- },\n-\n- /**\n- * Property: parseGeometry\n- * Properties of this object are the functions that parse geometries based\n- * on their type.\n- */\n- parseGeometry: {\n-\n- /**\n- * Method: parseGeometry.point\n- * Given a KML node representing a point geometry, create an OpenLayers\n- * point geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A KML Point node.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} A point geometry.\n- */\n- point: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n- \"coordinates\");\n- var coords = [];\n- if (nodeList.length > 0) {\n- var coordString = nodeList[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.removeSpace, \"\");\n- coords = coordString.split(\",\");\n- }\n-\n- var point = null;\n- if (coords.length > 1) {\n- // preserve third dimension\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n- point = new OpenLayers.Geometry.Point(coords[0], coords[1],\n- coords[2]);\n- } else {\n- throw \"Bad coordinate string: \" + coordString;\n- }\n- return point;\n- },\n-\n- /**\n- * Method: parseGeometry.linestring\n- * Given a KML node representing a linestring geometry, create an\n- * OpenLayers linestring geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A KML LineString node.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.LineString>} A linestring geometry.\n- */\n- linestring: function(node, ring) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n- \"coordinates\");\n- var line = null;\n- if (nodeList.length > 0) {\n- var coordString = this.getChildValue(nodeList[0]);\n-\n- coordString = coordString.replace(this.regExes.trimSpace,\n- \"\");\n- coordString = coordString.replace(this.regExes.trimComma,\n- \",\");\n- var pointList = coordString.split(this.regExes.splitSpace);\n- var numPoints = pointList.length;\n- var points = new Array(numPoints);\n- var coords, numCoords;\n- for (var i = 0; i < numPoints; ++i) {\n- coords = pointList[i].split(\",\");\n- numCoords = coords.length;\n- if (numCoords > 1) {\n- if (coords.length == 2) {\n- coords[2] = null;\n- }\n- points[i] = new OpenLayers.Geometry.Point(coords[0],\n- coords[1],\n- coords[2]);\n- } else {\n- throw \"Bad LineString point coordinates: \" +\n- pointList[i];\n- }\n- }\n- if (numPoints) {\n- if (ring) {\n- line = new OpenLayers.Geometry.LinearRing(points);\n- } else {\n- line = new OpenLayers.Geometry.LineString(points);\n- }\n- } else {\n- throw \"Bad LineString coordinates: \" + coordString;\n- }\n- }\n-\n- return line;\n- },\n-\n- /**\n- * Method: parseGeometry.polygon\n- * Given a KML node representing a polygon geometry, create an\n- * OpenLayers polygon geometry.\n- *\n- * Parameters:\n- * node - {DOMElement} A KML Polygon node.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Polygon>} A polygon geometry.\n- */\n- polygon: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n- \"LinearRing\");\n- var numRings = nodeList.length;\n- var components = new Array(numRings);\n- if (numRings > 0) {\n- // this assumes exterior ring first, inner rings after\n- var ring;\n- for (var i = 0, len = nodeList.length; i < len; ++i) {\n- ring = this.parseGeometry.linestring.apply(this,\n- [nodeList[i], true]);\n- if (ring) {\n- components[i] = ring;\n- } else {\n- throw \"Bad LinearRing geometry: \" + i;\n- }\n- }\n- }\n- return new OpenLayers.Geometry.Polygon(components);\n- },\n-\n- /**\n- * Method: parseGeometry.multigeometry\n- * Given a KML node representing a multigeometry, create an\n- * OpenLayers geometry collection.\n- *\n- * Parameters:\n- * node - {DOMElement} A KML MultiGeometry node.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Collection>} A geometry collection.\n- */\n- multigeometry: function(node) {\n- var child, parser;\n- var parts = [];\n- var children = node.childNodes;\n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- var type = (child.prefix) ?\n- child.nodeName.split(\":\")[1] :\n- child.nodeName;\n- var parser = this.parseGeometry[type.toLowerCase()];\n- if (parser) {\n- parts.push(parser.apply(this, [child]));\n- }\n- }\n- }\n- return new OpenLayers.Geometry.Collection(parts);\n- }\n-\n- },\n-\n- /**\n- * Method: parseAttributes\n- *\n- * Parameters:\n- * node - {DOMElement}\n- *\n- * Returns:\n- * {Object} An attributes object.\n- */\n- parseAttributes: function(node) {\n- var attributes = {};\n-\n- // Extended Data is parsed first.\n- var edNodes = node.getElementsByTagName(\"ExtendedData\");\n- if (edNodes.length) {\n- attributes = this.parseExtendedData(edNodes[0]);\n- }\n-\n- // assume attribute nodes are type 1 children with a type 3 or 4 child\n- var child, grandchildren, grandchild;\n- var children = node.childNodes;\n+ var requestEncoding = config.requestEncoding;\n+ if (!requestEncoding) {\n+ requestEncoding = \"KVP\";\n+ if (capabilities.operationsMetadata.GetTile.dcp.http) {\n+ var http = capabilities.operationsMetadata.GetTile.dcp.http;\n+ // Get first get method\n+ if (http.get[0].constraints) {\n+ var constraints = http.get[0].constraints;\n+ var allowedValues = constraints.GetEncoding.allowedValues;\n \n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- grandchildren = child.childNodes;\n- if (grandchildren.length >= 1 && grandchildren.length <= 3) {\n- var grandchild;\n- switch (grandchildren.length) {\n- case 1:\n- grandchild = grandchildren[0];\n- break;\n- case 2:\n- var c1 = grandchildren[0];\n- var c2 = grandchildren[1];\n- grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ?\n- c1 : c2;\n- break;\n- case 3:\n- default:\n- grandchild = grandchildren[1];\n- break;\n- }\n- if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {\n- var name = (child.prefix) ?\n- child.nodeName.split(\":\")[1] :\n- child.nodeName;\n- var value = OpenLayers.Util.getXmlNodeValue(grandchild);\n- if (value) {\n- value = value.replace(this.regExes.trimSpace, \"\");\n- attributes[name] = value;\n- }\n+ // The OGC documentation is not clear if we should use\n+ // REST or RESTful, ArcGis use RESTful,\n+ // and OpenLayers use REST.\n+ if (!allowedValues.KVP &&\n+ (allowedValues.REST || allowedValues.RESTful)) {\n+ requestEncoding = \"REST\";\n }\n }\n }\n }\n- return attributes;\n- },\n-\n- /**\n- * Method: parseExtendedData\n- * Parse ExtendedData from KML. Limited support for schemas/datatypes.\n- * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata\n- * for more information on extendeddata.\n- */\n- parseExtendedData: function(node) {\n- var attributes = {};\n- var i, len, data, key;\n- var dataNodes = node.getElementsByTagName(\"Data\");\n- for (i = 0, len = dataNodes.length; i < len; i++) {\n- data = dataNodes[i];\n- key = data.getAttribute(\"name\");\n- var ed = {};\n- var valueNode = data.getElementsByTagName(\"value\");\n- if (valueNode.length) {\n- ed['value'] = this.getChildValue(valueNode[0]);\n- }\n- if (this.kvpAttributes) {\n- attributes[key] = ed['value'];\n- } else {\n- var nameNode = data.getElementsByTagName(\"displayName\");\n- if (nameNode.length) {\n- ed['displayName'] = this.getChildValue(nameNode[0]);\n- }\n- attributes[key] = ed;\n- }\n- }\n- var simpleDataNodes = node.getElementsByTagName(\"SimpleData\");\n- for (i = 0, len = simpleDataNodes.length; i < len; i++) {\n- var ed = {};\n- data = simpleDataNodes[i];\n- key = data.getAttribute(\"name\");\n- ed['value'] = this.getChildValue(data);\n- if (this.kvpAttributes) {\n- attributes[key] = ed['value'];\n- } else {\n- ed['displayName'] = key;\n- attributes[key] = ed;\n- }\n- }\n-\n- return attributes;\n- },\n-\n- /**\n- * Method: parseProperty\n- * Convenience method to find a node and return its value\n- *\n- * Parameters:\n- * xmlNode - {<DOMElement>}\n- * namespace - {String} namespace of the node to find\n- * tagName - {String} name of the property to parse\n- * \n- * Returns:\n- * {String} The value for the requested property (defaults to null)\n- */\n- parseProperty: function(xmlNode, namespace, tagName) {\n- var value;\n- var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);\n- try {\n- value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);\n- } catch (e) {\n- value = null;\n- }\n-\n- return value;\n- },\n-\n- /**\n- * APIMethod: write\n- * Accept Feature Collection, and return a string. \n- * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} An array of features.\n- *\n- * Returns:\n- * {String} A KML string.\n- */\n- write: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- var kml = this.createElementNS(this.kmlns, \"kml\");\n- var folder = this.createFolderXML();\n- for (var i = 0, len = features.length; i < len; ++i) {\n- folder.appendChild(this.createPlacemarkXML(features[i]));\n- }\n- kml.appendChild(folder);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);\n- },\n-\n- /**\n- * Method: createFolderXML\n- * Creates and returns a KML folder node\n- * \n- * Returns:\n- * {DOMElement}\n- */\n- createFolderXML: function() {\n- // Folder\n- var folder = this.createElementNS(this.kmlns, \"Folder\");\n-\n- // Folder name\n- if (this.foldersName) {\n- var folderName = this.createElementNS(this.kmlns, \"name\");\n- var folderNameText = this.createTextNode(this.foldersName);\n- folderName.appendChild(folderNameText);\n- folder.appendChild(folderName);\n- }\n-\n- // Folder description\n- if (this.foldersDesc) {\n- var folderDesc = this.createElementNS(this.kmlns, \"description\");\n- var folderDescText = this.createTextNode(this.foldersDesc);\n- folderDesc.appendChild(folderDescText);\n- folder.appendChild(folderDesc);\n- }\n-\n- return folder;\n- },\n-\n- /**\n- * Method: createPlacemarkXML\n- * Creates and returns a KML placemark node representing the given feature. \n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * \n- * Returns:\n- * {DOMElement}\n- */\n- createPlacemarkXML: function(feature) {\n- // Placemark name\n- var placemarkName = this.createElementNS(this.kmlns, \"name\");\n- var label = (feature.style && feature.style.label) ? feature.style.label : feature.id;\n- var name = feature.attributes.name || label;\n- placemarkName.appendChild(this.createTextNode(name));\n-\n- // Placemark description\n- var placemarkDesc = this.createElementNS(this.kmlns, \"description\");\n- var desc = feature.attributes.description || this.placemarksDesc;\n- placemarkDesc.appendChild(this.createTextNode(desc));\n-\n- // Placemark\n- var placemarkNode = this.createElementNS(this.kmlns, \"Placemark\");\n- if (feature.fid != null) {\n- placemarkNode.setAttribute(\"id\", feature.fid);\n- }\n- placemarkNode.appendChild(placemarkName);\n- placemarkNode.appendChild(placemarkDesc);\n-\n- // Geometry node (Point, LineString, etc. nodes)\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n- placemarkNode.appendChild(geometryNode);\n-\n- // output attributes as extendedData\n- if (feature.attributes) {\n- var edNode = this.buildExtendedData(feature.attributes);\n- if (edNode) {\n- placemarkNode.appendChild(edNode);\n- }\n- }\n-\n- return placemarkNode;\n- },\n-\n- /**\n- * Method: buildGeometryNode\n- * Builds and returns a KML geometry node with the given geometry.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement}\n- */\n- buildGeometryNode: function(geometry) {\n- var className = geometry.CLASS_NAME;\n- var type = className.substring(className.lastIndexOf(\".\") + 1);\n- var builder = this.buildGeometry[type.toLowerCase()];\n- var node = null;\n- if (builder) {\n- node = builder.apply(this, [geometry]);\n- }\n- return node;\n- },\n-\n- /**\n- * Property: buildGeometry\n- * Object containing methods to do the actual geometry node building\n- * based on geometry type.\n- */\n- buildGeometry: {\n- // TBD: Anybody care about namespace aliases here (these nodes have\n- // no prefixes)?\n-\n- /**\n- * Method: buildGeometry.point\n- * Given an OpenLayers point geometry, create a KML point.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>} A point geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML point node.\n- */\n- point: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"Point\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml;\n- },\n-\n- /**\n- * Method: buildGeometry.multipoint\n- * Given an OpenLayers multipoint geometry, create a KML\n- * GeometryCollection.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML GeometryCollection node.\n- */\n- multipoint: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry]);\n- },\n-\n- /**\n- * Method: buildGeometry.linestring\n- * Given an OpenLayers linestring geometry, create a KML linestring.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML linestring node.\n- */\n- linestring: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"LineString\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml;\n- },\n-\n- /**\n- * Method: buildGeometry.multilinestring\n- * Given an OpenLayers multilinestring geometry, create a KML\n- * GeometryCollection.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML GeometryCollection node.\n- */\n- multilinestring: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry]);\n- },\n-\n- /**\n- * Method: buildGeometry.linearring\n- * Given an OpenLayers linearring geometry, create a KML linearring.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML linearring node.\n- */\n- linearring: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"LinearRing\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml;\n- },\n-\n- /**\n- * Method: buildGeometry.polygon\n- * Given an OpenLayers polygon geometry, create a KML polygon.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML polygon node.\n- */\n- polygon: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"Polygon\");\n- var rings = geometry.components;\n- var ringMember, ringGeom, type;\n- for (var i = 0, len = rings.length; i < len; ++i) {\n- type = (i == 0) ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n- ringMember = this.createElementNS(this.kmlns, type);\n- ringGeom = this.buildGeometry.linearring.apply(this,\n- [rings[i]]);\n- ringMember.appendChild(ringGeom);\n- kml.appendChild(ringMember);\n- }\n- return kml;\n- },\n-\n- /**\n- * Method: buildGeometry.multipolygon\n- * Given an OpenLayers multipolygon geometry, create a KML\n- * GeometryCollection.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.\n- *\n- * Returns:\n- * {DOMElement} A KML GeometryCollection node.\n- */\n- multipolygon: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry]);\n- },\n \n- /**\n- * Method: buildGeometry.collection\n- * Given an OpenLayers geometry collection, create a KML MultiGeometry.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.\n- *\n- * Returns:\n- * {DOMElement} A KML MultiGeometry node.\n- */\n- collection: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"MultiGeometry\");\n- var child;\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- child = this.buildGeometryNode.apply(this,\n- [geometry.components[i]]);\n- if (child) {\n- kml.appendChild(child);\n- }\n+ var dimensions = [];\n+ var params = config.params || {};\n+ // to don't overwrite the changes in the applyDefaults\n+ delete config.params;\n+ for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n+ var dimension = layerDef.dimensions[id];\n+ dimensions.push(dimension.identifier);\n+ if (!params.hasOwnProperty(dimension.identifier)) {\n+ params[dimension.identifier] = dimension['default'];\n }\n- return kml;\n }\n- },\n \n- /**\n- * Method: buildCoordinatesNode\n- * Builds and returns the KML coordinates node with the given geometry\n- * <coordinates>...</coordinates>\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement}\n- */\n- buildCoordinatesNode: function(geometry) {\n- var coordinatesNode = this.createElementNS(this.kmlns, \"coordinates\");\n+ var projection = config.projection || matrixSet.supportedCRS.replace(\n+ /urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n+ var units = config.units ||\n+ (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n \n- var path;\n- var points = geometry.components;\n- if (points) {\n- // LineString or LinearRing\n- var point;\n- var numPoints = points.length;\n- var parts = new Array(numPoints);\n- for (var i = 0; i < numPoints; ++i) {\n- point = points[i];\n- parts[i] = this.buildCoordinates(point);\n+ var resolutions = [];\n+ for (var mid in matrixSet.matrixIds) {\n+ if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n+ resolutions.push(\n+ matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 /\n+ OpenLayers.METERS_PER_INCH /\n+ OpenLayers.INCHES_PER_UNIT[units]);\n }\n- path = parts.join(\" \");\n- } else {\n- // Point\n- path = this.buildCoordinates(geometry);\n- }\n-\n- var txtNode = this.createTextNode(path);\n- coordinatesNode.appendChild(txtNode);\n-\n- return coordinatesNode;\n- },\n-\n- /**\n- * Method: buildCoordinates\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns\n- * {String} a coordinate pair\n- */\n- buildCoordinates: function(point) {\n- if (this.internalProjection && this.externalProjection) {\n- point = point.clone();\n- point.transform(this.internalProjection,\n- this.externalProjection);\n }\n- return point.x + \",\" + point.y;\n- },\n \n- /**\n- * Method: buildExtendedData\n- *\n- * Parameters:\n- * attributes - {Object}\n- *\n- * Returns\n- * {DOMElement} A KML ExtendedData node or {null} if no attributes.\n- */\n- buildExtendedData: function(attributes) {\n- var extendedData = this.createElementNS(this.kmlns, \"ExtendedData\");\n- for (var attributeName in attributes) {\n- // empty, name, description, styleUrl attributes ignored\n- if (attributes[attributeName] && attributeName != \"name\" && attributeName != \"description\" && attributeName != \"styleUrl\") {\n- var data = this.createElementNS(this.kmlns, \"Data\");\n- data.setAttribute(\"name\", attributeName);\n- var value = this.createElementNS(this.kmlns, \"value\");\n- if (typeof attributes[attributeName] == \"object\") {\n- // cater for object attributes with 'value' properties\n- // other object properties will output an empty node\n- if (attributes[attributeName].value) {\n- value.appendChild(this.createTextNode(attributes[attributeName].value));\n- }\n- if (attributes[attributeName].displayName) {\n- var displayName = this.createElementNS(this.kmlns, \"displayName\");\n- // displayName always written as CDATA\n- displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName));\n- data.appendChild(displayName);\n- }\n- } else {\n- value.appendChild(this.createTextNode(attributes[attributeName]));\n+ var url;\n+ if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n+ url = [];\n+ var resourceUrls = layerDef.resourceUrls,\n+ resourceUrl;\n+ for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n+ resourceUrl = layerDef.resourceUrls[t];\n+ if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n+ url.push(resourceUrl.template);\n }\n- data.appendChild(value);\n- extendedData.appendChild(data);\n }\n- }\n- if (this.isSimpleContent(extendedData)) {\n- return null;\n } else {\n- return extendedData;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.KML\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WCSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WCSCapabilities\n- * Read WCS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n- */\n- defaultVersion: \"1.1.0\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WCSCapabilities\n- * Create a new parser for WCS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of coverages. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named coverages.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/GPX.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.GPX\n- * Read/write GPX parser. Create a new instance with the \n- * <OpenLayers.Format.GPX> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n-\n- /** \n- * APIProperty: defaultDesc\n- * {String} Default description for the waypoints/tracks in the case\n- * where the feature has no \"description\" attribute.\n- * Default is \"No description available\".\n- */\n- defaultDesc: \"No description available\",\n-\n- /**\n- * APIProperty: extractWaypoints\n- * {Boolean} Extract waypoints from GPX. (default: true)\n- */\n- extractWaypoints: true,\n-\n- /**\n- * APIProperty: extractTracks\n- * {Boolean} Extract tracks from GPX. (default: true)\n- */\n- extractTracks: true,\n-\n- /**\n- * APIProperty: extractRoutes\n- * {Boolean} Extract routes from GPX. (default: true)\n- */\n- extractRoutes: true,\n-\n- /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract feature attributes from GPX. (default: true)\n- * NOTE: Attributes as part of extensions to the GPX standard may not\n- * be extracted.\n- */\n- extractAttributes: true,\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- gpx: \"http://www.topografix.com/GPX/1/1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location. Defaults to\n- * \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\"\n- */\n- schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n-\n- /**\n- * APIProperty: creator\n- * {String} The creator attribute to be added to the written GPX files.\n- * Defaults to \"OpenLayers\"\n- */\n- creator: \"OpenLayers\",\n-\n- /**\n- * Constructor: OpenLayers.Format.GPX\n- * Create a new parser for GPX.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- // GPX coordinates are always in longlat WGS84\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n-\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Return a list of features from a GPX doc\n- *\n- * Parameters:\n- * doc - {Element} \n- *\n- * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n- */\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n- }\n- var features = [];\n-\n- if (this.extractTracks) {\n- var tracks = doc.getElementsByTagName(\"trk\");\n- for (var i = 0, len = tracks.length; i < len; i++) {\n- // Attributes are only in trk nodes, not trkseg nodes\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(tracks[i]);\n- }\n-\n- var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n- for (var j = 0, seglen = segs.length; j < seglen; j++) {\n- // We don't yet support extraction of trkpt attributes\n- // All trksegs of a trk get that trk's attributes\n- var track = this.extractSegment(segs[j], \"trkpt\");\n- features.push(new OpenLayers.Feature.Vector(track, attrs));\n- }\n- }\n- }\n-\n- if (this.extractRoutes) {\n- var routes = doc.getElementsByTagName(\"rte\");\n- for (var k = 0, klen = routes.length; k < klen; k++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(routes[k]);\n- }\n- var route = this.extractSegment(routes[k], \"rtept\");\n- features.push(new OpenLayers.Feature.Vector(route, attrs));\n- }\n- }\n-\n- if (this.extractWaypoints) {\n- var waypoints = doc.getElementsByTagName(\"wpt\");\n- for (var l = 0, len = waypoints.length; l < len; l++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(waypoints[l]);\n- }\n- var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n- features.push(new OpenLayers.Feature.Vector(wpt, attrs));\n- }\n- }\n-\n- if (this.internalProjection && this.externalProjection) {\n- for (var g = 0, featLength = features.length; g < featLength; g++) {\n- features[g].geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- }\n-\n- return features;\n- },\n-\n- /**\n- * Method: extractSegment\n- *\n- * Parameters:\n- * segment - {DOMElement} a trkseg or rte node to parse\n- * segmentType - {String} nodeName of waypoints that form the line\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.LineString>} A linestring geometry\n- */\n- extractSegment: function(segment, segmentType) {\n- var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n- var point_features = [];\n- for (var i = 0, len = points.length; i < len; i++) {\n- point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")));\n- }\n- return new OpenLayers.Geometry.LineString(point_features);\n- },\n-\n- /**\n- * Method: parseAttributes\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An attributes object.\n- */\n- parseAttributes: function(node) {\n- // node is either a wpt, trk or rte\n- // attributes are children of the form <attr>value</attr>\n- var attributes = {};\n- var attrNode = node.firstChild,\n- value, name;\n- while (attrNode) {\n- if (attrNode.nodeType == 1 && attrNode.firstChild) {\n- value = attrNode.firstChild;\n- if (value.nodeType == 3 || value.nodeType == 4) {\n- name = (attrNode.prefix) ?\n- attrNode.nodeName.split(\":\")[1] :\n- attrNode.nodeName;\n- if (name != \"trkseg\" && name != \"rtept\") {\n- attributes[name] = value.nodeValue;\n- }\n+ var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n+ url = [];\n+ var constraint;\n+ for (var i = 0, ii = httpGet.length; i < ii; i++) {\n+ constraint = httpGet[i].constraints;\n+ if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) {\n+ url.push(httpGet[i].url);\n }\n }\n- attrNode = attrNode.nextSibling;\n- }\n- return attributes;\n- },\n-\n- /**\n- * APIMethod: write\n- * Accepts Feature Collection, and returns a string. \n- * \n- * Parameters: \n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n- * metadata - {Object} A key/value pairs object to build a metadata node to\n- * add to the gpx. Supported keys are 'name', 'desc', 'author'.\n- */\n- write: function(features, metadata) {\n- features = OpenLayers.Util.isArray(features) ?\n- features : [features];\n- var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n- gpx.setAttribute(\"version\", \"1.1\");\n- gpx.setAttribute(\"creator\", this.creator);\n- this.setAttributes(gpx, {\n- \"xsi:schemaLocation\": this.schemaLocation\n- });\n-\n- if (metadata && typeof metadata == 'object') {\n- gpx.appendChild(this.buildMetadataNode(metadata));\n- }\n- for (var i = 0, len = features.length; i < len; i++) {\n- gpx.appendChild(this.buildFeatureNode(features[i]));\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);\n- },\n-\n- /**\n- * Method: buildMetadataNode\n- * Creates a \"metadata\" node.\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- buildMetadataNode: function(metadata) {\n- var types = ['name', 'desc', 'author'],\n- node = this.createElementNS(this.namespaces.gpx, 'metadata');\n- for (var i = 0; i < types.length; i++) {\n- var type = types[i];\n- if (metadata[type]) {\n- var n = this.createElementNS(this.namespaces.gpx, type);\n- n.appendChild(this.createTextNode(metadata[type]));\n- node.appendChild(n);\n- }\n- }\n- return node;\n- },\n-\n- /**\n- * Method: buildFeatureNode\n- * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- *\n- * Returns:\n- * {DOMElement} - The created node, either a 'wpt' or a 'trk'.\n- */\n- buildFeatureNode: function(feature) {\n- var geometry = feature.geometry;\n- geometry = geometry.clone();\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- var wpt = this.buildWptNode(geometry);\n- this.appendAttributesNode(wpt, feature);\n- return wpt;\n- } else {\n- var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n- this.appendAttributesNode(trkNode, feature);\n- var trkSegNodes = this.buildTrkSegNode(geometry);\n- trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?\n- trkSegNodes : [trkSegNodes];\n- for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n- trkNode.appendChild(trkSegNodes[i]);\n- }\n- return trkNode;\n- }\n- },\n-\n- /**\n- * Method: buildTrkSegNode\n- * Builds trkseg node(s) given a geometry\n- *\n- * Parameters:\n- * trknode\n- * geometry - {<OpenLayers.Geometry>}\n- */\n- buildTrkSegNode: function(geometry) {\n- var node,\n- i,\n- len,\n- point,\n- nodes;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n- geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- point = geometry.components[i];\n- node.appendChild(this.buildTrkPtNode(point));\n- }\n- return node;\n- } else {\n- nodes = [];\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- nodes.push(this.buildTrkSegNode(geometry.components[i]));\n- }\n- return nodes;\n }\n- },\n-\n- /**\n- * Method: buildTrkPtNode\n- * Builds a trkpt node given a point \n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {DOMElement} A trkpt node\n- */\n- buildTrkPtNode: function(point) {\n- var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n- node.setAttribute(\"lon\", point.x);\n- node.setAttribute(\"lat\", point.y);\n- return node;\n- },\n \n- /**\n- * Method: buildWptNode\n- * Builds a wpt node given a point\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {DOMElement} A wpt node\n- */\n- buildWptNode: function(geometry) {\n- var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n- node.setAttribute(\"lon\", geometry.x);\n- node.setAttribute(\"lat\", geometry.y);\n- return node;\n+ return new OpenLayers.Layer.WMTS(\n+ OpenLayers.Util.applyDefaults(config, {\n+ url: url,\n+ requestEncoding: requestEncoding,\n+ name: layerDef.title,\n+ style: style.identifier,\n+ format: format,\n+ matrixIds: matrixSet.matrixIds,\n+ matrixSet: matrixSet.identifier,\n+ projection: projection,\n+ units: units,\n+ resolutions: config.isBaseLayer === false ? undefined : resolutions,\n+ serverResolutions: resolutions,\n+ tileFullExtent: matrixSet.bounds,\n+ dimensions: dimensions,\n+ params: params\n+ })\n+ );\n },\n \n- /**\n- * Method: appendAttributesNode\n- * Adds some attributes node.\n- *\n- * Parameters:\n- * node - {DOMElement} the node to append the attribute nodes to.\n- * feature - {<OpenLayers.Feature.Vector>}\n- */\n- appendAttributesNode: function(node, feature) {\n- var name = this.createElementNS(this.namespaces.gpx, 'name');\n- name.appendChild(this.createTextNode(\n- feature.attributes.name || feature.id));\n- node.appendChild(name);\n- var desc = this.createElementNS(this.namespaces.gpx, 'desc');\n- desc.appendChild(this.createTextNode(\n- feature.attributes.description || this.defaultDesc));\n- node.appendChild(desc);\n- // TBD - deal with remaining (non name/description) attributes.\n- },\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n \n- CLASS_NAME: \"OpenLayers.Format.GPX\"\n });\n /* ======================================================================\n OpenLayers/Format/GeoRSS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -50294,510 +38751,181 @@\n */\n _getChildValue: function(node, nsuri, name, def) {\n var value;\n var eles = this.getElementsByTagNameNS(node, nsuri, name);\n if (eles && eles[0] && eles[0].firstChild &&\n eles[0].firstChild.nodeValue) {\n value = this.getChildValue(eles[0]);\n- } else {\n- value = (def == undefined) ? \"\" : def;\n- }\n- return value;\n- },\n-\n- /**\n- * APIMethod: read\n- * Return a list of features from a GeoRSS doc\n- *\n- * Parameters:\n- * doc - {Element} \n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)}\n- */\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n- }\n-\n- /* Try RSS items first, then Atom entries */\n- var itemlist = null;\n- itemlist = this.getElementsByTagNameNS(doc, '*', 'item');\n- if (itemlist.length == 0) {\n- itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');\n- }\n-\n- var numItems = itemlist.length;\n- var features = new Array(numItems);\n- for (var i = 0; i < numItems; i++) {\n- features[i] = this.createFeatureFromItem(itemlist[i]);\n- }\n- return features;\n- },\n-\n-\n- /**\n- * APIMethod: write\n- * Accept Feature Collection, and return a string. \n- * \n- * Parameters: \n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n- */\n- write: function(features) {\n- var georss;\n- if (OpenLayers.Util.isArray(features)) {\n- georss = this.createElementNS(this.rssns, \"rss\");\n- for (var i = 0, len = features.length; i < len; i++) {\n- georss.appendChild(this.createFeatureXML(features[i]));\n- }\n- } else {\n- georss = this.createFeatureXML(features);\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);\n- },\n-\n- /**\n- * Method: createFeatureXML\n- * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- *\n- * Returns:\n- * {DOMElement}\n- */\n- createFeatureXML: function(feature) {\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n- var featureNode = this.createElementNS(this.rssns, \"item\");\n- var titleNode = this.createElementNS(this.rssns, \"title\");\n- titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : \"\"));\n- var descNode = this.createElementNS(this.rssns, \"description\");\n- descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : \"\"));\n- featureNode.appendChild(titleNode);\n- featureNode.appendChild(descNode);\n- if (feature.attributes.link) {\n- var linkNode = this.createElementNS(this.rssns, \"link\");\n- linkNode.appendChild(this.createTextNode(feature.attributes.link));\n- featureNode.appendChild(linkNode);\n- }\n- for (var attr in feature.attributes) {\n- if (attr == \"link\" || attr == \"title\" || attr == \"description\") {\n- continue;\n- }\n- var attrText = this.createTextNode(feature.attributes[attr]);\n- var nodename = attr;\n- if (attr.search(\":\") != -1) {\n- nodename = attr.split(\":\")[1];\n- }\n- var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n- attrContainer.appendChild(attrText);\n- featureNode.appendChild(attrContainer);\n- }\n- featureNode.appendChild(geometryNode);\n- return featureNode;\n- },\n-\n- /** \n- * Method: buildGeometryNode\n- * builds a GeoRSS node with a given geometry\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- *\n- * Returns:\n- * {DOMElement} A gml node.\n- */\n- buildGeometryNode: function(geometry) {\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- var node;\n- // match Polygon\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n- node = this.createElementNS(this.georssns, 'georss:polygon');\n-\n- node.appendChild(this.buildCoordinatesNode(geometry.components[0]));\n- }\n- // match LineString\n- else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n- node = this.createElementNS(this.georssns, 'georss:line');\n-\n- node.appendChild(this.buildCoordinatesNode(geometry));\n- }\n- // match Point\n- else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- node = this.createElementNS(this.georssns, 'georss:point');\n- node.appendChild(this.buildCoordinatesNode(geometry));\n- } else {\n- throw \"Couldn't parse \" + geometry.CLASS_NAME;\n- }\n- return node;\n- },\n-\n- /** \n- * Method: buildCoordinatesNode\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- */\n- buildCoordinatesNode: function(geometry) {\n- var points = null;\n-\n- if (geometry.components) {\n- points = geometry.components;\n- }\n-\n- var path;\n- if (points) {\n- var numPoints = points.length;\n- var parts = new Array(numPoints);\n- for (var i = 0; i < numPoints; i++) {\n- parts[i] = points[i].y + \" \" + points[i].x;\n- }\n- path = parts.join(\" \");\n- } else {\n- path = geometry.y + \" \" + geometry.x;\n- }\n- return this.createTextNode(path);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.GeoRSS\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSCapabilities\n- * Read WMS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.1\".\n- */\n- defaultVersion: \"1.1.1\",\n-\n- /**\n- * APIProperty: profile\n- * {String} If provided, use a custom profile.\n- *\n- * Currently supported profiles:\n- * - WMSC - parses vendor specific capabilities for WMS-C.\n- */\n- profile: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSCapabilities\n- * Create a new parser for WMS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of layers. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named layers.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMTSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMTSCapabilities\n- * Read WMTS Capabilities.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n-\n- /**\n- * APIProperty: yx\n- * {Object} Members in the yx object are used to determine if a CRS URN\n- * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n- * and values are boolean. By default, the following CRS URN are\n- * assumed to correspond to a CRS with y,x axis order:\n- *\n- * * urn:ogc:def:crs:EPSG::4326\n- */\n- yx: {\n- \"urn:ogc:def:crs:EPSG::4326\": true\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WMTSCapabilities\n- * Create a new parser for WMTS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n+ } else {\n+ value = (def == undefined) ? \"\" : def;\n+ }\n+ return value;\n+ },\n \n /**\n * APIMethod: read\n- * Read capabilities data from a string, and return information about\n- * the service (offering and observedProperty mostly).\n+ * Return a list of features from a GeoRSS doc\n *\n * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n+ * doc - {Element} \n *\n * Returns:\n- * {Object} Info about the WMTS Capabilities\n+ * {Array(<OpenLayers.Feature.Vector>)}\n */\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ }\n \n- /**\n- * APIMethod: createLayer\n- * Create a WMTS layer given a capabilities object.\n- *\n- * Parameters:\n- * capabilities - {Object} The object returned from a <read> call to this\n- * format.\n- * config - {Object} Configuration properties for the layer. Defaults for\n- * the layer will apply if not provided.\n- *\n- * Required config properties:\n- * layer - {String} The layer identifier.\n- *\n- * Optional config properties:\n- * matrixSet - {String} The matrix set identifier, required if there is \n- * more than one matrix set in the layer capabilities.\n- * style - {String} The name of the style\n- * format - {String} Image format for the layer. Default is the first\n- * format returned in the GetCapabilities response.\n- * param - {Object} The dimensions values eg: {\"Year\": \"2012\"}\n- *\n- * Returns:\n- * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an\n- * error if an incomplete config is provided. Returns undefined if no\n- * layer could be created with the provided config.\n- */\n- createLayer: function(capabilities, config) {\n- var layer;\n+ /* Try RSS items first, then Atom entries */\n+ var itemlist = null;\n+ itemlist = this.getElementsByTagNameNS(doc, '*', 'item');\n+ if (itemlist.length == 0) {\n+ itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');\n+ }\n \n- // confirm required properties are supplied in config\n- if (!('layer' in config)) {\n- throw new Error(\"Missing property 'layer' in configuration.\");\n+ var numItems = itemlist.length;\n+ var features = new Array(numItems);\n+ for (var i = 0; i < numItems; i++) {\n+ features[i] = this.createFeatureFromItem(itemlist[i]);\n }\n+ return features;\n+ },\n \n- var contents = capabilities.contents;\n \n- // find the layer definition with the given identifier\n- var layers = contents.layers;\n- var layerDef;\n- for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n- if (contents.layers[i].identifier === config.layer) {\n- layerDef = contents.layers[i];\n- break;\n+ /**\n+ * APIMethod: write\n+ * Accept Feature Collection, and return a string. \n+ * \n+ * Parameters: \n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n+ */\n+ write: function(features) {\n+ var georss;\n+ if (OpenLayers.Util.isArray(features)) {\n+ georss = this.createElementNS(this.rssns, \"rss\");\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ georss.appendChild(this.createFeatureXML(features[i]));\n }\n+ } else {\n+ georss = this.createFeatureXML(features);\n }\n- if (!layerDef) {\n- throw new Error(\"Layer not found\");\n- }\n-\n- var format = config.format;\n- if (!format && layerDef.formats && layerDef.formats.length) {\n- format = layerDef.formats[0];\n- }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);\n+ },\n \n- // find the matrixSet definition\n- var matrixSet;\n- if (config.matrixSet) {\n- matrixSet = contents.tileMatrixSets[config.matrixSet];\n- } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n- matrixSet = contents.tileMatrixSets[\n- layerDef.tileMatrixSetLinks[0].tileMatrixSet];\n- }\n- if (!matrixSet) {\n- throw new Error(\"matrixSet not found\");\n+ /**\n+ * Method: createFeatureXML\n+ * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ createFeatureXML: function(feature) {\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n+ var featureNode = this.createElementNS(this.rssns, \"item\");\n+ var titleNode = this.createElementNS(this.rssns, \"title\");\n+ titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : \"\"));\n+ var descNode = this.createElementNS(this.rssns, \"description\");\n+ descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : \"\"));\n+ featureNode.appendChild(titleNode);\n+ featureNode.appendChild(descNode);\n+ if (feature.attributes.link) {\n+ var linkNode = this.createElementNS(this.rssns, \"link\");\n+ linkNode.appendChild(this.createTextNode(feature.attributes.link));\n+ featureNode.appendChild(linkNode);\n }\n-\n- // get the default style for the layer\n- var style;\n- for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n- style = layerDef.styles[i];\n- if (style.isDefault) {\n- break;\n+ for (var attr in feature.attributes) {\n+ if (attr == \"link\" || attr == \"title\" || attr == \"description\") {\n+ continue;\n+ }\n+ var attrText = this.createTextNode(feature.attributes[attr]);\n+ var nodename = attr;\n+ if (attr.search(\":\") != -1) {\n+ nodename = attr.split(\":\")[1];\n }\n+ var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n+ attrContainer.appendChild(attrText);\n+ featureNode.appendChild(attrContainer);\n }\n+ featureNode.appendChild(geometryNode);\n+ return featureNode;\n+ },\n \n- var requestEncoding = config.requestEncoding;\n- if (!requestEncoding) {\n- requestEncoding = \"KVP\";\n- if (capabilities.operationsMetadata.GetTile.dcp.http) {\n- var http = capabilities.operationsMetadata.GetTile.dcp.http;\n- // Get first get method\n- if (http.get[0].constraints) {\n- var constraints = http.get[0].constraints;\n- var allowedValues = constraints.GetEncoding.allowedValues;\n+ /** \n+ * Method: buildGeometryNode\n+ * builds a GeoRSS node with a given geometry\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {DOMElement} A gml node.\n+ */\n+ buildGeometryNode: function(geometry) {\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n+ }\n+ var node;\n+ // match Polygon\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n+ node = this.createElementNS(this.georssns, 'georss:polygon');\n \n- // The OGC documentation is not clear if we should use\n- // REST or RESTful, ArcGis use RESTful,\n- // and OpenLayers use REST.\n- if (!allowedValues.KVP &&\n- (allowedValues.REST || allowedValues.RESTful)) {\n- requestEncoding = \"REST\";\n- }\n- }\n- }\n+ node.appendChild(this.buildCoordinatesNode(geometry.components[0]));\n }\n+ // match LineString\n+ else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n+ node = this.createElementNS(this.georssns, 'georss:line');\n \n- var dimensions = [];\n- var params = config.params || {};\n- // to don't overwrite the changes in the applyDefaults\n- delete config.params;\n- for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n- var dimension = layerDef.dimensions[id];\n- dimensions.push(dimension.identifier);\n- if (!params.hasOwnProperty(dimension.identifier)) {\n- params[dimension.identifier] = dimension['default'];\n- }\n+ node.appendChild(this.buildCoordinatesNode(geometry));\n+ }\n+ // match Point\n+ else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ node = this.createElementNS(this.georssns, 'georss:point');\n+ node.appendChild(this.buildCoordinatesNode(geometry));\n+ } else {\n+ throw \"Couldn't parse \" + geometry.CLASS_NAME;\n }\n+ return node;\n+ },\n \n- var projection = config.projection || matrixSet.supportedCRS.replace(\n- /urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n- var units = config.units ||\n- (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n+ /** \n+ * Method: buildCoordinatesNode\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ */\n+ buildCoordinatesNode: function(geometry) {\n+ var points = null;\n \n- var resolutions = [];\n- for (var mid in matrixSet.matrixIds) {\n- if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n- resolutions.push(\n- matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 /\n- OpenLayers.METERS_PER_INCH /\n- OpenLayers.INCHES_PER_UNIT[units]);\n- }\n+ if (geometry.components) {\n+ points = geometry.components;\n }\n \n- var url;\n- if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n- url = [];\n- var resourceUrls = layerDef.resourceUrls,\n- resourceUrl;\n- for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n- resourceUrl = layerDef.resourceUrls[t];\n- if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n- url.push(resourceUrl.template);\n- }\n+ var path;\n+ if (points) {\n+ var numPoints = points.length;\n+ var parts = new Array(numPoints);\n+ for (var i = 0; i < numPoints; i++) {\n+ parts[i] = points[i].y + \" \" + points[i].x;\n }\n+ path = parts.join(\" \");\n } else {\n- var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n- url = [];\n- var constraint;\n- for (var i = 0, ii = httpGet.length; i < ii; i++) {\n- constraint = httpGet[i].constraints;\n- if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) {\n- url.push(httpGet[i].url);\n- }\n- }\n+ path = geometry.y + \" \" + geometry.x;\n }\n-\n- return new OpenLayers.Layer.WMTS(\n- OpenLayers.Util.applyDefaults(config, {\n- url: url,\n- requestEncoding: requestEncoding,\n- name: layerDef.title,\n- style: style.identifier,\n- format: format,\n- matrixIds: matrixSet.matrixIds,\n- matrixSet: matrixSet.identifier,\n- projection: projection,\n- units: units,\n- resolutions: config.isBaseLayer === false ? undefined : resolutions,\n- serverResolutions: resolutions,\n- tileFullExtent: matrixSet.bounds,\n- dimensions: dimensions,\n- params: params\n- })\n- );\n+ return this.createTextNode(path);\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n-\n+ CLASS_NAME: \"OpenLayers.Format.GeoRSS\"\n });\n /* ======================================================================\n- OpenLayers/Format/CSWGetRecords.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CSWGetRecords\n- * Default version is 2.0.2.\n- *\n- * Returns:\n- * {<OpenLayers.Format>} A CSWGetRecords format of the given version.\n- */\n-OpenLayers.Format.CSWGetRecords = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Format.CSWGetRecords.DEFAULTS\n- );\n- var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetRecords version: \" + options.version;\n- }\n- return new cls(options);\n-};\n-\n-/**\n- * Constant: DEFAULTS\n- * {Object} Default properties for the CSWGetRecords format.\n- */\n-OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n- \"version\": \"2.0.2\"\n-};\n-/* ======================================================================\n OpenLayers/Format/CQL.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -51484,99 +39612,14 @@\n return schema;\n },\n \n CLASS_NAME: \"OpenLayers.Format.WFSDescribeFeatureType\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/SLD.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- * @requires OpenLayers/Style.js\n- * @requires OpenLayers/Rule.js\n- * @requires OpenLayers/Filter/FeatureId.js\n- * @requires OpenLayers/Filter/Logical.js\n- * @requires OpenLayers/Filter/Comparison.js\n- * @requires OpenLayers/Filter/Spatial.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SLD\n- * Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: profile\n- * {String} If provided, use a custom profile.\n- *\n- * Currently supported profiles:\n- * - GeoServer - parses GeoServer vendor specific capabilities for SLD.\n- */\n- profile: null,\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n-\n- /**\n- * APIProperty: stringifyOutput\n- * {Boolean} If true, write will return a string otherwise a DOMElement.\n- * Default is true.\n- */\n- stringifyOutput: true,\n-\n- /**\n- * APIProperty: namedLayersAsArray\n- * {Boolean} Generate a namedLayers array. If false, the namedLayers\n- * property value will be an object keyed by layer name. Default is\n- * false.\n- */\n- namedLayersAsArray: false,\n-\n- /**\n- * APIMethod: write\n- * Write a SLD document given a list of styles.\n- *\n- * Parameters:\n- * sld - {Object} An object representing the SLD.\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {String} An SLD document string.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read and SLD doc and return an object representing the SLD.\n- *\n- * Parameters:\n- * data - {String | DOMElement} Data to read.\n- * options - {Object} Options for the reader.\n- *\n- * Returns:\n- * {Object} An object representing the SLD.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.SLD\"\n-});\n-/* ======================================================================\n OpenLayers/Format/WFS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -51798,84 +39841,1751 @@\n destroy: function() {\n this.layer = null;\n },\n \n CLASS_NAME: \"OpenLayers.Format.WFS\"\n });\n /* ======================================================================\n- OpenLayers/Format/XLS.js\n+ OpenLayers/Format/KML.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Date.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n+ * @requires OpenLayers/Projection.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.KML\n+ * Read/Write KML. Create a new instance with the <OpenLayers.Format.KML>\n+ * constructor. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ kml: \"http://www.opengis.net/kml/2.2\",\n+ gx: \"http://www.google.com/kml/ext/2.2\"\n+ },\n+\n+ /**\n+ * APIProperty: kmlns\n+ * {String} KML Namespace to use. Defaults to 2.0 namespace.\n+ */\n+ kmlns: \"http://earth.google.com/kml/2.0\",\n+\n+ /** \n+ * APIProperty: placemarksDesc\n+ * {String} Name of the placemarks. Default is \"No description available\".\n+ */\n+ placemarksDesc: \"No description available\",\n+\n+ /** \n+ * APIProperty: foldersName\n+ * {String} Name of the folders. Default is \"OpenLayers export\".\n+ * If set to null, no name element will be created.\n+ */\n+ foldersName: \"OpenLayers export\",\n+\n+ /** \n+ * APIProperty: foldersDesc\n+ * {String} Description of the folders. Default is \"Exported on [date].\"\n+ * If set to null, no description element will be created.\n+ */\n+ foldersDesc: \"Exported on \" + new Date(),\n+\n+ /**\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract attributes from KML. Default is true.\n+ * Extracting styleUrls requires this to be set to true\n+ * Note that currently only Data and SimpleData \n+ * elements are handled.\n+ */\n+ extractAttributes: true,\n+\n+ /**\n+ * APIProperty: kvpAttributes\n+ * {Boolean} Only used if extractAttributes is true.\n+ * If set to true, attributes will be simple\n+ * key-value pairs, compatible with other formats,\n+ * Any displayName elements will be ignored.\n+ * If set to false, attributes will be objects,\n+ * retaining any displayName elements, but not\n+ * compatible with other formats. Any CDATA in\n+ * displayName will be read in as a string value.\n+ * Default is false.\n+ */\n+ kvpAttributes: false,\n+\n+ /**\n+ * Property: extractStyles\n+ * {Boolean} Extract styles from KML. Default is false.\n+ * Extracting styleUrls also requires extractAttributes to be\n+ * set to true\n+ */\n+ extractStyles: false,\n+\n+ /**\n+ * APIProperty: extractTracks\n+ * {Boolean} Extract gx:Track elements from Placemark elements. Default\n+ * is false. If true, features will be generated for all points in\n+ * all gx:Track elements. Features will have a when (Date) attribute\n+ * based on when elements in the track. If tracks include angle\n+ * elements, features will have heading, tilt, and roll attributes.\n+ * If track point coordinates have three values, features will have\n+ * an altitude attribute with the third coordinate value.\n+ */\n+ extractTracks: false,\n+\n+ /**\n+ * APIProperty: trackAttributes\n+ * {Array} If <extractTracks> is true, points within gx:Track elements will \n+ * be parsed as features with when, heading, tilt, and roll attributes.\n+ * Any additional attribute names can be provided in <trackAttributes>.\n+ */\n+ trackAttributes: null,\n+\n+ /**\n+ * Property: internalns\n+ * {String} KML Namespace to use -- defaults to the namespace of the\n+ * Placemark node being parsed, but falls back to kmlns. \n+ */\n+ internalns: null,\n+\n+ /**\n+ * Property: features\n+ * {Array} Array of features\n+ * \n+ */\n+ features: null,\n+\n+ /**\n+ * Property: styles\n+ * {Object} Storage of style objects\n+ * \n+ */\n+ styles: null,\n+\n+ /**\n+ * Property: styleBaseUrl\n+ * {String}\n+ */\n+ styleBaseUrl: \"\",\n+\n+ /**\n+ * Property: fetched\n+ * {Object} Storage of KML URLs that have been fetched before\n+ * in order to prevent reloading them.\n+ */\n+ fetched: null,\n+\n+ /**\n+ * APIProperty: maxDepth\n+ * {Integer} Maximum depth for recursive loading external KML URLs \n+ * Defaults to 0: do no external fetching\n+ */\n+ maxDepth: 0,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.KML\n+ * Create a new parser for KML.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ // compile regular expressions once instead of every time they are used\n+ this.regExes = {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g),\n+ kmlColor: (/(\\w{2})(\\w{2})(\\w{2})(\\w{2})/),\n+ kmlIconPalette: (/root:\\/\\/icons\\/palette-(\\d+)(\\.\\w+)/),\n+ straightBracket: (/\\$\\[(.*?)\\]/g)\n+ };\n+ // KML coordinates are always in longlat WGS84\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read data from a string, and return a list of features. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} List of features.\n+ */\n+ read: function(data) {\n+ this.features = [];\n+ this.styles = {};\n+ this.fetched = {};\n+\n+ // Set default options \n+ var options = {\n+ depth: 0,\n+ styleBaseUrl: this.styleBaseUrl\n+ };\n+\n+ return this.parseData(data, options);\n+ },\n+\n+ /**\n+ * Method: parseData\n+ * Read data from a string, and return a list of features. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ * options - {Object} Hash of options\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} List of features.\n+ */\n+ parseData: function(data, options) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+\n+ // Loop throught the following node types in this order and\n+ // process the nodes found \n+ var types = [\"Link\", \"NetworkLink\", \"Style\", \"StyleMap\", \"Placemark\"];\n+ for (var i = 0, len = types.length; i < len; ++i) {\n+ var type = types[i];\n+\n+ var nodes = this.getElementsByTagNameNS(data, \"*\", type);\n+\n+ // skip to next type if no nodes are found\n+ if (nodes.length == 0) {\n+ continue;\n+ }\n+\n+ switch (type.toLowerCase()) {\n+\n+ // Fetch external links \n+ case \"link\":\n+ case \"networklink\":\n+ this.parseLinks(nodes, options);\n+ break;\n+\n+ // parse style information\n+ case \"style\":\n+ if (this.extractStyles) {\n+ this.parseStyles(nodes, options);\n+ }\n+ break;\n+ case \"stylemap\":\n+ if (this.extractStyles) {\n+ this.parseStyleMaps(nodes, options);\n+ }\n+ break;\n+\n+ // parse features\n+ case \"placemark\":\n+ this.parseFeatures(nodes, options);\n+ break;\n+ }\n+ }\n+\n+ return this.features;\n+ },\n+\n+ /**\n+ * Method: parseLinks\n+ * Finds URLs of linked KML documents and fetches them\n+ * \n+ * Parameters: \n+ * nodes - {Array} of {DOMElement} data to read/parse.\n+ * options - {Object} Hash of options\n+ * \n+ */\n+ parseLinks: function(nodes, options) {\n+\n+ // Fetch external links <NetworkLink> and <Link>\n+ // Don't do anything if we have reached our maximum depth for recursion\n+ if (options.depth >= this.maxDepth) {\n+ return false;\n+ }\n+\n+ // increase depth\n+ var newOptions = OpenLayers.Util.extend({}, options);\n+ newOptions.depth++;\n+\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var href = this.parseProperty(nodes[i], \"*\", \"href\");\n+ if (href && !this.fetched[href]) {\n+ this.fetched[href] = true; // prevent reloading the same urls\n+ var data = this.fetchLink(href);\n+ if (data) {\n+ this.parseData(data, newOptions);\n+ }\n+ }\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: fetchLink\n+ * Fetches a URL and returns the result\n+ * \n+ * Parameters: \n+ * href - {String} url to be fetched\n+ * \n+ */\n+ fetchLink: function(href) {\n+ var request = OpenLayers.Request.GET({\n+ url: href,\n+ async: false\n+ });\n+ if (request) {\n+ return request.responseText;\n+ }\n+ },\n+\n+ /**\n+ * Method: parseStyles\n+ * Parses <Style> nodes\n+ * \n+ * Parameters: \n+ * nodes - {Array} of {DOMElement} data to read/parse.\n+ * options - {Object} Hash of options\n+ * \n+ */\n+ parseStyles: function(nodes, options) {\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var style = this.parseStyle(nodes[i]);\n+ if (style) {\n+ var styleName = (options.styleBaseUrl || \"\") + \"#\" + style.id;\n+\n+ this.styles[styleName] = style;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: parseKmlColor\n+ * Parses a kml color (in 'aabbggrr' format) and returns the corresponding \n+ * color and opacity or null if the color is invalid.\n+ *\n+ * Parameters: \n+ * kmlColor - {String} a kml formated color\n+ *\n+ * Returns:\n+ * {Object}\n+ */\n+ parseKmlColor: function(kmlColor) {\n+ var color = null;\n+ if (kmlColor) {\n+ var matches = kmlColor.match(this.regExes.kmlColor);\n+ if (matches) {\n+ color = {\n+ color: '#' + matches[4] + matches[3] + matches[2],\n+ opacity: parseInt(matches[1], 16) / 255\n+ };\n+ }\n+ }\n+ return color;\n+ },\n+\n+ /**\n+ * Method: parseStyle\n+ * Parses the children of a <Style> node and builds the style hash\n+ * accordingly\n+ * \n+ * Parameters: \n+ * node - {DOMElement} <Style> node\n+ * \n+ */\n+ parseStyle: function(node) {\n+ var style = {};\n+\n+ var types = [\"LineStyle\", \"PolyStyle\", \"IconStyle\", \"BalloonStyle\",\n+ \"LabelStyle\"\n+ ];\n+ var type, styleTypeNode, nodeList, geometry, parser;\n+ for (var i = 0, len = types.length; i < len; ++i) {\n+ type = types[i];\n+ styleTypeNode = this.getElementsByTagNameNS(node, \"*\", type)[0];\n+ if (!styleTypeNode) {\n+ continue;\n+ }\n+\n+ // only deal with first geometry of this type\n+ switch (type.toLowerCase()) {\n+ case \"linestyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"strokeColor\"] = color.color;\n+ style[\"strokeOpacity\"] = color.opacity;\n+ }\n+\n+ var width = this.parseProperty(styleTypeNode, \"*\", \"width\");\n+ if (width) {\n+ style[\"strokeWidth\"] = width;\n+ }\n+ break;\n+\n+ case \"polystyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"fillOpacity\"] = color.opacity;\n+ style[\"fillColor\"] = color.color;\n+ }\n+ // Check if fill is disabled\n+ var fill = this.parseProperty(styleTypeNode, \"*\", \"fill\");\n+ if (fill == \"0\") {\n+ style[\"fillColor\"] = \"none\";\n+ }\n+ // Check if outline is disabled\n+ var outline = this.parseProperty(styleTypeNode, \"*\", \"outline\");\n+ if (outline == \"0\") {\n+ style[\"strokeWidth\"] = \"0\";\n+ }\n+\n+ break;\n+\n+ case \"iconstyle\":\n+ // set scale\n+ var scale = parseFloat(this.parseProperty(styleTypeNode,\n+ \"*\", \"scale\") || 1);\n+\n+ // set default width and height of icon\n+ var width = 32 * scale;\n+ var height = 32 * scale;\n+\n+ var iconNode = this.getElementsByTagNameNS(styleTypeNode,\n+ \"*\",\n+ \"Icon\")[0];\n+ if (iconNode) {\n+ var href = this.parseProperty(iconNode, \"*\", \"href\");\n+ if (href) {\n+\n+ var w = this.parseProperty(iconNode, \"*\", \"w\");\n+ var h = this.parseProperty(iconNode, \"*\", \"h\");\n+\n+ // Settings for Google specific icons that are 64x64\n+ // We set the width and height to 64 and halve the\n+ // scale to prevent icons from being too big\n+ var google = \"http://maps.google.com/mapfiles/kml\";\n+ if (OpenLayers.String.startsWith(\n+ href, google) && !w && !h) {\n+ w = 64;\n+ h = 64;\n+ scale = scale / 2;\n+ }\n+\n+ // if only dimension is defined, make sure the\n+ // other one has the same value\n+ w = w || h;\n+ h = h || w;\n+\n+ if (w) {\n+ width = parseInt(w) * scale;\n+ }\n+\n+ if (h) {\n+ height = parseInt(h) * scale;\n+ }\n+\n+ // support for internal icons \n+ // (/root://icons/palette-x.png)\n+ // x and y tell the position on the palette:\n+ // - in pixels\n+ // - starting from the left bottom\n+ // We translate that to a position in the list \n+ // and request the appropriate icon from the \n+ // google maps website\n+ var matches = href.match(this.regExes.kmlIconPalette);\n+ if (matches) {\n+ var palette = matches[1];\n+ var file_extension = matches[2];\n+\n+ var x = this.parseProperty(iconNode, \"*\", \"x\");\n+ var y = this.parseProperty(iconNode, \"*\", \"y\");\n+\n+ var posX = x ? x / 32 : 0;\n+ var posY = y ? (7 - y / 32) : 7;\n+\n+ var pos = posY * 8 + posX;\n+ href = \"http://maps.google.com/mapfiles/kml/pal\" +\n+ palette + \"/icon\" + pos + file_extension;\n+ }\n+\n+ style[\"graphicOpacity\"] = 1; // fully opaque\n+ style[\"externalGraphic\"] = href;\n+ }\n+\n+ }\n+\n+\n+ // hotSpots define the offset for an Icon\n+ var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode,\n+ \"*\",\n+ \"hotSpot\")[0];\n+ if (hotSpotNode) {\n+ var x = parseFloat(hotSpotNode.getAttribute(\"x\"));\n+ var y = parseFloat(hotSpotNode.getAttribute(\"y\"));\n+\n+ var xUnits = hotSpotNode.getAttribute(\"xunits\");\n+ if (xUnits == \"pixels\") {\n+ style[\"graphicXOffset\"] = -x * scale;\n+ } else if (xUnits == \"insetPixels\") {\n+ style[\"graphicXOffset\"] = -width + (x * scale);\n+ } else if (xUnits == \"fraction\") {\n+ style[\"graphicXOffset\"] = -width * x;\n+ }\n+\n+ var yUnits = hotSpotNode.getAttribute(\"yunits\");\n+ if (yUnits == \"pixels\") {\n+ style[\"graphicYOffset\"] = -height + (y * scale) + 1;\n+ } else if (yUnits == \"insetPixels\") {\n+ style[\"graphicYOffset\"] = -(y * scale) + 1;\n+ } else if (yUnits == \"fraction\") {\n+ style[\"graphicYOffset\"] = -height * (1 - y) + 1;\n+ }\n+ }\n+\n+ style[\"graphicWidth\"] = width;\n+ style[\"graphicHeight\"] = height;\n+ break;\n+\n+ case \"balloonstyle\":\n+ var balloonStyle = OpenLayers.Util.getXmlNodeValue(\n+ styleTypeNode);\n+ if (balloonStyle) {\n+ style[\"balloonStyle\"] = balloonStyle.replace(\n+ this.regExes.straightBracket, \"${$1}\");\n+ }\n+ break;\n+ case \"labelstyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"fontColor\"] = color.color;\n+ style[\"fontOpacity\"] = color.opacity;\n+ }\n+ break;\n+\n+ default:\n+ }\n+ }\n+\n+ // Some polygons have no line color, so we use the fillColor for that\n+ if (!style[\"strokeColor\"] && style[\"fillColor\"]) {\n+ style[\"strokeColor\"] = style[\"fillColor\"];\n+ }\n+\n+ var id = node.getAttribute(\"id\");\n+ if (id && style) {\n+ style.id = id;\n+ }\n+\n+ return style;\n+ },\n+\n+ /**\n+ * Method: parseStyleMaps\n+ * Parses <StyleMap> nodes, but only uses the 'normal' key\n+ * \n+ * Parameters: \n+ * nodes - {Array} of {DOMElement} data to read/parse.\n+ * options - {Object} Hash of options\n+ * \n+ */\n+ parseStyleMaps: function(nodes, options) {\n+ // Only the default or \"normal\" part of the StyleMap is processed now\n+ // To do the select or \"highlight\" bit, we'd need to change lots more\n+\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var node = nodes[i];\n+ var pairs = this.getElementsByTagNameNS(node, \"*\",\n+ \"Pair\");\n+\n+ var id = node.getAttribute(\"id\");\n+ for (var j = 0, jlen = pairs.length; j < jlen; j++) {\n+ var pair = pairs[j];\n+ // Use the shortcut in the SLD format to quickly retrieve the \n+ // value of a node. Maybe it's good to have a method in \n+ // Format.XML to do this\n+ var key = this.parseProperty(pair, \"*\", \"key\");\n+ var styleUrl = this.parseProperty(pair, \"*\", \"styleUrl\");\n+\n+ if (styleUrl && key == \"normal\") {\n+ this.styles[(options.styleBaseUrl || \"\") + \"#\" + id] =\n+ this.styles[(options.styleBaseUrl || \"\") + styleUrl];\n+ }\n+\n+ // TODO: implement the \"select\" part\n+ //if (styleUrl && key == \"highlight\") {\n+ //}\n+\n+ }\n+ }\n+\n+ },\n+\n+\n+ /**\n+ * Method: parseFeatures\n+ * Loop through all Placemark nodes and parse them.\n+ * Will create a list of features\n+ * \n+ * Parameters: \n+ * nodes - {Array} of {DOMElement} data to read/parse.\n+ * options - {Object} Hash of options\n+ * \n+ */\n+ parseFeatures: function(nodes, options) {\n+ var features = [];\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var featureNode = nodes[i];\n+ var feature = this.parseFeature.apply(this, [featureNode]);\n+ if (feature) {\n+\n+ // Create reference to styleUrl \n+ if (this.extractStyles && feature.attributes &&\n+ feature.attributes.styleUrl) {\n+ feature.style = this.getStyle(feature.attributes.styleUrl, options);\n+ }\n+\n+ if (this.extractStyles) {\n+ // Make sure that <Style> nodes within a placemark are \n+ // processed as well\n+ var inlineStyleNode = this.getElementsByTagNameNS(featureNode,\n+ \"*\",\n+ \"Style\")[0];\n+ if (inlineStyleNode) {\n+ var inlineStyle = this.parseStyle(inlineStyleNode);\n+ if (inlineStyle) {\n+ feature.style = OpenLayers.Util.extend(\n+ feature.style, inlineStyle\n+ );\n+ }\n+ }\n+ }\n+\n+ // check if gx:Track elements should be parsed\n+ if (this.extractTracks) {\n+ var tracks = this.getElementsByTagNameNS(\n+ featureNode, this.namespaces.gx, \"Track\"\n+ );\n+ if (tracks && tracks.length > 0) {\n+ var track = tracks[0];\n+ var container = {\n+ features: [],\n+ feature: feature\n+ };\n+ this.readNode(track, container);\n+ if (container.features.length > 0) {\n+ features.push.apply(features, container.features);\n+ }\n+ }\n+ } else {\n+ // add feature to list of features\n+ features.push(feature);\n+ }\n+ } else {\n+ throw \"Bad Placemark: \" + i;\n+ }\n+ }\n+\n+ // add new features to existing feature list\n+ this.features = this.features.concat(features);\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"kml\": {\n+ \"when\": function(node, container) {\n+ container.whens.push(OpenLayers.Date.parse(\n+ this.getChildValue(node)\n+ ));\n+ },\n+ \"_trackPointAttribute\": function(node, container) {\n+ var name = node.nodeName.split(\":\").pop();\n+ container.attributes[name].push(this.getChildValue(node));\n+ }\n+ },\n+ \"gx\": {\n+ \"Track\": function(node, container) {\n+ var obj = {\n+ whens: [],\n+ points: [],\n+ angles: []\n+ };\n+ if (this.trackAttributes) {\n+ var name;\n+ obj.attributes = {};\n+ for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) {\n+ name = this.trackAttributes[i];\n+ obj.attributes[name] = [];\n+ if (!(name in this.readers.kml)) {\n+ this.readers.kml[name] = this.readers.kml._trackPointAttribute;\n+ }\n+ }\n+ }\n+ this.readChildNodes(node, obj);\n+ if (obj.whens.length !== obj.points.length) {\n+ throw new Error(\"gx:Track with unequal number of when (\" +\n+ obj.whens.length + \") and gx:coord (\" +\n+ obj.points.length + \") elements.\");\n+ }\n+ var hasAngles = obj.angles.length > 0;\n+ if (hasAngles && obj.whens.length !== obj.angles.length) {\n+ throw new Error(\"gx:Track with unequal number of when (\" +\n+ obj.whens.length + \") and gx:angles (\" +\n+ obj.angles.length + \") elements.\");\n+ }\n+ var feature, point, angles;\n+ for (var i = 0, ii = obj.whens.length; i < ii; ++i) {\n+ feature = container.feature.clone();\n+ feature.fid = container.feature.fid || container.feature.id;\n+ point = obj.points[i];\n+ feature.geometry = point;\n+ if (\"z\" in point) {\n+ feature.attributes.altitude = point.z;\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ feature.geometry.transform(\n+ this.externalProjection, this.internalProjection\n+ );\n+ }\n+ if (this.trackAttributes) {\n+ for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) {\n+ var name = this.trackAttributes[j];\n+ feature.attributes[name] = obj.attributes[name][i];\n+ }\n+ }\n+ feature.attributes.when = obj.whens[i];\n+ feature.attributes.trackId = container.feature.id;\n+ if (hasAngles) {\n+ angles = obj.angles[i];\n+ feature.attributes.heading = parseFloat(angles[0]);\n+ feature.attributes.tilt = parseFloat(angles[1]);\n+ feature.attributes.roll = parseFloat(angles[2]);\n+ }\n+ container.features.push(feature);\n+ }\n+ },\n+ \"coord\": function(node, container) {\n+ var str = this.getChildValue(node);\n+ var coords = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n+ var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);\n+ if (coords.length > 2) {\n+ point.z = parseFloat(coords[2]);\n+ }\n+ container.points.push(point);\n+ },\n+ \"angles\": function(node, container) {\n+ var str = this.getChildValue(node);\n+ var parts = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n+ container.angles.push(parts);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: parseFeature\n+ * This function is the core of the KML parsing code in OpenLayers.\n+ * It creates the geometries that are then attached to the returned\n+ * feature, and calls parseAttributes() to get attribute data out.\n+ *\n+ * Parameters:\n+ * node - {DOMElement}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A vector feature.\n+ */\n+ parseFeature: function(node) {\n+ // only accept one geometry per feature - look for highest \"order\"\n+ var order = [\"MultiGeometry\", \"Polygon\", \"LineString\", \"Point\"];\n+ var type, nodeList, geometry, parser;\n+ for (var i = 0, len = order.length; i < len; ++i) {\n+ type = order[i];\n+ this.internalns = node.namespaceURI ?\n+ node.namespaceURI : this.kmlns;\n+ nodeList = this.getElementsByTagNameNS(node,\n+ this.internalns, type);\n+ if (nodeList.length > 0) {\n+ // only deal with first geometry of this type\n+ var parser = this.parseGeometry[type.toLowerCase()];\n+ if (parser) {\n+ geometry = parser.apply(this, [nodeList[0]]);\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ } else {\n+ throw new TypeError(\"Unsupported geometry type: \" + type);\n+ }\n+ // stop looking for different geometry types\n+ break;\n+ }\n+ }\n+\n+ // construct feature (optionally with attributes)\n+ var attributes;\n+ if (this.extractAttributes) {\n+ attributes = this.parseAttributes(node);\n+ }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+\n+ var fid = node.getAttribute(\"id\") || node.getAttribute(\"name\");\n+ if (fid != null) {\n+ feature.fid = fid;\n+ }\n+\n+ return feature;\n+ },\n+\n+ /**\n+ * Method: getStyle\n+ * Retrieves a style from a style hash using styleUrl as the key\n+ * If the styleUrl doesn't exist yet, we try to fetch it \n+ * Internet\n+ * \n+ * Parameters: \n+ * styleUrl - {String} URL of style\n+ * options - {Object} Hash of options \n+ *\n+ * Returns:\n+ * {Object} - (reference to) Style hash\n+ */\n+ getStyle: function(styleUrl, options) {\n+\n+ var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);\n+\n+ var newOptions = OpenLayers.Util.extend({}, options);\n+ newOptions.depth++;\n+ newOptions.styleBaseUrl = styleBaseUrl;\n+\n+ // Fetch remote Style URLs (if not fetched before) \n+ if (!this.styles[styleUrl] &&\n+ !OpenLayers.String.startsWith(styleUrl, \"#\") &&\n+ newOptions.depth <= this.maxDepth &&\n+ !this.fetched[styleBaseUrl]) {\n+\n+ var data = this.fetchLink(styleBaseUrl);\n+ if (data) {\n+ this.parseData(data, newOptions);\n+ }\n+\n+ }\n+\n+ // return requested style\n+ var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);\n+ return style;\n+ },\n+\n+ /**\n+ * Property: parseGeometry\n+ * Properties of this object are the functions that parse geometries based\n+ * on their type.\n+ */\n+ parseGeometry: {\n+\n+ /**\n+ * Method: parseGeometry.point\n+ * Given a KML node representing a point geometry, create an OpenLayers\n+ * point geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A KML Point node.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>} A point geometry.\n+ */\n+ point: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n+ \"coordinates\");\n+ var coords = [];\n+ if (nodeList.length > 0) {\n+ var coordString = nodeList[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.removeSpace, \"\");\n+ coords = coordString.split(\",\");\n+ }\n+\n+ var point = null;\n+ if (coords.length > 1) {\n+ // preserve third dimension\n+ if (coords.length == 2) {\n+ coords[2] = null;\n+ }\n+ point = new OpenLayers.Geometry.Point(coords[0], coords[1],\n+ coords[2]);\n+ } else {\n+ throw \"Bad coordinate string: \" + coordString;\n+ }\n+ return point;\n+ },\n+\n+ /**\n+ * Method: parseGeometry.linestring\n+ * Given a KML node representing a linestring geometry, create an\n+ * OpenLayers linestring geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A KML LineString node.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.LineString>} A linestring geometry.\n+ */\n+ linestring: function(node, ring) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n+ \"coordinates\");\n+ var line = null;\n+ if (nodeList.length > 0) {\n+ var coordString = this.getChildValue(nodeList[0]);\n+\n+ coordString = coordString.replace(this.regExes.trimSpace,\n+ \"\");\n+ coordString = coordString.replace(this.regExes.trimComma,\n+ \",\");\n+ var pointList = coordString.split(this.regExes.splitSpace);\n+ var numPoints = pointList.length;\n+ var points = new Array(numPoints);\n+ var coords, numCoords;\n+ for (var i = 0; i < numPoints; ++i) {\n+ coords = pointList[i].split(\",\");\n+ numCoords = coords.length;\n+ if (numCoords > 1) {\n+ if (coords.length == 2) {\n+ coords[2] = null;\n+ }\n+ points[i] = new OpenLayers.Geometry.Point(coords[0],\n+ coords[1],\n+ coords[2]);\n+ } else {\n+ throw \"Bad LineString point coordinates: \" +\n+ pointList[i];\n+ }\n+ }\n+ if (numPoints) {\n+ if (ring) {\n+ line = new OpenLayers.Geometry.LinearRing(points);\n+ } else {\n+ line = new OpenLayers.Geometry.LineString(points);\n+ }\n+ } else {\n+ throw \"Bad LineString coordinates: \" + coordString;\n+ }\n+ }\n+\n+ return line;\n+ },\n+\n+ /**\n+ * Method: parseGeometry.polygon\n+ * Given a KML node representing a polygon geometry, create an\n+ * OpenLayers polygon geometry.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A KML Polygon node.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Polygon>} A polygon geometry.\n+ */\n+ polygon: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns,\n+ \"LinearRing\");\n+ var numRings = nodeList.length;\n+ var components = new Array(numRings);\n+ if (numRings > 0) {\n+ // this assumes exterior ring first, inner rings after\n+ var ring;\n+ for (var i = 0, len = nodeList.length; i < len; ++i) {\n+ ring = this.parseGeometry.linestring.apply(this,\n+ [nodeList[i], true]);\n+ if (ring) {\n+ components[i] = ring;\n+ } else {\n+ throw \"Bad LinearRing geometry: \" + i;\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.Polygon(components);\n+ },\n+\n+ /**\n+ * Method: parseGeometry.multigeometry\n+ * Given a KML node representing a multigeometry, create an\n+ * OpenLayers geometry collection.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A KML MultiGeometry node.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Collection>} A geometry collection.\n+ */\n+ multigeometry: function(node) {\n+ var child, parser;\n+ var parts = [];\n+ var children = node.childNodes;\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ var type = (child.prefix) ?\n+ child.nodeName.split(\":\")[1] :\n+ child.nodeName;\n+ var parser = this.parseGeometry[type.toLowerCase()];\n+ if (parser) {\n+ parts.push(parser.apply(this, [child]));\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.Collection(parts);\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: parseAttributes\n+ *\n+ * Parameters:\n+ * node - {DOMElement}\n+ *\n+ * Returns:\n+ * {Object} An attributes object.\n+ */\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+\n+ // Extended Data is parsed first.\n+ var edNodes = node.getElementsByTagName(\"ExtendedData\");\n+ if (edNodes.length) {\n+ attributes = this.parseExtendedData(edNodes[0]);\n+ }\n+\n+ // assume attribute nodes are type 1 children with a type 3 or 4 child\n+ var child, grandchildren, grandchild;\n+ var children = node.childNodes;\n+\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ grandchildren = child.childNodes;\n+ if (grandchildren.length >= 1 && grandchildren.length <= 3) {\n+ var grandchild;\n+ switch (grandchildren.length) {\n+ case 1:\n+ grandchild = grandchildren[0];\n+ break;\n+ case 2:\n+ var c1 = grandchildren[0];\n+ var c2 = grandchildren[1];\n+ grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ?\n+ c1 : c2;\n+ break;\n+ case 3:\n+ default:\n+ grandchild = grandchildren[1];\n+ break;\n+ }\n+ if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {\n+ var name = (child.prefix) ?\n+ child.nodeName.split(\":\")[1] :\n+ child.nodeName;\n+ var value = OpenLayers.Util.getXmlNodeValue(grandchild);\n+ if (value) {\n+ value = value.replace(this.regExes.trimSpace, \"\");\n+ attributes[name] = value;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return attributes;\n+ },\n+\n+ /**\n+ * Method: parseExtendedData\n+ * Parse ExtendedData from KML. Limited support for schemas/datatypes.\n+ * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata\n+ * for more information on extendeddata.\n+ */\n+ parseExtendedData: function(node) {\n+ var attributes = {};\n+ var i, len, data, key;\n+ var dataNodes = node.getElementsByTagName(\"Data\");\n+ for (i = 0, len = dataNodes.length; i < len; i++) {\n+ data = dataNodes[i];\n+ key = data.getAttribute(\"name\");\n+ var ed = {};\n+ var valueNode = data.getElementsByTagName(\"value\");\n+ if (valueNode.length) {\n+ ed['value'] = this.getChildValue(valueNode[0]);\n+ }\n+ if (this.kvpAttributes) {\n+ attributes[key] = ed['value'];\n+ } else {\n+ var nameNode = data.getElementsByTagName(\"displayName\");\n+ if (nameNode.length) {\n+ ed['displayName'] = this.getChildValue(nameNode[0]);\n+ }\n+ attributes[key] = ed;\n+ }\n+ }\n+ var simpleDataNodes = node.getElementsByTagName(\"SimpleData\");\n+ for (i = 0, len = simpleDataNodes.length; i < len; i++) {\n+ var ed = {};\n+ data = simpleDataNodes[i];\n+ key = data.getAttribute(\"name\");\n+ ed['value'] = this.getChildValue(data);\n+ if (this.kvpAttributes) {\n+ attributes[key] = ed['value'];\n+ } else {\n+ ed['displayName'] = key;\n+ attributes[key] = ed;\n+ }\n+ }\n+\n+ return attributes;\n+ },\n+\n+ /**\n+ * Method: parseProperty\n+ * Convenience method to find a node and return its value\n+ *\n+ * Parameters:\n+ * xmlNode - {<DOMElement>}\n+ * namespace - {String} namespace of the node to find\n+ * tagName - {String} name of the property to parse\n+ * \n+ * Returns:\n+ * {String} The value for the requested property (defaults to null)\n+ */\n+ parseProperty: function(xmlNode, namespace, tagName) {\n+ var value;\n+ var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);\n+ try {\n+ value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);\n+ } catch (e) {\n+ value = null;\n+ }\n+\n+ return value;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Accept Feature Collection, and return a string. \n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} An array of features.\n+ *\n+ * Returns:\n+ * {String} A KML string.\n+ */\n+ write: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ var kml = this.createElementNS(this.kmlns, \"kml\");\n+ var folder = this.createFolderXML();\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ folder.appendChild(this.createPlacemarkXML(features[i]));\n+ }\n+ kml.appendChild(folder);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);\n+ },\n+\n+ /**\n+ * Method: createFolderXML\n+ * Creates and returns a KML folder node\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ createFolderXML: function() {\n+ // Folder\n+ var folder = this.createElementNS(this.kmlns, \"Folder\");\n+\n+ // Folder name\n+ if (this.foldersName) {\n+ var folderName = this.createElementNS(this.kmlns, \"name\");\n+ var folderNameText = this.createTextNode(this.foldersName);\n+ folderName.appendChild(folderNameText);\n+ folder.appendChild(folderName);\n+ }\n+\n+ // Folder description\n+ if (this.foldersDesc) {\n+ var folderDesc = this.createElementNS(this.kmlns, \"description\");\n+ var folderDescText = this.createTextNode(this.foldersDesc);\n+ folderDesc.appendChild(folderDescText);\n+ folder.appendChild(folderDesc);\n+ }\n+\n+ return folder;\n+ },\n+\n+ /**\n+ * Method: createPlacemarkXML\n+ * Creates and returns a KML placemark node representing the given feature. \n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ createPlacemarkXML: function(feature) {\n+ // Placemark name\n+ var placemarkName = this.createElementNS(this.kmlns, \"name\");\n+ var label = (feature.style && feature.style.label) ? feature.style.label : feature.id;\n+ var name = feature.attributes.name || label;\n+ placemarkName.appendChild(this.createTextNode(name));\n+\n+ // Placemark description\n+ var placemarkDesc = this.createElementNS(this.kmlns, \"description\");\n+ var desc = feature.attributes.description || this.placemarksDesc;\n+ placemarkDesc.appendChild(this.createTextNode(desc));\n+\n+ // Placemark\n+ var placemarkNode = this.createElementNS(this.kmlns, \"Placemark\");\n+ if (feature.fid != null) {\n+ placemarkNode.setAttribute(\"id\", feature.fid);\n+ }\n+ placemarkNode.appendChild(placemarkName);\n+ placemarkNode.appendChild(placemarkDesc);\n+\n+ // Geometry node (Point, LineString, etc. nodes)\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n+ placemarkNode.appendChild(geometryNode);\n+\n+ // output attributes as extendedData\n+ if (feature.attributes) {\n+ var edNode = this.buildExtendedData(feature.attributes);\n+ if (edNode) {\n+ placemarkNode.appendChild(edNode);\n+ }\n+ }\n+\n+ return placemarkNode;\n+ },\n+\n+ /**\n+ * Method: buildGeometryNode\n+ * Builds and returns a KML geometry node with the given geometry.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ buildGeometryNode: function(geometry) {\n+ var className = geometry.CLASS_NAME;\n+ var type = className.substring(className.lastIndexOf(\".\") + 1);\n+ var builder = this.buildGeometry[type.toLowerCase()];\n+ var node = null;\n+ if (builder) {\n+ node = builder.apply(this, [geometry]);\n+ }\n+ return node;\n+ },\n+\n+ /**\n+ * Property: buildGeometry\n+ * Object containing methods to do the actual geometry node building\n+ * based on geometry type.\n+ */\n+ buildGeometry: {\n+ // TBD: Anybody care about namespace aliases here (these nodes have\n+ // no prefixes)?\n+\n+ /**\n+ * Method: buildGeometry.point\n+ * Given an OpenLayers point geometry, create a KML point.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>} A point geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML point node.\n+ */\n+ point: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"Point\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml;\n+ },\n+\n+ /**\n+ * Method: buildGeometry.multipoint\n+ * Given an OpenLayers multipoint geometry, create a KML\n+ * GeometryCollection.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML GeometryCollection node.\n+ */\n+ multipoint: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry]);\n+ },\n+\n+ /**\n+ * Method: buildGeometry.linestring\n+ * Given an OpenLayers linestring geometry, create a KML linestring.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML linestring node.\n+ */\n+ linestring: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"LineString\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml;\n+ },\n+\n+ /**\n+ * Method: buildGeometry.multilinestring\n+ * Given an OpenLayers multilinestring geometry, create a KML\n+ * GeometryCollection.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML GeometryCollection node.\n+ */\n+ multilinestring: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry]);\n+ },\n+\n+ /**\n+ * Method: buildGeometry.linearring\n+ * Given an OpenLayers linearring geometry, create a KML linearring.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML linearring node.\n+ */\n+ linearring: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"LinearRing\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml;\n+ },\n+\n+ /**\n+ * Method: buildGeometry.polygon\n+ * Given an OpenLayers polygon geometry, create a KML polygon.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML polygon node.\n+ */\n+ polygon: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"Polygon\");\n+ var rings = geometry.components;\n+ var ringMember, ringGeom, type;\n+ for (var i = 0, len = rings.length; i < len; ++i) {\n+ type = (i == 0) ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n+ ringMember = this.createElementNS(this.kmlns, type);\n+ ringGeom = this.buildGeometry.linearring.apply(this,\n+ [rings[i]]);\n+ ringMember.appendChild(ringGeom);\n+ kml.appendChild(ringMember);\n+ }\n+ return kml;\n+ },\n+\n+ /**\n+ * Method: buildGeometry.multipolygon\n+ * Given an OpenLayers multipolygon geometry, create a KML\n+ * GeometryCollection.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML GeometryCollection node.\n+ */\n+ multipolygon: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry]);\n+ },\n+\n+ /**\n+ * Method: buildGeometry.collection\n+ * Given an OpenLayers geometry collection, create a KML MultiGeometry.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection.\n+ *\n+ * Returns:\n+ * {DOMElement} A KML MultiGeometry node.\n+ */\n+ collection: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"MultiGeometry\");\n+ var child;\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ child = this.buildGeometryNode.apply(this,\n+ [geometry.components[i]]);\n+ if (child) {\n+ kml.appendChild(child);\n+ }\n+ }\n+ return kml;\n+ }\n+ },\n+\n+ /**\n+ * Method: buildCoordinatesNode\n+ * Builds and returns the KML coordinates node with the given geometry\n+ * <coordinates>...</coordinates>\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ buildCoordinatesNode: function(geometry) {\n+ var coordinatesNode = this.createElementNS(this.kmlns, \"coordinates\");\n+\n+ var path;\n+ var points = geometry.components;\n+ if (points) {\n+ // LineString or LinearRing\n+ var point;\n+ var numPoints = points.length;\n+ var parts = new Array(numPoints);\n+ for (var i = 0; i < numPoints; ++i) {\n+ point = points[i];\n+ parts[i] = this.buildCoordinates(point);\n+ }\n+ path = parts.join(\" \");\n+ } else {\n+ // Point\n+ path = this.buildCoordinates(geometry);\n+ }\n+\n+ var txtNode = this.createTextNode(path);\n+ coordinatesNode.appendChild(txtNode);\n+\n+ return coordinatesNode;\n+ },\n+\n+ /**\n+ * Method: buildCoordinates\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns\n+ * {String} a coordinate pair\n+ */\n+ buildCoordinates: function(point) {\n+ if (this.internalProjection && this.externalProjection) {\n+ point = point.clone();\n+ point.transform(this.internalProjection,\n+ this.externalProjection);\n+ }\n+ return point.x + \",\" + point.y;\n+ },\n+\n+ /**\n+ * Method: buildExtendedData\n+ *\n+ * Parameters:\n+ * attributes - {Object}\n+ *\n+ * Returns\n+ * {DOMElement} A KML ExtendedData node or {null} if no attributes.\n+ */\n+ buildExtendedData: function(attributes) {\n+ var extendedData = this.createElementNS(this.kmlns, \"ExtendedData\");\n+ for (var attributeName in attributes) {\n+ // empty, name, description, styleUrl attributes ignored\n+ if (attributes[attributeName] && attributeName != \"name\" && attributeName != \"description\" && attributeName != \"styleUrl\") {\n+ var data = this.createElementNS(this.kmlns, \"Data\");\n+ data.setAttribute(\"name\", attributeName);\n+ var value = this.createElementNS(this.kmlns, \"value\");\n+ if (typeof attributes[attributeName] == \"object\") {\n+ // cater for object attributes with 'value' properties\n+ // other object properties will output an empty node\n+ if (attributes[attributeName].value) {\n+ value.appendChild(this.createTextNode(attributes[attributeName].value));\n+ }\n+ if (attributes[attributeName].displayName) {\n+ var displayName = this.createElementNS(this.kmlns, \"displayName\");\n+ // displayName always written as CDATA\n+ displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName));\n+ data.appendChild(displayName);\n+ }\n+ } else {\n+ value.appendChild(this.createTextNode(attributes[attributeName]));\n+ }\n+ data.appendChild(value);\n+ extendedData.appendChild(data);\n+ }\n+ }\n+ if (this.isSimpleContent(extendedData)) {\n+ return null;\n+ } else {\n+ return extendedData;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.KML\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/SLD.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Rule.js\n+ * @requires OpenLayers/Filter/FeatureId.js\n+ * @requires OpenLayers/Filter/Logical.js\n+ * @requires OpenLayers/Filter/Comparison.js\n+ * @requires OpenLayers/Filter/Spatial.js\n */\n \n /**\n- * Class: OpenLayers.Format.XLS\n- * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS>\n- * constructor. Currently only implemented for Location Utility Services, more\n- * specifically only for Geocoding. No support for Reverse Geocoding as yet.\n+ * Class: OpenLayers.Format.SLD\n+ * Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD>\n+ * constructor.\n * \n * Inherits from:\n * - <OpenLayers.Format.XML.VersionedOGC>\n */\n-OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: profile\n+ * {String} If provided, use a custom profile.\n+ *\n+ * Currently supported profiles:\n+ * - GeoServer - parses GeoServer vendor specific capabilities for SLD.\n+ */\n+ profile: null,\n \n /**\n * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n */\n- defaultVersion: \"1.1.0\",\n+ defaultVersion: \"1.0.0\",\n \n /**\n * APIProperty: stringifyOutput\n * {Boolean} If true, write will return a string otherwise a DOMElement.\n * Default is true.\n */\n stringifyOutput: true,\n \n /**\n- * Constructor: OpenLayers.Format.XLS\n- * Create a new parser for XLS.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n+ * APIProperty: namedLayersAsArray\n+ * {Boolean} Generate a namedLayers array. If false, the namedLayers\n+ * property value will be an object keyed by layer name. Default is\n+ * false.\n */\n+ namedLayersAsArray: false,\n \n /**\n * APIMethod: write\n- * Write out an XLS request.\n+ * Write a SLD document given a list of styles.\n *\n * Parameters:\n- * request - {Object} An object representing the LUS request.\n+ * sld - {Object} An object representing the SLD.\n * options - {Object} Optional configuration object.\n *\n * Returns:\n- * {String} An XLS document string.\n+ * {String} An SLD document string.\n */\n \n /**\n * APIMethod: read\n- * Read an XLS doc and return an object representing the result.\n+ * Read and SLD doc and return an object representing the SLD.\n *\n * Parameters:\n * data - {String | DOMElement} Data to read.\n * options - {Object} Options for the reader.\n *\n * Returns:\n- * {Object} An object representing the GeocodeResponse.\n+ * {Object} An object representing the SLD.\n */\n \n- CLASS_NAME: \"OpenLayers.Format.XLS\"\n+ CLASS_NAME: \"OpenLayers.Format.SLD\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/CSWGetDomain.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.CSWGetDomain\n+ * Default version is 2.0.2.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format>} A CSWGetDomain format of the given version.\n+ */\n+OpenLayers.Format.CSWGetDomain = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Format.CSWGetDomain.DEFAULTS\n+ );\n+ var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetDomain version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n+\n+/**\n+ * Constant: DEFAULTS\n+ * {Object} Default properties for the CSWGetDomain format.\n+ */\n+OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n+ \"version\": \"2.0.2\"\n+};\n+/* ======================================================================\n+ OpenLayers/Format/CSWGetRecords.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.CSWGetRecords\n+ * Default version is 2.0.2.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format>} A CSWGetRecords format of the given version.\n+ */\n+OpenLayers.Format.CSWGetRecords = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Format.CSWGetRecords.DEFAULTS\n+ );\n+ var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetRecords version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n+\n+/**\n+ * Constant: DEFAULTS\n+ * {Object} Default properties for the CSWGetRecords format.\n+ */\n+OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n+ \"version\": \"2.0.2\"\n+};\n+/* ======================================================================\n+ OpenLayers/Format/WMSDescribeLayer.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSDescribeLayer\n+ * Read SLD WMS DescribeLayer response\n+ * DescribeLayer is meant to couple WMS to WFS and WCS\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.1\".\n+ */\n+ defaultVersion: \"1.1.1\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSDescribeLayer\n+ * Create a new parser for WMS DescribeLayer responses.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read DescribeLayer data from a string, and return the response. \n+ * The OGC currently defines 2 formats which are allowed for output,\n+ * so we need to parse these 2 types\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} Array of {<LayerDescription>} objects which have:\n+ * - {String} owsType: WFS/WCS\n+ * - {String} owsURL: the online resource\n+ * - {String} typeName: the name of the typename on the service\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n+\n });\n /* ======================================================================\n OpenLayers/Format/GML/v2.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -52262,485 +41972,305 @@\n },\n \n \n CLASS_NAME: \"OpenLayers.Format.Filter.v1_0_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/CSWGetRecords/v2_0_2.js\n+ OpenLayers/Format/WMSDescribeLayer/v1_1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/CSWGetRecords.js\n- * @requires OpenLayers/Format/Filter/v1_0_0.js\n- * @requires OpenLayers/Format/Filter/v1_1_0.js\n- * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n+ * @requires OpenLayers/Format/WMSDescribeLayer.js\n+ * @requires OpenLayers/Format/OGCExceptionReport.js\n */\n \n /**\n- * Class: OpenLayers.Format.CSWGetRecords.v2_0_2\n- * A format for creating CSWGetRecords v2.0.2 transactions. \n- * Create a new instance with the\n- * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor.\n+ * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1\n+ * Read SLD WMS DescribeLayer response for WMS 1.1.X\n+ * WMS 1.1.X is tightly coupled to SLD 1.0.0\n+ *\n+ * Example DescribeLayer request: \n+ * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states\n *\n * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * - <OpenLayers.Format.WMSDescribeLayer>\n */\n-OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\",\n- dc: \"http://purl.org/dc/elements/1.1/\",\n- dct: \"http://purl.org/dc/terms/\",\n- gmd: \"http://www.isotc211.org/2005/gmd\",\n- geonet: \"http://www.fao.org/geonetwork\",\n- ogc: \"http://www.opengis.net/ogc\",\n- ows: \"http://www.opengis.net/ows\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: defaultPrefix\n- * {String} The default prefix (used by Format.XML).\n- */\n- defaultPrefix: \"csw\",\n-\n- /**\n- * Property: version\n- * {String} CSW version number.\n- */\n- version: \"2.0.2\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} http://www.opengis.net/cat/csw/2.0.2\n- * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n- */\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n-\n- /**\n- * APIProperty: requestId\n- * {String} Value of the requestId attribute of the GetRecords element.\n- */\n- requestId: null,\n+OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(\n+ OpenLayers.Format.WMSDescribeLayer, {\n \n- /**\n- * APIProperty: resultType\n- * {String} Value of the resultType attribute of the GetRecords element,\n- * specifies the result type in the GetRecords response, \"hits\" is\n- * the default.\n- */\n- resultType: null,\n+ /**\n+ * Constructor: OpenLayers.Format.WMSDescribeLayer\n+ * Create a new parser for WMS DescribeLayer responses.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,\n+ [options]);\n+ },\n \n- /**\n- * APIProperty: outputFormat\n- * {String} Value of the outputFormat attribute of the GetRecords element,\n- * specifies the format of the GetRecords response,\n- * \"application/xml\" is the default.\n- */\n- outputFormat: null,\n+ /**\n+ * APIMethod: read\n+ * Read DescribeLayer data from a string, and return the response. \n+ * The OGC defines 2 formats which are allowed for output,\n+ * so we need to parse these 2 types for version 1.1.X\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Object with a layerDescriptions property, which holds an Array\n+ * of {<LayerDescription>} objects which have:\n+ * - {String} owsType: WFS/WCS\n+ * - {String} owsURL: the online resource\n+ * - {String} typeName: the name of the typename on the owsType service\n+ * - {String} layerName: the name of the WMS layer we did a lookup for\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ var root = data.documentElement;\n+ var children = root.childNodes;\n+ var describelayer = {\n+ layerDescriptions: []\n+ };\n+ var childNode, nodeName;\n+ for (var i = 0; i < children.length; ++i) {\n+ childNode = children[i];\n+ nodeName = childNode.nodeName;\n+ if (nodeName == 'LayerDescription') {\n+ var layerName = childNode.getAttribute('name');\n+ var owsType = '';\n+ var owsURL = '';\n+ var typeName = '';\n+ // check for owsType and owsURL attributes\n+ if (childNode.getAttribute('owsType')) {\n+ owsType = childNode.getAttribute('owsType');\n+ owsURL = childNode.getAttribute('owsURL');\n+ } else {\n+ // look for wfs or wcs attribute\n+ if (childNode.getAttribute('wfs') != '') {\n+ owsType = 'WFS';\n+ owsURL = childNode.getAttribute('wfs');\n+ } else if (childNode.getAttribute('wcs') != '') {\n+ owsType = 'WCS';\n+ owsURL = childNode.getAttribute('wcs');\n+ }\n+ }\n+ // look for Query child\n+ var query = childNode.getElementsByTagName('Query');\n+ if (query.length > 0) {\n+ typeName = query[0].getAttribute('typeName');\n+ if (!typeName) {\n+ // because of Ionic bug\n+ typeName = query[0].getAttribute('typename');\n+ }\n+ }\n+ var layerDescription = {\n+ layerName: layerName,\n+ owsType: owsType,\n+ owsURL: owsURL,\n+ typeName: typeName\n+ };\n+ describelayer.layerDescriptions.push(layerDescription);\n \n- /**\n- * APIProperty: outputSchema\n- * {String} Value of the outputSchema attribute of the GetRecords element,\n- * specifies the schema of the GetRecords response.\n- */\n- outputSchema: null,\n+ //TODO do this in deprecated.js instead:\n+ // array style index for backwards compatibility\n+ describelayer.length = describelayer.layerDescriptions.length;\n+ describelayer[describelayer.length - 1] = layerDescription;\n \n- /**\n- * APIProperty: startPosition\n- * {String} Value of the startPosition attribute of the GetRecords element,\n- * specifies the start position (offset+1) for the GetRecords response,\n- * 1 is the default.\n- */\n- startPosition: null,\n+ } else if (nodeName == 'ServiceException') {\n+ // an exception must have occurred, so parse it\n+ var parser = new OpenLayers.Format.OGCExceptionReport();\n+ return {\n+ error: parser.read(data)\n+ };\n+ }\n+ }\n+ return describelayer;\n+ },\n \n- /**\n- * APIProperty: maxRecords\n- * {String} Value of the maxRecords attribute of the GetRecords element,\n- * specifies the maximum number of records in the GetRecords response,\n- * 10 is the default.\n- */\n- maxRecords: null,\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n \n- /**\n- * APIProperty: DistributedSearch\n- * {String} Value of the csw:DistributedSearch element, used when writing\n- * a csw:GetRecords document.\n- */\n- DistributedSearch: null,\n+ });\n \n- /**\n- * APIProperty: ResponseHandler\n- * {Array({String})} Values of the csw:ResponseHandler elements, used when\n- * writting a csw:GetRecords document.\n- */\n- ResponseHandler: null,\n+// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257\n+OpenLayers.Format.WMSDescribeLayer.v1_1_0 =\n+ OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n+/* ======================================================================\n+ OpenLayers/Format/SOSCapabilities/v1_0_0.js\n+ ====================================================================== */\n \n- /**\n- * APIProperty: Query\n- * {String} Value of the csw:Query element, used when writing a csw:GetRecords\n- * document.\n- */\n- Query: null,\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+/**\n+ * @requires OpenLayers/Format/SOSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/GML/v3.js\n+ */\n \n- /**\n- * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2\n- * A class for parsing and generating CSWGetRecords v2.0.2 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties (documented as class properties):\n- * - requestId\n- * - resultType\n- * - outputFormat\n- * - outputSchema\n- * - startPosition\n- * - maxRecords\n- * - DistributedSearch\n- * - ResponseHandler\n- * - Query\n- */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n+/**\n+ * Class: OpenLayers.Format.SOSCapabilities.v1_0_0\n+ * Read SOS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.SOSCapabilities>\n+ */\n+OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.SOSCapabilities, {\n \n- /**\n- * APIMethod: read\n- * Parse the response from a GetRecords request.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj;\n- },\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ sos: \"http://www.opengis.net/sos/1.0\",\n+ gml: \"http://www.opengis.net/gml\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"csw\": {\n- \"GetRecordsResponse\": function(node, obj) {\n- obj.records = [];\n- this.readChildNodes(node, obj);\n- var version = this.getAttributeNS(node, \"\", 'version');\n- if (version != \"\") {\n- obj.version = version;\n- }\n- },\n- \"RequestId\": function(node, obj) {\n- obj.RequestId = this.getChildValue(node);\n- },\n- \"SearchStatus\": function(node, obj) {\n- obj.SearchStatus = {};\n- var timestamp = this.getAttributeNS(node, \"\", 'timestamp');\n- if (timestamp != \"\") {\n- obj.SearchStatus.timestamp = timestamp;\n- }\n- },\n- \"SearchResults\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- var attrs = node.attributes;\n- var SearchResults = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- if ((attrs[i].name == \"numberOfRecordsMatched\") ||\n- (attrs[i].name == \"numberOfRecordsReturned\") ||\n- (attrs[i].name == \"nextRecord\")) {\n- SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue);\n- } else {\n- SearchResults[attrs[i].name] = attrs[i].nodeValue;\n- }\n- }\n- obj.SearchResults = SearchResults;\n- },\n- \"SummaryRecord\": function(node, obj) {\n- var record = {\n- type: \"SummaryRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"BriefRecord\": function(node, obj) {\n- var record = {\n- type: \"BriefRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"DCMIRecord\": function(node, obj) {\n- var record = {\n- type: \"DCMIRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"Record\": function(node, obj) {\n- var record = {\n- type: \"Record\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- obj[name] = this.getChildValue(node);\n- }\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n- \"geonet\": {\n- \"info\": function(node, obj) {\n- var gninfo = {};\n- this.readChildNodes(node, gninfo);\n- obj.gninfo = gninfo;\n- }\n+\n+ /**\n+ * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0\n+ * Create a new parser for SOS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options;\n },\n- \"dc\": {\n- // audience, contributor, coverage, creator, date, description, format,\n- // identifier, language, provenance, publisher, relation, rights,\n- // rightsHolder, source, subject, title, type, URI\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!(OpenLayers.Util.isArray(obj[name]))) {\n- obj[name] = [];\n- }\n- var dc_element = {};\n- var attrs = node.attributes;\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- dc_element[attrs[i].name] = attrs[i].nodeValue;\n- }\n- dc_element.value = this.getChildValue(node);\n- if (dc_element.value != \"\") {\n- obj[name].push(dc_element);\n- }\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the SOS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the SOS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n- },\n- \"dct\": {\n- // abstract, modified, spatial\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!(OpenLayers.Util.isArray(obj[name]))) {\n- obj[name] = [];\n- }\n- obj[name].push(this.getChildValue(node));\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities;\n },\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"BoundingBox\": function(node, obj) {\n- if (obj.bounds) {\n- obj.BoundingBox = [{\n- crs: obj.projection,\n- value: [\n- obj.bounds.left,\n- obj.bounds.bottom,\n- obj.bounds.right,\n- obj.bounds.top\n- ]\n- }];\n- delete obj.projection;\n- delete obj.bounds;\n- }\n- OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"][\"BoundingBox\"].apply(\n- this, arguments);\n- }\n- }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"])\n- },\n-\n- /**\n- * Method: write\n- * Given an configuration js object, write a CSWGetRecords request. \n- *\n- * Parameters:\n- * options - {Object} A object mapping the request.\n- *\n- * Returns:\n- * {String} A serialized CSWGetRecords request.\n- */\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetRecords\", options);\n- node.setAttribute(\"xmlns:gmd\", this.namespaces.gmd);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"csw\": {\n- \"GetRecords\": function(options) {\n- if (!options) {\n- options = {};\n- }\n- var node = this.createElementNSPlus(\"csw:GetRecords\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version,\n- requestId: options.requestId || this.requestId,\n- resultType: options.resultType || this.resultType,\n- outputFormat: options.outputFormat || this.outputFormat,\n- outputSchema: options.outputSchema || this.outputSchema,\n- startPosition: options.startPosition || this.startPosition,\n- maxRecords: options.maxRecords || this.maxRecords\n- }\n- });\n- if (options.DistributedSearch || this.DistributedSearch) {\n- this.writeNode(\n- \"csw:DistributedSearch\",\n- options.DistributedSearch || this.DistributedSearch,\n- node\n- );\n- }\n- var ResponseHandler = options.ResponseHandler || this.ResponseHandler;\n- if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {\n- // ResponseHandler must be a non-empty array\n- for (var i = 0, len = ResponseHandler.length; i < len; i++) {\n- this.writeNode(\n- \"csw:ResponseHandler\",\n- ResponseHandler[i],\n- node\n- );\n- }\n- }\n- this.writeNode(\"Query\", options.Query || this.Query, node);\n- return node;\n- },\n- \"DistributedSearch\": function(options) {\n- var node = this.createElementNSPlus(\"csw:DistributedSearch\", {\n- attributes: {\n- hopCount: options.hopCount\n- }\n- });\n- return node;\n- },\n- \"ResponseHandler\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ResponseHandler\", {\n- value: options.value\n- });\n- return node;\n- },\n- \"Query\": function(options) {\n- if (!options) {\n- options = {};\n- }\n- var node = this.createElementNSPlus(\"csw:Query\", {\n- attributes: {\n- typeNames: options.typeNames || \"csw:Record\"\n- }\n- });\n- var ElementName = options.ElementName;\n- if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {\n- // ElementName must be a non-empty array\n- for (var i = 0, len = ElementName.length; i < len; i++) {\n- this.writeNode(\n- \"csw:ElementName\",\n- ElementName[i],\n- node\n- );\n- }\n- } else {\n- this.writeNode(\n- \"csw:ElementSetName\",\n- options.ElementSetName || {\n- value: 'summary'\n- },\n- node\n- );\n- }\n- if (options.Constraint) {\n- this.writeNode(\n- \"csw:Constraint\",\n- options.Constraint,\n- node\n- );\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"name\": function(node, obj) {\n+ obj.name = this.getChildValue(node);\n+ },\n+ \"TimePeriod\": function(node, obj) {\n+ obj.timePeriod = {};\n+ this.readChildNodes(node, obj.timePeriod);\n+ },\n+ \"beginPosition\": function(node, timePeriod) {\n+ timePeriod.beginPosition = this.getChildValue(node);\n+ },\n+ \"endPosition\": function(node, timePeriod) {\n+ timePeriod.endPosition = this.getChildValue(node);\n }\n- if (options.SortBy) {\n- this.writeNode(\n- \"ogc:SortBy\",\n- options.SortBy,\n- node\n- );\n+ }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n+ \"sos\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Contents\": function(node, obj) {\n+ obj.contents = {};\n+ this.readChildNodes(node, obj.contents);\n+ },\n+ \"ObservationOfferingList\": function(node, contents) {\n+ contents.offeringList = {};\n+ this.readChildNodes(node, contents.offeringList);\n+ },\n+ \"ObservationOffering\": function(node, offeringList) {\n+ var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n+ offeringList[id] = {\n+ procedures: [],\n+ observedProperties: [],\n+ featureOfInterestIds: [],\n+ responseFormats: [],\n+ resultModels: [],\n+ responseModes: []\n+ };\n+ this.readChildNodes(node, offeringList[id]);\n+ },\n+ \"time\": function(node, offering) {\n+ offering.time = {};\n+ this.readChildNodes(node, offering.time);\n+ },\n+ \"procedure\": function(node, offering) {\n+ offering.procedures.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"observedProperty\": function(node, offering) {\n+ offering.observedProperties.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"featureOfInterest\": function(node, offering) {\n+ offering.featureOfInterestIds.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"responseFormat\": function(node, offering) {\n+ offering.responseFormats.push(this.getChildValue(node));\n+ },\n+ \"resultModel\": function(node, offering) {\n+ offering.resultModels.push(this.getChildValue(node));\n+ },\n+ \"responseMode\": function(node, offering) {\n+ offering.responseModes.push(this.getChildValue(node));\n }\n- return node;\n- },\n- \"ElementName\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementName\", {\n- value: options.value\n- });\n- return node;\n- },\n- \"ElementSetName\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementSetName\", {\n- attributes: {\n- typeNames: options.typeNames\n- },\n- value: options.value\n- });\n- return node;\n },\n- \"Constraint\": function(options) {\n- var node = this.createElementNSPlus(\"csw:Constraint\", {\n- attributes: {\n- version: options.version\n- }\n- });\n- if (options.Filter) {\n- var format = new OpenLayers.Format.Filter({\n- version: options.version\n- });\n- node.appendChild(format.write(options.Filter));\n- } else if (options.CqlText) {\n- var child = this.createElementNSPlus(\"CqlText\", {\n- value: options.CqlText.value\n- });\n- node.appendChild(child);\n- }\n- return node;\n- }\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n- },\n \n- CLASS_NAME: \"OpenLayers.Format.CSWGetRecords.v2_0_2\"\n-});\n+ CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n+\n+ });\n /* ======================================================================\n OpenLayers/Format/SLD/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -54761,14 +44291,823 @@\n \"feature\": OpenLayers.Format.GML.v2.prototype.writers.feature\n },\n \n CLASS_NAME: \"OpenLayers.Format.OWSContext.v0_3_1\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/WPSCapabilities/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WPSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSCapabilities.v1_0_0\n+ * Read WPS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0\n+ * Create a new parser for WPS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the WPS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the WPS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wps\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"ProcessOfferings\": function(node, obj) {\n+ obj.processOfferings = {};\n+ this.readChildNodes(node, obj.processOfferings);\n+ },\n+ \"Process\": function(node, processOfferings) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var process = {\n+ processVersion: processVersion\n+ };\n+ this.readChildNodes(node, process);\n+ processOfferings[process.identifier] = process;\n+ },\n+ \"Languages\": function(node, obj) {\n+ obj.languages = [];\n+ this.readChildNodes(node, obj.languages);\n+ },\n+ \"Default\": function(node, languages) {\n+ var language = {\n+ isDefault: true\n+ };\n+ this.readChildNodes(node, language);\n+ languages.push(language);\n+ },\n+ \"Supported\": function(node, languages) {\n+ var language = {};\n+ this.readChildNodes(node, language);\n+ languages.push(language);\n+ }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WFST/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WFST/v1.js\n+ * @requires OpenLayers/Format/Filter/v1_0_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFST.v1_0_0\n+ * A format for creating WFS v1.0.0 transactions. Create a new instance with the\n+ * <OpenLayers.Format.WFST.v1_0_0> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.Filter.v1_0_0>\n+ * - <OpenLayers.Format.WFST.v1>\n+ */\n+OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n+\n+ /**\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * APIProperty: srsNameInQuery\n+ * {Boolean} If true the reference system is passed in Query requests\n+ * via the \"srsName\" attribute to the \"wfs:Query\" element, this\n+ * property defaults to false as it isn't WFS 1.0.0 compliant.\n+ */\n+ srsNameInQuery: false,\n+\n+ /**\n+ * Property: schemaLocations\n+ * {Object} Properties are namespace aliases, values are schema locations.\n+ */\n+ schemaLocations: {\n+ \"wfs\": \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFST.v1_0_0\n+ * A class for parsing and generating WFS v1.0.0 transactions.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n+ OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\n+ * first - {Boolean} Should be set to true for the first node read. This\n+ * is usually the readNode call in the read method. Without this being\n+ * set, auto-configured properties will stick on subsequent reads.\n+ *\n+ * Returns:\n+ * {Object} The input object, modified (or a new one if none was provided).\n+ */\n+ readNode: function(node, obj, first) {\n+ // Not the superclass, only the mixin classes inherit from\n+ // Format.GML.v2. We need this because we don't want to get readNode\n+ // from the superclass's superclass, which is OpenLayers.Format.XML.\n+ return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"WFS_TransactionResponse\": function(node, obj) {\n+ obj.insertIds = [];\n+ obj.success = false;\n+ this.readChildNodes(node, obj);\n+ },\n+ \"InsertResult\": function(node, container) {\n+ var obj = {\n+ fids: []\n+ };\n+ this.readChildNodes(node, obj);\n+ container.insertIds = container.insertIds.concat(obj.fids);\n+ },\n+ \"TransactionResult\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Status\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"SUCCESS\": function(node, obj) {\n+ obj.success = true;\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"Query\": function(options) {\n+ options = OpenLayers.Util.extend({\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ featureType: this.featureType,\n+ srsName: this.srsName,\n+ srsNameInQuery: this.srsNameInQuery\n+ }, options);\n+ var prefix = options.featurePrefix;\n+ var node = this.createElementNSPlus(\"wfs:Query\", {\n+ attributes: {\n+ typeName: (prefix ? prefix + \":\" : \"\") +\n+ options.featureType\n+ }\n+ });\n+ if (options.srsNameInQuery && options.srsName) {\n+ node.setAttribute(\"srsName\", options.srsName);\n+ }\n+ if (options.featureNS) {\n+ node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n+ }\n+ if (options.propertyNames) {\n+ for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n+ this.writeNode(\n+ \"ogc:PropertyName\", {\n+ property: options.propertyNames[i]\n+ },\n+ node\n+ );\n+ }\n+ }\n+ if (options.filter) {\n+ this.setFilterProperty(options.filter);\n+ this.writeNode(\"ogc:Filter\", options.filter, node);\n+ }\n+ return node;\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WMTSCapabilities/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WMTSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0\n+ * Read WMTS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.WMTSCapabilities>\n+ */\n+OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.OWSCommon.v1_1_0, {\n+\n+ /**\n+ * Property: version\n+ * {String} The parser version (\"1.0.0\").\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wmts: \"http://www.opengis.net/wmts/1.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+\n+ /**\n+ * Property: yx\n+ * {Object} Members in the yx object are used to determine if a CRS URN\n+ * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n+ * and values are boolean. Defaults come from the \n+ * <OpenLayers.Format.WMTSCapabilities> prototype.\n+ */\n+ yx: null,\n+\n+ /**\n+ * Property: defaultPrefix\n+ * {String} The default namespace alias for creating element nodes.\n+ */\n+ defaultPrefix: \"wmts\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0\n+ * Create a new parser for WMTS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options;\n+ var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n+ this.yx = OpenLayers.Util.extend(yx, this.yx);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the WMTS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the SOS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ capabilities.version = this.version;\n+ return capabilities;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wmts\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Contents\": function(node, obj) {\n+ obj.contents = {};\n+ obj.contents.layers = [];\n+ obj.contents.tileMatrixSets = {};\n+ this.readChildNodes(node, obj.contents);\n+ },\n+ \"Layer\": function(node, obj) {\n+ var layer = {\n+ styles: [],\n+ formats: [],\n+ dimensions: [],\n+ tileMatrixSetLinks: []\n+ };\n+ layer.layers = [];\n+ this.readChildNodes(node, layer);\n+ obj.layers.push(layer);\n+ },\n+ \"Style\": function(node, obj) {\n+ var style = {};\n+ style.isDefault = (node.getAttribute(\"isDefault\") === \"true\");\n+ this.readChildNodes(node, style);\n+ obj.styles.push(style);\n+ },\n+ \"Format\": function(node, obj) {\n+ obj.formats.push(this.getChildValue(node));\n+ },\n+ \"TileMatrixSetLink\": function(node, obj) {\n+ var tileMatrixSetLink = {};\n+ this.readChildNodes(node, tileMatrixSetLink);\n+ obj.tileMatrixSetLinks.push(tileMatrixSetLink);\n+ },\n+ \"TileMatrixSet\": function(node, obj) {\n+ // node could be child of wmts:Contents or wmts:TileMatrixSetLink\n+ // duck type wmts:Contents by looking for layers\n+ if (obj.layers) {\n+ // TileMatrixSet as object type in schema\n+ var tileMatrixSet = {\n+ matrixIds: []\n+ };\n+ this.readChildNodes(node, tileMatrixSet);\n+ obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;\n+ } else {\n+ // TileMatrixSet as string type in schema\n+ obj.tileMatrixSet = this.getChildValue(node);\n+ }\n+ },\n+ \"TileMatrix\": function(node, obj) {\n+ var tileMatrix = {\n+ supportedCRS: obj.supportedCRS\n+ };\n+ this.readChildNodes(node, tileMatrix);\n+ obj.matrixIds.push(tileMatrix);\n+ },\n+ \"ScaleDenominator\": function(node, obj) {\n+ obj.scaleDenominator = parseFloat(this.getChildValue(node));\n+ },\n+ \"TopLeftCorner\": function(node, obj) {\n+ var topLeftCorner = this.getChildValue(node);\n+ var coords = topLeftCorner.split(\" \");\n+ // decide on axis order for the given CRS\n+ var yx;\n+ if (obj.supportedCRS) {\n+ // extract out version from URN\n+ var crs = obj.supportedCRS.replace(\n+ /urn:ogc:def:crs:(\\w+):.+:(\\w+)$/,\n+ \"urn:ogc:def:crs:$1::$2\"\n+ );\n+ yx = !!this.yx[crs];\n+ }\n+ if (yx) {\n+ obj.topLeftCorner = new OpenLayers.LonLat(\n+ coords[1], coords[0]\n+ );\n+ } else {\n+ obj.topLeftCorner = new OpenLayers.LonLat(\n+ coords[0], coords[1]\n+ );\n+ }\n+ },\n+ \"TileWidth\": function(node, obj) {\n+ obj.tileWidth = parseInt(this.getChildValue(node));\n+ },\n+ \"TileHeight\": function(node, obj) {\n+ obj.tileHeight = parseInt(this.getChildValue(node));\n+ },\n+ \"MatrixWidth\": function(node, obj) {\n+ obj.matrixWidth = parseInt(this.getChildValue(node));\n+ },\n+ \"MatrixHeight\": function(node, obj) {\n+ obj.matrixHeight = parseInt(this.getChildValue(node));\n+ },\n+ \"ResourceURL\": function(node, obj) {\n+ obj.resourceUrl = obj.resourceUrl || {};\n+ var resourceType = node.getAttribute(\"resourceType\");\n+ if (!obj.resourceUrls) {\n+ obj.resourceUrls = [];\n+ }\n+ var resourceUrl = obj.resourceUrl[resourceType] = {\n+ format: node.getAttribute(\"format\"),\n+ template: node.getAttribute(\"template\"),\n+ resourceType: resourceType\n+ };\n+ obj.resourceUrls.push(resourceUrl);\n+ },\n+ // not used for now, can be added in the future though\n+ /*\"Themes\": function(node, obj) {\n+ obj.themes = [];\n+ this.readChildNodes(node, obj.themes);\n+ },\n+ \"Theme\": function(node, obj) {\n+ var theme = {}; \n+ this.readChildNodes(node, theme);\n+ obj.push(theme);\n+ },*/\n+ \"WSDL\": function(node, obj) {\n+ obj.wsdl = {};\n+ obj.wsdl.href = node.getAttribute(\"xlink:href\");\n+ // TODO: other attributes of <WSDL> element \n+ },\n+ \"ServiceMetadataURL\": function(node, obj) {\n+ obj.serviceMetadataUrl = {};\n+ obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\");\n+ // TODO: other attributes of <ServiceMetadataURL> element \n+ },\n+ \"LegendURL\": function(node, obj) {\n+ obj.legend = {};\n+ obj.legend.href = node.getAttribute(\"xlink:href\");\n+ obj.legend.format = node.getAttribute(\"format\");\n+ },\n+ \"Dimension\": function(node, obj) {\n+ var dimension = {\n+ values: []\n+ };\n+ this.readChildNodes(node, dimension);\n+ obj.dimensions.push(dimension);\n+ },\n+ \"Default\": function(node, obj) {\n+ obj[\"default\"] = this.getChildValue(node);\n+ },\n+ \"Value\": function(node, obj) {\n+ obj.values.push(this.getChildValue(node));\n+ }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/CSWGetDomain/v2_0_2.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/CSWGetDomain.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.CSWGetDomain.v2_0_2\n+ * A format for creating CSWGetDomain v2.0.2 transactions. \n+ * Create a new instance with the\n+ * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n+ },\n+\n+ /**\n+ * Property: defaultPrefix\n+ * {String} The default prefix (used by Format.XML).\n+ */\n+ defaultPrefix: \"csw\",\n+\n+ /**\n+ * Property: version\n+ * {String} CSW version number.\n+ */\n+ version: \"2.0.2\",\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} http://www.opengis.net/cat/csw/2.0.2\n+ * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n+ */\n+ schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n+\n+ /**\n+ * APIProperty: PropertyName\n+ * {String} Value of the csw:PropertyName element, used when\n+ * writing a GetDomain document.\n+ */\n+ PropertyName: null,\n+\n+ /**\n+ * APIProperty: ParameterName\n+ * {String} Value of the csw:ParameterName element, used when\n+ * writing a GetDomain document.\n+ */\n+ ParameterName: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2\n+ * A class for parsing and generating CSWGetDomain v2.0.2 transactions.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * - PropertyName\n+ * - ParameterName\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Parse the response from a GetDomain request.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var obj = {};\n+ this.readNode(data, obj);\n+ return obj;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"csw\": {\n+ \"GetDomainResponse\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"DomainValues\": function(node, obj) {\n+ if (!(OpenLayers.Util.isArray(obj.DomainValues))) {\n+ obj.DomainValues = [];\n+ }\n+ var attrs = node.attributes;\n+ var domainValue = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ domainValue[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ this.readChildNodes(node, domainValue);\n+ obj.DomainValues.push(domainValue);\n+ },\n+ \"PropertyName\": function(node, obj) {\n+ obj.PropertyName = this.getChildValue(node);\n+ },\n+ \"ParameterName\": function(node, obj) {\n+ obj.ParameterName = this.getChildValue(node);\n+ },\n+ \"ListOfValues\": function(node, obj) {\n+ if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {\n+ obj.ListOfValues = [];\n+ }\n+ this.readChildNodes(node, obj.ListOfValues);\n+ },\n+ \"Value\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.push({\n+ Value: value\n+ });\n+ },\n+ \"ConceptualScheme\": function(node, obj) {\n+ obj.ConceptualScheme = {};\n+ this.readChildNodes(node, obj.ConceptualScheme);\n+ },\n+ \"Name\": function(node, obj) {\n+ obj.Name = this.getChildValue(node);\n+ },\n+ \"Document\": function(node, obj) {\n+ obj.Document = this.getChildValue(node);\n+ },\n+ \"Authority\": function(node, obj) {\n+ obj.Authority = this.getChildValue(node);\n+ },\n+ \"RangeOfValues\": function(node, obj) {\n+ obj.RangeOfValues = {};\n+ this.readChildNodes(node, obj.RangeOfValues);\n+ },\n+ \"MinValue\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MinValue = value;\n+ },\n+ \"MaxValue\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MaxValue = value;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Given an configuration js object, write a CSWGetDomain request. \n+ *\n+ * Parameters:\n+ * options - {Object} A object mapping the request.\n+ *\n+ * Returns:\n+ * {String} A serialized CSWGetDomain request.\n+ */\n+ write: function(options) {\n+ var node = this.writeNode(\"csw:GetDomain\", options);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"csw\": {\n+ \"GetDomain\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:GetDomain\", {\n+ attributes: {\n+ service: \"CSW\",\n+ version: this.version\n+ }\n+ });\n+ if (options.PropertyName || this.PropertyName) {\n+ this.writeNode(\n+ \"csw:PropertyName\",\n+ options.PropertyName || this.PropertyName,\n+ node\n+ );\n+ } else if (options.ParameterName || this.ParameterName) {\n+ this.writeNode(\n+ \"csw:ParameterName\",\n+ options.ParameterName || this.ParameterName,\n+ node\n+ );\n+ }\n+ this.readChildNodes(node, options);\n+ return node;\n+ },\n+ \"PropertyName\": function(value) {\n+ var node = this.createElementNSPlus(\"csw:PropertyName\", {\n+ value: value\n+ });\n+ return node;\n+ },\n+ \"ParameterName\": function(value) {\n+ var node = this.createElementNSPlus(\"csw:ParameterName\", {\n+ value: value\n+ });\n+ return node;\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n+});\n+/* ======================================================================\n OpenLayers/Format/XLS/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -55332,878 +45671,224 @@\n contentMetadata.push(coverageOfferingBrief);\n },\n \"name\": function(node, coverageOfferingBrief) {\n coverageOfferingBrief.name = this.getChildValue(node);\n },\n \"label\": function(node, coverageOfferingBrief) {\n coverageOfferingBrief.label = this.getChildValue(node);\n- },\n- \"lonLatEnvelope\": function(node, coverageOfferingBrief) {\n- var nodeList = this.getElementsByTagNameNS(node, \"http://www.opengis.net/gml\", \"pos\");\n-\n- // We expect two nodes here, to create the corners of a bounding box\n- if (nodeList.length == 2) {\n- var min = {};\n- var max = {};\n-\n- OpenLayers.Format.GML.v3.prototype.readers[\"gml\"].pos.apply(this, [nodeList[0], min]);\n- OpenLayers.Format.GML.v3.prototype.readers[\"gml\"].pos.apply(this, [nodeList[1], max]);\n-\n- coverageOfferingBrief.lonLatEnvelope = {};\n- coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute(\"srsName\");\n- coverageOfferingBrief.lonLatEnvelope.min = min.points[0];\n- coverageOfferingBrief.lonLatEnvelope.max = max.points[0];\n- }\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_0_0\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/WCSCapabilities/v1_1_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WCSCapabilities/v1.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WCSCapabilities/v1_1_0\n- * Read WCS Capabilities version 1.1.0.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.WCSCapabilities.v1>\n- */\n-OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class(\n- OpenLayers.Format.WCSCapabilities.v1, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- wcs: \"http://www.opengis.net/wcs/1.1\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- ows: \"http://www.opengis.net/ows/1.1\"\n- },\n-\n- /**\n- * APIProperty: errorProperty\n- * {String} Which property of the returned object to check for in order to\n- * determine whether or not parsing has failed. In the case that the\n- * errorProperty is undefined on the returned object, the document will be\n- * run through an OGCExceptionReport parser.\n- */\n- errorProperty: \"operationsMetadata\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0\n- * Create a new parser for WCS capabilities version 1.1.0.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wcs\": OpenLayers.Util.applyDefaults({\n- // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities\n- \"Capabilities\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Contents\": function(node, request) {\n- request.contentMetadata = [];\n- this.readChildNodes(node, request.contentMetadata);\n- },\n- \"CoverageSummary\": function(node, contentMetadata) {\n- var coverageSummary = {};\n- // Read the summary:\n- this.readChildNodes(node, coverageSummary);\n-\n- // Add it to the contentMetadata array: \n- contentMetadata.push(coverageSummary);\n- },\n- \"Identifier\": function(node, coverageSummary) {\n- coverageSummary.identifier = this.getChildValue(node);\n- },\n- \"Title\": function(node, coverageSummary) {\n- coverageSummary.title = this.getChildValue(node);\n- },\n- \"Abstract\": function(node, coverageSummary) {\n- coverageSummary[\"abstract\"] = this.getChildValue(node);\n- },\n- \"SupportedCRS\": function(node, coverageSummary) {\n- var crs = this.getChildValue(node);\n- if (crs) {\n- if (!coverageSummary.supportedCRS) {\n- coverageSummary.supportedCRS = [];\n- }\n- coverageSummary.supportedCRS.push(crs);\n- }\n- },\n- \"SupportedFormat\": function(node, coverageSummary) {\n- var format = this.getChildValue(node);\n- if (format) {\n- if (!coverageSummary.supportedFormat) {\n- coverageSummary.supportedFormat = [];\n- }\n- coverageSummary.supportedFormat.push(format);\n- }\n- }\n- }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers[\"wcs\"]),\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_1_0\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/SLD/v1_0_0_GeoServer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/SLD/v1_0_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer\n- * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options.\n- * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd\n- * for more information.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.SLD.v1_0_0>\n- */\n-OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(\n- OpenLayers.Format.SLD.v1_0_0, {\n-\n- /**\n- * Property: version\n- * {String} The specific parser version.\n- */\n- version: \"1.0.0\",\n-\n- /**\n- * Property: profile\n- * {String} The specific profile\n- */\n- profile: \"GeoServer\",\n-\n- /**\n- * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer\n- * Create a new parser for GeoServer-enhanced SLD version 1.0.0.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: OpenLayers.Util.applyDefaults({\n- \"sld\": OpenLayers.Util.applyDefaults({\n- \"Priority\": function(node, obj) {\n- var value = this.readers.ogc._expression.call(this, node);\n- if (value) {\n- obj.priority = value;\n- }\n- },\n- \"VendorOption\": function(node, obj) {\n- if (!obj.vendorOptions) {\n- obj.vendorOptions = {};\n- }\n- obj.vendorOptions[node.getAttribute(\"name\")] = this.getChildValue(node);\n- },\n- \"TextSymbolizer\": function(node, rule) {\n- OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);\n- var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer[\"Text\"];\n- if (symbolizer.graphic === undefined) {\n- symbolizer.graphic = false;\n- }\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers),\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: OpenLayers.Util.applyDefaults({\n- \"sld\": OpenLayers.Util.applyDefaults({\n- \"Priority\": function(priority) {\n- return this.writers.sld._OGCExpression.call(\n- this, \"sld:Priority\", priority\n- );\n- },\n- \"VendorOption\": function(option) {\n- return this.createElementNSPlus(\"sld:VendorOption\", {\n- attributes: {\n- name: option.name\n- },\n- value: option.value\n- });\n- },\n- \"TextSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"TextSymbolizer\"].apply(this, arguments);\n- if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {\n- this.writeNode(\"Graphic\", symbolizer, node);\n- }\n- if (\"priority\" in symbolizer) {\n- this.writeNode(\"Priority\", symbolizer.priority, node);\n- }\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"PointSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PointSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"LineSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"LineSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"PolygonSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PolygonSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers),\n-\n- /**\n- * Method: addVendorOptions\n- * Add in the VendorOption tags and return the node again.\n- *\n- * Parameters:\n- * node - {DOMElement} A DOM node.\n- * symbolizer - {Object}\n- *\n- * Returns:\n- * {DOMElement} A DOM node.\n- */\n- addVendorOptions: function(node, symbolizer) {\n- var options = symbolizer.vendorOptions;\n- if (options) {\n- for (var key in symbolizer.vendorOptions) {\n- this.writeNode(\"VendorOption\", {\n- name: key,\n- value: symbolizer.vendorOptions[key]\n- }, node);\n- }\n- }\n- return node;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.SLD.v1_0_0_GeoServer\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/CSWGetDomain/v2_0_2.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/CSWGetDomain.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CSWGetDomain.v2_0_2\n- * A format for creating CSWGetDomain v2.0.2 transactions. \n- * Create a new instance with the\n- * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n- },\n-\n- /**\n- * Property: defaultPrefix\n- * {String} The default prefix (used by Format.XML).\n- */\n- defaultPrefix: \"csw\",\n-\n- /**\n- * Property: version\n- * {String} CSW version number.\n- */\n- version: \"2.0.2\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} http://www.opengis.net/cat/csw/2.0.2\n- * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n- */\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n-\n- /**\n- * APIProperty: PropertyName\n- * {String} Value of the csw:PropertyName element, used when\n- * writing a GetDomain document.\n- */\n- PropertyName: null,\n-\n- /**\n- * APIProperty: ParameterName\n- * {String} Value of the csw:ParameterName element, used when\n- * writing a GetDomain document.\n- */\n- ParameterName: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2\n- * A class for parsing and generating CSWGetDomain v2.0.2 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * - PropertyName\n- * - ParameterName\n- */\n-\n- /**\n- * APIMethod: read\n- * Parse the response from a GetDomain request.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"csw\": {\n- \"GetDomainResponse\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"DomainValues\": function(node, obj) {\n- if (!(OpenLayers.Util.isArray(obj.DomainValues))) {\n- obj.DomainValues = [];\n- }\n- var attrs = node.attributes;\n- var domainValue = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- domainValue[attrs[i].name] = attrs[i].nodeValue;\n- }\n- this.readChildNodes(node, domainValue);\n- obj.DomainValues.push(domainValue);\n- },\n- \"PropertyName\": function(node, obj) {\n- obj.PropertyName = this.getChildValue(node);\n- },\n- \"ParameterName\": function(node, obj) {\n- obj.ParameterName = this.getChildValue(node);\n- },\n- \"ListOfValues\": function(node, obj) {\n- if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {\n- obj.ListOfValues = [];\n- }\n- this.readChildNodes(node, obj.ListOfValues);\n- },\n- \"Value\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.push({\n- Value: value\n- });\n- },\n- \"ConceptualScheme\": function(node, obj) {\n- obj.ConceptualScheme = {};\n- this.readChildNodes(node, obj.ConceptualScheme);\n- },\n- \"Name\": function(node, obj) {\n- obj.Name = this.getChildValue(node);\n- },\n- \"Document\": function(node, obj) {\n- obj.Document = this.getChildValue(node);\n- },\n- \"Authority\": function(node, obj) {\n- obj.Authority = this.getChildValue(node);\n- },\n- \"RangeOfValues\": function(node, obj) {\n- obj.RangeOfValues = {};\n- this.readChildNodes(node, obj.RangeOfValues);\n- },\n- \"MinValue\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.MinValue = value;\n- },\n- \"MaxValue\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.MaxValue = value;\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: write\n- * Given an configuration js object, write a CSWGetDomain request. \n- *\n- * Parameters:\n- * options - {Object} A object mapping the request.\n- *\n- * Returns:\n- * {String} A serialized CSWGetDomain request.\n- */\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetDomain\", options);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n+ },\n+ \"lonLatEnvelope\": function(node, coverageOfferingBrief) {\n+ var nodeList = this.getElementsByTagNameNS(node, \"http://www.opengis.net/gml\", \"pos\");\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"csw\": {\n- \"GetDomain\": function(options) {\n- var node = this.createElementNSPlus(\"csw:GetDomain\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version\n+ // We expect two nodes here, to create the corners of a bounding box\n+ if (nodeList.length == 2) {\n+ var min = {};\n+ var max = {};\n+\n+ OpenLayers.Format.GML.v3.prototype.readers[\"gml\"].pos.apply(this, [nodeList[0], min]);\n+ OpenLayers.Format.GML.v3.prototype.readers[\"gml\"].pos.apply(this, [nodeList[1], max]);\n+\n+ coverageOfferingBrief.lonLatEnvelope = {};\n+ coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute(\"srsName\");\n+ coverageOfferingBrief.lonLatEnvelope.min = min.points[0];\n+ coverageOfferingBrief.lonLatEnvelope.max = max.points[0];\n }\n- });\n- if (options.PropertyName || this.PropertyName) {\n- this.writeNode(\n- \"csw:PropertyName\",\n- options.PropertyName || this.PropertyName,\n- node\n- );\n- } else if (options.ParameterName || this.ParameterName) {\n- this.writeNode(\n- \"csw:ParameterName\",\n- options.ParameterName || this.ParameterName,\n- node\n- );\n }\n- this.readChildNodes(node, options);\n- return node;\n- },\n- \"PropertyName\": function(value) {\n- var node = this.createElementNSPlus(\"csw:PropertyName\", {\n- value: value\n- });\n- return node;\n- },\n- \"ParameterName\": function(value) {\n- var node = this.createElementNSPlus(\"csw:ParameterName\", {\n- value: value\n- });\n- return node;\n }\n- }\n- },\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n-});\n+ CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_0_0\"\n+\n+ });\n /* ======================================================================\n- OpenLayers/Format/WMTSCapabilities/v1_0_0.js\n+ OpenLayers/Format/WCSCapabilities/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WMTSCapabilities.js\n+ * @requires OpenLayers/Format/WCSCapabilities/v1.js\n * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n */\n \n /**\n- * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0\n- * Read WMTS Capabilities version 1.0.0.\n+ * Class: OpenLayers.Format.WCSCapabilities/v1_1_0\n+ * Read WCS Capabilities version 1.1.0.\n * \n * Inherits from:\n- * - <OpenLayers.Format.WMTSCapabilities>\n+ * - <OpenLayers.Format.WCSCapabilities.v1>\n */\n-OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.OWSCommon.v1_1_0, {\n-\n- /**\n- * Property: version\n- * {String} The parser version (\"1.0.0\").\n- */\n- version: \"1.0.0\",\n+OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.WCSCapabilities.v1, {\n \n /**\n * Property: namespaces\n * {Object} Mapping of namespace aliases to namespace URIs.\n */\n namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wmts: \"http://www.opengis.net/wmts/1.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n+ wcs: \"http://www.opengis.net/wcs/1.1\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ ows: \"http://www.opengis.net/ows/1.1\"\n },\n \n /**\n- * Property: yx\n- * {Object} Members in the yx object are used to determine if a CRS URN\n- * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n- * and values are boolean. Defaults come from the \n- * <OpenLayers.Format.WMTSCapabilities> prototype.\n- */\n- yx: null,\n-\n- /**\n- * Property: defaultPrefix\n- * {String} The default namespace alias for creating element nodes.\n+ * APIProperty: errorProperty\n+ * {String} Which property of the returned object to check for in order to\n+ * determine whether or not parsing has failed. In the case that the\n+ * errorProperty is undefined on the returned object, the document will be\n+ * run through an OGCExceptionReport parser.\n */\n- defaultPrefix: \"wmts\",\n+ errorProperty: \"operationsMetadata\",\n \n /**\n- * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0\n- * Create a new parser for WMTS capabilities version 1.0.0. \n+ * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0\n+ * Create a new parser for WCS capabilities version 1.1.0.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options;\n- var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n- this.yx = OpenLayers.Util.extend(yx, this.yx);\n- },\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return info about the WMTS.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Information about the SOS service.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- capabilities.version = this.version;\n- return capabilities;\n- },\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n- \"wmts\": {\n+ \"wcs\": OpenLayers.Util.applyDefaults({\n+ // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities\n \"Capabilities\": function(node, obj) {\n this.readChildNodes(node, obj);\n },\n- \"Contents\": function(node, obj) {\n- obj.contents = {};\n- obj.contents.layers = [];\n- obj.contents.tileMatrixSets = {};\n- this.readChildNodes(node, obj.contents);\n- },\n- \"Layer\": function(node, obj) {\n- var layer = {\n- styles: [],\n- formats: [],\n- dimensions: [],\n- tileMatrixSetLinks: []\n- };\n- layer.layers = [];\n- this.readChildNodes(node, layer);\n- obj.layers.push(layer);\n- },\n- \"Style\": function(node, obj) {\n- var style = {};\n- style.isDefault = (node.getAttribute(\"isDefault\") === \"true\");\n- this.readChildNodes(node, style);\n- obj.styles.push(style);\n- },\n- \"Format\": function(node, obj) {\n- obj.formats.push(this.getChildValue(node));\n+ \"Contents\": function(node, request) {\n+ request.contentMetadata = [];\n+ this.readChildNodes(node, request.contentMetadata);\n },\n- \"TileMatrixSetLink\": function(node, obj) {\n- var tileMatrixSetLink = {};\n- this.readChildNodes(node, tileMatrixSetLink);\n- obj.tileMatrixSetLinks.push(tileMatrixSetLink);\n+ \"CoverageSummary\": function(node, contentMetadata) {\n+ var coverageSummary = {};\n+ // Read the summary:\n+ this.readChildNodes(node, coverageSummary);\n+\n+ // Add it to the contentMetadata array: \n+ contentMetadata.push(coverageSummary);\n },\n- \"TileMatrixSet\": function(node, obj) {\n- // node could be child of wmts:Contents or wmts:TileMatrixSetLink\n- // duck type wmts:Contents by looking for layers\n- if (obj.layers) {\n- // TileMatrixSet as object type in schema\n- var tileMatrixSet = {\n- matrixIds: []\n- };\n- this.readChildNodes(node, tileMatrixSet);\n- obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;\n- } else {\n- // TileMatrixSet as string type in schema\n- obj.tileMatrixSet = this.getChildValue(node);\n- }\n+ \"Identifier\": function(node, coverageSummary) {\n+ coverageSummary.identifier = this.getChildValue(node);\n },\n- \"TileMatrix\": function(node, obj) {\n- var tileMatrix = {\n- supportedCRS: obj.supportedCRS\n- };\n- this.readChildNodes(node, tileMatrix);\n- obj.matrixIds.push(tileMatrix);\n+ \"Title\": function(node, coverageSummary) {\n+ coverageSummary.title = this.getChildValue(node);\n },\n- \"ScaleDenominator\": function(node, obj) {\n- obj.scaleDenominator = parseFloat(this.getChildValue(node));\n+ \"Abstract\": function(node, coverageSummary) {\n+ coverageSummary[\"abstract\"] = this.getChildValue(node);\n },\n- \"TopLeftCorner\": function(node, obj) {\n- var topLeftCorner = this.getChildValue(node);\n- var coords = topLeftCorner.split(\" \");\n- // decide on axis order for the given CRS\n- var yx;\n- if (obj.supportedCRS) {\n- // extract out version from URN\n- var crs = obj.supportedCRS.replace(\n- /urn:ogc:def:crs:(\\w+):.+:(\\w+)$/,\n- \"urn:ogc:def:crs:$1::$2\"\n- );\n- yx = !!this.yx[crs];\n- }\n- if (yx) {\n- obj.topLeftCorner = new OpenLayers.LonLat(\n- coords[1], coords[0]\n- );\n- } else {\n- obj.topLeftCorner = new OpenLayers.LonLat(\n- coords[0], coords[1]\n- );\n+ \"SupportedCRS\": function(node, coverageSummary) {\n+ var crs = this.getChildValue(node);\n+ if (crs) {\n+ if (!coverageSummary.supportedCRS) {\n+ coverageSummary.supportedCRS = [];\n+ }\n+ coverageSummary.supportedCRS.push(crs);\n }\n },\n- \"TileWidth\": function(node, obj) {\n- obj.tileWidth = parseInt(this.getChildValue(node));\n- },\n- \"TileHeight\": function(node, obj) {\n- obj.tileHeight = parseInt(this.getChildValue(node));\n- },\n- \"MatrixWidth\": function(node, obj) {\n- obj.matrixWidth = parseInt(this.getChildValue(node));\n- },\n- \"MatrixHeight\": function(node, obj) {\n- obj.matrixHeight = parseInt(this.getChildValue(node));\n- },\n- \"ResourceURL\": function(node, obj) {\n- obj.resourceUrl = obj.resourceUrl || {};\n- var resourceType = node.getAttribute(\"resourceType\");\n- if (!obj.resourceUrls) {\n- obj.resourceUrls = [];\n+ \"SupportedFormat\": function(node, coverageSummary) {\n+ var format = this.getChildValue(node);\n+ if (format) {\n+ if (!coverageSummary.supportedFormat) {\n+ coverageSummary.supportedFormat = [];\n+ }\n+ coverageSummary.supportedFormat.push(format);\n }\n- var resourceUrl = obj.resourceUrl[resourceType] = {\n- format: node.getAttribute(\"format\"),\n- template: node.getAttribute(\"template\"),\n- resourceType: resourceType\n- };\n- obj.resourceUrls.push(resourceUrl);\n- },\n- // not used for now, can be added in the future though\n- /*\"Themes\": function(node, obj) {\n- obj.themes = [];\n- this.readChildNodes(node, obj.themes);\n- },\n- \"Theme\": function(node, obj) {\n- var theme = {}; \n- this.readChildNodes(node, theme);\n- obj.push(theme);\n- },*/\n- \"WSDL\": function(node, obj) {\n- obj.wsdl = {};\n- obj.wsdl.href = node.getAttribute(\"xlink:href\");\n- // TODO: other attributes of <WSDL> element \n- },\n- \"ServiceMetadataURL\": function(node, obj) {\n- obj.serviceMetadataUrl = {};\n- obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\");\n- // TODO: other attributes of <ServiceMetadataURL> element \n- },\n- \"LegendURL\": function(node, obj) {\n- obj.legend = {};\n- obj.legend.href = node.getAttribute(\"xlink:href\");\n- obj.legend.format = node.getAttribute(\"format\");\n- },\n- \"Dimension\": function(node, obj) {\n- var dimension = {\n- values: []\n- };\n- this.readChildNodes(node, dimension);\n- obj.dimensions.push(dimension);\n- },\n- \"Default\": function(node, obj) {\n- obj[\"default\"] = this.getChildValue(node);\n- },\n- \"Value\": function(node, obj) {\n- obj.values.push(this.getChildValue(node));\n }\n- },\n+ }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers[\"wcs\"]),\n \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_1_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WPSCapabilities/v1_0_0.js\n+ OpenLayers/Format/WFSCapabilities/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WPSCapabilities.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/WFSCapabilities.js\n */\n \n /**\n- * Class: OpenLayers.Format.WPSCapabilities.v1_0_0\n- * Read WPS Capabilities version 1.0.0.\n+ * Class: OpenLayers.Format.WFSCapabilities.v1\n+ * Abstract class not to be instantiated directly.\n * \n * Inherits from:\n * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(\n+OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(\n OpenLayers.Format.XML, {\n \n /**\n * Property: namespaces\n * {Object} Mapping of namespace aliases to namespace URIs.\n */\n namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n+ wfs: \"http://www.opengis.net/wfs\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ ows: \"http://www.opengis.net/ows\"\n },\n \n+\n /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n+ * APIProperty: errorProperty\n+ * {String} Which property of the returned object to check for in order to\n+ * determine whether or not parsing has failed. In the case that the\n+ * errorProperty is undefined on the returned object, the document will be\n+ * run through an OGCExceptionReport parser.\n */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+ errorProperty: \"featureTypeList\",\n \n /**\n- * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0\n- * Create a new parser for WPS capabilities version 1.0.0. \n+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"wfs\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_1\n+ * Create an instance of one of the subclasses.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n \n /**\n * APIMethod: read\n- * Read capabilities data from a string, and return info about the WPS.\n+ * Read capabilities data from a string, and return a list of layers. \n * \n * Parameters: \n * data - {String} or {DOMElement} data to read/parse.\n *\n * Returns:\n- * {Object} Information about the WPS service.\n+ * {Array} List of named layers.\n */\n read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n+ var raw = data;\n if (data && data.nodeType == 9) {\n data = data.documentElement;\n }\n var capabilities = {};\n this.readNode(data, capabilities);\n return capabilities;\n },\n@@ -56213,577 +45898,294 @@\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n- \"wps\": {\n- \"Capabilities\": function(node, obj) {\n+ \"wfs\": {\n+ \"WFS_Capabilities\": function(node, obj) {\n this.readChildNodes(node, obj);\n },\n- \"ProcessOfferings\": function(node, obj) {\n- obj.processOfferings = {};\n- this.readChildNodes(node, obj.processOfferings);\n- },\n- \"Process\": function(node, processOfferings) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var process = {\n- processVersion: processVersion\n+ \"FeatureTypeList\": function(node, request) {\n+ request.featureTypeList = {\n+ featureTypes: []\n };\n- this.readChildNodes(node, process);\n- processOfferings[process.identifier] = process;\n- },\n- \"Languages\": function(node, obj) {\n- obj.languages = [];\n- this.readChildNodes(node, obj.languages);\n+ this.readChildNodes(node, request.featureTypeList);\n },\n- \"Default\": function(node, languages) {\n- var language = {\n- isDefault: true\n- };\n- this.readChildNodes(node, language);\n- languages.push(language);\n+ \"FeatureType\": function(node, featureTypeList) {\n+ var featureType = {};\n+ this.readChildNodes(node, featureType);\n+ featureTypeList.featureTypes.push(featureType);\n },\n- \"Supported\": function(node, languages) {\n- var language = {};\n- this.readChildNodes(node, language);\n- languages.push(language);\n- }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/WMSDescribeLayer/v1_1.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WMSDescribeLayer.js\n- * @requires OpenLayers/Format/OGCExceptionReport.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1\n- * Read SLD WMS DescribeLayer response for WMS 1.1.X\n- * WMS 1.1.X is tightly coupled to SLD 1.0.0\n- *\n- * Example DescribeLayer request: \n- * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.WMSDescribeLayer>\n- */\n-OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(\n- OpenLayers.Format.WMSDescribeLayer, {\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSDescribeLayer\n- * Create a new parser for WMS DescribeLayer responses.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,\n- [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Read DescribeLayer data from a string, and return the response. \n- * The OGC defines 2 formats which are allowed for output,\n- * so we need to parse these 2 types for version 1.1.X\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Object with a layerDescriptions property, which holds an Array\n- * of {<LayerDescription>} objects which have:\n- * - {String} owsType: WFS/WCS\n- * - {String} owsURL: the online resource\n- * - {String} typeName: the name of the typename on the owsType service\n- * - {String} layerName: the name of the WMS layer we did a lookup for\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- var root = data.documentElement;\n- var children = root.childNodes;\n- var describelayer = {\n- layerDescriptions: []\n- };\n- var childNode, nodeName;\n- for (var i = 0; i < children.length; ++i) {\n- childNode = children[i];\n- nodeName = childNode.nodeName;\n- if (nodeName == 'LayerDescription') {\n- var layerName = childNode.getAttribute('name');\n- var owsType = '';\n- var owsURL = '';\n- var typeName = '';\n- // check for owsType and owsURL attributes\n- if (childNode.getAttribute('owsType')) {\n- owsType = childNode.getAttribute('owsType');\n- owsURL = childNode.getAttribute('owsURL');\n- } else {\n- // look for wfs or wcs attribute\n- if (childNode.getAttribute('wfs') != '') {\n- owsType = 'WFS';\n- owsURL = childNode.getAttribute('wfs');\n- } else if (childNode.getAttribute('wcs') != '') {\n- owsType = 'WCS';\n- owsURL = childNode.getAttribute('wcs');\n+ \"Name\": function(node, obj) {\n+ var name = this.getChildValue(node);\n+ if (name) {\n+ var parts = name.split(\":\");\n+ obj.name = parts.pop();\n+ if (parts.length > 0) {\n+ obj.featureNS = this.lookupNamespaceURI(node, parts[0]);\n }\n }\n- // look for Query child\n- var query = childNode.getElementsByTagName('Query');\n- if (query.length > 0) {\n- typeName = query[0].getAttribute('typeName');\n- if (!typeName) {\n- // because of Ionic bug\n- typeName = query[0].getAttribute('typename');\n- }\n+ },\n+ \"Title\": function(node, obj) {\n+ var title = this.getChildValue(node);\n+ if (title) {\n+ obj.title = title;\n+ }\n+ },\n+ \"Abstract\": function(node, obj) {\n+ var abst = this.getChildValue(node);\n+ if (abst) {\n+ obj[\"abstract\"] = abst;\n }\n- var layerDescription = {\n- layerName: layerName,\n- owsType: owsType,\n- owsURL: owsURL,\n- typeName: typeName\n- };\n- describelayer.layerDescriptions.push(layerDescription);\n-\n- //TODO do this in deprecated.js instead:\n- // array style index for backwards compatibility\n- describelayer.length = describelayer.layerDescriptions.length;\n- describelayer[describelayer.length - 1] = layerDescription;\n-\n- } else if (nodeName == 'ServiceException') {\n- // an exception must have occurred, so parse it\n- var parser = new OpenLayers.Format.OGCExceptionReport();\n- return {\n- error: parser.read(data)\n- };\n }\n }\n- return describelayer;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1\"\n \n });\n-\n-// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257\n-OpenLayers.Format.WMSDescribeLayer.v1_1_0 =\n- OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n /* ======================================================================\n- OpenLayers/Format/ArcXML/Features.js\n+ OpenLayers/Format/WFSCapabilities/v1_0_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/ArcXML.js\n+ * @requires OpenLayers/Format/WFSCapabilities/v1.js\n */\n \n /**\n- * Class: OpenLayers.Format.ArcXML.Features\n- * Read/Write ArcXML features. Create a new instance with the \n- * <OpenLayers.Format.ArcXML.Features> constructor.\n+ * Class: OpenLayers.Format.WFSCapabilities/v1_0_0\n+ * Read WFS Capabilities version 1.0.0.\n * \n * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Constructor: OpenLayers.Format.ArcXML.Features\n- * Create a new parser/writer for ArcXML Features. Create an instance of this class\n- * to get a set of features from an ArcXML response.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read data from a string of ArcXML, and return a set of OpenLayers features. \n- * \n- * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} A collection of features.\n- */\n- read: function(data) {\n- var axl = new OpenLayers.Format.ArcXML();\n- var parsed = axl.read(data);\n-\n- return parsed.features.feature;\n- }\n-});\n-/* ======================================================================\n- OpenLayers/Format/WFST/v1_0_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WFST/v1.js\n- * @requires OpenLayers/Format/Filter/v1_0_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WFST.v1_0_0\n- * A format for creating WFS v1.0.0 transactions. Create a new instance with the\n- * <OpenLayers.Format.WFST.v1_0_0> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.Filter.v1_0_0>\n- * - <OpenLayers.Format.WFST.v1>\n+ * - <OpenLayers.Format.WFSCapabilities.v1>\n */\n-OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n-\n- /**\n- * Property: version\n- * {String} WFS version number.\n- */\n- version: \"1.0.0\",\n-\n- /**\n- * APIProperty: srsNameInQuery\n- * {Boolean} If true the reference system is passed in Query requests\n- * via the \"srsName\" attribute to the \"wfs:Query\" element, this\n- * property defaults to false as it isn't WFS 1.0.0 compliant.\n- */\n- srsNameInQuery: false,\n-\n- /**\n- * Property: schemaLocations\n- * {Object} Properties are namespace aliases, values are schema locations.\n- */\n- schemaLocations: {\n- \"wfs\": \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WFST.v1_0_0\n- * A class for parsing and generating WFS v1.0.0 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n- OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n- },\n+OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.WFSCapabilities.v1, {\n \n /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0\n+ * Create a new parser for WFS capabilities version 1.0.0.\n *\n * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n- * first - {Boolean} Should be set to true for the first node read. This\n- * is usually the readNode call in the read method. Without this being\n- * set, auto-configured properties will stick on subsequent reads.\n- *\n- * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n */\n- readNode: function(node, obj, first) {\n- // Not the superclass, only the mixin classes inherit from\n- // Format.GML.v2. We need this because we don't want to get readNode\n- // from the superclass's superclass, which is OpenLayers.Format.XML.\n- return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);\n- },\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n \"wfs\": OpenLayers.Util.applyDefaults({\n- \"WFS_TransactionResponse\": function(node, obj) {\n- obj.insertIds = [];\n- obj.success = false;\n- this.readChildNodes(node, obj);\n- },\n- \"InsertResult\": function(node, container) {\n- var obj = {\n- fids: []\n- };\n- this.readChildNodes(node, obj);\n- container.insertIds = container.insertIds.concat(obj.fids);\n+ \"Service\": function(node, capabilities) {\n+ capabilities.service = {};\n+ this.readChildNodes(node, capabilities.service);\n },\n- \"TransactionResult\": function(node, obj) {\n- this.readChildNodes(node, obj);\n+ \"Fees\": function(node, service) {\n+ var fees = this.getChildValue(node);\n+ if (fees && fees.toLowerCase() != \"none\") {\n+ service.fees = fees;\n+ }\n },\n- \"Status\": function(node, obj) {\n- this.readChildNodes(node, obj);\n+ \"AccessConstraints\": function(node, service) {\n+ var constraints = this.getChildValue(node);\n+ if (constraints && constraints.toLowerCase() != \"none\") {\n+ service.accessConstraints = constraints;\n+ }\n },\n- \"SUCCESS\": function(node, obj) {\n- obj.success = true;\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"Query\": function(options) {\n- options = OpenLayers.Util.extend({\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- featureType: this.featureType,\n- srsName: this.srsName,\n- srsNameInQuery: this.srsNameInQuery\n- }, options);\n- var prefix = options.featurePrefix;\n- var node = this.createElementNSPlus(\"wfs:Query\", {\n- attributes: {\n- typeName: (prefix ? prefix + \":\" : \"\") +\n- options.featureType\n- }\n- });\n- if (options.srsNameInQuery && options.srsName) {\n- node.setAttribute(\"srsName\", options.srsName);\n+ \"OnlineResource\": function(node, service) {\n+ var onlineResource = this.getChildValue(node);\n+ if (onlineResource && onlineResource.toLowerCase() != \"none\") {\n+ service.onlineResource = onlineResource;\n }\n- if (options.featureNS) {\n- node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n+ },\n+ \"Keywords\": function(node, service) {\n+ var keywords = this.getChildValue(node);\n+ if (keywords && keywords.toLowerCase() != \"none\") {\n+ service.keywords = keywords.split(', ');\n }\n- if (options.propertyNames) {\n- for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n- this.writeNode(\n- \"ogc:PropertyName\", {\n- property: options.propertyNames[i]\n- },\n- node\n- );\n+ },\n+ \"Capability\": function(node, capabilities) {\n+ capabilities.capability = {};\n+ this.readChildNodes(node, capabilities.capability);\n+ },\n+ \"Request\": function(node, obj) {\n+ obj.request = {};\n+ this.readChildNodes(node, obj.request);\n+ },\n+ \"GetFeature\": function(node, request) {\n+ request.getfeature = {\n+ href: {}, // DCPType\n+ formats: [] // ResultFormat\n+ };\n+ this.readChildNodes(node, request.getfeature);\n+ },\n+ \"ResultFormat\": function(node, obj) {\n+ var children = node.childNodes;\n+ var childNode;\n+ for (var i = 0; i < children.length; i++) {\n+ childNode = children[i];\n+ if (childNode.nodeType == 1) {\n+ obj.formats.push(childNode.nodeName);\n }\n }\n- if (options.filter) {\n- this.setFilterProperty(options.filter);\n- this.writeNode(\"ogc:Filter\", options.filter, node);\n+ },\n+ \"DCPType\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"HTTP\": function(node, obj) {\n+ this.readChildNodes(node, obj.href);\n+ },\n+ \"Get\": function(node, obj) {\n+ obj.get = node.getAttribute(\"onlineResource\");\n+ },\n+ \"Post\": function(node, obj) {\n+ obj.post = node.getAttribute(\"onlineResource\");\n+ },\n+ \"SRS\": function(node, obj) {\n+ var srs = this.getChildValue(node);\n+ if (srs) {\n+ obj.srs = srs;\n }\n- return node;\n }\n- }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"])\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_0_0\"\n+\n });\n /* ======================================================================\n- OpenLayers/Format/SOSCapabilities/v1_0_0.js\n+ OpenLayers/Format/WFSCapabilities/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/SOSCapabilities.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- * @requires OpenLayers/Format/GML/v3.js\n+ * @requires OpenLayers/Format/WFSCapabilities/v1.js\n+ * @requires OpenLayers/Format/OWSCommon/v1.js\n */\n \n /**\n- * Class: OpenLayers.Format.SOSCapabilities.v1_0_0\n- * Read SOS Capabilities version 1.0.0.\n+ * Class: OpenLayers.Format.WFSCapabilities/v1_1_0\n+ * Read WFS Capabilities version 1.1.0.\n * \n * Inherits from:\n- * - <OpenLayers.Format.SOSCapabilities>\n+ * - <OpenLayers.Format.WFSCapabilities>\n */\n-OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.SOSCapabilities, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- sos: \"http://www.opengis.net/sos/1.0\",\n- gml: \"http://www.opengis.net/gml\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n+OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.WFSCapabilities.v1, {\n \n /**\n * Property: regExes\n * Compiled regular expressions for manipulating strings.\n */\n regExes: {\n trimSpace: (/^\\s*|\\s*$/g),\n removeSpace: (/\\s*/g),\n splitSpace: (/\\s+/),\n trimComma: (/\\s*,\\s*/g)\n },\n \n /**\n- * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0\n- * Create a new parser for SOS capabilities version 1.0.0. \n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0\n+ * Create a new parser for WFS capabilities version 1.1.0.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options;\n- },\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return info about the SOS.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Information about the SOS service.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities;\n- },\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"name\": function(node, obj) {\n- obj.name = this.getChildValue(node);\n- },\n- \"TimePeriod\": function(node, obj) {\n- obj.timePeriod = {};\n- this.readChildNodes(node, obj.timePeriod);\n- },\n- \"beginPosition\": function(node, timePeriod) {\n- timePeriod.beginPosition = this.getChildValue(node);\n- },\n- \"endPosition\": function(node, timePeriod) {\n- timePeriod.endPosition = this.getChildValue(node);\n- }\n- }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n- \"sos\": {\n- \"Capabilities\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Contents\": function(node, obj) {\n- obj.contents = {};\n- this.readChildNodes(node, obj.contents);\n- },\n- \"ObservationOfferingList\": function(node, contents) {\n- contents.offeringList = {};\n- this.readChildNodes(node, contents.offeringList);\n- },\n- \"ObservationOffering\": function(node, offeringList) {\n- var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n- offeringList[id] = {\n- procedures: [],\n- observedProperties: [],\n- featureOfInterestIds: [],\n- responseFormats: [],\n- resultModels: [],\n- responseModes: []\n- };\n- this.readChildNodes(node, offeringList[id]);\n- },\n- \"time\": function(node, offering) {\n- offering.time = {};\n- this.readChildNodes(node, offering.time);\n- },\n- \"procedure\": function(node, offering) {\n- offering.procedures.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"observedProperty\": function(node, offering) {\n- offering.observedProperties.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"featureOfInterest\": function(node, offering) {\n- offering.featureOfInterestIds.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"responseFormat\": function(node, offering) {\n- offering.responseFormats.push(this.getChildValue(node));\n- },\n- \"resultModel\": function(node, offering) {\n- offering.resultModels.push(this.getChildValue(node));\n- },\n- \"responseMode\": function(node, offering) {\n- offering.responseModes.push(this.getChildValue(node));\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"DefaultSRS\": function(node, obj) {\n+ var defaultSRS = this.getChildValue(node);\n+ if (defaultSRS) {\n+ obj.srs = defaultSRS;\n+ }\n }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"]),\n+ \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows\n },\n \n- CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_1_0\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/ArcXML/Features.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/ArcXML.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.ArcXML.Features\n+ * Read/Write ArcXML features. Create a new instance with the \n+ * <OpenLayers.Format.ArcXML.Features> constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Constructor: OpenLayers.Format.ArcXML.Features\n+ * Create a new parser/writer for ArcXML Features. Create an instance of this class\n+ * to get a set of features from an ArcXML response.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read data from a string of ArcXML, and return a set of OpenLayers features. \n+ * \n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} A collection of features.\n+ */\n+ read: function(data) {\n+ var axl = new OpenLayers.Format.ArcXML();\n+ var parsed = axl.read(data);\n+\n+ return parsed.features.feature;\n+ }\n+});\n+/* ======================================================================\n OpenLayers/Format/WMC/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -58886,14 +48288,109 @@\n }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers[\"wms\"])\n },\n \n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC\n+ * Read WMS-C Capabilities version 1.1.1.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.WMSCapabilities.v1_1_1>\n+ */\n+OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(\n+ OpenLayers.Format.WMSCapabilities.v1_1_1, {\n+\n+ /**\n+ * Property: version\n+ * {String} The specific parser version.\n+ */\n+ version: \"1.1.1\",\n+\n+ /**\n+ * Property: profile\n+ * {String} The specific profile\n+ */\n+ profile: \"WMSC\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1\n+ * Create a new parser for WMS-C capabilities version 1.1.1.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wms\": OpenLayers.Util.applyDefaults({\n+ \"VendorSpecificCapabilities\": function(node, obj) {\n+ obj.vendorSpecific = {\n+ tileSets: []\n+ };\n+ this.readChildNodes(node, obj.vendorSpecific);\n+ },\n+ \"TileSet\": function(node, vendorSpecific) {\n+ var tileset = {\n+ srs: {},\n+ bbox: {},\n+ resolutions: []\n+ };\n+ this.readChildNodes(node, tileset);\n+ vendorSpecific.tileSets.push(tileset);\n+ },\n+ \"Resolutions\": function(node, tileset) {\n+ var res = this.getChildValue(node).split(\" \");\n+ for (var i = 0, len = res.length; i < len; i++) {\n+ if (res[i] != \"\") {\n+ tileset.resolutions.push(parseFloat(res[i]));\n+ }\n+ }\n+ },\n+ \"Width\": function(node, tileset) {\n+ tileset.width = parseInt(this.getChildValue(node));\n+ },\n+ \"Height\": function(node, tileset) {\n+ tileset.height = parseInt(this.getChildValue(node));\n+ },\n+ \"Layers\": function(node, tileset) {\n+ tileset.layers = this.getChildValue(node);\n+ },\n+ \"Styles\": function(node, tileset) {\n+ tileset.styles = this.getChildValue(node);\n+ }\n+ }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers[\"wms\"])\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC\"\n+\n+ });\n+/* ======================================================================\n OpenLayers/Format/WMSCapabilities/v1_3.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -59054,109 +48551,14 @@\n */\n version: \"1.3.0\",\n \n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_3_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC\n- * Read WMS-C Capabilities version 1.1.1.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.WMSCapabilities.v1_1_1>\n- */\n-OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(\n- OpenLayers.Format.WMSCapabilities.v1_1_1, {\n-\n- /**\n- * Property: version\n- * {String} The specific parser version.\n- */\n- version: \"1.1.1\",\n-\n- /**\n- * Property: profile\n- * {String} The specific profile\n- */\n- profile: \"WMSC\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1\n- * Create a new parser for WMS-C capabilities version 1.1.1.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wms\": OpenLayers.Util.applyDefaults({\n- \"VendorSpecificCapabilities\": function(node, obj) {\n- obj.vendorSpecific = {\n- tileSets: []\n- };\n- this.readChildNodes(node, obj.vendorSpecific);\n- },\n- \"TileSet\": function(node, vendorSpecific) {\n- var tileset = {\n- srs: {},\n- bbox: {},\n- resolutions: []\n- };\n- this.readChildNodes(node, tileset);\n- vendorSpecific.tileSets.push(tileset);\n- },\n- \"Resolutions\": function(node, tileset) {\n- var res = this.getChildValue(node).split(\" \");\n- for (var i = 0, len = res.length; i < len; i++) {\n- if (res[i] != \"\") {\n- tileset.resolutions.push(parseFloat(res[i]));\n- }\n- }\n- },\n- \"Width\": function(node, tileset) {\n- tileset.width = parseInt(this.getChildValue(node));\n- },\n- \"Height\": function(node, tileset) {\n- tileset.height = parseInt(this.getChildValue(node));\n- },\n- \"Layers\": function(node, tileset) {\n- tileset.layers = this.getChildValue(node);\n- },\n- \"Styles\": function(node, tileset) {\n- tileset.styles = this.getChildValue(node);\n- }\n- }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers[\"wms\"])\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC\"\n-\n- });\n-/* ======================================================================\n OpenLayers/Format/WMSCapabilities/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -59210,1454 +48612,3880 @@\n }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers[\"wms\"])\n },\n \n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WFSCapabilities/v1.js\n+ OpenLayers/Format/CSWGetRecords/v2_0_2.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WFSCapabilities.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/CSWGetRecords.js\n+ * @requires OpenLayers/Format/Filter/v1_0_0.js\n+ * @requires OpenLayers/Format/Filter/v1_1_0.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n */\n \n /**\n- * Class: OpenLayers.Format.WFSCapabilities.v1\n- * Abstract class not to be instantiated directly.\n- * \n+ * Class: OpenLayers.Format.CSWGetRecords.v2_0_2\n+ * A format for creating CSWGetRecords v2.0.2 transactions. \n+ * Create a new instance with the\n+ * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor.\n+ *\n * Inherits from:\n * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n+OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n \n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- wfs: \"http://www.opengis.net/wfs\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- ows: \"http://www.opengis.net/ows\"\n- },\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ csw: \"http://www.opengis.net/cat/csw/2.0.2\",\n+ dc: \"http://purl.org/dc/elements/1.1/\",\n+ dct: \"http://purl.org/dc/terms/\",\n+ gmd: \"http://www.isotc211.org/2005/gmd\",\n+ geonet: \"http://www.fao.org/geonetwork\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ ows: \"http://www.opengis.net/ows\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n \n+ /**\n+ * Property: defaultPrefix\n+ * {String} The default prefix (used by Format.XML).\n+ */\n+ defaultPrefix: \"csw\",\n \n- /**\n- * APIProperty: errorProperty\n- * {String} Which property of the returned object to check for in order to\n- * determine whether or not parsing has failed. In the case that the\n- * errorProperty is undefined on the returned object, the document will be\n- * run through an OGCExceptionReport parser.\n- */\n- errorProperty: \"featureTypeList\",\n+ /**\n+ * Property: version\n+ * {String} CSW version number.\n+ */\n+ version: \"2.0.2\",\n \n- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"wfs\",\n+ /**\n+ * Property: schemaLocation\n+ * {String} http://www.opengis.net/cat/csw/2.0.2\n+ * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n+ */\n+ schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n \n- /**\n- * Constructor: OpenLayers.Format.WFSCapabilities.v1_1\n- * Create an instance of one of the subclasses.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n+ /**\n+ * APIProperty: requestId\n+ * {String} Value of the requestId attribute of the GetRecords element.\n+ */\n+ requestId: null,\n \n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of layers. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named layers.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ /**\n+ * APIProperty: resultType\n+ * {String} Value of the resultType attribute of the GetRecords element,\n+ * specifies the result type in the GetRecords response, \"hits\" is\n+ * the default.\n+ */\n+ resultType: null,\n+\n+ /**\n+ * APIProperty: outputFormat\n+ * {String} Value of the outputFormat attribute of the GetRecords element,\n+ * specifies the format of the GetRecords response,\n+ * \"application/xml\" is the default.\n+ */\n+ outputFormat: null,\n+\n+ /**\n+ * APIProperty: outputSchema\n+ * {String} Value of the outputSchema attribute of the GetRecords element,\n+ * specifies the schema of the GetRecords response.\n+ */\n+ outputSchema: null,\n+\n+ /**\n+ * APIProperty: startPosition\n+ * {String} Value of the startPosition attribute of the GetRecords element,\n+ * specifies the start position (offset+1) for the GetRecords response,\n+ * 1 is the default.\n+ */\n+ startPosition: null,\n+\n+ /**\n+ * APIProperty: maxRecords\n+ * {String} Value of the maxRecords attribute of the GetRecords element,\n+ * specifies the maximum number of records in the GetRecords response,\n+ * 10 is the default.\n+ */\n+ maxRecords: null,\n+\n+ /**\n+ * APIProperty: DistributedSearch\n+ * {String} Value of the csw:DistributedSearch element, used when writing\n+ * a csw:GetRecords document.\n+ */\n+ DistributedSearch: null,\n+\n+ /**\n+ * APIProperty: ResponseHandler\n+ * {Array({String})} Values of the csw:ResponseHandler elements, used when\n+ * writting a csw:GetRecords document.\n+ */\n+ ResponseHandler: null,\n+\n+ /**\n+ * APIProperty: Query\n+ * {String} Value of the csw:Query element, used when writing a csw:GetRecords\n+ * document.\n+ */\n+ Query: null,\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2\n+ * A class for parsing and generating CSWGetRecords v2.0.2 transactions.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties (documented as class properties):\n+ * - requestId\n+ * - resultType\n+ * - outputFormat\n+ * - outputSchema\n+ * - startPosition\n+ * - maxRecords\n+ * - DistributedSearch\n+ * - ResponseHandler\n+ * - Query\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Parse the response from a GetRecords request.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var obj = {};\n+ this.readNode(data, obj);\n+ return obj;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"csw\": {\n+ \"GetRecordsResponse\": function(node, obj) {\n+ obj.records = [];\n+ this.readChildNodes(node, obj);\n+ var version = this.getAttributeNS(node, \"\", 'version');\n+ if (version != \"\") {\n+ obj.version = version;\n+ }\n+ },\n+ \"RequestId\": function(node, obj) {\n+ obj.RequestId = this.getChildValue(node);\n+ },\n+ \"SearchStatus\": function(node, obj) {\n+ obj.SearchStatus = {};\n+ var timestamp = this.getAttributeNS(node, \"\", 'timestamp');\n+ if (timestamp != \"\") {\n+ obj.SearchStatus.timestamp = timestamp;\n+ }\n+ },\n+ \"SearchResults\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ var attrs = node.attributes;\n+ var SearchResults = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ if ((attrs[i].name == \"numberOfRecordsMatched\") ||\n+ (attrs[i].name == \"numberOfRecordsReturned\") ||\n+ (attrs[i].name == \"nextRecord\")) {\n+ SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue);\n+ } else {\n+ SearchResults[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ }\n+ obj.SearchResults = SearchResults;\n+ },\n+ \"SummaryRecord\": function(node, obj) {\n+ var record = {\n+ type: \"SummaryRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record);\n+ },\n+ \"BriefRecord\": function(node, obj) {\n+ var record = {\n+ type: \"BriefRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record);\n+ },\n+ \"DCMIRecord\": function(node, obj) {\n+ var record = {\n+ type: \"DCMIRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record);\n+ },\n+ \"Record\": function(node, obj) {\n+ var record = {\n+ type: \"Record\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record);\n+ },\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ obj[name] = this.getChildValue(node);\n }\n- var raw = data;\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n+ },\n+ \"geonet\": {\n+ \"info\": function(node, obj) {\n+ var gninfo = {};\n+ this.readChildNodes(node, gninfo);\n+ obj.gninfo = gninfo;\n+ }\n+ },\n+ \"dc\": {\n+ // audience, contributor, coverage, creator, date, description, format,\n+ // identifier, language, provenance, publisher, relation, rights,\n+ // rightsHolder, source, subject, title, type, URI\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ if (!(OpenLayers.Util.isArray(obj[name]))) {\n+ obj[name] = [];\n+ }\n+ var dc_element = {};\n+ var attrs = node.attributes;\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ dc_element[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ dc_element.value = this.getChildValue(node);\n+ if (dc_element.value != \"\") {\n+ obj[name].push(dc_element);\n+ }\n+ }\n+ },\n+ \"dct\": {\n+ // abstract, modified, spatial\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ if (!(OpenLayers.Util.isArray(obj[name]))) {\n+ obj[name] = [];\n+ }\n+ obj[name].push(this.getChildValue(node));\n }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities;\n },\n+ \"ows\": OpenLayers.Util.applyDefaults({\n+ \"BoundingBox\": function(node, obj) {\n+ if (obj.bounds) {\n+ obj.BoundingBox = [{\n+ crs: obj.projection,\n+ value: [\n+ obj.bounds.left,\n+ obj.bounds.bottom,\n+ obj.bounds.right,\n+ obj.bounds.top\n+ ]\n+ }];\n+ delete obj.projection;\n+ delete obj.bounds;\n+ }\n+ OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"][\"BoundingBox\"].apply(\n+ this, arguments);\n+ }\n+ }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"])\n+ },\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": {\n- \"WFS_Capabilities\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"FeatureTypeList\": function(node, request) {\n- request.featureTypeList = {\n- featureTypes: []\n- };\n- this.readChildNodes(node, request.featureTypeList);\n- },\n- \"FeatureType\": function(node, featureTypeList) {\n- var featureType = {};\n- this.readChildNodes(node, featureType);\n- featureTypeList.featureTypes.push(featureType);\n- },\n- \"Name\": function(node, obj) {\n- var name = this.getChildValue(node);\n- if (name) {\n- var parts = name.split(\":\");\n- obj.name = parts.pop();\n- if (parts.length > 0) {\n- obj.featureNS = this.lookupNamespaceURI(node, parts[0]);\n- }\n+ /**\n+ * Method: write\n+ * Given an configuration js object, write a CSWGetRecords request. \n+ *\n+ * Parameters:\n+ * options - {Object} A object mapping the request.\n+ *\n+ * Returns:\n+ * {String} A serialized CSWGetRecords request.\n+ */\n+ write: function(options) {\n+ var node = this.writeNode(\"csw:GetRecords\", options);\n+ node.setAttribute(\"xmlns:gmd\", this.namespaces.gmd);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"csw\": {\n+ \"GetRecords\": function(options) {\n+ if (!options) {\n+ options = {};\n+ }\n+ var node = this.createElementNSPlus(\"csw:GetRecords\", {\n+ attributes: {\n+ service: \"CSW\",\n+ version: this.version,\n+ requestId: options.requestId || this.requestId,\n+ resultType: options.resultType || this.resultType,\n+ outputFormat: options.outputFormat || this.outputFormat,\n+ outputSchema: options.outputSchema || this.outputSchema,\n+ startPosition: options.startPosition || this.startPosition,\n+ maxRecords: options.maxRecords || this.maxRecords\n }\n- },\n- \"Title\": function(node, obj) {\n- var title = this.getChildValue(node);\n- if (title) {\n- obj.title = title;\n+ });\n+ if (options.DistributedSearch || this.DistributedSearch) {\n+ this.writeNode(\n+ \"csw:DistributedSearch\",\n+ options.DistributedSearch || this.DistributedSearch,\n+ node\n+ );\n+ }\n+ var ResponseHandler = options.ResponseHandler || this.ResponseHandler;\n+ if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {\n+ // ResponseHandler must be a non-empty array\n+ for (var i = 0, len = ResponseHandler.length; i < len; i++) {\n+ this.writeNode(\n+ \"csw:ResponseHandler\",\n+ ResponseHandler[i],\n+ node\n+ );\n }\n- },\n- \"Abstract\": function(node, obj) {\n- var abst = this.getChildValue(node);\n- if (abst) {\n- obj[\"abstract\"] = abst;\n+ }\n+ this.writeNode(\"Query\", options.Query || this.Query, node);\n+ return node;\n+ },\n+ \"DistributedSearch\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:DistributedSearch\", {\n+ attributes: {\n+ hopCount: options.hopCount\n+ }\n+ });\n+ return node;\n+ },\n+ \"ResponseHandler\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:ResponseHandler\", {\n+ value: options.value\n+ });\n+ return node;\n+ },\n+ \"Query\": function(options) {\n+ if (!options) {\n+ options = {};\n+ }\n+ var node = this.createElementNSPlus(\"csw:Query\", {\n+ attributes: {\n+ typeNames: options.typeNames || \"csw:Record\"\n+ }\n+ });\n+ var ElementName = options.ElementName;\n+ if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {\n+ // ElementName must be a non-empty array\n+ for (var i = 0, len = ElementName.length; i < len; i++) {\n+ this.writeNode(\n+ \"csw:ElementName\",\n+ ElementName[i],\n+ node\n+ );\n+ }\n+ } else {\n+ this.writeNode(\n+ \"csw:ElementSetName\",\n+ options.ElementSetName || {\n+ value: 'summary'\n+ },\n+ node\n+ );\n+ }\n+ if (options.Constraint) {\n+ this.writeNode(\n+ \"csw:Constraint\",\n+ options.Constraint,\n+ node\n+ );\n+ }\n+ if (options.SortBy) {\n+ this.writeNode(\n+ \"ogc:SortBy\",\n+ options.SortBy,\n+ node\n+ );\n+ }\n+ return node;\n+ },\n+ \"ElementName\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:ElementName\", {\n+ value: options.value\n+ });\n+ return node;\n+ },\n+ \"ElementSetName\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:ElementSetName\", {\n+ attributes: {\n+ typeNames: options.typeNames\n+ },\n+ value: options.value\n+ });\n+ return node;\n+ },\n+ \"Constraint\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:Constraint\", {\n+ attributes: {\n+ version: options.version\n }\n+ });\n+ if (options.Filter) {\n+ var format = new OpenLayers.Format.Filter({\n+ version: options.version\n+ });\n+ node.appendChild(format.write(options.Filter));\n+ } else if (options.CqlText) {\n+ var child = this.createElementNSPlus(\"CqlText\", {\n+ value: options.CqlText.value\n+ });\n+ node.appendChild(child);\n }\n+ return node;\n }\n },\n+ \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1\"\n-\n- });\n+ CLASS_NAME: \"OpenLayers.Format.CSWGetRecords.v2_0_2\"\n+});\n /* ======================================================================\n- OpenLayers/Format/WFSCapabilities/v1_0_0.js\n+ OpenLayers/Format/SLD/v1_0_0_GeoServer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WFSCapabilities/v1.js\n+ * @requires OpenLayers/Format/SLD/v1_0_0.js\n */\n \n /**\n- * Class: OpenLayers.Format.WFSCapabilities/v1_0_0\n- * Read WFS Capabilities version 1.0.0.\n- * \n+ * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer\n+ * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options.\n+ * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd\n+ * for more information.\n+ *\n * Inherits from:\n- * - <OpenLayers.Format.WFSCapabilities.v1>\n+ * - <OpenLayers.Format.SLD.v1_0_0>\n */\n-OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.WFSCapabilities.v1, {\n+OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(\n+ OpenLayers.Format.SLD.v1_0_0, {\n \n /**\n- * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0\n- * Create a new parser for WFS capabilities version 1.0.0.\n+ * Property: version\n+ * {String} The specific parser version.\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * Property: profile\n+ * {String} The specific profile\n+ */\n+ profile: \"GeoServer\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer\n+ * Create a new parser for GeoServer-enhanced SLD version 1.0.0.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n- readers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"Service\": function(node, capabilities) {\n- capabilities.service = {};\n- this.readChildNodes(node, capabilities.service);\n- },\n- \"Fees\": function(node, service) {\n- var fees = this.getChildValue(node);\n- if (fees && fees.toLowerCase() != \"none\") {\n- service.fees = fees;\n- }\n- },\n- \"AccessConstraints\": function(node, service) {\n- var constraints = this.getChildValue(node);\n- if (constraints && constraints.toLowerCase() != \"none\") {\n- service.accessConstraints = constraints;\n+ readers: OpenLayers.Util.applyDefaults({\n+ \"sld\": OpenLayers.Util.applyDefaults({\n+ \"Priority\": function(node, obj) {\n+ var value = this.readers.ogc._expression.call(this, node);\n+ if (value) {\n+ obj.priority = value;\n }\n },\n- \"OnlineResource\": function(node, service) {\n- var onlineResource = this.getChildValue(node);\n- if (onlineResource && onlineResource.toLowerCase() != \"none\") {\n- service.onlineResource = onlineResource;\n+ \"VendorOption\": function(node, obj) {\n+ if (!obj.vendorOptions) {\n+ obj.vendorOptions = {};\n }\n+ obj.vendorOptions[node.getAttribute(\"name\")] = this.getChildValue(node);\n },\n- \"Keywords\": function(node, service) {\n- var keywords = this.getChildValue(node);\n- if (keywords && keywords.toLowerCase() != \"none\") {\n- service.keywords = keywords.split(', ');\n+ \"TextSymbolizer\": function(node, rule) {\n+ OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);\n+ var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer[\"Text\"];\n+ if (symbolizer.graphic === undefined) {\n+ symbolizer.graphic = false;\n }\n+ }\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.readers[\"sld\"])\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.readers),\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: OpenLayers.Util.applyDefaults({\n+ \"sld\": OpenLayers.Util.applyDefaults({\n+ \"Priority\": function(priority) {\n+ return this.writers.sld._OGCExpression.call(\n+ this, \"sld:Priority\", priority\n+ );\n },\n- \"Capability\": function(node, capabilities) {\n- capabilities.capability = {};\n- this.readChildNodes(node, capabilities.capability);\n- },\n- \"Request\": function(node, obj) {\n- obj.request = {};\n- this.readChildNodes(node, obj.request);\n- },\n- \"GetFeature\": function(node, request) {\n- request.getfeature = {\n- href: {}, // DCPType\n- formats: [] // ResultFormat\n- };\n- this.readChildNodes(node, request.getfeature);\n+ \"VendorOption\": function(option) {\n+ return this.createElementNSPlus(\"sld:VendorOption\", {\n+ attributes: {\n+ name: option.name\n+ },\n+ value: option.value\n+ });\n },\n- \"ResultFormat\": function(node, obj) {\n- var children = node.childNodes;\n- var childNode;\n- for (var i = 0; i < children.length; i++) {\n- childNode = children[i];\n- if (childNode.nodeType == 1) {\n- obj.formats.push(childNode.nodeName);\n- }\n+ \"TextSymbolizer\": function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"TextSymbolizer\"].apply(this, arguments);\n+ if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {\n+ this.writeNode(\"Graphic\", symbolizer, node);\n }\n+ if (\"priority\" in symbolizer) {\n+ this.writeNode(\"Priority\", symbolizer.priority, node);\n+ }\n+ return this.addVendorOptions(node, symbolizer);\n },\n- \"DCPType\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"HTTP\": function(node, obj) {\n- this.readChildNodes(node, obj.href);\n- },\n- \"Get\": function(node, obj) {\n- obj.get = node.getAttribute(\"onlineResource\");\n+ \"PointSymbolizer\": function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"PointSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer);\n },\n- \"Post\": function(node, obj) {\n- obj.post = node.getAttribute(\"onlineResource\");\n+ \"LineSymbolizer\": function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"LineSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer);\n },\n- \"SRS\": function(node, obj) {\n- var srs = this.getChildValue(node);\n- if (srs) {\n- obj.srs = srs;\n- }\n+ \"PolygonSymbolizer\": function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"PolygonSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer);\n }\n- }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"])\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.writers[\"sld\"])\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.writers),\n+\n+ /**\n+ * Method: addVendorOptions\n+ * Add in the VendorOption tags and return the node again.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} A DOM node.\n+ * symbolizer - {Object}\n+ *\n+ * Returns:\n+ * {DOMElement} A DOM node.\n+ */\n+ addVendorOptions: function(node, symbolizer) {\n+ var options = symbolizer.vendorOptions;\n+ if (options) {\n+ for (var key in symbolizer.vendorOptions) {\n+ this.writeNode(\"VendorOption\", {\n+ name: key,\n+ value: symbolizer.vendorOptions[key]\n+ }, node);\n+ }\n+ }\n+ return node;\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.SLD.v1_0_0_GeoServer\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WFSCapabilities/v1_1_0.js\n+ OpenLayers/Handler/Click.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WFSCapabilities/v1.js\n- * @requires OpenLayers/Format/OWSCommon/v1.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Format.WFSCapabilities/v1_1_0\n- * Read WFS Capabilities version 1.1.0.\n+ * Class: OpenLayers.Handler.Click\n+ * A handler for mouse clicks. The intention of this handler is to give\n+ * controls more flexibility with handling clicks. Browsers trigger\n+ * click events twice for a double-click. In addition, the mousedown,\n+ * mousemove, mouseup sequence fires a click event. With this handler,\n+ * controls can decide whether to ignore clicks associated with a double\n+ * click. By setting a <pixelTolerance>, controls can also ignore clicks\n+ * that include a drag. Create a new instance with the\n+ * <OpenLayers.Handler.Click> constructor.\n * \n * Inherits from:\n- * - <OpenLayers.Format.WFSCapabilities>\n+ * - <OpenLayers.Handler> \n */\n-OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(\n- OpenLayers.Format.WFSCapabilities.v1, {\n+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n+ /**\n+ * APIProperty: delay\n+ * {Number} Number of milliseconds between clicks before the event is\n+ * considered a double-click.\n+ */\n+ delay: 300,\n \n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+ /**\n+ * APIProperty: single\n+ * {Boolean} Handle single clicks. Default is true. If false, clicks\n+ * will not be reported. If true, single-clicks will be reported.\n+ */\n+ single: true,\n \n- /**\n- * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0\n- * Create a new parser for WFS capabilities version 1.1.0.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n+ /**\n+ * APIProperty: double\n+ * {Boolean} Handle double-clicks. Default is false.\n+ */\n+ 'double': false,\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"DefaultSRS\": function(node, obj) {\n- var defaultSRS = this.getChildValue(node);\n- if (defaultSRS) {\n- obj.srs = defaultSRS;\n+ /**\n+ * APIProperty: pixelTolerance\n+ * {Number} Maximum number of pixels between mouseup and mousedown for an\n+ * event to be considered a click. Default is 0. If set to an\n+ * integer value, clicks with a drag greater than the value will be\n+ * ignored. This property can only be set when the handler is\n+ * constructed.\n+ */\n+ pixelTolerance: 0,\n+\n+ /**\n+ * APIProperty: dblclickTolerance\n+ * {Number} Maximum distance in pixels between clicks for a sequence of \n+ * events to be considered a double click. Default is 13. If the\n+ * distance between two clicks is greater than this value, a double-\n+ * click will not be fired.\n+ */\n+ dblclickTolerance: 13,\n+\n+ /**\n+ * APIProperty: stopSingle\n+ * {Boolean} Stop other listeners from being notified of clicks. Default\n+ * is false. If true, any listeners registered before this one for \n+ * click or rightclick events will not be notified.\n+ */\n+ stopSingle: false,\n+\n+ /**\n+ * APIProperty: stopDouble\n+ * {Boolean} Stop other listeners from being notified of double-clicks.\n+ * Default is false. If true, any click listeners registered before\n+ * this one will not be notified of *any* double-click events.\n+ * \n+ * The one caveat with stopDouble is that given a map with two click\n+ * handlers, one with stopDouble true and the other with stopSingle\n+ * true, the stopSingle handler should be activated last to get\n+ * uniform cross-browser performance. Since IE triggers one click\n+ * with a dblclick and FF triggers two, if a stopSingle handler is\n+ * activated first, all it gets in IE is a single click when the\n+ * second handler stops propagation on the dblclick.\n+ */\n+ stopDouble: false,\n+\n+ /**\n+ * Property: timerId\n+ * {Number} The id of the timeout waiting to clear the <delayedCall>.\n+ */\n+ timerId: null,\n+\n+ /**\n+ * Property: down\n+ * {Object} Object that store relevant information about the last\n+ * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives\n+ * the average location of the mouse/touch event. Its 'touches'\n+ * property records clientX/clientY of each touches.\n+ */\n+ down: null,\n+\n+ /**\n+ * Property: last\n+ * {Object} Object that store relevant information about the last\n+ * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives\n+ * the average location of the mouse/touch event. Its 'touches'\n+ * property records clientX/clientY of each touches.\n+ */\n+ last: null,\n+\n+ /** \n+ * Property: first\n+ * {Object} When waiting for double clicks, this object will store \n+ * information about the first click in a two click sequence.\n+ */\n+ first: null,\n+\n+ /**\n+ * Property: rightclickTimerId\n+ * {Number} The id of the right mouse timeout waiting to clear the \n+ * <delayedEvent>.\n+ */\n+ rightclickTimerId: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Click\n+ * Create a new click handler.\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handler's setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object with keys corresponding to callbacks\n+ * that will be called by the handler. The callbacks should\n+ * expect to recieve a single argument, the click event.\n+ * Callbacks for 'click' and 'dblclick' are supported.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * handler.\n+ */\n+\n+ /**\n+ * Method: touchstart\n+ * Handle touchstart.\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: touchmove\n+ * Store position of last move, because touchend event can have\n+ * an empty \"touches\" property.\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ touchmove: function(evt) {\n+ this.last = this.getEventInfo(evt);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: touchend\n+ * Correctly set event xy property, and add lastTouches to have\n+ * touches property from last touchstart or touchmove\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ touchend: function(evt) {\n+ // touchstart may not have been allowed to propagate\n+ if (this.down) {\n+ evt.xy = this.last.xy;\n+ evt.lastTouches = this.last.touches;\n+ this.handleSingle(evt);\n+ this.down = null;\n+ }\n+ return true;\n+ },\n+\n+ /**\n+ * Method: mousedown\n+ * Handle mousedown.\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ mousedown: function(evt) {\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: mouseup\n+ * Handle mouseup. Installed to support collection of right mouse events.\n+ * \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ mouseup: function(evt) {\n+ var propagate = true;\n+\n+ // Collect right mouse clicks from the mouseup\n+ // IE - ignores the second right click in mousedown so using\n+ // mouseup instead\n+ if (this.checkModifiers(evt) && this.control.handleRightClicks &&\n+ OpenLayers.Event.isRightClick(evt)) {\n+ propagate = this.rightclick(evt);\n+ }\n+\n+ return propagate;\n+ },\n+\n+ /**\n+ * Method: rightclick\n+ * Handle rightclick. For a dblrightclick, we get two clicks so we need \n+ * to always register for dblrightclick to properly handle single \n+ * clicks.\n+ * \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ rightclick: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.rightclickTimerId != null) {\n+ //Second click received before timeout this must be \n+ // a double click\n+ this.clearTimer();\n+ this.callback('dblrightclick', [evt]);\n+ return !this.stopDouble;\n+ } else {\n+ //Set the rightclickTimerId, send evt only if double is \n+ // true else trigger single\n+ var clickEvent = this['double'] ?\n+ OpenLayers.Util.extend({}, evt) :\n+ this.callback('rightclick', [evt]);\n+\n+ var delayedRightCall = OpenLayers.Function.bind(\n+ this.delayedRightCall,\n+ this,\n+ clickEvent\n+ );\n+ this.rightclickTimerId = window.setTimeout(\n+ delayedRightCall, this.delay\n+ );\n+ }\n+ }\n+ return !this.stopSingle;\n+ },\n+\n+ /**\n+ * Method: delayedRightCall\n+ * Sets <rightclickTimerId> to null. And optionally triggers the \n+ * rightclick callback if evt is set.\n+ */\n+ delayedRightCall: function(evt) {\n+ this.rightclickTimerId = null;\n+ if (evt) {\n+ this.callback('rightclick', [evt]);\n+ }\n+ },\n+\n+ /**\n+ * Method: click\n+ * Handle click events from the browser. This is registered as a listener\n+ * for click events and should not be called from other events in this\n+ * handler.\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ click: function(evt) {\n+ if (!this.last) {\n+ this.last = this.getEventInfo(evt);\n+ }\n+ this.handleSingle(evt);\n+ return !this.stopSingle;\n+ },\n+\n+ /**\n+ * Method: dblclick\n+ * Handle dblclick. For a dblclick, we get two clicks in some browsers\n+ * (FF) and one in others (IE). So we need to always register for\n+ * dblclick to properly handle single clicks. This method is registered\n+ * as a listener for the dblclick browser event. It should *not* be\n+ * called by other methods in this handler.\n+ * \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ dblclick: function(evt) {\n+ this.handleDouble(evt);\n+ return !this.stopDouble;\n+ },\n+\n+ /** \n+ * Method: handleDouble\n+ * Handle double-click sequence.\n+ */\n+ handleDouble: function(evt) {\n+ if (this.passesDblclickTolerance(evt)) {\n+ if (this[\"double\"]) {\n+ this.callback(\"dblclick\", [evt]);\n+ }\n+ // to prevent a dblclick from firing the click callback in IE\n+ this.clearTimer();\n+ }\n+ },\n+\n+ /** \n+ * Method: handleSingle\n+ * Handle single click sequence.\n+ */\n+ handleSingle: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.timerId != null) {\n+ // already received a click\n+ if (this.last.touches && this.last.touches.length === 1) {\n+ // touch device, no dblclick event - this may be a double\n+ if (this[\"double\"]) {\n+ // on Android don't let the browser zoom on the page\n+ OpenLayers.Event.preventDefault(evt);\n }\n+ this.handleDouble(evt);\n }\n- }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"]),\n- \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows\n- },\n+ // if we're not in a touch environment we clear the click timer\n+ // if we've got a second touch, we'll get two touchend events\n+ if (!this.last.touches || this.last.touches.length !== 2) {\n+ this.clearTimer();\n+ }\n+ } else {\n+ // remember the first click info so we can compare to the second\n+ this.first = this.getEventInfo(evt);\n+ // set the timer, send evt only if single is true\n+ //use a clone of the event object because it will no longer \n+ //be a valid event object in IE in the timer callback\n+ var clickEvent = this.single ?\n+ OpenLayers.Util.extend({}, evt) : null;\n+ this.queuePotentialClick(clickEvent);\n+ }\n+ }\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_1_0\"\n+ /** \n+ * Method: queuePotentialClick\n+ * This method is separated out largely to make testing easier (so we\n+ * don't have to override window.setTimeout)\n+ */\n+ queuePotentialClick: function(evt) {\n+ this.timerId = window.setTimeout(\n+ OpenLayers.Function.bind(this.delayedCall, this, evt),\n+ this.delay\n+ );\n+ },\n \n- });\n+ /**\n+ * Method: passesTolerance\n+ * Determine whether the event is within the optional pixel tolerance. Note\n+ * that the pixel tolerance check only works if mousedown events get to\n+ * the listeners registered here. If they are stopped by other elements,\n+ * the <pixelTolerance> will have no effect here (this method will always\n+ * return true).\n+ *\n+ * Returns:\n+ * {Boolean} The click is within the pixel tolerance (if specified).\n+ */\n+ passesTolerance: function(evt) {\n+ var passes = true;\n+ if (this.pixelTolerance != null && this.down && this.down.xy) {\n+ passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n+ // for touch environments, we also enforce that all touches\n+ // start and end within the given tolerance to be considered a click\n+ if (passes && this.touch &&\n+ this.down.touches.length === this.last.touches.length) {\n+ // the touchend event doesn't come with touches, so we check\n+ // down and last\n+ for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n+ if (this.getTouchDistance(\n+ this.down.touches[i],\n+ this.last.touches[i]\n+ ) > this.pixelTolerance) {\n+ passes = false;\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ return passes;\n+ },\n+\n+ /** \n+ * Method: getTouchDistance\n+ *\n+ * Returns:\n+ * {Boolean} The pixel displacement between two touches.\n+ */\n+ getTouchDistance: function(from, to) {\n+ return Math.sqrt(\n+ Math.pow(from.clientX - to.clientX, 2) +\n+ Math.pow(from.clientY - to.clientY, 2)\n+ );\n+ },\n+\n+ /**\n+ * Method: passesDblclickTolerance\n+ * Determine whether the event is within the optional double-cick pixel \n+ * tolerance.\n+ *\n+ * Returns:\n+ * {Boolean} The click is within the double-click pixel tolerance.\n+ */\n+ passesDblclickTolerance: function(evt) {\n+ var passes = true;\n+ if (this.down && this.first) {\n+ passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;\n+ }\n+ return passes;\n+ },\n+\n+ /**\n+ * Method: clearTimer\n+ * Clear the timer and set <timerId> to null.\n+ */\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n+ }\n+ if (this.rightclickTimerId != null) {\n+ window.clearTimeout(this.rightclickTimerId);\n+ this.rightclickTimerId = null;\n+ }\n+ },\n+\n+ /**\n+ * Method: delayedCall\n+ * Sets <timerId> to null. And optionally triggers the click callback if\n+ * evt is set.\n+ */\n+ delayedCall: function(evt) {\n+ this.timerId = null;\n+ if (evt) {\n+ this.callback(\"click\", [evt]);\n+ }\n+ },\n+\n+ /**\n+ * Method: getEventInfo\n+ * This method allows us to store event information without storing the\n+ * actual event. In touch devices (at least), the same event is \n+ * modified between touchstart, touchmove, and touchend.\n+ *\n+ * Returns:\n+ * {Object} An object with event related info.\n+ */\n+ getEventInfo: function(evt) {\n+ var touches;\n+ if (evt.touches) {\n+ var len = evt.touches.length;\n+ touches = new Array(len);\n+ var touch;\n+ for (var i = 0; i < len; i++) {\n+ touch = evt.touches[i];\n+ touches[i] = {\n+ clientX: touch.olClientX,\n+ clientY: touch.olClientY\n+ };\n+ }\n+ }\n+ return {\n+ xy: evt.xy,\n+ touches: touches\n+ };\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * Deactivate the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully deactivated.\n+ */\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ this.down = null;\n+ this.first = null;\n+ this.last = null;\n+ deactivated = true;\n+ }\n+ return deactivated;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Click\"\n+});\n /* ======================================================================\n- OpenLayers/Events/featureclick.js\n+ OpenLayers/Handler/MouseWheel.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Events.featureclick\n- *\n- * Extension event type for handling feature click events, including overlapping\n- * features. \n+ * Class: OpenLayers.Handler.MouseWheel\n+ * Handler for wheel up/down events.\n * \n- * Event types provided by this extension:\n- * - featureclick \n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n */\n-OpenLayers.Events.featureclick = OpenLayers.Class({\n+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n+ /** \n+ * Property: wheelListener \n+ * {function} \n+ */\n+ wheelListener: null,\n \n /**\n- * Property: cache\n- * {Object} A cache of features under the mouse.\n+ * Property: interval\n+ * {Integer} In order to increase server performance, an interval (in \n+ * milliseconds) can be set to reduce the number of up/down events \n+ * called. If set, a new up/down event will not be set until the \n+ * interval has passed. \n+ * Defaults to 0, meaning no interval. \n */\n- cache: null,\n+ interval: 0,\n \n /**\n- * Property: map\n- * {<OpenLayers.Map>} The map to register browser events on.\n+ * Property: maxDelta\n+ * {Integer} Maximum delta to collect before breaking from the current\n+ * interval. In cumulative mode, this also limits the maximum delta\n+ * returned from the handler. Default is Number.POSITIVE_INFINITY.\n */\n- map: null,\n+ maxDelta: Number.POSITIVE_INFINITY,\n \n /**\n- * Property: provides\n- * {Array(String)} The event types provided by this extension.\n+ * Property: delta\n+ * {Integer} When interval is set, delta collects the mousewheel z-deltas\n+ * of the events that occur within the interval.\n+ * See also the cumulative option\n */\n- provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n+ delta: 0,\n \n /**\n- * Constructor: OpenLayers.Events.featureclick\n- * Create a new featureclick event type.\n+ * Property: cumulative\n+ * {Boolean} When interval is set: true to collect all the mousewheel \n+ * z-deltas, false to only record the delta direction (positive or\n+ * negative)\n+ */\n+ cumulative: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.MouseWheel\n *\n * Parameters:\n- * target - {<OpenLayers.Events>} The events instance to create the events\n- * for.\n+ * control - {<OpenLayers.Control>} \n+ * callbacks - {Object} An object containing a single function to be\n+ * called when the drag operation is finished.\n+ * The callback should expect to recieve a single\n+ * argument, the point geometry.\n+ * options - {Object} \n */\n- initialize: function(target) {\n- this.target = target;\n- if (target.object instanceof OpenLayers.Map) {\n- this.setMap(target.object);\n- } else if (target.object instanceof OpenLayers.Layer.Vector) {\n- if (target.object.map) {\n- this.setMap(target.object.map);\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.wheelListener = OpenLayers.Function.bindAsEventListener(\n+ this.onWheelEvent, this\n+ );\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ this.wheelListener = null;\n+ },\n+\n+ /**\n+ * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/\n+ */\n+\n+ /** \n+ * Method: onWheelEvent\n+ * Catch the wheel event and handle it xbrowserly\n+ * \n+ * Parameters:\n+ * e - {Event} \n+ */\n+ onWheelEvent: function(e) {\n+\n+ // make sure we have a map and check keyboard modifiers\n+ if (!this.map || !this.checkModifiers(e)) {\n+ return;\n+ }\n+\n+ // Ride up the element's DOM hierarchy to determine if it or any of \n+ // its ancestors was: \n+ // * specifically marked as scrollable (CSS overflow property)\n+ // * one of our layer divs or a div marked as scrollable\n+ // ('olScrollable' CSS class)\n+ // * the map div\n+ //\n+ var overScrollableDiv = false;\n+ var allowScroll = false;\n+ var overMapDiv = false;\n+\n+ var elem = OpenLayers.Event.element(e);\n+ while ((elem != null) && !overMapDiv && !overScrollableDiv) {\n+\n+ if (!overScrollableDiv) {\n+ try {\n+ var overflow;\n+ if (elem.currentStyle) {\n+ overflow = elem.currentStyle[\"overflow\"];\n+ } else {\n+ var style =\n+ document.defaultView.getComputedStyle(elem, null);\n+ overflow = style.getPropertyValue(\"overflow\");\n+ }\n+ overScrollableDiv = (overflow &&\n+ (overflow == \"auto\") || (overflow == \"scroll\"));\n+ } catch (err) {\n+ //sometimes when scrolling in a popup, this causes \n+ // obscure browser error\n+ }\n+ }\n+\n+ if (!allowScroll) {\n+ allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');\n+ if (!allowScroll) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ // Are we in the layer div? Note that we have two cases\n+ // here: one is to catch EventPane layers, which have a\n+ // pane above the layer (layer.pane)\n+ var layer = this.map.layers[i];\n+ if (elem == layer.div || elem == layer.pane) {\n+ allowScroll = true;\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ overMapDiv = (elem == this.map.div);\n+\n+ elem = elem.parentNode;\n+ }\n+\n+ // Logic below is the following:\n+ //\n+ // If we are over a scrollable div or not over the map div:\n+ // * do nothing (let the browser handle scrolling)\n+ //\n+ // otherwise \n+ // \n+ // If we are over the layer div or a 'olScrollable' div:\n+ // * zoom/in out\n+ // then\n+ // * kill event (so as not to also scroll the page after zooming)\n+ //\n+ // otherwise\n+ //\n+ // Kill the event (dont scroll the page if we wheel over the \n+ // layerswitcher or the pan/zoom control)\n+ //\n+ if (!overScrollableDiv && overMapDiv) {\n+ if (allowScroll) {\n+ var delta = 0;\n+\n+ if (e.wheelDelta) {\n+ delta = e.wheelDelta;\n+ if (delta % 160 === 0) {\n+ // opera have steps of 160 instead of 120\n+ delta = delta * 0.75;\n+ }\n+ delta = delta / 120;\n+ } else if (e.detail) {\n+ // detail in Firefox on OS X is 1/3 of Windows\n+ // so force delta 1 / -1\n+ delta = -(e.detail / Math.abs(e.detail));\n+ }\n+ this.delta += delta;\n+\n+ window.clearTimeout(this._timeoutId);\n+ if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n+ // store e because window.event might change during delay\n+ var evt = OpenLayers.Util.extend({}, e);\n+ this._timeoutId = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.wheelZoom(evt);\n+ }, this),\n+ this.interval\n+ );\n+ } else {\n+ this.wheelZoom(e);\n+ }\n+ }\n+ OpenLayers.Event.stop(e);\n+ }\n+ },\n+\n+ /**\n+ * Method: wheelZoom\n+ * Given the wheel event, we carry out the appropriate zooming in or out,\n+ * based on the 'wheelDelta' or 'detail' property of the event.\n+ * \n+ * Parameters:\n+ * e - {Event}\n+ */\n+ wheelZoom: function(e) {\n+ var delta = this.delta;\n+ this.delta = 0;\n+\n+ if (delta) {\n+ e.xy = this.map.events.getMousePosition(e);\n+ if (delta < 0) {\n+ this.callback(\"down\",\n+ [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);\n } else {\n- target.object.events.register(\"added\", this, function(evt) {\n- this.setMap(target.object.map);\n- });\n+ this.callback(\"up\",\n+ [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);\n }\n+ }\n+ },\n+\n+ /**\n+ * Method: activate \n+ */\n+ activate: function(evt) {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ //register mousewheel events specifically on the window and document\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n+ return true;\n } else {\n- throw (\"Listeners for '\" + this.provides.join(\"', '\") +\n- \"' events can only be registered for OpenLayers.Layer.Vector \" +\n- \"or OpenLayers.Map instances\");\n+ return false;\n }\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- target.extensions[this.provides[i]] = true;\n+ },\n+\n+ /**\n+ * Method: deactivate \n+ */\n+ deactivate: function(evt) {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ // unregister mousewheel events specifically on the window and document\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Point.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.Point\n+ * Handler to draw a point on the map. Point is displayed on activation,\n+ * moves on mouse move, and is finished on mouse up. The handler triggers\n+ * callbacks for 'done', 'cancel', and 'modify'. The modify callback is\n+ * called with each change in the sketch and will receive the latest point\n+ * drawn. Create a new instance with the <OpenLayers.Handler.Point>\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n+ */\n+OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {\n+\n /**\n- * Method: setMap\n+ * Property: point\n+ * {<OpenLayers.Feature.Vector>} The currently drawn point\n+ */\n+ point: null,\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The temporary drawing layer\n+ */\n+ layer: null,\n+\n+ /**\n+ * APIProperty: multi\n+ * {Boolean} Cast features to multi-part geometries before passing to the\n+ * layer. Default is false.\n+ */\n+ multi: false,\n+\n+ /**\n+ * APIProperty: citeCompliant\n+ * {Boolean} If set to true, coordinates of features drawn in a map extent\n+ * crossing the date line won't exceed the world bounds. Default is false.\n+ */\n+ citeCompliant: false,\n+\n+ /**\n+ * Property: mouseDown\n+ * {Boolean} The mouse is down\n+ */\n+ mouseDown: false,\n+\n+ /**\n+ * Property: stoppedDown\n+ * {Boolean} Indicate whether the last mousedown stopped the event\n+ * propagation.\n+ */\n+ stoppedDown: null,\n+\n+ /**\n+ * Property: lastDown\n+ * {<OpenLayers.Pixel>} Location of the last mouse down\n+ */\n+ lastDown: null,\n+\n+ /**\n+ * Property: lastUp\n+ * {<OpenLayers.Pixel>}\n+ */\n+ lastUp: null,\n+\n+ /**\n+ * APIProperty: persist\n+ * {Boolean} Leave the feature rendered until destroyFeature is called.\n+ * Default is false. If set to true, the feature remains rendered until\n+ * destroyFeature is called, typically by deactivating the handler or\n+ * starting another drawing.\n+ */\n+ persist: false,\n+\n+ /**\n+ * APIProperty: stopDown\n+ * {Boolean} Stop event propagation on mousedown. Must be false to\n+ * allow \"pan while drawing\". Defaults to false.\n+ */\n+ stopDown: false,\n+\n+ /**\n+ * APIPropery: stopUp\n+ * {Boolean} Stop event propagation on mouse. Must be false to\n+ * allow \"pan while dragging\". Defaults to fase.\n+ */\n+ stopUp: false,\n+\n+ /**\n+ * Property: layerOptions\n+ * {Object} Any optional properties to be set on the sketch layer.\n+ */\n+ layerOptions: null,\n+\n+ /**\n+ * APIProperty: pixelTolerance\n+ * {Number} Maximum number of pixels between down and up (mousedown\n+ * and mouseup, or touchstart and touchend) for the handler to\n+ * add a new point. If set to an integer value, if the\n+ * displacement between down and up is great to this value\n+ * no point will be added. Default value is 5.\n+ */\n+ pixelTolerance: 5,\n+\n+ /**\n+ * Property: lastTouchPx\n+ * {<OpenLayers.Pixel>} The last pixel used to know the distance between\n+ * two touches (for double touch).\n+ */\n+ lastTouchPx: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Point\n+ * Create a new point handler.\n *\n * Parameters:\n- * map - {<OpenLayers.Map>} The map to register browser events on.\n+ * control - {<OpenLayers.Control>} The control that owns this handler\n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} An optional object with properties to be set on the\n+ * handler\n+ *\n+ * Named callbacks:\n+ * create - Called when a sketch is first created. Callback called with\n+ * the creation point geometry and sketch feature.\n+ * modify - Called with each move of a vertex with the vertex (point)\n+ * geometry and the sketch feature.\n+ * done - Called when the point drawing is finished. The callback will\n+ * recieve a single argument, the point geometry.\n+ * cancel - Called when the handler is deactivated while drawing. The\n+ * cancel callback will receive a geometry.\n */\n- setMap: function(map) {\n- this.map = map;\n- this.cache = {};\n- map.events.register(\"mousedown\", this, this.start, {\n- extension: true\n- });\n- map.events.register(\"mouseup\", this, this.onClick, {\n- extension: true\n- });\n- map.events.register(\"touchstart\", this, this.start, {\n- extension: true\n- });\n- map.events.register(\"touchmove\", this, this.cancel, {\n- extension: true\n- });\n- map.events.register(\"touchend\", this, this.onClick, {\n- extension: true\n+ initialize: function(control, callbacks, options) {\n+ if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n+ this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});\n+ }\n+\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ * turn on the handler\n+ */\n+ activate: function() {\n+ if (!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ return false;\n+ }\n+ // create temporary vector layer for rendering geometry sketch\n+ // TBD: this could be moved to initialize/destroy - setting visibility here\n+ var options = OpenLayers.Util.extend({\n+ displayInLayerSwitcher: false,\n+ // indicate that the temp vector layer will never be out of range\n+ // without this, resolution properties must be specified at the\n+ // map-level for this temporary layer to init its resolutions\n+ // correctly\n+ calculateInRange: OpenLayers.Function.True,\n+ wrapDateLine: this.citeCompliant\n+ }, this.layerOptions);\n+ this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n+ this.map.addLayer(this.layer);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: createFeature\n+ * Add temporary features\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} A pixel location on the map.\n+ */\n+ createFeature: function(pixel) {\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ var geometry = new OpenLayers.Geometry.Point(\n+ lonlat.lon, lonlat.lat\n+ );\n+ this.point = new OpenLayers.Feature.Vector(geometry);\n+ this.callback(\"create\", [this.point.geometry, this.point]);\n+ this.point.geometry.clearBounds();\n+ this.layer.addFeatures([this.point], {\n+ silent: true\n });\n- map.events.register(\"mousemove\", this, this.onMousemove, {\n- extension: true\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * turn off the handler\n+ */\n+ deactivate: function() {\n+ if (!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ return false;\n+ }\n+ this.cancel();\n+ // If a layer's map property is set to null, it means that that layer\n+ // isn't added to the map. Since we ourself added the layer to the map\n+ // in activate(), we can assume that if this.layer.map is null it means\n+ // that the layer has been destroyed (as a result of map.destroy() for\n+ // example.\n+ if (this.layer.map != null) {\n+ this.destroyFeature(true);\n+ this.layer.destroy(false);\n+ }\n+ this.layer = null;\n+ return true;\n+ },\n+\n+ /**\n+ * Method: destroyFeature\n+ * Destroy the temporary geometries\n+ *\n+ * Parameters:\n+ * force - {Boolean} Destroy even if persist is true.\n+ */\n+ destroyFeature: function(force) {\n+ if (this.layer && (force || !this.persist)) {\n+ this.layer.destroyFeatures();\n+ }\n+ this.point = null;\n+ },\n+\n+ /**\n+ * Method: destroyPersistedFeature\n+ * Destroy the persisted feature.\n+ */\n+ destroyPersistedFeature: function() {\n+ var layer = this.layer;\n+ if (layer && layer.features.length > 1) {\n+ this.layer.features[0].destroy();\n+ }\n+ },\n+\n+ /**\n+ * Method: finalize\n+ * Finish the geometry and call the \"done\" callback.\n+ *\n+ * Parameters:\n+ * cancel - {Boolean} Call cancel instead of done callback. Default\n+ * is false.\n+ */\n+ finalize: function(cancel) {\n+ var key = cancel ? \"cancel\" : \"done\";\n+ this.mouseDown = false;\n+ this.lastDown = null;\n+ this.lastUp = null;\n+ this.lastTouchPx = null;\n+ this.callback(key, [this.geometryClone()]);\n+ this.destroyFeature(cancel);\n+ },\n+\n+ /**\n+ * APIMethod: cancel\n+ * Finish the geometry and call the \"cancel\" callback.\n+ */\n+ cancel: function() {\n+ this.finalize(true);\n+ },\n+\n+ /**\n+ * Method: click\n+ * Handle clicks. Clicks are stopped from propagating to other listeners\n+ * on map.events or other dom elements.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ click: function(evt) {\n+ OpenLayers.Event.stop(evt);\n+ return false;\n+ },\n+\n+ /**\n+ * Method: dblclick\n+ * Handle double-clicks. Double-clicks are stopped from propagating to other\n+ * listeners on map.events or other dom elements.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ dblclick: function(evt) {\n+ OpenLayers.Event.stop(evt);\n+ return false;\n+ },\n+\n+ /**\n+ * Method: modifyFeature\n+ * Modify the existing geometry given a pixel location.\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} A pixel location on the map.\n+ */\n+ modifyFeature: function(pixel) {\n+ if (!this.point) {\n+ this.createFeature(pixel);\n+ }\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ this.point.geometry.x = lonlat.lon;\n+ this.point.geometry.y = lonlat.lat;\n+ this.callback(\"modify\", [this.point.geometry, this.point, false]);\n+ this.point.geometry.clearBounds();\n+ this.drawFeature();\n+ },\n+\n+ /**\n+ * Method: drawFeature\n+ * Render features on the temporary layer.\n+ */\n+ drawFeature: function() {\n+ this.layer.drawFeature(this.point, this.style);\n+ },\n+\n+ /**\n+ * Method: getGeometry\n+ * Return the sketch geometry. If <multi> is true, this will return\n+ * a multi-part geometry.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>}\n+ */\n+ getGeometry: function() {\n+ var geometry = this.point && this.point.geometry;\n+ if (geometry && this.multi) {\n+ geometry = new OpenLayers.Geometry.MultiPoint([geometry]);\n+ }\n+ return geometry;\n+ },\n+\n+ /**\n+ * Method: geometryClone\n+ * Return a clone of the relevant geometry.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>}\n+ */\n+ geometryClone: function() {\n+ var geom = this.getGeometry();\n+ return geom && geom.clone();\n+ },\n+\n+ /**\n+ * Method: mousedown\n+ * Handle mousedown.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ mousedown: function(evt) {\n+ return this.down(evt);\n+ },\n+\n+ /**\n+ * Method: touchstart\n+ * Handle touchstart.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ this.lastTouchPx = evt.xy;\n+ return this.down(evt);\n+ },\n+\n+ /**\n+ * Method: mousemove\n+ * Handle mousemove.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ mousemove: function(evt) {\n+ return this.move(evt);\n+ },\n+\n+ /**\n+ * Method: touchmove\n+ * Handle touchmove.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ touchmove: function(evt) {\n+ this.lastTouchPx = evt.xy;\n+ return this.move(evt);\n+ },\n+\n+ /**\n+ * Method: mouseup\n+ * Handle mouseup.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ mouseup: function(evt) {\n+ return this.up(evt);\n+ },\n+\n+ /**\n+ * Method: touchend\n+ * Handle touchend.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ touchend: function(evt) {\n+ evt.xy = this.lastTouchPx;\n+ return this.up(evt);\n+ },\n+\n+ /**\n+ * Method: down\n+ * Handle mousedown and touchstart. Adjust the geometry and redraw.\n+ * Return determines whether to propagate the event on the map.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ down: function(evt) {\n+ this.mouseDown = true;\n+ this.lastDown = evt.xy;\n+ if (!this.touch) { // no point displayed until up on touch devices\n+ this.modifyFeature(evt.xy);\n+ }\n+ this.stoppedDown = this.stopDown;\n+ return !this.stopDown;\n+ },\n+\n+ /**\n+ * Method: move\n+ * Handle mousemove and touchmove. Adjust the geometry and redraw.\n+ * Return determines whether to propagate the event on the map.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ move: function(evt) {\n+ if (!this.touch // no point displayed until up on touch devices\n+ &&\n+ (!this.mouseDown || this.stoppedDown)) {\n+ this.modifyFeature(evt.xy);\n+ }\n+ return true;\n+ },\n+\n+ /**\n+ * Method: up\n+ * Handle mouseup and touchend. Send the latest point in the geometry to the control.\n+ * Return determines whether to propagate the event on the map.\n+ *\n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ up: function(evt) {\n+ this.mouseDown = false;\n+ this.stoppedDown = this.stopDown;\n+\n+ // check keyboard modifiers\n+ if (!this.checkModifiers(evt)) {\n+ return true;\n+ }\n+ // ignore double-clicks\n+ if (this.lastUp && this.lastUp.equals(evt.xy)) {\n+ return true;\n+ }\n+ if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy,\n+ this.pixelTolerance)) {\n+ if (this.touch) {\n+ this.modifyFeature(evt.xy);\n+ }\n+ if (this.persist) {\n+ this.destroyPersistedFeature();\n+ }\n+ this.lastUp = evt.xy;\n+ this.finalize();\n+ return !this.stopUp;\n+ } else {\n+ return true;\n+ }\n+ },\n+\n+ /**\n+ * Method: mouseout\n+ * Handle mouse out. For better user experience reset mouseDown\n+ * and stoppedDown when the mouse leaves the map viewport.\n+ *\n+ * Parameters:\n+ * evt - {Event} The browser event\n+ */\n+ mouseout: function(evt) {\n+ if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ this.stoppedDown = this.stopDown;\n+ this.mouseDown = false;\n+ }\n+ },\n+\n+ /**\n+ * Method: passesTolerance\n+ * Determine whether the event is within the optional pixel tolerance.\n+ *\n+ * Returns:\n+ * {Boolean} The event is within the pixel tolerance (if specified).\n+ */\n+ passesTolerance: function(pixel1, pixel2, tolerance) {\n+ var passes = true;\n+\n+ if (tolerance != null && pixel1 && pixel2) {\n+ var dist = pixel1.distanceTo(pixel2);\n+ if (dist > tolerance) {\n+ passes = false;\n+ }\n+ }\n+ return passes;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Point\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Path.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Handler/Point.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.Path\n+ * Handler to draw a path on the map. Path is displayed on mouse down,\n+ * moves on mouse move, and is finished on mouse up.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler.Point>\n+ */\n+OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {\n+\n+ /**\n+ * Property: line\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ line: null,\n+\n+ /**\n+ * APIProperty: maxVertices\n+ * {Number} The maximum number of vertices which can be drawn by this\n+ * handler. When the number of vertices reaches maxVertices, the\n+ * geometry is automatically finalized. Default is null.\n+ */\n+ maxVertices: null,\n+\n+ /**\n+ * Property: doubleTouchTolerance\n+ * {Number} Maximum number of pixels between two touches for\n+ * the gesture to be considered a \"finalize feature\" action.\n+ * Default is 20.\n+ */\n+ doubleTouchTolerance: 20,\n+\n+ /**\n+ * Property: freehand\n+ * {Boolean} In freehand mode, the handler starts the path on mouse down,\n+ * adds a point for every mouse move, and finishes the path on mouse up.\n+ * Outside of freehand mode, a point is added to the path on every mouse\n+ * click and double-click finishes the path.\n+ */\n+ freehand: false,\n+\n+ /**\n+ * Property: freehandToggle\n+ * {String} If set, freehandToggle is checked on mouse events and will set\n+ * the freehand mode to the opposite of this.freehand. To disallow\n+ * toggling between freehand and non-freehand mode, set freehandToggle to\n+ * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.\n+ */\n+ freehandToggle: 'shiftKey',\n+\n+ /**\n+ * Property: timerId\n+ * {Integer} The timer used to test the double touch.\n+ */\n+ timerId: null,\n+\n+ /**\n+ * Property: redoStack\n+ * {Array} Stack containing points removed with <undo>.\n+ */\n+ redoStack: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Path\n+ * Create a new path hander\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that owns this handler\n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} An optional object with properties to be set on the\n+ * handler\n+ *\n+ * Named callbacks:\n+ * create - Called when a sketch is first created. Callback called with\n+ * the creation point geometry and sketch feature.\n+ * modify - Called with each move of a vertex with the vertex (point)\n+ * geometry and the sketch feature.\n+ * point - Called as each point is added. Receives the new point geometry.\n+ * done - Called when the point drawing is finished. The callback will\n+ * recieve a single argument, the linestring geometry.\n+ * cancel - Called when the handler is deactivated while drawing. The\n+ * cancel callback will receive a geometry.\n+ */\n+\n+ /**\n+ * Method: createFeature\n+ * Add temporary geometries\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new\n+ * feature.\n+ */\n+ createFeature: function(pixel) {\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ var geometry = new OpenLayers.Geometry.Point(\n+ lonlat.lon, lonlat.lat\n+ );\n+ this.point = new OpenLayers.Feature.Vector(geometry);\n+ this.line = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.LineString([this.point.geometry])\n+ );\n+ this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n+ this.point.geometry.clearBounds();\n+ this.layer.addFeatures([this.line, this.point], {\n+ silent: true\n });\n },\n \n /**\n- * Method: start\n- * Sets startEvt = evt.\n+ * Method: destroyFeature\n+ * Destroy temporary geometries\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>}\n+ * force - {Boolean} Destroy even if persist is true.\n */\n- start: function(evt) {\n- this.startEvt = evt;\n+ destroyFeature: function(force) {\n+ OpenLayers.Handler.Point.prototype.destroyFeature.call(\n+ this, force);\n+ this.line = null;\n },\n \n /**\n- * Method: cancel\n- * Deletes the start event.\n+ * Method: destroyPersistedFeature\n+ * Destroy the persisted feature.\n+ */\n+ destroyPersistedFeature: function() {\n+ var layer = this.layer;\n+ if (layer && layer.features.length > 2) {\n+ this.layer.features[0].destroy();\n+ }\n+ },\n+\n+ /**\n+ * Method: removePoint\n+ * Destroy the temporary point.\n+ */\n+ removePoint: function() {\n+ if (this.point) {\n+ this.layer.removeFeatures([this.point]);\n+ }\n+ },\n+\n+ /**\n+ * Method: addPoint\n+ * Add point to geometry. Send the point index to override\n+ * the behavior of LinearRing that disregards adding duplicate points.\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.\n+ */\n+ addPoint: function(pixel) {\n+ this.layer.removeFeatures([this.point]);\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ this.point = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)\n+ );\n+ this.line.geometry.addComponent(\n+ this.point.geometry, this.line.geometry.components.length\n+ );\n+ this.layer.addFeatures([this.point]);\n+ this.callback(\"point\", [this.point.geometry, this.getGeometry()]);\n+ this.callback(\"modify\", [this.point.geometry, this.getSketch()]);\n+ this.drawFeature();\n+ delete this.redoStack;\n+ },\n+\n+ /**\n+ * Method: insertXY\n+ * Insert a point in the current sketch given x & y coordinates. The new\n+ * point is inserted immediately before the most recently drawn point.\n+ *\n+ * Parameters:\n+ * x - {Number} The x-coordinate of the point.\n+ * y - {Number} The y-coordinate of the point.\n+ */\n+ insertXY: function(x, y) {\n+ this.line.geometry.addComponent(\n+ new OpenLayers.Geometry.Point(x, y),\n+ this.getCurrentPointIndex()\n+ );\n+ this.drawFeature();\n+ delete this.redoStack;\n+ },\n+\n+ /**\n+ * Method: insertDeltaXY\n+ * Insert a point given offsets from the previously inserted point.\n+ *\n+ * Parameters:\n+ * dx - {Number} The x-coordinate offset of the point.\n+ * dy - {Number} The y-coordinate offset of the point.\n+ */\n+ insertDeltaXY: function(dx, dy) {\n+ var previousIndex = this.getCurrentPointIndex() - 1;\n+ var p0 = this.line.geometry.components[previousIndex];\n+ if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {\n+ this.insertXY(p0.x + dx, p0.y + dy);\n+ }\n+ },\n+\n+ /**\n+ * Method: insertDirectionLength\n+ * Insert a point in the current sketch given a direction and a length.\n+ *\n+ * Parameters:\n+ * direction - {Number} Degrees clockwise from the positive x-axis.\n+ * length - {Number} Distance from the previously drawn point.\n+ */\n+ insertDirectionLength: function(direction, length) {\n+ direction *= Math.PI / 180;\n+ var dx = length * Math.cos(direction);\n+ var dy = length * Math.sin(direction);\n+ this.insertDeltaXY(dx, dy);\n+ },\n+\n+ /**\n+ * Method: insertDeflectionLength\n+ * Insert a point in the current sketch given a deflection and a length.\n+ * The deflection should be degrees clockwise from the previously \n+ * digitized segment.\n+ *\n+ * Parameters:\n+ * deflection - {Number} Degrees clockwise from the previous segment.\n+ * length - {Number} Distance from the previously drawn point.\n+ */\n+ insertDeflectionLength: function(deflection, length) {\n+ var previousIndex = this.getCurrentPointIndex() - 1;\n+ if (previousIndex > 0) {\n+ var p1 = this.line.geometry.components[previousIndex];\n+ var p0 = this.line.geometry.components[previousIndex - 1];\n+ var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);\n+ this.insertDirectionLength(\n+ (theta * 180 / Math.PI) + deflection, length\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: getCurrentPointIndex\n+ * \n+ * Returns:\n+ * {Number} The index of the most recently drawn point.\n+ */\n+ getCurrentPointIndex: function() {\n+ return this.line.geometry.components.length - 1;\n+ },\n+\n+\n+ /**\n+ * Method: undo\n+ * Remove the most recently added point in the sketch geometry.\n+ *\n+ * Returns: \n+ * {Boolean} A point was removed.\n+ */\n+ undo: function() {\n+ var geometry = this.line.geometry;\n+ var components = geometry.components;\n+ var index = this.getCurrentPointIndex() - 1;\n+ var target = components[index];\n+ var undone = geometry.removeComponent(target);\n+ if (undone) {\n+ // On touch devices, set the current (\"mouse location\") point to\n+ // match the last digitized point.\n+ if (this.touch && index > 0) {\n+ components = geometry.components; // safety\n+ var lastpt = components[index - 1];\n+ var curptidx = this.getCurrentPointIndex();\n+ var curpt = components[curptidx];\n+ curpt.x = lastpt.x;\n+ curpt.y = lastpt.y;\n+ }\n+ if (!this.redoStack) {\n+ this.redoStack = [];\n+ }\n+ this.redoStack.push(target);\n+ this.drawFeature();\n+ }\n+ return undone;\n+ },\n+\n+ /**\n+ * Method: redo\n+ * Reinsert the most recently removed point resulting from an <undo> call.\n+ * The undo stack is deleted whenever a point is added by other means.\n+ *\n+ * Returns: \n+ * {Boolean} A point was added.\n+ */\n+ redo: function() {\n+ var target = this.redoStack && this.redoStack.pop();\n+ if (target) {\n+ this.line.geometry.addComponent(target, this.getCurrentPointIndex());\n+ this.drawFeature();\n+ }\n+ return !!target;\n+ },\n+\n+ /**\n+ * Method: freehandMode\n+ * Determine whether to behave in freehand mode or not.\n+ *\n+ * Returns:\n+ * {Boolean}\n+ */\n+ freehandMode: function(evt) {\n+ return (this.freehandToggle && evt[this.freehandToggle]) ?\n+ !this.freehand : this.freehand;\n+ },\n+\n+ /**\n+ * Method: modifyFeature\n+ * Modify the existing geometry given the new point\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest\n+ * point.\n+ * drawing - {Boolean} Indicate if we're currently drawing.\n+ */\n+ modifyFeature: function(pixel, drawing) {\n+ if (!this.line) {\n+ this.createFeature(pixel);\n+ }\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ this.point.geometry.x = lonlat.lon;\n+ this.point.geometry.y = lonlat.lat;\n+ this.callback(\"modify\", [this.point.geometry, this.getSketch(), drawing]);\n+ this.point.geometry.clearBounds();\n+ this.drawFeature();\n+ },\n+\n+ /**\n+ * Method: drawFeature\n+ * Render geometries on the temporary layer.\n+ */\n+ drawFeature: function() {\n+ this.layer.drawFeature(this.line, this.style);\n+ this.layer.drawFeature(this.point, this.style);\n+ },\n+\n+ /**\n+ * Method: getSketch\n+ * Return the sketch feature.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ getSketch: function() {\n+ return this.line;\n+ },\n+\n+ /**\n+ * Method: getGeometry\n+ * Return the sketch geometry. If <multi> is true, this will return\n+ * a multi-part geometry.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.LineString>}\n+ */\n+ getGeometry: function() {\n+ var geometry = this.line && this.line.geometry;\n+ if (geometry && this.multi) {\n+ geometry = new OpenLayers.Geometry.MultiLineString([geometry]);\n+ }\n+ return geometry;\n+ },\n+\n+ /**\n+ * method: touchstart\n+ * handle touchstart.\n+ *\n+ * parameters:\n+ * evt - {event} the browser event\n+ *\n+ * returns:\n+ * {boolean} allow event propagation\n+ */\n+ touchstart: function(evt) {\n+ if (this.timerId &&\n+ this.passesTolerance(this.lastTouchPx, evt.xy,\n+ this.doubleTouchTolerance)) {\n+ // double-tap, finalize the geometry\n+ this.finishGeometry();\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n+ return false;\n+ } else {\n+ if (this.timerId) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n+ }\n+ this.timerId = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.timerId = null;\n+ }, this), 300);\n+ return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt);\n+ }\n+ },\n+\n+ /**\n+ * Method: down\n+ * Handle mousedown and touchstart. Add a new point to the geometry and\n+ * render it. Return determines whether to propagate the event on the map.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ down: function(evt) {\n+ var stopDown = this.stopDown;\n+ if (this.freehandMode(evt)) {\n+ stopDown = true;\n+ if (this.touch) {\n+ this.modifyFeature(evt.xy, !!this.lastUp);\n+ OpenLayers.Event.stop(evt);\n+ }\n+ }\n+ if (!this.touch && (!this.lastDown ||\n+ !this.passesTolerance(this.lastDown, evt.xy,\n+ this.pixelTolerance))) {\n+ this.modifyFeature(evt.xy, !!this.lastUp);\n+ }\n+ this.mouseDown = true;\n+ this.lastDown = evt.xy;\n+ this.stoppedDown = stopDown;\n+ return !stopDown;\n+ },\n+\n+ /**\n+ * Method: move\n+ * Handle mousemove and touchmove. Adjust the geometry and redraw.\n+ * Return determines whether to propagate the event on the map.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ move: function(evt) {\n+ if (this.stoppedDown && this.freehandMode(evt)) {\n+ if (this.persist) {\n+ this.destroyPersistedFeature();\n+ }\n+ if (this.maxVertices && this.line &&\n+ this.line.geometry.components.length === this.maxVertices) {\n+ this.removePoint();\n+ this.finalize();\n+ } else {\n+ this.addPoint(evt.xy);\n+ }\n+ return false;\n+ }\n+ if (!this.touch && (!this.mouseDown || this.stoppedDown)) {\n+ this.modifyFeature(evt.xy, !!this.lastUp);\n+ }\n+ return true;\n+ },\n+\n+ /**\n+ * Method: up\n+ * Handle mouseup and touchend. Send the latest point in the geometry to\n+ * the control. Return determines whether to propagate the event on the map.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ up: function(evt) {\n+ if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {\n+ if (this.stoppedDown && this.freehandMode(evt)) {\n+ if (this.persist) {\n+ this.destroyPersistedFeature();\n+ }\n+ this.removePoint();\n+ this.finalize();\n+ } else {\n+ if (this.passesTolerance(this.lastDown, evt.xy,\n+ this.pixelTolerance)) {\n+ if (this.touch) {\n+ this.modifyFeature(evt.xy);\n+ }\n+ if (this.lastUp == null && this.persist) {\n+ this.destroyPersistedFeature();\n+ }\n+ this.addPoint(evt.xy);\n+ this.lastUp = evt.xy;\n+ if (this.line.geometry.components.length === this.maxVertices + 1) {\n+ this.finishGeometry();\n+ }\n+ }\n+ }\n+ }\n+ this.stoppedDown = this.stopDown;\n+ this.mouseDown = false;\n+ return !this.stopUp;\n+ },\n+\n+ /**\n+ * APIMethod: finishGeometry\n+ * Finish the geometry and send it back to the control.\n+ */\n+ finishGeometry: function() {\n+ var index = this.line.geometry.components.length - 1;\n+ this.line.geometry.removeComponent(this.line.geometry.components[index]);\n+ this.removePoint();\n+ this.finalize();\n+ },\n+\n+ /**\n+ * Method: dblclick \n+ * Handle double-clicks.\n+ * \n+ * Parameters:\n+ * evt - {Event} The browser event\n+ *\n+ * Returns: \n+ * {Boolean} Allow event propagation\n+ */\n+ dblclick: function(evt) {\n+ if (!this.freehandMode(evt)) {\n+ this.finishGeometry();\n+ }\n+ return false;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Path\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Hover.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.Hover\n+ * The hover handler is to be used to emulate mouseovers on objects\n+ * on the map that aren't DOM elements. For example one can use\n+ * this handler to send WMS/GetFeatureInfo requests as the user\n+ * moves the mouve over the map.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler> \n+ */\n+OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {\n+\n+ /**\n+ * APIProperty: delay\n+ * {Integer} - Number of milliseconds between mousemoves before\n+ * the event is considered a hover. Default is 500.\n+ */\n+ delay: 500,\n+\n+ /**\n+ * APIProperty: pixelTolerance\n+ * {Integer} - Maximum number of pixels between mousemoves for\n+ * an event to be considered a hover. Default is null.\n+ */\n+ pixelTolerance: null,\n+\n+ /**\n+ * APIProperty: stopMove\n+ * {Boolean} - Stop other listeners from being notified on mousemoves.\n+ * Default is false.\n+ */\n+ stopMove: false,\n+\n+ /**\n+ * Property: px\n+ * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed\n+ * in pixels.\n+ */\n+ px: null,\n+\n+ /**\n+ * Property: timerId\n+ * {Number} - The id of the timer.\n+ */\n+ timerId: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Hover\n+ * Construct a hover handler.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that initialized this\n+ * handler. The control is assumed to have a valid map property; that\n+ * map is used in the handler's own setMap method.\n+ * callbacks - {Object} An object with keys corresponding to callbacks\n+ * that will be called by the handler. The callbacks should\n+ * expect to receive a single argument, the event. Callbacks for\n+ * 'move', the mouse is moving, and 'pause', the mouse is pausing,\n+ * are supported.\n+ * options - {Object} An optional object whose properties will be set on\n+ * the handler.\n+ */\n+\n+ /**\n+ * Method: mousemove\n+ * Called when the mouse moves on the map.\n *\n * Parameters:\n * evt - {<OpenLayers.Event>}\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- cancel: function(evt) {\n- delete this.startEvt;\n+ mousemove: function(evt) {\n+ if (this.passesTolerance(evt.xy)) {\n+ this.clearTimer();\n+ this.callback('move', [evt]);\n+ this.px = evt.xy;\n+ // clone the evt so original properties can be accessed even\n+ // if the browser deletes them during the delay\n+ evt = OpenLayers.Util.extend({}, evt);\n+ this.timerId = window.setTimeout(\n+ OpenLayers.Function.bind(this.delayedCall, this, evt),\n+ this.delay\n+ );\n+ }\n+ return !this.stopMove;\n },\n \n /**\n- * Method: onClick\n- * Listener for the click event.\n+ * Method: mouseout\n+ * Called when the mouse goes out of the map.\n *\n * Parameters:\n * evt - {<OpenLayers.Event>}\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- onClick: function(evt) {\n- if (!this.startEvt || evt.type !== \"touchend\" &&\n- !OpenLayers.Event.isLeftClick(evt)) {\n- return;\n+ mouseout: function(evt) {\n+ if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ this.clearTimer();\n+ this.callback('move', [evt]);\n }\n- var features = this.getFeatures(this.startEvt);\n- delete this.startEvt;\n- // fire featureclick events\n- var feature, layer, more, clicked = {};\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- layer = feature.layer;\n- clicked[layer.id] = true;\n- more = this.triggerEvent(\"featureclick\", {\n- feature: feature\n- });\n- if (more === false) {\n- break;\n+ return true;\n+ },\n+\n+ /**\n+ * Method: passesTolerance\n+ * Determine whether the mouse move is within the optional pixel tolerance.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ *\n+ * Returns:\n+ * {Boolean} The mouse move is within the pixel tolerance.\n+ */\n+ passesTolerance: function(px) {\n+ var passes = true;\n+ if (this.pixelTolerance && this.px) {\n+ var dpx = Math.sqrt(\n+ Math.pow(this.px.x - px.x, 2) +\n+ Math.pow(this.px.y - px.y, 2)\n+ );\n+ if (dpx < this.pixelTolerance) {\n+ passes = false;\n }\n }\n- // fire nofeatureclick events on all vector layers with no targets\n- for (i = 0, len = this.map.layers.length; i < len; ++i) {\n- layer = this.map.layers[i];\n- if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {\n- this.triggerEvent(\"nofeatureclick\", {\n- layer: layer\n- });\n- }\n+ return passes;\n+ },\n+\n+ /**\n+ * Method: clearTimer\n+ * Clear the timer and set <timerId> to null.\n+ */\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n }\n },\n \n /**\n- * Method: onMousemove\n- * Listener for the mousemove event.\n+ * Method: delayedCall\n+ * Triggers pause callback.\n *\n * Parameters:\n * evt - {<OpenLayers.Event>}\n */\n- onMousemove: function(evt) {\n- delete this.startEvt;\n- var features = this.getFeatures(evt);\n- var over = {},\n- newly = [],\n- feature;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- over[feature.id] = feature;\n- if (!this.cache[feature.id]) {\n- newly.push(feature);\n- }\n- }\n- // check if already over features\n- var out = [];\n- for (var id in this.cache) {\n- feature = this.cache[id];\n- if (feature.layer && feature.layer.map) {\n- if (!over[feature.id]) {\n- out.push(feature);\n- }\n- } else {\n- // removed\n- delete this.cache[id];\n- }\n- }\n- // fire featureover events\n- var more;\n- for (i = 0, len = newly.length; i < len; ++i) {\n- feature = newly[i];\n- this.cache[feature.id] = feature;\n- more = this.triggerEvent(\"featureover\", {\n- feature: feature\n- });\n- if (more === false) {\n- break;\n- }\n- }\n- // fire featureout events\n- for (i = 0, len = out.length; i < len; ++i) {\n- feature = out[i];\n- delete this.cache[feature.id];\n- more = this.triggerEvent(\"featureout\", {\n- feature: feature\n- });\n- if (more === false) {\n- break;\n- }\n- }\n+ delayedCall: function(evt) {\n+ this.callback('pause', [evt]);\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * Deactivate the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully deactivated.\n+ */\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ deactivated = true;\n+ }\n+ return deactivated;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Hover\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Keyboard.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Events.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.handler.Keyboard\n+ * A handler for keyboard events. Create a new instance with the\n+ * <OpenLayers.Handler.Keyboard> constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler> \n+ */\n+OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {\n+\n+ /* http://www.quirksmode.org/js/keys.html explains key x-browser\n+ key handling quirks in pretty nice detail */\n+\n+ /** \n+ * Constant: KEY_EVENTS\n+ * keydown, keypress, keyup\n+ */\n+ KEY_EVENTS: [\"keydown\", \"keyup\"],\n+\n+ /** \n+ * Property: eventListener\n+ * {Function}\n+ */\n+ eventListener: null,\n+\n+ /**\n+ * Property: observeElement\n+ * {DOMElement|String} The DOM element on which we listen for\n+ * key events. Default to the document.\n+ */\n+ observeElement: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Keyboard\n+ * Returns a new keyboard handler.\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handlers setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object containing a single function to be\n+ * called when the drag operation is finished. The callback should\n+ * expect to recieve a single argument, the pixel location of the event.\n+ * Callbacks for 'keydown', 'keypress', and 'keyup' are supported.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * handler.\n+ */\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ // cache the bound event listener method so it can be unobserved later\n+ this.eventListener = OpenLayers.Function.bindAsEventListener(\n+ this.handleKeyEvent, this\n+ );\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ this.deactivate();\n+ this.eventListener = null;\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: triggerEvent\n- * Determines where to trigger the event and triggers it.\n- *\n- * Parameters:\n- * type - {String} The event type to trigger\n- * evt - {Object} The listener argument\n- *\n- * Returns:\n- * {Boolean} The last listener return.\n+ * Method: activate\n */\n- triggerEvent: function(type, evt) {\n- var layer = evt.feature ? evt.feature.layer : evt.layer,\n- object = this.target.object;\n- if (object instanceof OpenLayers.Map || object === layer) {\n- return this.target.triggerEvent(type, evt);\n+ activate: function() {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.observeElement = this.observeElement || document;\n+ for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {\n+ OpenLayers.Event.observe(\n+ this.observeElement, this.KEY_EVENTS[i], this.eventListener);\n+ }\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n /**\n- * Method: getFeatures\n- * Get all features at the given screen location.\n- *\n- * Parameters:\n- * evt - {Object} Event object.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.\n+ * Method: deactivate\n */\n- getFeatures: function(evt) {\n- var x = evt.clientX,\n- y = evt.clientY,\n- features = [],\n- targets = [],\n- layers = [],\n- layer, target, feature, i, len;\n- // go through all layers looking for targets\n- for (i = this.map.layers.length - 1; i >= 0; --i) {\n- layer = this.map.layers[i];\n- if (layer.div.style.display !== \"none\") {\n- if (layer.renderer instanceof OpenLayers.Renderer.Elements) {\n- if (layer instanceof OpenLayers.Layer.Vector) {\n- target = document.elementFromPoint(x, y);\n- while (target && target._featureId) {\n- feature = layer.getFeatureById(target._featureId);\n- if (feature) {\n- features.push(feature);\n- target.style.display = \"none\";\n- targets.push(target);\n- target = document.elementFromPoint(x, y);\n- } else {\n- // sketch, all bets off\n- target = false;\n- }\n- }\n- }\n- layers.push(layer);\n- layer.div.style.display = \"none\";\n- } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {\n- feature = layer.renderer.getFeatureIdFromEvent(evt);\n- if (feature) {\n- features.push(feature);\n- layers.push(layer);\n- }\n- }\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {\n+ OpenLayers.Event.stopObserving(\n+ this.observeElement, this.KEY_EVENTS[i], this.eventListener);\n }\n+ deactivated = true;\n }\n- // restore feature visibility\n- for (i = 0, len = targets.length; i < len; ++i) {\n- targets[i].style.display = \"\";\n- }\n- // restore layer visibility\n- for (i = layers.length - 1; i >= 0; --i) {\n- layers[i].div.style.display = \"block\";\n- }\n- return features;\n+ return deactivated;\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up.\n+ * Method: handleKeyEvent \n */\n- destroy: function() {\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- delete this.target.extensions[this.provides[i]];\n+ handleKeyEvent: function(evt) {\n+ if (this.checkModifiers(evt)) {\n+ this.callback(evt.type, [evt]);\n }\n- this.map.events.un({\n- mousemove: this.onMousemove,\n- mousedown: this.start,\n- mouseup: this.onClick,\n- touchstart: this.start,\n- touchmove: this.cancel,\n- touchend: this.onClick,\n- scope: this\n- });\n- delete this.cache;\n- delete this.map;\n- delete this.target;\n- }\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Handler.Keyboard\"\n });\n-\n-/**\n- * Class: OpenLayers.Events.nofeatureclick\n- *\n- * Extension event type for handling click events that do not hit a feature. \n- * \n- * Event types provided by this extension:\n- * - nofeatureclick \n- */\n-OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;\n-\n-/**\n- * Class: OpenLayers.Events.featureover\n- *\n- * Extension event type for handling hovering over a feature. \n- * \n- * Event types provided by this extension:\n- * - featureover \n- */\n-OpenLayers.Events.featureover = OpenLayers.Events.featureclick;\n-\n-/**\n- * Class: OpenLayers.Events.featureout\n- *\n- * Extension event type for handling leaving a feature. \n- * \n- * Event types provided by this extension:\n- * - featureout \n- */\n-OpenLayers.Events.featureout = OpenLayers.Events.featureclick;\n /* ======================================================================\n- OpenLayers/Events/buttonclick.js\n+ OpenLayers/Handler/Pinch.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Events.buttonclick\n- * Extension event type for handling buttons on top of a dom element. This\n- * event type fires \"buttonclick\" on its <target> when a button was\n- * clicked. Buttons are detected by the \"olButton\" class.\n+ * Class: OpenLayers.Handler.Pinch\n+ * The pinch handler is used to deal with sequences of browser events related\n+ * to pinch gestures. The handler is used by controls that want to know\n+ * when a pinch sequence begins, when a pinch is happening, and when it has\n+ * finished.\n *\n- * This event type makes sure that button clicks do not interfere with other\n- * events that are registered on the same <element>.\n+ * Controls that use the pinch handler typically construct it with callbacks\n+ * for 'start', 'move', and 'done'. Callbacks for these keys are\n+ * called when the pinch begins, with each change, and when the pinch is\n+ * done.\n *\n- * Event types provided by this extension:\n- * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n- * object with a *buttonElement* property referencing the dom element of\n- * the clicked button, and an *buttonXY* property with the click position\n- * relative to the button.\n+ * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n */\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n+OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {\n \n /**\n- * Property: target\n- * {<OpenLayers.Events>} The events instance that the buttonclick event will\n- * be triggered on.\n+ * Property: started\n+ * {Boolean} When a touchstart event is received, we want to record it,\n+ * but not set 'pinching' until the touchmove get started after\n+ * starting.\n */\n- target: null,\n+ started: false,\n \n /**\n- * Property: events\n- * {Array} Events to observe and conditionally stop from propagating when\n- * an element with the olButton class (or its olAlphaImg child) is\n- * clicked.\n+ * Property: stopDown\n+ * {Boolean} Stop propagation of touchstart events from getting to\n+ * listeners on the same element. Default is false.\n */\n- events: [\n- 'mousedown', 'mouseup', 'click', 'dblclick',\n- 'touchstart', 'touchmove', 'touchend', 'keydown'\n- ],\n+ stopDown: false,\n \n /**\n- * Property: startRegEx\n- * {RegExp} Regular expression to test Event.type for events that start\n- * a buttonclick sequence.\n+ * Property: pinching\n+ * {Boolean}\n */\n- startRegEx: /^mousedown|touchstart$/,\n+ pinching: false,\n \n /**\n- * Property: cancelRegEx\n- * {RegExp} Regular expression to test Event.type for events that cancel\n- * a buttonclick sequence.\n+ * Property: last\n+ * {Object} Object that store informations related to pinch last touch.\n */\n- cancelRegEx: /^touchmove$/,\n+ last: null,\n \n /**\n- * Property: completeRegEx\n- * {RegExp} Regular expression to test Event.type for events that complete\n- * a buttonclick sequence.\n+ * Property: start\n+ * {Object} Object that store informations related to pinch touchstart.\n */\n- completeRegEx: /^mouseup|touchend$/,\n+ start: null,\n \n /**\n- * Property: startEvt\n- * {Event} The event that started the click sequence\n+ * Constructor: OpenLayers.Handler.Pinch\n+ * Returns OpenLayers.Handler.Pinch\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handlers setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object containing functions to be called when\n+ * the pinch operation start, change, or is finished. The callbacks\n+ * should expect to receive an object argument, which contains\n+ * information about scale, distance, and position of touch points.\n+ * options - {Object}\n */\n \n /**\n- * Constructor: OpenLayers.Events.buttonclick\n- * Construct a buttonclick event type. Applications are not supposed to\n- * create instances of this class - they are created on demand by\n- * <OpenLayers.Events> instances.\n+ * Method: touchstart\n+ * Handle touchstart events\n *\n * Parameters:\n- * target - {<OpenLayers.Events>} The events instance that the buttonclick\n- * event will be triggered on.\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- });\n+ touchstart: function(evt) {\n+ var propagate = true;\n+ this.pinching = false;\n+ if (OpenLayers.Event.isMultiTouch(evt)) {\n+ this.started = true;\n+ this.last = this.start = {\n+ distance: this.getDistance(evt.touches),\n+ delta: 0,\n+ scale: 1\n+ };\n+ this.callback(\"start\", [evt, this.start]);\n+ propagate = !this.stopDown;\n+ } else if (this.started) {\n+ // Some webkit versions send fake single-touch events during\n+ // multitouch, which cause the drag handler to trigger\n+ return false;\n+ } else {\n+ this.started = false;\n+ this.start = null;\n+ this.last = null;\n }\n+ // prevent document dragging\n+ OpenLayers.Event.preventDefault(evt);\n+ return propagate;\n },\n \n /**\n- * Method: destroy\n+ * Method: touchmove\n+ * Handle touchmove events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick);\n+ touchmove: function(evt) {\n+ if (this.started && OpenLayers.Event.isMultiTouch(evt)) {\n+ this.pinching = true;\n+ var current = this.getPinchData(evt);\n+ this.callback(\"move\", [evt, current]);\n+ this.last = current;\n+ // prevent document dragging\n+ OpenLayers.Event.stop(evt);\n+ } else if (this.started) {\n+ // Some webkit versions send fake single-touch events during\n+ // multitouch, which cause the drag handler to trigger\n+ return false;\n }\n- delete this.target;\n+ return true;\n },\n \n /**\n- * Method: getPressedButton\n- * Get the pressed button, if any. Returns undefined if no button\n- * was pressed.\n+ * Method: touchend\n+ * Handle touchend events\n *\n- * Arguments:\n- * element - {DOMElement} The event target.\n+ * Parameters:\n+ * evt - {Event}\n *\n * Returns:\n- * {DOMElement} The button element, or undefined.\n+ * {Boolean} Let the event propagate.\n */\n- getPressedButton: function(element) {\n- var depth = 3, // limit the search depth\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- // hit!\n- button = element;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return button;\n+ touchend: function(evt) {\n+ if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {\n+ this.started = false;\n+ this.pinching = false;\n+ this.callback(\"done\", [evt, this.start, this.last]);\n+ this.start = null;\n+ this.last = null;\n+ return false;\n+ }\n+ return true;\n },\n \n /**\n- * Method: ignore\n- * Check for event target elements that should be ignored by OpenLayers.\n+ * Method: activate\n+ * Activate the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully activated.\n+ */\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.pinching = false;\n+ activated = true;\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ * Deactivate the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully deactivated.\n+ */\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.started = false;\n+ this.pinching = false;\n+ this.start = null;\n+ this.last = null;\n+ deactivated = true;\n+ }\n+ return deactivated;\n+ },\n+\n+ /**\n+ * Method: getDistance\n+ * Get the distance in pixels between two touches.\n *\n * Parameters:\n- * element - {DOMElement} The event target.\n+ * touches - {Array(Object)}\n+ *\n+ * Returns:\n+ * {Number} The distance in pixels.\n */\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === 'a') {\n- ignore = true;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return ignore;\n+ getDistance: function(touches) {\n+ var t0 = touches[0];\n+ var t1 = touches[1];\n+ return Math.sqrt(\n+ Math.pow(t0.olClientX - t1.olClientX, 2) +\n+ Math.pow(t0.olClientY - t1.olClientY, 2)\n+ );\n },\n \n+\n /**\n- * Method: buttonClick\n- * Check if a button was clicked, and fire the buttonclick event\n+ * Method: getPinchData\n+ * Get informations about the pinch event.\n *\n * Parameters:\n * evt - {Event}\n+ *\n+ * Returns:\n+ * {Object} Object that contains data about the current pinch.\n */\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- // was a button pressed?\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- break;\n- }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n-\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- });\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt;\n- }\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt;\n- }\n- }\n- return propagate;\n- }\n+ getPinchData: function(evt) {\n+ var distance = this.getDistance(evt.touches);\n+ var scale = distance / this.start.distance;\n+ return {\n+ distance: distance,\n+ delta: this.last.distance - distance,\n+ scale: scale\n+ };\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Handler.Pinch\"\n });\n+\n /* ======================================================================\n- OpenLayers/Handler/Point.js\n+ OpenLayers/Handler/Drag.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n * @requires OpenLayers/Handler.js\n- * @requires OpenLayers/Geometry/Point.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Point\n- * Handler to draw a point on the map. Point is displayed on activation,\n- * moves on mouse move, and is finished on mouse up. The handler triggers\n- * callbacks for 'done', 'cancel', and 'modify'. The modify callback is\n- * called with each change in the sketch and will receive the latest point\n- * drawn. Create a new instance with the <OpenLayers.Handler.Point>\n- * constructor.\n- * \n+ * Class: OpenLayers.Handler.Drag\n+ * The drag handler is used to deal with sequences of browser events related\n+ * to dragging. The handler is used by controls that want to know when\n+ * a drag sequence begins, when a drag is happening, and when it has\n+ * finished.\n+ *\n+ * Controls that use the drag handler typically construct it with callbacks\n+ * for 'down', 'move', and 'done'. Callbacks for these keys are called\n+ * when the drag begins, with each move, and when the drag is done. In\n+ * addition, controls can have callbacks keyed to 'up' and 'out' if they\n+ * care to differentiate between the types of events that correspond with\n+ * the end of a drag sequence. If no drag actually occurs (no mouse move)\n+ * the 'down' and 'up' callbacks will be called, but not the 'done'\n+ * callback.\n+ *\n+ * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n+ *\n * Inherits from:\n * - <OpenLayers.Handler>\n */\n-OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n \n- /**\n- * Property: point\n- * {<OpenLayers.Feature.Vector>} The currently drawn point\n+ /** \n+ * Property: started\n+ * {Boolean} When a mousedown or touchstart event is received, we want to\n+ * record it, but not set 'dragging' until the mouse moves after starting.\n */\n- point: null,\n+ started: false,\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The temporary drawing layer\n+ * Property: stopDown\n+ * {Boolean} Stop propagation of mousedown events from getting to listeners\n+ * on the same element. Default is true.\n */\n- layer: null,\n+ stopDown: true,\n \n- /**\n- * APIProperty: multi\n- * {Boolean} Cast features to multi-part geometries before passing to the\n- * layer. Default is false.\n+ /** \n+ * Property: dragging \n+ * {Boolean} \n */\n- multi: false,\n+ dragging: false,\n \n- /**\n- * APIProperty: citeCompliant\n- * {Boolean} If set to true, coordinates of features drawn in a map extent\n- * crossing the date line won't exceed the world bounds. Default is false.\n+ /** \n+ * Property: last\n+ * {<OpenLayers.Pixel>} The last pixel location of the drag.\n */\n- citeCompliant: false,\n+ last: null,\n \n- /**\n- * Property: mouseDown\n- * {Boolean} The mouse is down\n+ /** \n+ * Property: start\n+ * {<OpenLayers.Pixel>} The first pixel location of the drag.\n */\n- mouseDown: false,\n+ start: null,\n \n /**\n- * Property: stoppedDown\n- * {Boolean} Indicate whether the last mousedown stopped the event\n- * propagation.\n+ * Property: lastMoveEvt\n+ * {Object} The last mousemove event that occurred. Used to\n+ * position the map correctly when our \"delay drag\"\n+ * timeout expired.\n */\n- stoppedDown: null,\n+ lastMoveEvt: null,\n \n /**\n- * Property: lastDown\n- * {<OpenLayers.Pixel>} Location of the last mouse down\n+ * Property: oldOnselectstart\n+ * {Function}\n */\n- lastDown: null,\n+ oldOnselectstart: null,\n \n /**\n- * Property: lastUp\n- * {<OpenLayers.Pixel>}\n+ * Property: interval\n+ * {Integer} In order to increase performance, an interval (in \n+ * milliseconds) can be set to reduce the number of drag events \n+ * called. If set, a new drag event will not be set until the \n+ * interval has passed. \n+ * Defaults to 0, meaning no interval. \n */\n- lastUp: null,\n+ interval: 0,\n \n /**\n- * APIProperty: persist\n- * {Boolean} Leave the feature rendered until destroyFeature is called.\n- * Default is false. If set to true, the feature remains rendered until\n- * destroyFeature is called, typically by deactivating the handler or\n- * starting another drawing.\n+ * Property: timeoutId\n+ * {String} The id of the timeout used for the mousedown interval.\n+ * This is \"private\", and should be left alone.\n */\n- persist: false,\n+ timeoutId: null,\n \n /**\n- * APIProperty: stopDown\n- * {Boolean} Stop event propagation on mousedown. Must be false to\n- * allow \"pan while drawing\". Defaults to false.\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, the handler will also handle mouse moves when\n+ * the cursor has moved out of the map viewport. Default is false.\n */\n- stopDown: false,\n+ documentDrag: false,\n \n /**\n- * APIPropery: stopUp\n- * {Boolean} Stop event propagation on mouse. Must be false to\n- * allow \"pan while dragging\". Defaults to fase.\n+ * Property: documentEvents\n+ * {Boolean} Are we currently observing document events?\n */\n- stopUp: false,\n+ documentEvents: null,\n \n /**\n- * Property: layerOptions\n- * {Object} Any optional properties to be set on the sketch layer.\n+ * Constructor: OpenLayers.Handler.Drag\n+ * Returns OpenLayers.Handler.Drag\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handlers setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object containing a single function to be\n+ * called when the drag operation is finished. The callback should\n+ * expect to recieve a single argument, the pixel location of the event.\n+ * Callbacks for 'move' and 'done' are supported. You can also speficy\n+ * callbacks for 'down', 'up', and 'out' to respond to those events.\n+ * options - {Object} \n */\n- layerOptions: null,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n \n- /**\n- * APIProperty: pixelTolerance\n- * {Number} Maximum number of pixels between down and up (mousedown\n- * and mouseup, or touchstart and touchend) for the handler to\n- * add a new point. If set to an integer value, if the\n- * displacement between down and up is great to this value\n- * no point will be added. Default value is 5.\n- */\n- pixelTolerance: 5,\n+ if (this.documentDrag === true) {\n+ var me = this;\n+ this._docMove = function(evt) {\n+ me.mousemove({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ },\n+ element: document\n+ });\n+ };\n+ this._docUp = function(evt) {\n+ me.mouseup({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ }\n+ });\n+ };\n+ }\n+ },\n \n- /**\n- * Property: lastTouchPx\n- * {<OpenLayers.Pixel>} The last pixel used to know the distance between\n- * two touches (for double touch).\n- */\n- lastTouchPx: null,\n \n /**\n- * Constructor: OpenLayers.Handler.Point\n- * Create a new point handler.\n+ * Method: dragstart\n+ * This private method is factorized from mousedown and touchstart methods\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} The control that owns this handler\n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} An optional object with properties to be set on the\n- * handler\n+ * evt - {Event} The event\n *\n- * Named callbacks:\n- * create - Called when a sketch is first created. Callback called with\n- * the creation point geometry and sketch feature.\n- * modify - Called with each move of a vertex with the vertex (point)\n- * geometry and the sketch feature.\n- * done - Called when the point drawing is finished. The callback will\n- * recieve a single argument, the point geometry.\n- * cancel - Called when the handler is deactivated while drawing. The\n- * cancel callback will receive a geometry.\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(control, callbacks, options) {\n- if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n- this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});\n- }\n+ dragstart: function(evt) {\n+ var propagate = true;\n+ this.dragging = false;\n+ if (this.checkModifiers(evt) &&\n+ (OpenLayers.Event.isLeftClick(evt) ||\n+ OpenLayers.Event.isSingleTouch(evt))) {\n+ this.started = true;\n+ this.start = evt.xy;\n+ this.last = evt.xy;\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.down(evt);\n+ this.callback(\"down\", [evt.xy]);\n \n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ // prevent document dragging\n+ OpenLayers.Event.preventDefault(evt);\n+\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart ?\n+ document.onselectstart : OpenLayers.Function.True;\n+ }\n+ document.onselectstart = OpenLayers.Function.False;\n+\n+ propagate = !this.stopDown;\n+ } else {\n+ this.started = false;\n+ this.start = null;\n+ this.last = null;\n+ }\n+ return propagate;\n },\n \n /**\n- * APIMethod: activate\n- * turn on the handler\n+ * Method: dragmove\n+ * This private method is factorized from mousemove and touchmove methods\n+ *\n+ * Parameters:\n+ * evt - {Event} The event\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- activate: function() {\n- if (!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- return false;\n+ dragmove: function(evt) {\n+ this.lastMoveEvt = evt;\n+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||\n+ evt.xy.y != this.last.y)) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ if (evt.element === document) {\n+ this.adjustXY(evt);\n+ // do setEvent manually because the documentEvents are not\n+ // registered with the map\n+ this.setEvent(evt);\n+ } else {\n+ this.removeDocumentEvents();\n+ }\n+ }\n+ if (this.interval > 0) {\n+ this.timeoutId = setTimeout(\n+ OpenLayers.Function.bind(this.removeTimeout, this),\n+ this.interval);\n+ }\n+ this.dragging = true;\n+\n+ this.move(evt);\n+ this.callback(\"move\", [evt.xy]);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart;\n+ document.onselectstart = OpenLayers.Function.False;\n+ }\n+ this.last = evt.xy;\n }\n- // create temporary vector layer for rendering geometry sketch\n- // TBD: this could be moved to initialize/destroy - setting visibility here\n- var options = OpenLayers.Util.extend({\n- displayInLayerSwitcher: false,\n- // indicate that the temp vector layer will never be out of range\n- // without this, resolution properties must be specified at the\n- // map-level for this temporary layer to init its resolutions\n- // correctly\n- calculateInRange: OpenLayers.Function.True,\n- wrapDateLine: this.citeCompliant\n- }, this.layerOptions);\n- this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n- this.map.addLayer(this.layer);\n return true;\n },\n \n /**\n- * Method: createFeature\n- * Add temporary features\n+ * Method: dragend\n+ * This private method is factorized from mouseup and touchend methods\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} A pixel location on the map.\n+ * evt - {Event} The event\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- createFeature: function(pixel) {\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- var geometry = new OpenLayers.Geometry.Point(\n- lonlat.lon, lonlat.lat\n- );\n- this.point = new OpenLayers.Feature.Vector(geometry);\n- this.callback(\"create\", [this.point.geometry, this.point]);\n- this.point.geometry.clearBounds();\n- this.layer.addFeatures([this.point], {\n- silent: true\n- });\n+ dragend: function(evt) {\n+ if (this.started) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ this.adjustXY(evt);\n+ this.removeDocumentEvents();\n+ }\n+ var dragged = (this.start != this.last);\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.up(evt);\n+ this.callback(\"up\", [evt.xy]);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy]);\n+ }\n+ document.onselectstart = this.oldOnselectstart;\n+ }\n+ return true;\n },\n \n /**\n- * APIMethod: deactivate\n- * turn off the handler\n+ * The four methods below (down, move, up, and out) are used by subclasses\n+ * to do their own processing related to these mouse events.\n */\n- deactivate: function() {\n- if (!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- return false;\n- }\n- this.cancel();\n- // If a layer's map property is set to null, it means that that layer\n- // isn't added to the map. Since we ourself added the layer to the map\n- // in activate(), we can assume that if this.layer.map is null it means\n- // that the layer has been destroyed (as a result of map.destroy() for\n- // example.\n- if (this.layer.map != null) {\n- this.destroyFeature(true);\n- this.layer.destroy(false);\n- }\n- this.layer = null;\n- return true;\n- },\n \n /**\n- * Method: destroyFeature\n- * Destroy the temporary geometries\n+ * Method: down\n+ * This method is called during the handling of the mouse down event.\n+ * Subclasses can do their own processing here.\n *\n * Parameters:\n- * force - {Boolean} Destroy even if persist is true.\n+ * evt - {Event} The mouse down event\n */\n- destroyFeature: function(force) {\n- if (this.layer && (force || !this.persist)) {\n- this.layer.destroyFeatures();\n- }\n- this.point = null;\n- },\n+ down: function(evt) {},\n \n /**\n- * Method: destroyPersistedFeature\n- * Destroy the persisted feature.\n+ * Method: move\n+ * This method is called during the handling of the mouse move event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse move event\n+ *\n */\n- destroyPersistedFeature: function() {\n- var layer = this.layer;\n- if (layer && layer.features.length > 1) {\n- this.layer.features[0].destroy();\n- }\n- },\n+ move: function(evt) {},\n \n /**\n- * Method: finalize\n- * Finish the geometry and call the \"done\" callback.\n+ * Method: up\n+ * This method is called during the handling of the mouse up event.\n+ * Subclasses can do their own processing here.\n *\n * Parameters:\n- * cancel - {Boolean} Call cancel instead of done callback. Default\n- * is false.\n+ * evt - {Event} The mouse up event\n */\n- finalize: function(cancel) {\n- var key = cancel ? \"cancel\" : \"done\";\n- this.mouseDown = false;\n- this.lastDown = null;\n- this.lastUp = null;\n- this.lastTouchPx = null;\n- this.callback(key, [this.geometryClone()]);\n- this.destroyFeature(cancel);\n- },\n+ up: function(evt) {},\n \n /**\n- * APIMethod: cancel\n- * Finish the geometry and call the \"cancel\" callback.\n+ * Method: out\n+ * This method is called during the handling of the mouse out event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse out event\n */\n- cancel: function() {\n- this.finalize(true);\n- },\n+ out: function(evt) {},\n \n /**\n- * Method: click\n- * Handle clicks. Clicks are stopped from propagating to other listeners\n- * on map.events or other dom elements.\n- * \n+ * The methods below are part of the magic of event handling. Because\n+ * they are named like browser events, they are registered as listeners\n+ * for the events they represent.\n+ */\n+\n+ /**\n+ * Method: mousedown\n+ * Handle mousedown events\n+ *\n * Parameters:\n- * evt - {Event} The browser event\n+ * evt - {Event}\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- click: function(evt) {\n- OpenLayers.Event.stop(evt);\n- return false;\n+ mousedown: function(evt) {\n+ return this.dragstart(evt);\n },\n \n /**\n- * Method: dblclick\n- * Handle double-clicks. Double-clicks are stopped from propagating to other\n- * listeners on map.events or other dom elements.\n- * \n+ * Method: touchstart\n+ * Handle touchstart events\n+ *\n * Parameters:\n- * evt - {Event} The browser event\n+ * evt - {Event}\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- dblclick: function(evt) {\n- OpenLayers.Event.stop(evt);\n- return false;\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return this.dragstart(evt);\n },\n \n /**\n- * Method: modifyFeature\n- * Modify the existing geometry given a pixel location.\n+ * Method: mousemove\n+ * Handle mousemove events\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} A pixel location on the map.\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- modifyFeature: function(pixel) {\n- if (!this.point) {\n- this.createFeature(pixel);\n- }\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- this.point.geometry.x = lonlat.lon;\n- this.point.geometry.y = lonlat.lat;\n- this.callback(\"modify\", [this.point.geometry, this.point, false]);\n- this.point.geometry.clearBounds();\n- this.drawFeature();\n+ mousemove: function(evt) {\n+ return this.dragmove(evt);\n },\n \n /**\n- * Method: drawFeature\n- * Render features on the temporary layer.\n+ * Method: touchmove\n+ * Handle touchmove events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- drawFeature: function() {\n- this.layer.drawFeature(this.point, this.style);\n+ touchmove: function(evt) {\n+ return this.dragmove(evt);\n },\n \n /**\n- * Method: getGeometry\n- * Return the sketch geometry. If <multi> is true, this will return\n- * a multi-part geometry.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>}\n+ * Method: removeTimeout\n+ * Private. Called by mousemove() to remove the drag timeout.\n */\n- getGeometry: function() {\n- var geometry = this.point && this.point.geometry;\n- if (geometry && this.multi) {\n- geometry = new OpenLayers.Geometry.MultiPoint([geometry]);\n+ removeTimeout: function() {\n+ this.timeoutId = null;\n+ // if timeout expires while we're still dragging (mouseup\n+ // hasn't occurred) then call mousemove to move to the\n+ // correct position\n+ if (this.dragging) {\n+ this.mousemove(this.lastMoveEvt);\n }\n- return geometry;\n },\n \n /**\n- * Method: geometryClone\n- * Return a clone of the relevant geometry.\n+ * Method: mouseup\n+ * Handle mouseup events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n *\n * Returns:\n- * {<OpenLayers.Geometry>}\n+ * {Boolean} Let the event propagate.\n */\n- geometryClone: function() {\n- var geom = this.getGeometry();\n- return geom && geom.clone();\n+ mouseup: function(evt) {\n+ return this.dragend(evt);\n },\n \n /**\n- * Method: mousedown\n- * Handle mousedown.\n- * \n+ * Method: touchend\n+ * Handle touchend events\n+ *\n * Parameters:\n- * evt - {Event} The browser event\n+ * evt - {Event}\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- mousedown: function(evt) {\n- return this.down(evt);\n+ touchend: function(evt) {\n+ // override evt.xy with last position since touchend does not have\n+ // any touch position\n+ evt.xy = this.last;\n+ return this.dragend(evt);\n },\n \n /**\n- * Method: touchstart\n- * Handle touchstart.\n- * \n+ * Method: mouseout\n+ * Handle mouseout events\n+ *\n * Parameters:\n- * evt - {Event} The browser event\n+ * evt - {Event}\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- touchstart: function(evt) {\n- this.startTouch();\n- this.lastTouchPx = evt.xy;\n- return this.down(evt);\n+ mouseout: function(evt) {\n+ if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ if (this.documentDrag === true) {\n+ this.addDocumentEvents();\n+ } else {\n+ var dragged = (this.start != this.last);\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.out(evt);\n+ this.callback(\"out\", []);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy]);\n+ }\n+ if (document.onselectstart) {\n+ document.onselectstart = this.oldOnselectstart;\n+ }\n+ }\n+ }\n+ return true;\n },\n \n /**\n- * Method: mousemove\n- * Handle mousemove.\n+ * Method: click\n+ * The drag handler captures the click event. If something else registers\n+ * for clicks on the same element, its listener will not be called \n+ * after a drag.\n * \n- * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Parameters: \n+ * evt - {Event} \n+ * \n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- mousemove: function(evt) {\n- return this.move(evt);\n+ click: function(evt) {\n+ // let the click event propagate only if the mouse moved\n+ return (this.start == this.last);\n },\n \n /**\n- * Method: touchmove\n- * Handle touchmove.\n+ * Method: activate\n+ * Activate the handler.\n * \n- * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} The handler was successfully activated.\n */\n- touchmove: function(evt) {\n- this.lastTouchPx = evt.xy;\n- return this.move(evt);\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragging = false;\n+ activated = true;\n+ }\n+ return activated;\n },\n \n /**\n- * Method: mouseup\n- * Handle mouseup.\n+ * Method: deactivate \n+ * Deactivate the handler.\n * \n- * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Returns:\n+ * {Boolean} The handler was successfully deactivated.\n */\n- mouseup: function(evt) {\n- return this.up(evt);\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.started = false;\n+ this.dragging = false;\n+ this.start = null;\n+ this.last = null;\n+ deactivated = true;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ }\n+ return deactivated;\n },\n \n /**\n- * Method: touchend\n- * Handle touchend.\n+ * Method: adjustXY\n+ * Converts event coordinates that are relative to the document body to\n+ * ones that are relative to the map viewport. The latter is the default in\n+ * OpenLayers.\n * \n * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * evt - {Object}\n */\n- touchend: function(evt) {\n- evt.xy = this.lastTouchPx;\n- return this.up(evt);\n+ adjustXY: function(evt) {\n+ var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n+ evt.xy.x -= pos[0];\n+ evt.xy.y -= pos[1];\n },\n \n /**\n- * Method: down\n- * Handle mousedown and touchstart. Adjust the geometry and redraw.\n- * Return determines whether to propagate the event on the map.\n- * \n+ * Method: addDocumentEvents\n+ * Start observing document events when documentDrag is true and the mouse\n+ * cursor leaves the map viewport while dragging.\n+ */\n+ addDocumentEvents: function() {\n+ OpenLayers.Element.addClass(document.body, \"olDragDown\");\n+ this.documentEvents = true;\n+ OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.observe(document, \"mouseup\", this._docUp);\n+ },\n+\n+ /**\n+ * Method: removeDocumentEvents\n+ * Stops observing document events when documentDrag is true and the mouse\n+ * cursor re-enters the map viewport while dragging.\n+ */\n+ removeDocumentEvents: function() {\n+ OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n+ this.documentEvents = false;\n+ OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Box.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Handler/Drag.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.Box\n+ * Handler for dragging a rectangle across the map. Box is displayed \n+ * on mouse down, moves on mouse move, and is finished on mouse up.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler> \n+ */\n+OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n+\n+ /** \n+ * Property: dragHandler \n+ * {<OpenLayers.Handler.Drag>} \n+ */\n+ dragHandler: null,\n+\n+ /**\n+ * APIProperty: boxDivClassName\n+ * {String} The CSS class to use for drawing the box. Default is\n+ * olHandlerBoxZoomBox\n+ */\n+ boxDivClassName: 'olHandlerBoxZoomBox',\n+\n+ /**\n+ * Property: boxOffsets\n+ * {Object} Caches box offsets from css. This is used by the getBoxOffsets\n+ * method.\n+ */\n+ boxOffsets: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Box\n+ *\n * Parameters:\n- * evt - {Event} The browser event\n+ * control - {<OpenLayers.Control>} \n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} \n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Named callbacks:\n+ * start - Called when the box drag operation starts.\n+ * done - Called when the box drag operation is finished.\n+ * The callback should expect to receive a single argument, the box \n+ * bounds or a pixel. If the box dragging didn't span more than a 5 \n+ * pixel distance, a pixel will be returned instead of a bounds object.\n */\n- down: function(evt) {\n- this.mouseDown = true;\n- this.lastDown = evt.xy;\n- if (!this.touch) { // no point displayed until up on touch devices\n- this.modifyFeature(evt.xy);\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.dragHandler = new OpenLayers.Handler.Drag(\n+ this, {\n+ down: this.startBox,\n+ move: this.moveBox,\n+ out: this.removeBox,\n+ up: this.endBox\n+ }, {\n+ keyMask: this.keyMask\n+ }\n+ );\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.destroy();\n+ this.dragHandler = null;\n }\n- this.stoppedDown = this.stopDown;\n- return !this.stopDown;\n },\n \n /**\n- * Method: move\n- * Handle mousemove and touchmove. Adjust the geometry and redraw.\n- * Return determines whether to propagate the event on the map.\n- * \n- * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Method: setMap\n */\n- move: function(evt) {\n- if (!this.touch // no point displayed until up on touch devices\n- &&\n- (!this.mouseDown || this.stoppedDown)) {\n- this.modifyFeature(evt.xy);\n+ setMap: function(map) {\n+ OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.setMap(map);\n }\n- return true;\n },\n \n /**\n- * Method: up\n- * Handle mouseup and touchend. Send the latest point in the geometry to the control.\n- * Return determines whether to propagate the event on the map.\n+ * Method: startBox\n *\n * Parameters:\n- * evt - {Event} The browser event\n- *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * xy - {<OpenLayers.Pixel>}\n */\n- up: function(evt) {\n- this.mouseDown = false;\n- this.stoppedDown = this.stopDown;\n+ startBox: function(xy) {\n+ this.callback(\"start\", []);\n+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {\n+ x: -9999,\n+ y: -9999\n+ });\n+ this.zoomBox.className = this.boxDivClassName;\n+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n \n- // check keyboard modifiers\n- if (!this.checkModifiers(evt)) {\n- return true;\n+ this.map.viewPortDiv.appendChild(this.zoomBox);\n+\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv, \"olDrawBox\"\n+ );\n+ },\n+\n+ /**\n+ * Method: moveBox\n+ */\n+ moveBox: function(xy) {\n+ var startX = this.dragHandler.start.x;\n+ var startY = this.dragHandler.start.y;\n+ var deltaX = Math.abs(startX - xy.x);\n+ var deltaY = Math.abs(startY - xy.y);\n+\n+ var offset = this.getBoxOffsets();\n+ this.zoomBox.style.width = (deltaX + offset.width + 1) + \"px\";\n+ this.zoomBox.style.height = (deltaY + offset.height + 1) + \"px\";\n+ this.zoomBox.style.left = (xy.x < startX ?\n+ startX - deltaX - offset.left : startX - offset.left) + \"px\";\n+ this.zoomBox.style.top = (xy.y < startY ?\n+ startY - deltaY - offset.top : startY - offset.top) + \"px\";\n+ },\n+\n+ /**\n+ * Method: endBox\n+ */\n+ endBox: function(end) {\n+ var result;\n+ if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||\n+ Math.abs(this.dragHandler.start.y - end.y) > 5) {\n+ var start = this.dragHandler.start;\n+ var top = Math.min(start.y, end.y);\n+ var bottom = Math.max(start.y, end.y);\n+ var left = Math.min(start.x, end.x);\n+ var right = Math.max(start.x, end.x);\n+ result = new OpenLayers.Bounds(left, bottom, right, top);\n+ } else {\n+ result = this.dragHandler.start.clone(); // i.e. OL.Pixel\n }\n- // ignore double-clicks\n- if (this.lastUp && this.lastUp.equals(evt.xy)) {\n+ this.removeBox();\n+\n+ this.callback(\"done\", [result]);\n+ },\n+\n+ /**\n+ * Method: removeBox\n+ * Remove the zoombox from the screen and nullify our reference to it.\n+ */\n+ removeBox: function() {\n+ this.map.viewPortDiv.removeChild(this.zoomBox);\n+ this.zoomBox = null;\n+ this.boxOffsets = null;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDrawBox\"\n+ );\n+\n+ },\n+\n+ /**\n+ * Method: activate\n+ */\n+ activate: function() {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragHandler.activate();\n return true;\n- }\n- if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy,\n- this.pixelTolerance)) {\n- if (this.touch) {\n- this.modifyFeature(evt.xy);\n- }\n- if (this.persist) {\n- this.destroyPersistedFeature();\n- }\n- this.lastUp = evt.xy;\n- this.finalize();\n- return !this.stopUp;\n } else {\n- return true;\n+ return false;\n }\n },\n \n /**\n- * Method: mouseout\n- * Handle mouse out. For better user experience reset mouseDown\n- * and stoppedDown when the mouse leaves the map viewport.\n- *\n- * Parameters:\n- * evt - {Event} The browser event\n+ * Method: deactivate\n */\n- mouseout: function(evt) {\n- if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- this.stoppedDown = this.stopDown;\n- this.mouseDown = false;\n+ deactivate: function() {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ if (this.dragHandler.deactivate()) {\n+ if (this.zoomBox) {\n+ this.removeBox();\n+ }\n+ }\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n /**\n- * Method: passesTolerance\n- * Determine whether the event is within the optional pixel tolerance.\n- *\n+ * Method: getBoxOffsets\n+ * Determines border offsets for a box, according to the box model.\n+ * \n * Returns:\n- * {Boolean} The event is within the pixel tolerance (if specified).\n+ * {Object} an object with the following offsets:\n+ * - left\n+ * - right\n+ * - top\n+ * - bottom\n+ * - width\n+ * - height\n */\n- passesTolerance: function(pixel1, pixel2, tolerance) {\n- var passes = true;\n+ getBoxOffsets: function() {\n+ if (!this.boxOffsets) {\n+ // Determine the box model. If the testDiv's clientWidth is 3, then\n+ // the borders are outside and we are dealing with the w3c box\n+ // model. Otherwise, the browser uses the traditional box model and\n+ // the borders are inside the box bounds, leaving us with a\n+ // clientWidth of 1.\n+ var testDiv = document.createElement(\"div\");\n+ //testDiv.style.visibility = \"hidden\";\n+ testDiv.style.position = \"absolute\";\n+ testDiv.style.border = \"1px solid black\";\n+ testDiv.style.width = \"3px\";\n+ document.body.appendChild(testDiv);\n+ var w3cBoxModel = testDiv.clientWidth == 3;\n+ document.body.removeChild(testDiv);\n \n- if (tolerance != null && pixel1 && pixel2) {\n- var dist = pixel1.distanceTo(pixel2);\n- if (dist > tolerance) {\n- passes = false;\n- }\n+ var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n+ \"border-left-width\"));\n+ var right = parseInt(OpenLayers.Element.getStyle(\n+ this.zoomBox, \"border-right-width\"));\n+ var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n+ \"border-top-width\"));\n+ var bottom = parseInt(OpenLayers.Element.getStyle(\n+ this.zoomBox, \"border-bottom-width\"));\n+ this.boxOffsets = {\n+ left: left,\n+ right: right,\n+ top: top,\n+ bottom: bottom,\n+ width: w3cBoxModel === false ? left + right : 0,\n+ height: w3cBoxModel === false ? top + bottom : 0\n+ };\n }\n- return passes;\n+ return this.boxOffsets;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Point\"\n+ CLASS_NAME: \"OpenLayers.Handler.Box\"\n });\n /* ======================================================================\n OpenLayers/Handler/Feature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -61108,6186 +52936,9263 @@\n this.map.getLayerIndex(this.layer));\n }\n },\n \n CLASS_NAME: \"OpenLayers.Handler.Feature\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Drag.js\n+ OpenLayers/Handler/Polygon.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Handler/Path.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Drag\n- * The drag handler is used to deal with sequences of browser events related\n- * to dragging. The handler is used by controls that want to know when\n- * a drag sequence begins, when a drag is happening, and when it has\n- * finished.\n+ * Class: OpenLayers.Handler.Polygon\n+ * Handler to draw a polygon on the map. Polygon is displayed on mouse down,\n+ * moves on mouse move, and is finished on mouse up.\n *\n- * Controls that use the drag handler typically construct it with callbacks\n- * for 'down', 'move', and 'done'. Callbacks for these keys are called\n- * when the drag begins, with each move, and when the drag is done. In\n- * addition, controls can have callbacks keyed to 'up' and 'out' if they\n- * care to differentiate between the types of events that correspond with\n- * the end of a drag sequence. If no drag actually occurs (no mouse move)\n- * the 'down' and 'up' callbacks will be called, but not the 'done'\n- * callback.\n+ * Inherits from:\n+ * - <OpenLayers.Handler.Path>\n+ * - <OpenLayers.Handler>\n+ */\n+OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {\n+\n+ /** \n+ * APIProperty: holeModifier\n+ * {String} Key modifier to trigger hole digitizing. Acceptable values are\n+ * \"altKey\", \"shiftKey\", or \"ctrlKey\". If not set, no hole digitizing\n+ * will take place. Default is null.\n+ */\n+ holeModifier: null,\n+\n+ /**\n+ * Property: drawingHole\n+ * {Boolean} Currently drawing an interior ring.\n+ */\n+ drawingHole: false,\n+\n+ /**\n+ * Property: polygon\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ polygon: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Polygon\n+ * Create a Polygon Handler.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that owns this handler\n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} An optional object with properties to be set on the\n+ * handler\n+ *\n+ * Named callbacks:\n+ * create - Called when a sketch is first created. Callback called with\n+ * the creation point geometry and sketch feature.\n+ * modify - Called with each move of a vertex with the vertex (point)\n+ * geometry and the sketch feature.\n+ * point - Called as each point is added. Receives the new point geometry.\n+ * done - Called when the point drawing is finished. The callback will\n+ * recieve a single argument, the polygon geometry.\n+ * cancel - Called when the handler is deactivated while drawing. The\n+ * cancel callback will receive a geometry.\n+ */\n+\n+ /**\n+ * Method: createFeature\n+ * Add temporary geometries\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new\n+ * feature.\n+ */\n+ createFeature: function(pixel) {\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ var geometry = new OpenLayers.Geometry.Point(\n+ lonlat.lon, lonlat.lat\n+ );\n+ this.point = new OpenLayers.Feature.Vector(geometry);\n+ this.line = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.LinearRing([this.point.geometry])\n+ );\n+ this.polygon = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Polygon([this.line.geometry])\n+ );\n+ this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n+ this.point.geometry.clearBounds();\n+ this.layer.addFeatures([this.polygon, this.point], {\n+ silent: true\n+ });\n+ },\n+\n+ /**\n+ * Method: addPoint\n+ * Add point to geometry.\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.\n+ */\n+ addPoint: function(pixel) {\n+ if (!this.drawingHole && this.holeModifier &&\n+ this.evt && this.evt[this.holeModifier]) {\n+ var geometry = this.point.geometry;\n+ var features = this.control.layer.features;\n+ var candidate, polygon;\n+ // look for intersections, last drawn gets priority\n+ for (var i = features.length - 1; i >= 0; --i) {\n+ candidate = features[i].geometry;\n+ if ((candidate instanceof OpenLayers.Geometry.Polygon ||\n+ candidate instanceof OpenLayers.Geometry.MultiPolygon) &&\n+ candidate.intersects(geometry)) {\n+ polygon = features[i];\n+ this.control.layer.removeFeatures([polygon], {\n+ silent: true\n+ });\n+ this.control.layer.events.registerPriority(\n+ \"sketchcomplete\", this, this.finalizeInteriorRing\n+ );\n+ this.control.layer.events.registerPriority(\n+ \"sketchmodified\", this, this.enforceTopology\n+ );\n+ polygon.geometry.addComponent(this.line.geometry);\n+ this.polygon = polygon;\n+ this.drawingHole = true;\n+ break;\n+ }\n+ }\n+ }\n+ OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: getCurrentPointIndex\n+ * \n+ * Returns:\n+ * {Number} The index of the most recently drawn point.\n+ */\n+ getCurrentPointIndex: function() {\n+ return this.line.geometry.components.length - 2;\n+ },\n+\n+ /**\n+ * Method: enforceTopology\n+ * Simple topology enforcement for drawing interior rings. Ensures vertices\n+ * of interior rings are contained by exterior ring. Other topology \n+ * rules are enforced in <finalizeInteriorRing> to allow drawing of \n+ * rings that intersect only during the sketch (e.g. a \"C\" shaped ring\n+ * that nearly encloses another ring).\n+ */\n+ enforceTopology: function(event) {\n+ var point = event.vertex;\n+ var components = this.line.geometry.components;\n+ // ensure that vertices of interior ring are contained by exterior ring\n+ if (!this.polygon.geometry.intersects(point)) {\n+ var last = components[components.length - 3];\n+ point.x = last.x;\n+ point.y = last.y;\n+ }\n+ },\n+\n+ /**\n+ * Method: finishGeometry\n+ * Finish the geometry and send it back to the control.\n+ */\n+ finishGeometry: function() {\n+ var index = this.line.geometry.components.length - 2;\n+ this.line.geometry.removeComponent(this.line.geometry.components[index]);\n+ this.removePoint();\n+ this.finalize();\n+ },\n+\n+ /**\n+ * Method: finalizeInteriorRing\n+ * Enforces that new ring has some area and doesn't contain vertices of any\n+ * other rings.\n+ */\n+ finalizeInteriorRing: function() {\n+ var ring = this.line.geometry;\n+ // ensure that ring has some area\n+ var modified = (ring.getArea() !== 0);\n+ if (modified) {\n+ // ensure that new ring doesn't intersect any other rings\n+ var rings = this.polygon.geometry.components;\n+ for (var i = rings.length - 2; i >= 0; --i) {\n+ if (ring.intersects(rings[i])) {\n+ modified = false;\n+ break;\n+ }\n+ }\n+ if (modified) {\n+ // ensure that new ring doesn't contain any other rings\n+ var target;\n+ outer: for (var i = rings.length - 2; i > 0; --i) {\n+ var points = rings[i].components;\n+ for (var j = 0, jj = points.length; j < jj; ++j) {\n+ if (ring.containsPoint(points[j])) {\n+ modified = false;\n+ break outer;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ if (modified) {\n+ if (this.polygon.state !== OpenLayers.State.INSERT) {\n+ this.polygon.state = OpenLayers.State.UPDATE;\n+ }\n+ } else {\n+ this.polygon.geometry.removeComponent(ring);\n+ }\n+ this.restoreFeature();\n+ return false;\n+ },\n+\n+ /**\n+ * APIMethod: cancel\n+ * Finish the geometry and call the \"cancel\" callback.\n+ */\n+ cancel: function() {\n+ if (this.drawingHole) {\n+ this.polygon.geometry.removeComponent(this.line.geometry);\n+ this.restoreFeature(true);\n+ }\n+ return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: restoreFeature\n+ * Move the feature from the sketch layer to the target layer.\n+ *\n+ * Properties: \n+ * cancel - {Boolean} Cancel drawing. If falsey, the \"sketchcomplete\" event\n+ * will be fired.\n+ */\n+ restoreFeature: function(cancel) {\n+ this.control.layer.events.unregister(\n+ \"sketchcomplete\", this, this.finalizeInteriorRing\n+ );\n+ this.control.layer.events.unregister(\n+ \"sketchmodified\", this, this.enforceTopology\n+ );\n+ this.layer.removeFeatures([this.polygon], {\n+ silent: true\n+ });\n+ this.control.layer.addFeatures([this.polygon], {\n+ silent: true\n+ });\n+ this.drawingHole = false;\n+ if (!cancel) {\n+ // Re-trigger \"sketchcomplete\" so other listeners can do their\n+ // business. While this is somewhat sloppy (if a listener is \n+ // registered with registerPriority - not common - between the start\n+ // and end of a single ring drawing - very uncommon - it will be \n+ // called twice).\n+ // TODO: In 3.0, collapse sketch handlers into geometry specific\n+ // drawing controls.\n+ this.control.layer.events.triggerEvent(\n+ \"sketchcomplete\", {\n+ feature: this.polygon\n+ }\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: destroyFeature\n+ * Destroy temporary geometries\n+ *\n+ * Parameters:\n+ * force - {Boolean} Destroy even if persist is true.\n+ */\n+ destroyFeature: function(force) {\n+ OpenLayers.Handler.Path.prototype.destroyFeature.call(\n+ this, force);\n+ this.polygon = null;\n+ },\n+\n+ /**\n+ * Method: drawFeature\n+ * Render geometries on the temporary layer.\n+ */\n+ drawFeature: function() {\n+ this.layer.drawFeature(this.polygon, this.style);\n+ this.layer.drawFeature(this.point, this.style);\n+ },\n+\n+ /**\n+ * Method: getSketch\n+ * Return the sketch feature.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ getSketch: function() {\n+ return this.polygon;\n+ },\n+\n+ /**\n+ * Method: getGeometry\n+ * Return the sketch geometry. If <multi> is true, this will return\n+ * a multi-part geometry.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Polygon>}\n+ */\n+ getGeometry: function() {\n+ var geometry = this.polygon && this.polygon.geometry;\n+ if (geometry && this.multi) {\n+ geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);\n+ }\n+ return geometry;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/RegularPolygon.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Handler/Drag.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.RegularPolygon\n+ * Handler to draw a regular polygon on the map. Polygon is displayed on mouse\n+ * down, moves or is modified on mouse move, and is finished on mouse up.\n+ * The handler triggers callbacks for 'done' and 'cancel'. Create a new\n+ * instance with the <OpenLayers.Handler.RegularPolygon> constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler.Drag>\n+ */\n+OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {\n+\n+ /**\n+ * APIProperty: sides\n+ * {Integer} Number of sides for the regular polygon. Needs to be greater\n+ * than 2. Defaults to 4.\n+ */\n+ sides: 4,\n+\n+ /**\n+ * APIProperty: radius\n+ * {Float} Optional radius in map units of the regular polygon. If this is\n+ * set to some non-zero value, a polygon with a fixed radius will be\n+ * drawn and dragged with mose movements. If this property is not\n+ * set, dragging changes the radius of the polygon. Set to null by\n+ * default.\n+ */\n+ radius: null,\n+\n+ /**\n+ * APIProperty: snapAngle\n+ * {Float} If set to a non-zero value, the handler will snap the polygon\n+ * rotation to multiples of the snapAngle. Value is an angle measured\n+ * in degrees counterclockwise from the positive x-axis. \n+ */\n+ snapAngle: null,\n+\n+ /**\n+ * APIProperty: snapToggle\n+ * {String} If set, snapToggle is checked on mouse events and will set\n+ * the snap mode to the opposite of what it currently is. To disallow\n+ * toggling between snap and non-snap mode, set freehandToggle to\n+ * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and\n+ * 'altKey'. Snap mode is only possible if this.snapAngle is set to a\n+ * non-zero value.\n+ */\n+ snapToggle: 'shiftKey',\n+\n+ /**\n+ * Property: layerOptions\n+ * {Object} Any optional properties to be set on the sketch layer.\n+ */\n+ layerOptions: null,\n+\n+ /**\n+ * APIProperty: persist\n+ * {Boolean} Leave the feature rendered until clear is called. Default\n+ * is false. If set to true, the feature remains rendered until\n+ * clear is called, typically by deactivating the handler or starting\n+ * another drawing.\n+ */\n+ persist: false,\n+\n+ /**\n+ * APIProperty: irregular\n+ * {Boolean} Draw an irregular polygon instead of a regular polygon.\n+ * Default is false. If true, the initial mouse down will represent\n+ * one corner of the polygon bounds and with each mouse movement, the\n+ * polygon will be stretched so the opposite corner of its bounds\n+ * follows the mouse position. This property takes precedence over\n+ * the radius property. If set to true, the radius property will\n+ * be ignored.\n+ */\n+ irregular: false,\n+\n+ /**\n+ * APIProperty: citeCompliant\n+ * {Boolean} If set to true, coordinates of features drawn in a map extent\n+ * crossing the date line won't exceed the world bounds. Default is false.\n+ */\n+ citeCompliant: false,\n+\n+ /**\n+ * Property: angle\n+ * {Float} The angle from the origin (mouse down) to the current mouse\n+ * position, in radians. This is measured counterclockwise from the\n+ * positive x-axis.\n+ */\n+ angle: null,\n+\n+ /**\n+ * Property: fixedRadius\n+ * {Boolean} The polygon has a fixed radius. True if a radius is set before\n+ * drawing begins. False otherwise.\n+ */\n+ fixedRadius: false,\n+\n+ /**\n+ * Property: feature\n+ * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature\n+ */\n+ feature: null,\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The temporary drawing layer\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: origin\n+ * {<OpenLayers.Geometry.Point>} Location of the first mouse down\n+ */\n+ origin: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.RegularPolygon\n+ * Create a new regular polygon handler.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that owns this handler\n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} An object with properties to be set on the handler.\n+ * If the options.sides property is not specified, the number of sides\n+ * will default to 4.\n+ *\n+ * Named callbacks:\n+ * create - Called when a sketch is first created. Callback called with\n+ * the creation point geometry and sketch feature.\n+ * done - Called when the sketch drawing is finished. The callback will\n+ * recieve a single argument, the sketch geometry.\n+ * cancel - Called when the handler is deactivated while drawing. The\n+ * cancel callback will receive a geometry.\n+ */\n+ initialize: function(control, callbacks, options) {\n+ if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n+ this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});\n+ }\n+\n+ OpenLayers.Handler.Drag.prototype.initialize.apply(this,\n+ [control, callbacks, options]);\n+ this.options = (options) ? options : {};\n+ },\n+\n+ /**\n+ * APIMethod: setOptions\n+ * \n+ * Parameters:\n+ * newOptions - {Object} \n+ */\n+ setOptions: function(newOptions) {\n+ OpenLayers.Util.extend(this.options, newOptions);\n+ OpenLayers.Util.extend(this, newOptions);\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ * Turn on the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully activated\n+ */\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {\n+ // create temporary vector layer for rendering geometry sketch\n+ var options = OpenLayers.Util.extend({\n+ displayInLayerSwitcher: false,\n+ // indicate that the temp vector layer will never be out of range\n+ // without this, resolution properties must be specified at the\n+ // map-level for this temporary layer to init its resolutions\n+ // correctly\n+ calculateInRange: OpenLayers.Function.True,\n+ wrapDateLine: this.citeCompliant\n+ }, this.layerOptions);\n+ this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n+ this.map.addLayer(this.layer);\n+ activated = true;\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * Turn off the handler.\n+ *\n+ * Returns:\n+ * {Boolean} The handler was successfully deactivated\n+ */\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {\n+ // call the cancel callback if mid-drawing\n+ if (this.dragging) {\n+ this.cancel();\n+ }\n+ // If a layer's map property is set to null, it means that that\n+ // layer isn't added to the map. Since we ourself added the layer\n+ // to the map in activate(), we can assume that if this.layer.map\n+ // is null it means that the layer has been destroyed (as a result\n+ // of map.destroy() for example.\n+ if (this.layer.map != null) {\n+ this.layer.destroy(false);\n+ if (this.feature) {\n+ this.feature.destroy();\n+ }\n+ }\n+ this.layer = null;\n+ this.feature = null;\n+ deactivated = true;\n+ }\n+ return deactivated;\n+ },\n+\n+ /**\n+ * Method: down\n+ * Start drawing a new feature\n+ *\n+ * Parameters:\n+ * evt - {Event} The drag start event\n+ */\n+ down: function(evt) {\n+ this.fixedRadius = !!(this.radius);\n+ var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n+ this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n+ // create the new polygon\n+ if (!this.fixedRadius || this.irregular) {\n+ // smallest radius should not be less one pixel in map units\n+ // VML doesn't behave well with smaller\n+ this.radius = this.map.getResolution();\n+ }\n+ if (this.persist) {\n+ this.clear();\n+ }\n+ this.feature = new OpenLayers.Feature.Vector();\n+ this.createGeometry();\n+ this.callback(\"create\", [this.origin, this.feature]);\n+ this.layer.addFeatures([this.feature], {\n+ silent: true\n+ });\n+ this.layer.drawFeature(this.feature, this.style);\n+ },\n+\n+ /**\n+ * Method: move\n+ * Respond to drag move events\n+ *\n+ * Parameters:\n+ * evt - {Evt} The move event\n+ */\n+ move: function(evt) {\n+ var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n+ var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n+ if (this.irregular) {\n+ var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;\n+ this.radius = Math.max(this.map.getResolution() / 2, ry);\n+ } else if (this.fixedRadius) {\n+ this.origin = point;\n+ } else {\n+ this.calculateAngle(point, evt);\n+ this.radius = Math.max(this.map.getResolution() / 2,\n+ point.distanceTo(this.origin));\n+ }\n+ this.modifyGeometry();\n+ if (this.irregular) {\n+ var dx = point.x - this.origin.x;\n+ var dy = point.y - this.origin.y;\n+ var ratio;\n+ if (dy == 0) {\n+ ratio = dx / (this.radius * Math.sqrt(2));\n+ } else {\n+ ratio = dx / dy;\n+ }\n+ this.feature.geometry.resize(1, this.origin, ratio);\n+ this.feature.geometry.move(dx / 2, dy / 2);\n+ }\n+ this.layer.drawFeature(this.feature, this.style);\n+ },\n+\n+ /**\n+ * Method: up\n+ * Finish drawing the feature\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse up event\n+ */\n+ up: function(evt) {\n+ this.finalize();\n+ // the mouseup method of superclass doesn't call the\n+ // \"done\" callback if there's been no move between\n+ // down and up\n+ if (this.start == this.last) {\n+ this.callback(\"done\", [evt.xy]);\n+ }\n+ },\n+\n+ /**\n+ * Method: out\n+ * Finish drawing the feature.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse out event\n+ */\n+ out: function(evt) {\n+ this.finalize();\n+ },\n+\n+ /**\n+ * Method: createGeometry\n+ * Create the new polygon geometry. This is called at the start of the\n+ * drag and at any point during the drag if the number of sides\n+ * changes.\n+ */\n+ createGeometry: function() {\n+ this.angle = Math.PI * ((1 / this.sides) - (1 / 2));\n+ if (this.snapAngle) {\n+ this.angle += this.snapAngle * (Math.PI / 180);\n+ }\n+ this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(\n+ this.origin, this.radius, this.sides, this.snapAngle\n+ );\n+ },\n+\n+ /**\n+ * Method: modifyGeometry\n+ * Modify the polygon geometry in place.\n+ */\n+ modifyGeometry: function() {\n+ var angle, point;\n+ var ring = this.feature.geometry.components[0];\n+ // if the number of sides ever changes, create a new geometry\n+ if (ring.components.length != (this.sides + 1)) {\n+ this.createGeometry();\n+ ring = this.feature.geometry.components[0];\n+ }\n+ for (var i = 0; i < this.sides; ++i) {\n+ point = ring.components[i];\n+ angle = this.angle + (i * 2 * Math.PI / this.sides);\n+ point.x = this.origin.x + (this.radius * Math.cos(angle));\n+ point.y = this.origin.y + (this.radius * Math.sin(angle));\n+ point.clearBounds();\n+ }\n+ },\n+\n+ /**\n+ * Method: calculateAngle\n+ * Calculate the angle based on settings.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ * evt - {Event}\n+ */\n+ calculateAngle: function(point, evt) {\n+ var alpha = Math.atan2(point.y - this.origin.y,\n+ point.x - this.origin.x);\n+ if (this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {\n+ var snapAngleRad = (Math.PI / 180) * this.snapAngle;\n+ this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;\n+ } else {\n+ this.angle = alpha;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: cancel\n+ * Finish the geometry and call the \"cancel\" callback.\n+ */\n+ cancel: function() {\n+ // the polygon geometry gets cloned in the callback method\n+ this.callback(\"cancel\", null);\n+ this.finalize();\n+ },\n+\n+ /**\n+ * Method: finalize\n+ * Finish the geometry and call the \"done\" callback.\n+ */\n+ finalize: function() {\n+ this.origin = null;\n+ this.radius = this.options.radius;\n+ },\n+\n+ /**\n+ * APIMethod: clear\n+ * Clear any rendered features on the temporary layer. This is called\n+ * when the handler is deactivated, canceled, or done (unless persist\n+ * is true).\n+ */\n+ clear: function() {\n+ if (this.layer) {\n+ this.layer.renderer.clear();\n+ this.layer.destroyFeatures();\n+ }\n+ },\n+\n+ /**\n+ * Method: callback\n+ * Trigger the control's named callback with the given arguments\n+ *\n+ * Parameters:\n+ * name - {String} The key for the callback that is one of the properties\n+ * of the handler's callbacks object.\n+ * args - {Array} An array of arguments with which to call the callback\n+ * (defined by the control).\n+ */\n+ callback: function(name, args) {\n+ // override the callback method to always send the polygon geometry\n+ if (this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control,\n+ [this.feature.geometry.clone()]);\n+ }\n+ // since sketch features are added to the temporary layer\n+ // they must be cleared here if done or cancel\n+ if (!this.persist && (name == \"done\" || name == \"cancel\")) {\n+ this.clear();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.RegularPolygon\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Measure.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Measure\n+ * Allows for drawing of features for measurements.\n *\n- * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * measure - Triggered when a measurement sketch is complete. Listeners\n+ * will receive an event with measure, units, order, and geometry\n+ * properties.\n+ * measurepartial - Triggered when a new point is added to the\n+ * measurement sketch or if the <immediate> property is true and the\n+ * measurement sketch is modified. Listeners receive an event with measure,\n+ * units, order, and geometry.\n+ */\n+\n+ /**\n+ * APIProperty: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n+ */\n+\n+ /**\n+ * Property: callbacks\n+ * {Object} The functions that are sent to the handler for callback\n+ */\n+ callbacks: null,\n+\n+ /**\n+ * APIProperty: displaySystem\n+ * {String} Display system for output measurements. Supported values\n+ * are 'english', 'metric', and 'geographic'. Default is 'metric'.\n+ */\n+ displaySystem: 'metric',\n+\n+ /**\n+ * APIProperty: geodesic\n+ * {Boolean} Calculate geodesic metrics instead of planar metrics. This\n+ * requires that geometries can be transformed into Geographic/WGS84\n+ * (if that is not already the map projection). Default is false.\n+ */\n+ geodesic: false,\n+\n+ /**\n+ * Property: displaySystemUnits\n+ * {Object} Units for various measurement systems. Values are arrays\n+ * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing\n+ * order of length.\n+ */\n+ displaySystemUnits: {\n+ geographic: ['dd'],\n+ english: ['mi', 'ft', 'in'],\n+ metric: ['km', 'm']\n+ },\n+\n+ /**\n+ * Property: delay\n+ * {Number} Number of milliseconds between clicks before the event is\n+ * considered a double-click. The \"measurepartial\" event will not\n+ * be triggered if the sketch is completed within this time. This\n+ * is required for IE where creating a browser reflow (if a listener\n+ * is modifying the DOM by displaying the measurement values) messes\n+ * with the dblclick listener in the sketch handler.\n+ */\n+ partialDelay: 300,\n+\n+ /**\n+ * Property: delayedTrigger\n+ * {Number} Timeout id of trigger for measurepartial.\n+ */\n+ delayedTrigger: null,\n+\n+ /**\n+ * APIProperty: persist\n+ * {Boolean} Keep the temporary measurement sketch drawn after the\n+ * measurement is complete. The geometry will persist until a new\n+ * measurement is started, the control is deactivated, or <cancel> is\n+ * called.\n+ */\n+ persist: false,\n+\n+ /**\n+ * APIProperty: immediate\n+ * {Boolean} Activates the immediate measurement so that the \"measurepartial\"\n+ * event is also fired once the measurement sketch is modified.\n+ * Default is false.\n+ */\n+ immediate: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Measure\n+ *\n+ * Parameters:\n+ * handler - {<OpenLayers.Handler>}\n+ * options - {Object}\n+ */\n+ initialize: function(handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ var callbacks = {\n+ done: this.measureComplete,\n+ point: this.measurePartial\n+ };\n+ if (this.immediate) {\n+ callbacks.modify = this.measureImmediate;\n+ }\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+\n+ // let the handler options override, so old code that passes 'persist'\n+ // directly to the handler does not need an update\n+ this.handlerOptions = OpenLayers.Util.extend({\n+ persist: this.persist\n+ }, this.handlerOptions);\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions);\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ */\n+ deactivate: function() {\n+ this.cancelDelay();\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ },\n+\n+ /**\n+ * APIMethod: cancel\n+ * Stop the control from measuring. If <persist> is true, the temporary\n+ * sketch will be erased.\n+ */\n+ cancel: function() {\n+ this.cancelDelay();\n+ this.handler.cancel();\n+ },\n+\n+ /**\n+ * APIMethod: setImmediate\n+ * Sets the <immediate> property. Changes the activity of immediate\n+ * measurement.\n+ */\n+ setImmediate: function(immediate) {\n+ this.immediate = immediate;\n+ if (this.immediate) {\n+ this.callbacks.modify = this.measureImmediate;\n+ } else {\n+ delete this.callbacks.modify;\n+ }\n+ },\n+\n+ /**\n+ * Method: updateHandler\n+ *\n+ * Parameters:\n+ * handler - {Function} One of the sketch handler constructors.\n+ * options - {Object} Options for the handler.\n+ */\n+ updateHandler: function(handler, options) {\n+ var active = this.active;\n+ if (active) {\n+ this.deactivate();\n+ }\n+ this.handler = new handler(this, this.callbacks, options);\n+ if (active) {\n+ this.activate();\n+ }\n+ },\n+\n+ /**\n+ * Method: measureComplete\n+ * Called when the measurement sketch is done.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ */\n+ measureComplete: function(geometry) {\n+ this.cancelDelay();\n+ this.measure(geometry, \"measure\");\n+ },\n+\n+ /**\n+ * Method: measurePartial\n+ * Called each time a new point is added to the measurement sketch.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} The last point added.\n+ * geometry - {<OpenLayers.Geometry>} The sketch geometry.\n+ */\n+ measurePartial: function(point, geometry) {\n+ this.cancelDelay();\n+ geometry = geometry.clone();\n+ // when we're wating for a dblclick, we have to trigger measurepartial\n+ // after some delay to deal with reflow issues in IE\n+ if (this.handler.freehandMode(this.handler.evt)) {\n+ // no dblclick in freehand mode\n+ this.measure(geometry, \"measurepartial\");\n+ } else {\n+ this.delayedTrigger = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.delayedTrigger = null;\n+ this.measure(geometry, \"measurepartial\");\n+ }, this),\n+ this.partialDelay\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: measureImmediate\n+ * Called each time the measurement sketch is modified.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} The point at the mouse position.\n+ * feature - {<OpenLayers.Feature.Vector>} The sketch feature.\n+ * drawing - {Boolean} Indicates whether we're currently drawing.\n+ */\n+ measureImmediate: function(point, feature, drawing) {\n+ if (drawing && !this.handler.freehandMode(this.handler.evt)) {\n+ this.cancelDelay();\n+ this.measure(feature.geometry, \"measurepartial\");\n+ }\n+ },\n+\n+ /**\n+ * Method: cancelDelay\n+ * Cancels the delay measurement that measurePartial began.\n+ */\n+ cancelDelay: function() {\n+ if (this.delayedTrigger !== null) {\n+ window.clearTimeout(this.delayedTrigger);\n+ this.delayedTrigger = null;\n+ }\n+ },\n+\n+ /**\n+ * Method: measure\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * eventType - {String}\n+ */\n+ measure: function(geometry, eventType) {\n+ var stat, order;\n+ if (geometry.CLASS_NAME.indexOf('LineString') > -1) {\n+ stat = this.getBestLength(geometry);\n+ order = 1;\n+ } else {\n+ stat = this.getBestArea(geometry);\n+ order = 2;\n+ }\n+ this.events.triggerEvent(eventType, {\n+ measure: stat[0],\n+ units: stat[1],\n+ order: order,\n+ geometry: geometry\n+ });\n+ },\n+\n+ /**\n+ * Method: getBestArea\n+ * Based on the <displaySystem> returns the area of a geometry.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {Array([Float, String])} Returns a two item array containing the\n+ * area and the units abbreviation.\n+ */\n+ getBestArea: function(geometry) {\n+ var units = this.displaySystemUnits[this.displaySystem];\n+ var unit, area;\n+ for (var i = 0, len = units.length; i < len; ++i) {\n+ unit = units[i];\n+ area = this.getArea(geometry, unit);\n+ if (area > 1) {\n+ break;\n+ }\n+ }\n+ return [area, unit];\n+ },\n+\n+ /**\n+ * Method: getArea\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * units - {String} Unit abbreviation\n+ *\n+ * Returns:\n+ * {Float} The geometry area in the given units.\n+ */\n+ getArea: function(geometry, units) {\n+ var area, geomUnits;\n+ if (this.geodesic) {\n+ area = geometry.getGeodesicArea(this.map.getProjectionObject());\n+ geomUnits = \"m\";\n+ } else {\n+ area = geometry.getArea();\n+ geomUnits = this.map.getUnits();\n+ }\n+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n+ if (inPerDisplayUnit) {\n+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n+ area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);\n+ }\n+ return area;\n+ },\n+\n+ /**\n+ * Method: getBestLength\n+ * Based on the <displaySystem> returns the length of a geometry.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {Array([Float, String])} Returns a two item array containing the\n+ * length and the units abbreviation.\n+ */\n+ getBestLength: function(geometry) {\n+ var units = this.displaySystemUnits[this.displaySystem];\n+ var unit, length;\n+ for (var i = 0, len = units.length; i < len; ++i) {\n+ unit = units[i];\n+ length = this.getLength(geometry, unit);\n+ if (length > 1) {\n+ break;\n+ }\n+ }\n+ return [length, unit];\n+ },\n+\n+ /**\n+ * Method: getLength\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * units - {String} Unit abbreviation\n+ *\n+ * Returns:\n+ * {Float} The geometry length in the given units.\n+ */\n+ getLength: function(geometry, units) {\n+ var length, geomUnits;\n+ if (this.geodesic) {\n+ length = geometry.getGeodesicLength(this.map.getProjectionObject());\n+ geomUnits = \"m\";\n+ } else {\n+ length = geometry.getLength();\n+ geomUnits = this.map.getUnits();\n+ }\n+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n+ if (inPerDisplayUnit) {\n+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n+ length *= (inPerMapUnit / inPerDisplayUnit);\n+ }\n+ return length;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Measure\"\n+});\n+/* ======================================================================\n+ OpenLayers/Events/buttonclick.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Events.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Events.buttonclick\n+ * Extension event type for handling buttons on top of a dom element. This\n+ * event type fires \"buttonclick\" on its <target> when a button was\n+ * clicked. Buttons are detected by the \"olButton\" class.\n+ *\n+ * This event type makes sure that button clicks do not interfere with other\n+ * events that are registered on the same <element>.\n+ *\n+ * Event types provided by this extension:\n+ * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n+ * object with a *buttonElement* property referencing the dom element of\n+ * the clicked button, and an *buttonXY* property with the click position\n+ * relative to the button.\n+ */\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n+\n+ /**\n+ * Property: target\n+ * {<OpenLayers.Events>} The events instance that the buttonclick event will\n+ * be triggered on.\n+ */\n+ target: null,\n+\n+ /**\n+ * Property: events\n+ * {Array} Events to observe and conditionally stop from propagating when\n+ * an element with the olButton class (or its olAlphaImg child) is\n+ * clicked.\n+ */\n+ events: [\n+ 'mousedown', 'mouseup', 'click', 'dblclick',\n+ 'touchstart', 'touchmove', 'touchend', 'keydown'\n+ ],\n+\n+ /**\n+ * Property: startRegEx\n+ * {RegExp} Regular expression to test Event.type for events that start\n+ * a buttonclick sequence.\n+ */\n+ startRegEx: /^mousedown|touchstart$/,\n+\n+ /**\n+ * Property: cancelRegEx\n+ * {RegExp} Regular expression to test Event.type for events that cancel\n+ * a buttonclick sequence.\n+ */\n+ cancelRegEx: /^touchmove$/,\n+\n+ /**\n+ * Property: completeRegEx\n+ * {RegExp} Regular expression to test Event.type for events that complete\n+ * a buttonclick sequence.\n+ */\n+ completeRegEx: /^mouseup|touchend$/,\n+\n+ /**\n+ * Property: startEvt\n+ * {Event} The event that started the click sequence\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Events.buttonclick\n+ * Construct a buttonclick event type. Applications are not supposed to\n+ * create instances of this class - they are created on demand by\n+ * <OpenLayers.Events> instances.\n+ *\n+ * Parameters:\n+ * target - {<OpenLayers.Events>} The events instance that the buttonclick\n+ * event will be triggered on.\n+ */\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick);\n+ }\n+ delete this.target;\n+ },\n+\n+ /**\n+ * Method: getPressedButton\n+ * Get the pressed button, if any. Returns undefined if no button\n+ * was pressed.\n+ *\n+ * Arguments:\n+ * element - {DOMElement} The event target.\n+ *\n+ * Returns:\n+ * {DOMElement} The button element, or undefined.\n+ */\n+ getPressedButton: function(element) {\n+ var depth = 3, // limit the search depth\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ // hit!\n+ button = element;\n+ break;\n+ }\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return button;\n+ },\n+\n+ /**\n+ * Method: ignore\n+ * Check for event target elements that should be ignored by OpenLayers.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The event target.\n+ */\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === 'a') {\n+ ignore = true;\n+ break;\n+ }\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return ignore;\n+ },\n+\n+ /**\n+ * Method: buttonClick\n+ * Check if a button was clicked, and fire the buttonclick event\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ // was a button pressed?\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break;\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ });\n+ }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt;\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ }\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt;\n+ }\n+ }\n+ return propagate;\n+ }\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Panel.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Panel\n+ * The Panel control is a container for other controls. With it toolbars\n+ * may be composed.\n *\n * Inherits from:\n- * - <OpenLayers.Handler>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n+ /**\n+ * Property: controls\n+ * {Array(<OpenLayers.Control>)}\n+ */\n+ controls: null,\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n \n /** \n- * Property: started\n- * {Boolean} When a mousedown or touchstart event is received, we want to\n- * record it, but not set 'dragging' until the mouse moves after starting.\n+ * APIProperty: defaultControl\n+ * {<OpenLayers.Control>} The control which is activated when the control is\n+ * activated (turned on), which also happens at instantiation.\n+ * If <saveState> is true, <defaultControl> will be nullified after the\n+ * first activation of the panel.\n */\n- started: false,\n+ defaultControl: null,\n \n /**\n- * Property: stopDown\n- * {Boolean} Stop propagation of mousedown events from getting to listeners\n- * on the same element. Default is true.\n+ * APIProperty: saveState\n+ * {Boolean} If set to true, the active state of this panel's controls will\n+ * be stored on panel deactivation, and restored on reactivation. Default\n+ * is false.\n+ */\n+ saveState: false,\n+\n+ /**\n+ * APIProperty: allowDepress\n+ * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n+ * be deactivated by clicking the icon that represents them. Default \n+ * is false.\n+ */\n+ allowDepress: false,\n+\n+ /**\n+ * Property: activeState\n+ * {Object} stores the active state of this panel's controls.\n+ */\n+ activeState: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Panel\n+ * Create a new control panel.\n+ *\n+ * Each control in the panel is represented by an icon. When clicking \n+ * on an icon, the <activateControl> method is called.\n+ *\n+ * Specific properties for controls on a panel:\n+ * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n+ * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n+ * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n+ * title - {string} Text displayed when mouse is over the icon that \n+ * represents the control. \n+ *\n+ * The <OpenLayers.Control.type> of a control determines the behavior when\n+ * clicking its icon:\n+ * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n+ * controls of this type in the same panel are deactivated. This is\n+ * the default type.\n+ * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n+ * toggled.\n+ * <OpenLayers.Control.TYPE_BUTTON> - The\n+ * <OpenLayers.Control.Button.trigger> method of the control is called,\n+ * but its active state is not changed.\n+ *\n+ * If a control is <OpenLayers.Control.active>, it will be drawn with the\n+ * olControl[Name]ItemActive class, otherwise with the\n+ * olControl[Name]ItemInactive class.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.controls = [];\n+ this.activeState = {};\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n+ ctl = this.controls[i];\n+ if (ctl.events) {\n+ ctl.events.un({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n+ }\n+ ctl.panel_div = null;\n+ }\n+ this.activeState = null;\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ */\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ if (control === this.defaultControl ||\n+ (this.saveState && this.activeState[control.id])) {\n+ control.activate();\n+ }\n+ }\n+ if (this.saveState === true) {\n+ this.defaultControl = null;\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ */\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ this.activeState[control.id] = control.deactivate();\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: draw\n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ }\n+ this.addControlsToMap(this.controls);\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: redraw\n+ */\n+ redraw: function() {\n+ for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n+ this.div.removeChild(this.div.childNodes[i]);\n+ }\n+ this.div.innerHTML = \"\";\n+ if (this.active) {\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.div.appendChild(this.controls[i].panel_div);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: activateControl\n+ * This method is called when the user click on the icon representing a \n+ * control in the panel.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>}\n+ */\n+ activateControl: function(control) {\n+ if (!this.active) {\n+ return false;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n+ control.trigger();\n+ return;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n+ if (control.active) {\n+ control.deactivate();\n+ } else {\n+ control.activate();\n+ }\n+ return;\n+ }\n+ if (this.allowDepress && control.active) {\n+ control.deactivate();\n+ } else {\n+ var c;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ c = this.controls[i];\n+ if (c != control &&\n+ (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n+ c.deactivate();\n+ }\n+ }\n+ control.activate();\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: addControls\n+ * To build a toolbar, you add a set of controls to it. addControls\n+ * lets you add a single control or a list of controls to the \n+ * Control Panel.\n+ *\n+ * Parameters:\n+ * controls - {<OpenLayers.Control>} Controls to add in the panel.\n+ */\n+ addControls: function(controls) {\n+ if (!(OpenLayers.Util.isArray(controls))) {\n+ controls = [controls];\n+ }\n+ this.controls = this.controls.concat(controls);\n+\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var control = controls[i],\n+ element = this.createControlMarkup(control);\n+ OpenLayers.Element.addClass(element,\n+ control.displayClass + \"ItemInactive\");\n+ OpenLayers.Element.addClass(element, \"olButton\");\n+ if (control.title != \"\" && !element.title) {\n+ element.title = control.title;\n+ }\n+ control.panel_div = element;\n+ }\n+\n+ if (this.map) { // map.addControl() has already been called on the panel\n+ this.addControlsToMap(controls);\n+ this.redraw();\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: createControlMarkup\n+ * This function just creates a div for the control. If specific HTML\n+ * markup is needed this function can be overridden in specific classes,\n+ * or at panel instantiation time:\n+ *\n+ * Example:\n+ * (code)\n+ * var panel = new OpenLayers.Control.Panel({\n+ * defaultControl: control,\n+ * // ovverride createControlMarkup to create actual buttons\n+ * // including texts wrapped into span elements.\n+ * createControlMarkup: function(control) {\n+ * var button = document.createElement('button'),\n+ * span = document.createElement('span');\n+ * if (control.text) {\n+ * span.innerHTML = control.text;\n+ * }\n+ * return button;\n+ * }\n+ * });\n+ * (end)\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control to create the HTML\n+ * markup for.\n+ *\n+ * Returns:\n+ * {DOMElement} The markup.\n+ */\n+ createControlMarkup: function(control) {\n+ return document.createElement(\"div\");\n+ },\n+\n+ /**\n+ * Method: addControlsToMap\n+ * Only for internal use in draw() and addControls() methods.\n+ *\n+ * Parameters:\n+ * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n+ */\n+ addControlsToMap: function(controls) {\n+ var control;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ control = controls[i];\n+ if (control.autoActivate === true) {\n+ control.autoActivate = false;\n+ this.map.addControl(control);\n+ control.autoActivate = true;\n+ } else {\n+ this.map.addControl(control);\n+ control.deactivate();\n+ }\n+ control.events.on({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: iconOn\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOn: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n+ d.className = d.className.replace(re, \"$1Active\");\n+ },\n+\n+ /**\n+ * Method: iconOff\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOff: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n+ d.className = d.className.replace(re, \"$1Inactive\");\n+ },\n+\n+ /**\n+ * Method: onButtonClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ onButtonClick: function(evt) {\n+ var controls = this.controls,\n+ button = evt.buttonElement;\n+ for (var i = controls.length - 1; i >= 0; --i) {\n+ if (controls[i].panel_div === button) {\n+ this.activateControl(controls[i]);\n+ break;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: getControlsBy\n+ * Get a list of controls with properties matching the given criteria.\n+ *\n+ * Parameters:\n+ * property - {String} A control property to be matched.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(control[property]) evaluates to true, the control will be\n+ * included in the array returned. If no controls are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsBy: function(property, match) {\n+ var test = (typeof match.test == \"function\");\n+ var found = OpenLayers.Array.filter(this.controls, function(item) {\n+ return item[property] == match || (test && match.test(item[property]));\n+ });\n+ return found;\n+ },\n+\n+ /**\n+ * APIMethod: getControlsByName\n+ * Get a list of contorls with names matching the given name.\n+ *\n+ * Parameters:\n+ * match - {String | Object} A control name. The name can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * name.test(control.name) evaluates to true, the control will be included\n+ * in the list of controls returned. If no controls are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsByName: function(match) {\n+ return this.getControlsBy(\"name\", match);\n+ },\n+\n+ /**\n+ * APIMethod: getControlsByClass\n+ * Get a list of controls of a given type (CLASS_NAME).\n+ *\n+ * Parameters:\n+ * match - {String | Object} A control class name. The type can also be a\n+ * regular expression literal or object. In addition, it can be any\n+ * object with a method named test. For reqular expressions or other,\n+ * if type.test(control.CLASS_NAME) evaluates to true, the control will\n+ * be included in the list of controls returned. If no controls are\n+ * found, an empty array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Panel\"\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Control/Button.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Button \n+ * The Button control is a very simple push-button, for use with \n+ * <OpenLayers.Control.Panel>.\n+ * When clicked, the function trigger() is executed.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ *\n+ * Use:\n+ * (code)\n+ * var button = new OpenLayers.Control.Button({\n+ * displayClass: \"MyButton\", trigger: myFunction\n+ * });\n+ * panel.addControls([button]);\n+ * (end)\n+ * \n+ * Will create a button with CSS class MyButtonItemInactive, that\n+ * will call the function MyFunction() when clicked.\n+ */\n+OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {\n+ /**\n+ * Property: type\n+ * {Integer} OpenLayers.Control.TYPE_BUTTON.\n+ */\n+ type: OpenLayers.Control.TYPE_BUTTON,\n+\n+ /**\n+ * Method: trigger\n+ * Called by a control panel when the button is clicked.\n+ */\n+ trigger: function() {},\n+\n+ CLASS_NAME: \"OpenLayers.Control.Button\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/ZoomIn.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control/Button.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.ZoomIn\n+ * The ZoomIn control is a button to increase the zoom level of a map.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {\n+\n+ /**\n+ * Method: trigger\n+ */\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomIn();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.ZoomIn\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/ZoomOut.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control/Button.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.ZoomOut\n+ * The ZoomOut control is a button to decrease the zoom level of a map.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {\n+\n+ /**\n+ * Method: trigger\n+ */\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomOut();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.ZoomOut\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/ZoomToMaxExtent.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control/Button.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.ZoomToMaxExtent \n+ * The ZoomToMaxExtent control is a button that zooms out to the maximum\n+ * extent of the map. It is designed to be used with a \n+ * <OpenLayers.Control.Panel>.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {\n+\n+ /**\n+ * Method: trigger\n+ * \n+ * Called whenever this control is being rendered inside of a panel and a \n+ * click occurs on this controls element. Actually zooms to the maximum\n+ * extent of this controls map.\n+ */\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomToMaxExtent();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.ZoomToMaxExtent\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/ZoomPanel.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control/Panel.js\n+ * @requires OpenLayers/Control/ZoomIn.js\n+ * @requires OpenLayers/Control/ZoomOut.js\n+ * @requires OpenLayers/Control/ZoomToMaxExtent.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.ZoomPanel\n+ * The ZoomPanel control is a compact collecton of 3 zoom controls: a \n+ * <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a\n+ * <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left \n+ * corner of the map.\n+ *\n+ * Note: \n+ * If you wish to use this class with the default images and you want \n+ * it to look nice in ie6, you should add the following, conditionally\n+ * added css stylesheet to your HTML file:\n+ * \n+ * (code)\n+ * <!--[if lte IE 6]>\n+ * <link rel=\"stylesheet\" href=\"../theme/default/ie6-style.css\" type=\"text/css\" />\n+ * <![endif]-->\n+ * (end)\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Control.Panel>\n+ */\n+OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n+\n+ /**\n+ * Constructor: OpenLayers.Control.ZoomPanel \n+ * Add the three zooming controls.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ this.addControls([\n+ new OpenLayers.Control.ZoomIn(),\n+ new OpenLayers.Control.ZoomToMaxExtent(),\n+ new OpenLayers.Control.ZoomOut()\n+ ]);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.ZoomPanel\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/UTFGrid.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Hover.js\n+ * @requires OpenLayers/Handler/Click.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.UTFGrid\n+ *\n+ * This Control provides behavior associated with UTFGrid Layers.\n+ * These 'hit grids' provide underlying feature attributes without\n+ * calling the server (again). This control allows Mousemove, Hovering \n+ * and Click events to trigger callbacks that use the attributes in \n+ * whatever way you need. \n+ *\n+ * The most common example may be a UTFGrid layer containing feature\n+ * attributes that are displayed in a div as you mouseover.\n+ *\n+ * Example Code:\n+ *\n+ * (start code)\n+ * var world_utfgrid = new OpenLayers.Layer.UTFGrid( \n+ * 'UTFGrid Layer', \n+ * \"http://tiles/world_utfgrid/${z}/${x}/${y}.json\"\n+ * );\n+ * map.addLayer(world_utfgrid);\n+ * \n+ * var control = new OpenLayers.Control.UTFGrid({\n+ * layers: [world_utfgrid],\n+ * handlerMode: 'move',\n+ * callback: function(infoLookup) {\n+ * // do something with returned data\n+ *\n+ * }\n+ * })\n+ * (end code)\n+ *\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n+\n+ /** \n+ * APIProperty: Layers\n+ * List of layers to consider. Must be Layer.UTFGrids\n+ * `null` is the default indicating all UTFGrid Layers are queried.\n+ * {Array} <OpenLayers.Layer.UTFGrid> \n+ */\n+ layers: null,\n+\n+ /* Property: defaultHandlerOptions\n+ * The default opts passed to the handler constructors\n+ */\n+ defaultHandlerOptions: {\n+ 'delay': 300,\n+ 'pixelTolerance': 4,\n+ 'stopMove': false,\n+ 'single': true,\n+ 'double': false,\n+ 'stopSingle': false,\n+ 'stopDouble': false\n+ },\n+\n+ /* APIProperty: handlerMode\n+ * Defaults to 'click'. Can be 'hover' or 'move'.\n+ */\n+ handlerMode: 'click',\n+\n+ /**\n+ * APIMethod: setHandler\n+ * sets this.handlerMode and calls resetHandler()\n+ *\n+ * Parameters:\n+ * hm - {String} Handler Mode string; 'click', 'hover' or 'move'.\n+ */\n+ setHandler: function(hm) {\n+ this.handlerMode = hm;\n+ this.resetHandler();\n+ },\n+\n+ /**\n+ * Method: resetHandler\n+ * Deactivates the old hanlder and creates a new\n+ * <OpenLayers.Handler> based on the mode specified in\n+ * this.handlerMode\n+ *\n+ */\n+ resetHandler: function() {\n+ if (this.handler) {\n+ this.handler.deactivate();\n+ this.handler.destroy();\n+ this.handler = null;\n+ }\n+\n+ if (this.handlerMode == 'hover') {\n+ // Handle this event on hover\n+ this.handler = new OpenLayers.Handler.Hover(\n+ this, {\n+ 'pause': this.handleEvent,\n+ 'move': this.reset\n+ },\n+ this.handlerOptions\n+ );\n+ } else if (this.handlerMode == 'click') {\n+ // Handle this event on click\n+ this.handler = new OpenLayers.Handler.Click(\n+ this, {\n+ 'click': this.handleEvent\n+ }, this.handlerOptions\n+ );\n+ } else if (this.handlerMode == 'move') {\n+ this.handler = new OpenLayers.Handler.Hover(\n+ this,\n+ // Handle this event while hovering OR moving\n+ {\n+ 'pause': this.handleEvent,\n+ 'move': this.handleEvent\n+ },\n+ this.handlerOptions\n+ );\n+ }\n+ if (this.handler) {\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Constructor: <OpenLayers.Control.UTFGrid>\n+ *\n+ * Parameters:\n+ * options - {Object} \n+ */\n+ initialize: function(options) {\n+ options = options || {};\n+ options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.resetHandler();\n+ },\n+\n+ /**\n+ * Method: handleEvent\n+ * Internal method called when specified event is triggered.\n+ * \n+ * This method does several things:\n+ *\n+ * Gets the lonLat of the event.\n+ *\n+ * Loops through the appropriate hit grid layers and gathers the attributes.\n+ *\n+ * Passes the attributes to the callback\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n+ */\n+ handleEvent: function(evt) {\n+ if (evt == null) {\n+ this.reset();\n+ return;\n+ }\n+\n+ var lonLat = this.map.getLonLatFromPixel(evt.xy);\n+ if (!lonLat) {\n+ return;\n+ }\n+\n+ var layers = this.findLayers();\n+ if (layers.length > 0) {\n+ var infoLookup = {};\n+ var layer, idx;\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ layer = layers[i];\n+ idx = OpenLayers.Util.indexOf(this.map.layers, layer);\n+ infoLookup[idx] = layer.getFeatureInfo(lonLat);\n+ }\n+ this.callback(infoLookup, lonLat, evt.xy);\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: callback\n+ * Function to be called when a mouse event corresponds with a location that\n+ * includes data in one of the configured UTFGrid layers.\n+ *\n+ * Parameters:\n+ * infoLookup - {Object} Keys of this object are layer indexes and can be\n+ * used to resolve a layer in the map.layers array. The structure of\n+ * the property values depend on the data included in the underlying\n+ * UTFGrid and may be any valid JSON type. \n+ */\n+ callback: function(infoLookup) {\n+ // to be provided in the constructor\n+ },\n+\n+ /**\n+ * Method: reset\n+ * Calls the callback with null.\n+ */\n+ reset: function(evt) {\n+ this.callback(null);\n+ },\n+\n+ /**\n+ * Method: findLayers\n+ * Internal method to get the layers, independent of whether we are\n+ * inspecting the map or using a client-provided array\n+ *\n+ * The default value of this.layers is null; this causes the \n+ * findLayers method to return ALL UTFGrid layers encountered.\n+ *\n+ * Parameters:\n+ * None\n+ *\n+ * Returns:\n+ * {Array} Layers to handle on each event\n+ */\n+ findLayers: function() {\n+ var candidates = this.layers || this.map.layers;\n+ var layers = [];\n+ var layer;\n+ for (var i = candidates.length - 1; i >= 0; --i) {\n+ layer = candidates[i];\n+ if (layer instanceof OpenLayers.Layer.UTFGrid) {\n+ layers.push(layer);\n+ }\n+ }\n+ return layers;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.UTFGrid\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Vector.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/StyleMap.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Lang.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector\n+ * Instances of OpenLayers.Layer.Vector are used to render vector data from\n+ * a variety of sources. Create a new vector layer with the\n+ * <OpenLayers.Layer.Vector> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer>\n+ */\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n+ * beforefeatureadded - Triggered before a feature is added. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be added. To stop the feature from being added, a\n+ * listener should return false.\n+ * beforefeaturesadded - Triggered before an array of features is added.\n+ * Listeners will receive an object with a *features* property\n+ * referencing the feature to be added. To stop the features from\n+ * being added, a listener should return false.\n+ * featureadded - Triggered after a feature is added. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the added feature.\n+ * featuresadded - Triggered after features are added. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of added features.\n+ * beforefeatureremoved - Triggered before a feature is removed. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be removed.\n+ * beforefeaturesremoved - Triggered before multiple features are removed. \n+ * Listeners will receive an object with a *features* property\n+ * referencing the features to be removed.\n+ * featureremoved - Triggerd after a feature is removed. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the removed feature.\n+ * featuresremoved - Triggered after features are removed. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of removed features.\n+ * beforefeatureselected - Triggered before a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be selected. To stop the feature from being selectd, a\n+ * listener should return false.\n+ * featureselected - Triggered after a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * selected feature.\n+ * featureunselected - Triggered after a feature is unselected.\n+ * Listeners will receive an object with a *feature* property\n+ * referencing the unselected feature.\n+ * beforefeaturemodified - Triggered when a feature is selected to \n+ * be modified. Listeners will receive an object with a *feature* \n+ * property referencing the selected feature.\n+ * featuremodified - Triggered when a feature has been modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * afterfeaturemodified - Triggered when a feature is finished being modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * vertexmodified - Triggered when a vertex within any feature geometry\n+ * has been modified. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * modification.\n+ * vertexremoved - Triggered when a vertex within any feature geometry\n+ * has been deleted. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * removal.\n+ * sketchstarted - Triggered when a feature sketch bound for this layer\n+ * is started. Listeners will receive an object with a *feature*\n+ * property referencing the new sketch feature and a *vertex* property\n+ * referencing the creation point.\n+ * sketchmodified - Triggered when a feature sketch bound for this layer\n+ * is modified. Listeners will receive an object with a *vertex*\n+ * property referencing the modified vertex and a *feature* property\n+ * referencing the sketch feature.\n+ * sketchcomplete - Triggered when a feature sketch bound for this layer\n+ * is complete. Listeners will receive an object with a *feature*\n+ * property referencing the sketch feature. By returning false, a\n+ * listener can stop the sketch feature from being added to the layer.\n+ * refresh - Triggered when something wants a strategy to ask the protocol\n+ * for a new set of features.\n+ */\n+\n+ /**\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is false. Set this property\n+ * in the layer options.\n+ */\n+ isBaseLayer: false,\n+\n+ /** \n+ * APIProperty: isFixed\n+ * {Boolean} Whether the layer remains in one place while dragging the\n+ * map. Note that setting this to true will move the layer to the bottom\n+ * of the layer stack.\n+ */\n+ isFixed: false,\n+\n+ /** \n+ * APIProperty: features\n+ * {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ features: null,\n+\n+ /** \n+ * Property: filter\n+ * {<OpenLayers.Filter>} The filter set in this layer,\n+ * a strategy launching read requests can combined\n+ * this filter with its own filter.\n+ */\n+ filter: null,\n+\n+ /** \n+ * Property: selectedFeatures\n+ * {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ selectedFeatures: null,\n+\n+ /**\n+ * Property: unrenderedFeatures\n+ * {Object} hash of features, keyed by feature.id, that the renderer\n+ * failed to draw\n+ */\n+ unrenderedFeatures: null,\n+\n+ /**\n+ * APIProperty: reportError\n+ * {Boolean} report friendly error message when loading of renderer\n+ * fails.\n+ */\n+ reportError: true,\n+\n+ /** \n+ * APIProperty: style\n+ * {Object} Default style for the layer\n+ */\n+ style: null,\n+\n+ /**\n+ * Property: styleMap\n+ * {<OpenLayers.StyleMap>}\n+ */\n+ styleMap: null,\n+\n+ /**\n+ * Property: strategies\n+ * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n+ */\n+ strategies: null,\n+\n+ /**\n+ * Property: protocol\n+ * {<OpenLayers.Protocol>} Optional protocol for the layer.\n+ */\n+ protocol: null,\n+\n+ /**\n+ * Property: renderers\n+ * {Array(String)} List of supported Renderer classes. Add to this list to\n+ * add support for additional renderers. This list is ordered:\n+ * the first renderer which returns true for the 'supported()'\n+ * method will be used, if not defined in the 'renderer' option.\n+ */\n+ renderers: ['SVG', 'VML', 'Canvas'],\n+\n+ /** \n+ * Property: renderer\n+ * {<OpenLayers.Renderer>}\n+ */\n+ renderer: null,\n+\n+ /**\n+ * APIProperty: rendererOptions\n+ * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n+ * supported options.\n+ */\n+ rendererOptions: null,\n+\n+ /** \n+ * APIProperty: geometryType\n+ * {String} geometryType allows you to limit the types of geometries this\n+ * layer supports. This should be set to something like\n+ * \"OpenLayers.Geometry.Point\" to limit types.\n+ */\n+ geometryType: null,\n+\n+ /** \n+ * Property: drawn\n+ * {Boolean} Whether the Vector Layer features have been drawn yet.\n+ */\n+ drawn: false,\n+\n+ /** \n+ * APIProperty: ratio\n+ * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n+ */\n+ ratio: 1,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Vector\n+ * Create a new vector layer\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} A new vector layer\n+ */\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+\n+ // allow user-set renderer, otherwise assign one\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.assignRenderer();\n+ }\n+\n+ // if no valid renderer found, display error\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.renderer = null;\n+ this.displayError();\n+ }\n+\n+ if (!this.styleMap) {\n+ this.styleMap = new OpenLayers.StyleMap();\n+ }\n+\n+ this.features = [];\n+ this.selectedFeatures = [];\n+ this.unrenderedFeatures = {};\n+\n+ // Allow for custom layer behavior\n+ if (this.strategies) {\n+ for (var i = 0, len = this.strategies.length; i < len; i++) {\n+ this.strategies[i].setLayer(this);\n+ }\n+ }\n+\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Destroy this layer\n+ */\n+ destroy: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoDestroy) {\n+ strategy.destroy();\n+ }\n+ }\n+ this.strategies = null;\n+ }\n+ if (this.protocol) {\n+ if (this.protocol.autoDestroy) {\n+ this.protocol.destroy();\n+ }\n+ this.protocol = null;\n+ }\n+ this.destroyFeatures();\n+ this.features = null;\n+ this.selectedFeatures = null;\n+ this.unrenderedFeatures = null;\n+ if (this.renderer) {\n+ this.renderer.destroy();\n+ }\n+ this.renderer = null;\n+ this.geometryType = null;\n+ this.drawn = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer.\n+ * \n+ * Note: Features of the layer are also cloned.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+ var features = this.features;\n+ var len = features.length;\n+ var clonedFeatures = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ clonedFeatures[i] = features[i].clone();\n+ }\n+ obj.features = clonedFeatures;\n+\n+ return obj;\n+ },\n+\n+ /**\n+ * Method: refresh\n+ * Ask the layer to request features again and redraw them. Triggers\n+ * the refresh event if the layer is in range and visible.\n+ *\n+ * Parameters:\n+ * obj - {Object} Optional object with properties for any listener of\n+ * the refresh event.\n */\n- stopDown: true,\n+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj);\n+ }\n+ },\n \n /** \n- * Property: dragging \n- * {Boolean} \n+ * Method: assignRenderer\n+ * Iterates through the available renderer implementations and selects \n+ * and assigns the first one whose \"supported()\" function returns true.\n */\n- dragging: false,\n+ assignRenderer: function() {\n+ for (var i = 0, len = this.renderers.length; i < len; i++) {\n+ var rendererClass = this.renderers[i];\n+ var renderer = (typeof rendererClass == \"function\") ?\n+ rendererClass :\n+ OpenLayers.Renderer[rendererClass];\n+ if (renderer && renderer.prototype.supported()) {\n+ this.renderer = new renderer(this.div, this.rendererOptions);\n+ break;\n+ }\n+ }\n+ },\n \n /** \n- * Property: last\n- * {<OpenLayers.Pixel>} The last pixel location of the drag.\n+ * Method: displayError \n+ * Let the user know their browser isn't supported.\n */\n- last: null,\n+ displayError: function() {\n+ if (this.reportError) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n+ renderers: this.renderers.join('\\n')\n+ }));\n+ }\n+ },\n \n /** \n- * Property: start\n- * {<OpenLayers.Pixel>} The first pixel location of the drag.\n+ * Method: setMap\n+ * The layer has been added to the map. \n+ * \n+ * If there is no renderer set, the layer can't be used. Remove it.\n+ * Otherwise, give the renderer a reference to the map and set its size.\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n */\n- start: null,\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n \n- /**\n- * Property: lastMoveEvt\n- * {Object} The last mousemove event that occurred. Used to\n- * position the map correctly when our \"delay drag\"\n- * timeout expired.\n- */\n- lastMoveEvt: null,\n+ if (!this.renderer) {\n+ this.map.removeLayer(this);\n+ } else {\n+ this.renderer.map = this.map;\n \n- /**\n- * Property: oldOnselectstart\n- * {Function}\n- */\n- oldOnselectstart: null,\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n+ }\n+ },\n \n /**\n- * Property: interval\n- * {Integer} In order to increase performance, an interval (in \n- * milliseconds) can be set to reduce the number of drag events \n- * called. If set, a new drag event will not be set until the \n- * interval has passed. \n- * Defaults to 0, meaning no interval. \n+ * Method: afterAdd\n+ * Called at the end of the map.addLayer sequence. At this point, the map\n+ * will have a base layer. Any autoActivate strategies will be\n+ * activated here.\n */\n- interval: 0,\n+ afterAdd: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.activate();\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * Property: timeoutId\n- * {String} The id of the timeout used for the mousedown interval.\n- * This is \"private\", and should be left alone.\n+ * Method: removeMap\n+ * The layer has been removed from the map.\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- timeoutId: null,\n+ removeMap: function(map) {\n+ this.drawn = false;\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.deactivate();\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, the handler will also handle mouse moves when\n- * the cursor has moved out of the map viewport. Default is false.\n+ * Method: onMapResize\n+ * Notify the renderer of the change in size. \n+ * \n */\n- documentDrag: false,\n+ onMapResize: function() {\n+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n \n- /**\n- * Property: documentEvents\n- * {Boolean} Are we currently observing document events?\n- */\n- documentEvents: null,\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n+ },\n \n /**\n- * Constructor: OpenLayers.Handler.Drag\n- * Returns OpenLayers.Handler.Drag\n+ * Method: moveTo\n+ * Reset the vector layer's div so that it once again is lined up with \n+ * the map. Notify the renderer of the change of extent, and in the\n+ * case of a change of zoom level (resolution), have the \n+ * renderer redraw features.\n+ * \n+ * If the layer has not yet been drawn, cycle through the layer's \n+ * features and draw each one.\n * \n * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handlers setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object containing a single function to be\n- * called when the drag operation is finished. The callback should\n- * expect to recieve a single argument, the pixel location of the event.\n- * Callbacks for 'move' and 'done' are supported. You can also speficy\n- * callbacks for 'down', 'up', and 'out' to respond to those events.\n- * options - {Object} \n+ * bounds - {<OpenLayers.Bounds>} \n+ * zoomChanged - {Boolean} \n+ * dragging - {Boolean} \n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n \n- if (this.documentDrag === true) {\n- var me = this;\n- this._docMove = function(evt) {\n- me.mousemove({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- },\n- element: document\n- });\n- };\n- this._docUp = function(evt) {\n- me.mouseup({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- }\n- });\n- };\n+ var coordSysUnchanged = true;\n+ if (!dragging) {\n+ this.renderer.root.style.visibility = 'hidden';\n+\n+ var viewSize = this.map.getSize(),\n+ viewWidth = viewSize.w,\n+ viewHeight = viewSize.h,\n+ offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n+ offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n+ offsetLeft += this.map.layerContainerOriginPx.x;\n+ offsetLeft = -Math.round(offsetLeft);\n+ offsetTop += this.map.layerContainerOriginPx.y;\n+ offsetTop = -Math.round(offsetTop);\n+\n+ this.div.style.left = offsetLeft + 'px';\n+ this.div.style.top = offsetTop + 'px';\n+\n+ var extent = this.map.getExtent().scale(this.ratio);\n+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+\n+ this.renderer.root.style.visibility = 'visible';\n+\n+ // Force a reflow on gecko based browsers to prevent jump/flicker.\n+ // This seems to happen on only certain configurations; it was originally\n+ // noticed in FF 2.0 and Linux.\n+ if (OpenLayers.IS_GECKO === true) {\n+ this.div.scrollLeft = this.div.scrollLeft;\n+ }\n+\n+ if (!zoomChanged && coordSysUnchanged) {\n+ for (var i in this.unrenderedFeatures) {\n+ var feature = this.unrenderedFeatures[i];\n+ this.drawFeature(feature);\n+ }\n+ }\n+ }\n+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n+ this.drawn = true;\n+ var feature;\n+ for (var i = 0, len = this.features.length; i < len; i++) {\n+ this.renderer.locked = (i !== (len - 1));\n+ feature = this.features[i];\n+ this.drawFeature(feature);\n+ }\n }\n },\n \n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer\n+ * \n+ * Parameters:\n+ * display - {Boolean}\n+ */\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ // we need to set the display style of the root in case it is attached\n+ // to a foreign layer\n+ var currentDisplay = this.div.style.display;\n+ if (currentDisplay != this.renderer.root.style.display) {\n+ this.renderer.root.style.display = currentDisplay;\n+ }\n+ },\n \n /**\n- * Method: dragstart\n- * This private method is factorized from mousedown and touchstart methods\n+ * APIMethod: addFeatures\n+ * Add Features to the layer.\n *\n * Parameters:\n- * evt - {Event} The event\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * options - {Object}\n */\n- dragstart: function(evt) {\n- var propagate = true;\n- this.dragging = false;\n- if (this.checkModifiers(evt) &&\n- (OpenLayers.Event.isLeftClick(evt) ||\n- OpenLayers.Event.isSingleTouch(evt))) {\n- this.started = true;\n- this.start = evt.xy;\n- this.last = evt.xy;\n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- this.down(evt);\n- this.callback(\"down\", [evt.xy]);\n+ addFeatures: function(features, options) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n \n- // prevent document dragging\n- OpenLayers.Event.preventDefault(evt);\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ var event = {\n+ features: features\n+ };\n+ var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n+ if (ret === false) {\n+ return;\n+ }\n+ features = event.features;\n+ }\n \n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart ?\n- document.onselectstart : OpenLayers.Function.True;\n+ // Track successfully added features for featuresadded event, since\n+ // beforefeatureadded can veto single features.\n+ var featuresAdded = [];\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ if (i != (features.length - 1)) {\n+ this.renderer.locked = true;\n+ } else {\n+ this.renderer.locked = false;\n }\n- document.onselectstart = OpenLayers.Function.False;\n+ var feature = features[i];\n \n- propagate = !this.stopDown;\n- } else {\n- this.started = false;\n- this.start = null;\n- this.last = null;\n+ if (this.geometryType &&\n+ !(feature.geometry instanceof this.geometryType)) {\n+ throw new TypeError('addFeatures: component should be an ' +\n+ this.geometryType.prototype.CLASS_NAME);\n+ }\n+\n+ //give feature reference to its layer\n+ feature.layer = this;\n+\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style);\n+ }\n+\n+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue;\n+ }\n+ this.preFeatureInsert(feature);\n+ }\n+\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature);\n+ }\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ });\n }\n- return propagate;\n },\n \n+\n /**\n- * Method: dragmove\n- * This private method is factorized from mousemove and touchmove methods\n- *\n+ * APIMethod: removeFeatures\n+ * Remove features from the layer. This erases any drawn features and\n+ * removes them from the layer's control. The beforefeatureremoved\n+ * and featureremoved events will be triggered for each feature. The\n+ * featuresremoved event will be triggered after all features have\n+ * been removed. To supress event triggering, use the silent option.\n+ * \n * Parameters:\n- * evt - {Event} The event\n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n+ * removed.\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n */\n- dragmove: function(evt) {\n- this.lastMoveEvt = evt;\n- if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||\n- evt.xy.y != this.last.y)) {\n- if (this.documentDrag === true && this.documentEvents) {\n- if (evt.element === document) {\n- this.adjustXY(evt);\n- // do setEvent manually because the documentEvents are not\n- // registered with the map\n- this.setEvent(evt);\n- } else {\n- this.removeDocumentEvents();\n+ removeFeatures: function(features, options) {\n+ if (!features || features.length === 0) {\n+ return;\n+ }\n+ if (features === this.features) {\n+ return this.removeAllFeatures(options);\n+ }\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ if (features === this.selectedFeatures) {\n+ features = features.slice();\n+ }\n+\n+ var notify = !options || !options.silent;\n+\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n }\n+ );\n+ }\n+\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ // We remain locked so long as we're not at 0\n+ // and the 'next' feature has a geometry. We do the geometry check\n+ // because if all the features after the current one are 'null', we\n+ // won't call eraseGeometry, so we break the 'renderer functions\n+ // will always be called with locked=false *last*' rule. The end result\n+ // is a possible gratiutious unlocking to save a loop through the rest \n+ // of the list checking the remaining features every time. So long as\n+ // null geoms are rare, this is probably okay. \n+ if (i != 0 && features[i - 1].geometry) {\n+ this.renderer.locked = true;\n+ } else {\n+ this.renderer.locked = false;\n }\n- if (this.interval > 0) {\n- this.timeoutId = setTimeout(\n- OpenLayers.Function.bind(this.removeTimeout, this),\n- this.interval);\n+\n+ var feature = features[i];\n+ delete this.unrenderedFeatures[feature.id];\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n }\n- this.dragging = true;\n \n- this.move(evt);\n- this.callback(\"move\", [evt.xy]);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart;\n- document.onselectstart = OpenLayers.Function.False;\n+ this.features = OpenLayers.Util.removeItem(this.features, feature);\n+ // feature has no layer at this point\n+ feature.layer = null;\n+\n+ if (feature.geometry) {\n+ this.renderer.eraseFeatures(feature);\n+ }\n+\n+ //in the case that this feature is one of the selected features, \n+ // remove it from that array as well.\n+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n+ OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n }\n- this.last = evt.xy;\n }\n- return true;\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n+ }\n },\n \n- /**\n- * Method: dragend\n- * This private method is factorized from mouseup and touchend methods\n+ /** \n+ * APIMethod: removeAllFeatures\n+ * Remove all features from the layer.\n *\n * Parameters:\n- * evt - {Event} The event\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n */\n- dragend: function(evt) {\n- if (this.started) {\n- if (this.documentDrag === true && this.documentEvents) {\n- this.adjustXY(evt);\n- this.removeDocumentEvents();\n- }\n- var dragged = (this.start != this.last);\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n+ removeAllFeatures: function(options) {\n+ var notify = !options || !options.silent;\n+ var features = this.features;\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n+ }\n );\n- this.up(evt);\n- this.callback(\"up\", [evt.xy]);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy]);\n+ }\n+ var feature;\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ feature = features[i];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ feature.layer = null;\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n }\n- document.onselectstart = this.oldOnselectstart;\n }\n- return true;\n+ this.renderer.clear();\n+ this.features = [];\n+ this.unrenderedFeatures = {};\n+ this.selectedFeatures = [];\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n+ }\n },\n \n /**\n- * The four methods below (down, move, up, and out) are used by subclasses\n- * to do their own processing related to these mouse events.\n- */\n-\n- /**\n- * Method: down\n- * This method is called during the handling of the mouse down event.\n- * Subclasses can do their own processing here.\n- *\n- * Parameters:\n- * evt - {Event} The mouse down event\n- */\n- down: function(evt) {},\n-\n- /**\n- * Method: move\n- * This method is called during the handling of the mouse move event.\n- * Subclasses can do their own processing here.\n- *\n- * Parameters:\n- * evt - {Event} The mouse move event\n- *\n- */\n- move: function(evt) {},\n-\n- /**\n- * Method: up\n- * This method is called during the handling of the mouse up event.\n- * Subclasses can do their own processing here.\n+ * APIMethod: destroyFeatures\n+ * Erase and destroy features on the layer.\n *\n * Parameters:\n- * evt - {Event} The mouse up event\n+ * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n+ * features to destroy. If not supplied, all features on the layer\n+ * will be destroyed.\n+ * options - {Object}\n */\n- up: function(evt) {},\n+ destroyFeatures: function(features, options) {\n+ var all = (features == undefined); // evaluates to true if\n+ // features is null\n+ if (all) {\n+ features = this.features;\n+ }\n+ if (features) {\n+ this.removeFeatures(features, options);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ features[i].destroy();\n+ }\n+ }\n+ },\n \n /**\n- * Method: out\n- * This method is called during the handling of the mouse out event.\n- * Subclasses can do their own processing here.\n+ * APIMethod: drawFeature\n+ * Draw (or redraw) a feature on the layer. If the optional style argument\n+ * is included, this style will be used. If no style is included, the\n+ * feature's style will be used. If the feature doesn't have a style,\n+ * the layer's style will be used.\n+ * \n+ * This function is not designed to be used when adding features to \n+ * the layer (use addFeatures instead). It is meant to be used when\n+ * the style of a feature has changed, or in some other way needs to \n+ * visually updated *after* it has already been added to a layer. You\n+ * must add the feature to the layer for most layer-related events to \n+ * happen.\n *\n- * Parameters:\n- * evt - {Event} The mouse out event\n- */\n- out: function(evt) {},\n-\n- /**\n- * The methods below are part of the magic of event handling. Because\n- * they are named like browser events, they are registered as listeners\n- * for the events they represent.\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {String | Object} Named render intent or full symbolizer object.\n */\n+ drawFeature: function(feature, style) {\n+ // don't try to draw the feature with the renderer if the layer is not \n+ // drawn itself\n+ if (!this.drawn) {\n+ return;\n+ }\n+ if (typeof style != \"object\") {\n+ if (!style && feature.state === OpenLayers.State.DELETE) {\n+ style = \"delete\";\n+ }\n+ var renderIntent = style || feature.renderIntent;\n+ style = feature.style || this.style;\n+ if (!style) {\n+ style = this.styleMap.createSymbolizer(feature, renderIntent);\n+ }\n+ }\n \n- /**\n- * Method: mousedown\n- * Handle mousedown events\n- *\n- * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n- */\n- mousedown: function(evt) {\n- return this.dragstart(evt);\n+ var drawn = this.renderer.drawFeature(feature, style);\n+ //TODO remove the check for null when we get rid of Renderer.SVG\n+ if (drawn === false || drawn === null) {\n+ this.unrenderedFeatures[feature.id] = feature;\n+ } else {\n+ delete this.unrenderedFeatures[feature.id];\n+ }\n },\n \n /**\n- * Method: touchstart\n- * Handle touchstart events\n+ * Method: eraseFeatures\n+ * Erase features from the layer.\n *\n * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n */\n- touchstart: function(evt) {\n- this.startTouch();\n- return this.dragstart(evt);\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features);\n },\n \n /**\n- * Method: mousemove\n- * Handle mousemove events\n+ * Method: getFeatureFromEvent\n+ * Given an event, return a feature if the event occurred over one.\n+ * Otherwise, return null.\n *\n * Parameters:\n- * evt - {Event}\n+ * evt - {Event} \n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n */\n- mousemove: function(evt) {\n- return this.dragmove(evt);\n+ getFeatureFromEvent: function(evt) {\n+ if (!this.renderer) {\n+ throw new Error('getFeatureFromEvent called on layer with no ' +\n+ 'renderer. This usually means you destroyed a ' +\n+ 'layer, but not some handler which is associated ' +\n+ 'with it.');\n+ }\n+ var feature = null;\n+ var featureId = this.renderer.getFeatureIdFromEvent(evt);\n+ if (featureId) {\n+ if (typeof featureId === \"string\") {\n+ feature = this.getFeatureById(featureId);\n+ } else {\n+ feature = featureId;\n+ }\n+ }\n+ return feature;\n },\n \n /**\n- * Method: touchmove\n- * Handle touchmove events\n+ * APIMethod: getFeatureBy\n+ * Given a property value, return the feature if it exists in the features array\n *\n * Parameters:\n- * evt - {Event}\n+ * property - {String}\n+ * value - {String}\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n- */\n- touchmove: function(evt) {\n- return this.dragmove(evt);\n- },\n-\n- /**\n- * Method: removeTimeout\n- * Private. Called by mousemove() to remove the drag timeout.\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * property value or null if there is no such feature.\n */\n- removeTimeout: function() {\n- this.timeoutId = null;\n- // if timeout expires while we're still dragging (mouseup\n- // hasn't occurred) then call mousemove to move to the\n- // correct position\n- if (this.dragging) {\n- this.mousemove(this.lastMoveEvt);\n+ getFeatureBy: function(property, value) {\n+ //TBD - would it be more efficient to use a hash for this.features?\n+ var feature = null;\n+ for (var i = 0, len = this.features.length; i < len; ++i) {\n+ if (this.features[i][property] == value) {\n+ feature = this.features[i];\n+ break;\n+ }\n }\n+ return feature;\n },\n \n /**\n- * Method: mouseup\n- * Handle mouseup events\n+ * APIMethod: getFeatureById\n+ * Given a feature id, return the feature if it exists in the features array\n *\n * Parameters:\n- * evt - {Event}\n+ * featureId - {String}\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureId or null if there is no such feature.\n */\n- mouseup: function(evt) {\n- return this.dragend(evt);\n+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy('id', featureId);\n },\n \n /**\n- * Method: touchend\n- * Handle touchend events\n+ * APIMethod: getFeatureByFid\n+ * Given a feature fid, return the feature if it exists in the features array\n *\n * Parameters:\n- * evt - {Event}\n+ * featureFid - {String}\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureFid or null if there is no such feature.\n */\n- touchend: function(evt) {\n- // override evt.xy with last position since touchend does not have\n- // any touch position\n- evt.xy = this.last;\n- return this.dragend(evt);\n+ getFeatureByFid: function(featureFid) {\n+ return this.getFeatureBy('fid', featureFid);\n },\n \n /**\n- * Method: mouseout\n- * Handle mouseout events\n+ * APIMethod: getFeaturesByAttribute\n+ * Returns an array of features that have the given attribute key set to the\n+ * given value. Comparison of attribute values takes care of datatypes, e.g.\n+ * the string '1234' is not equal to the number 1234.\n *\n * Parameters:\n- * evt - {Event}\n+ * attrName - {String}\n+ * attrValue - {Mixed}\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n+ * passed named attribute set to the given value.\n */\n- mouseout: function(evt) {\n- if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- if (this.documentDrag === true) {\n- this.addDocumentEvents();\n- } else {\n- var dragged = (this.start != this.last);\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- this.out(evt);\n- this.callback(\"out\", []);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy]);\n- }\n- if (document.onselectstart) {\n- document.onselectstart = this.oldOnselectstart;\n+ getFeaturesByAttribute: function(attrName, attrValue) {\n+ var i,\n+ feature,\n+ len = this.features.length,\n+ foundFeatures = [];\n+ for (i = 0; i < len; i++) {\n+ feature = this.features[i];\n+ if (feature && feature.attributes) {\n+ if (feature.attributes[attrName] === attrValue) {\n+ foundFeatures.push(feature);\n }\n }\n }\n- return true;\n+ return foundFeatures;\n },\n \n /**\n- * Method: click\n- * The drag handler captures the click event. If something else registers\n- * for clicks on the same element, its listener will not be called \n- * after a drag.\n- * \n- * Parameters: \n- * evt - {Event} \n- * \n- * Returns:\n- * {Boolean} Let the event propagate.\n- */\n- click: function(evt) {\n- // let the click event propagate only if the mouse moved\n- return (this.start == this.last);\n- },\n+ * Unselect the selected features\n+ * i.e. clears the featureSelection array\n+ * change the style back\n+ clearSelection: function() {\n \n- /**\n- * Method: activate\n- * Activate the handler.\n- * \n- * Returns:\n- * {Boolean} The handler was successfully activated.\n- */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragging = false;\n- activated = true;\n+ var vectorLayer = this.map.vectorLayer;\n+ for (var i = 0; i < this.map.featureSelection.length; i++) {\n+ var featureSelection = this.map.featureSelection[i];\n+ vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n }\n- return activated;\n+ this.map.featureSelection = [];\n },\n-\n- /**\n- * Method: deactivate \n- * Deactivate the handler.\n- * \n- * Returns:\n- * {Boolean} The handler was successfully deactivated.\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.started = false;\n- this.dragging = false;\n- this.start = null;\n- this.last = null;\n- deactivated = true;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- }\n- return deactivated;\n- },\n+\n \n /**\n- * Method: adjustXY\n- * Converts event coordinates that are relative to the document body to\n- * ones that are relative to the map viewport. The latter is the default in\n- * OpenLayers.\n- * \n- * Parameters:\n- * evt - {Object}\n+ * APIMethod: onFeatureInsert\n+ * method called after a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something on feature updates.\n+ *\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- adjustXY: function(evt) {\n- var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n- evt.xy.x -= pos[0];\n- evt.xy.y -= pos[1];\n- },\n+ onFeatureInsert: function(feature) {},\n \n /**\n- * Method: addDocumentEvents\n- * Start observing document events when documentDrag is true and the mouse\n- * cursor leaves the map viewport while dragging.\n+ * APIMethod: preFeatureInsert\n+ * method called before a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something when features are first added to the\n+ * layer, but before they are drawn, such as adjust the style.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- addDocumentEvents: function() {\n- OpenLayers.Element.addClass(document.body, \"olDragDown\");\n- this.documentEvents = true;\n- OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.observe(document, \"mouseup\", this._docUp);\n- },\n+ preFeatureInsert: function(feature) {},\n \n- /**\n- * Method: removeDocumentEvents\n- * Stops observing document events when documentDrag is true and the mouse\n- * cursor re-enters the map viewport while dragging.\n+ /** \n+ * APIMethod: getDataExtent\n+ * Calculates the max extent which includes all of the features.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} or null if the layer has no features with\n+ * geometries.\n */\n- removeDocumentEvents: function() {\n- OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n- this.documentEvents = false;\n- OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp);\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ var features = this.features;\n+ if (features && (features.length > 0)) {\n+ var geometry = null;\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ geometry = features[i].geometry;\n+ if (geometry) {\n+ if (maxExtent === null) {\n+ maxExtent = new OpenLayers.Bounds();\n+ }\n+ maxExtent.extend(geometry.getBounds());\n+ }\n+ }\n+ }\n+ return maxExtent;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector\"\n });\n /* ======================================================================\n- OpenLayers/Handler/RegularPolygon.js\n+ OpenLayers/Control/Snapping.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Layer/Vector.js\n */\n \n /**\n- * Class: OpenLayers.Handler.RegularPolygon\n- * Handler to draw a regular polygon on the map. Polygon is displayed on mouse\n- * down, moves or is modified on mouse move, and is finished on mouse up.\n- * The handler triggers callbacks for 'done' and 'cancel'. Create a new\n- * instance with the <OpenLayers.Handler.RegularPolygon> constructor.\n- * \n+ * Class: OpenLayers.Control.Snapping\n+ * Acts as a snapping agent while editing vector features.\n+ *\n * Inherits from:\n- * - <OpenLayers.Handler.Drag>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {\n-\n- /**\n- * APIProperty: sides\n- * {Integer} Number of sides for the regular polygon. Needs to be greater\n- * than 2. Defaults to 4.\n- */\n- sides: 4,\n-\n- /**\n- * APIProperty: radius\n- * {Float} Optional radius in map units of the regular polygon. If this is\n- * set to some non-zero value, a polygon with a fixed radius will be\n- * drawn and dragged with mose movements. If this property is not\n- * set, dragging changes the radius of the polygon. Set to null by\n- * default.\n- */\n- radius: null,\n-\n- /**\n- * APIProperty: snapAngle\n- * {Float} If set to a non-zero value, the handler will snap the polygon\n- * rotation to multiples of the snapAngle. Value is an angle measured\n- * in degrees counterclockwise from the positive x-axis. \n- */\n- snapAngle: null,\n+OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {\n \n- /**\n- * APIProperty: snapToggle\n- * {String} If set, snapToggle is checked on mouse events and will set\n- * the snap mode to the opposite of what it currently is. To disallow\n- * toggling between snap and non-snap mode, set freehandToggle to\n- * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and\n- * 'altKey'. Snap mode is only possible if this.snapAngle is set to a\n- * non-zero value.\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforesnap - Triggered before a snap occurs. Listeners receive an\n+ * event object with *point*, *x*, *y*, *distance*, *layer*, and\n+ * *snapType* properties. The point property will be original point\n+ * geometry considered for snapping. The x and y properties represent\n+ * coordinates the point will receive. The distance is the distance\n+ * of the snap. The layer is the target layer. The snapType property\n+ * will be one of \"node\", \"vertex\", or \"edge\". Return false to stop\n+ * snapping from occurring.\n+ * snap - Triggered when a snap occurs. Listeners receive an event with\n+ * *point*, *snapType*, *layer*, and *distance* properties. The point\n+ * will be the location snapped to. The snapType will be one of \"node\",\n+ * \"vertex\", or \"edge\". The layer will be the target layer. The\n+ * distance will be the distance of the snap in map units.\n+ * unsnap - Triggered when a vertex is unsnapped. Listeners receive an\n+ * event with a *point* property.\n */\n- snapToggle: 'shiftKey',\n \n /**\n- * Property: layerOptions\n- * {Object} Any optional properties to be set on the sketch layer.\n+ * CONSTANT: DEFAULTS\n+ * Default target properties.\n */\n- layerOptions: null,\n+ DEFAULTS: {\n+ tolerance: 10,\n+ node: true,\n+ edge: true,\n+ vertex: true\n+ },\n \n /**\n- * APIProperty: persist\n- * {Boolean} Leave the feature rendered until clear is called. Default\n- * is false. If set to true, the feature remains rendered until\n- * clear is called, typically by deactivating the handler or starting\n- * another drawing.\n+ * Property: greedy\n+ * {Boolean} Snap to closest feature in first layer with an eligible\n+ * feature. Default is true.\n */\n- persist: false,\n+ greedy: true,\n \n /**\n- * APIProperty: irregular\n- * {Boolean} Draw an irregular polygon instead of a regular polygon.\n- * Default is false. If true, the initial mouse down will represent\n- * one corner of the polygon bounds and with each mouse movement, the\n- * polygon will be stretched so the opposite corner of its bounds\n- * follows the mouse position. This property takes precedence over\n- * the radius property. If set to true, the radius property will\n- * be ignored.\n+ * Property: precedence\n+ * {Array} List representing precedence of different snapping types.\n+ * Default is \"node\", \"vertex\", \"edge\".\n */\n- irregular: false,\n+ precedence: [\"node\", \"vertex\", \"edge\"],\n \n /**\n- * APIProperty: citeCompliant\n- * {Boolean} If set to true, coordinates of features drawn in a map extent\n- * crossing the date line won't exceed the world bounds. Default is false.\n+ * Property: resolution\n+ * {Float} The map resolution for the previously considered snap.\n */\n- citeCompliant: false,\n+ resolution: null,\n \n /**\n- * Property: angle\n- * {Float} The angle from the origin (mouse down) to the current mouse\n- * position, in radians. This is measured counterclockwise from the\n- * positive x-axis.\n+ * Property: geoToleranceCache\n+ * {Object} A cache of geo-tolerances. Tolerance values (in map units) are\n+ * calculated when the map resolution changes.\n */\n- angle: null,\n+ geoToleranceCache: null,\n \n /**\n- * Property: fixedRadius\n- * {Boolean} The polygon has a fixed radius. True if a radius is set before\n- * drawing begins. False otherwise.\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The current editable layer. Set at\n+ * construction or after construction with <setLayer>.\n */\n- fixedRadius: false,\n+ layer: null,\n \n /**\n * Property: feature\n- * {<OpenLayers.Feature.Vector>} The currently drawn polygon feature\n+ * {<OpenLayers.Feature.Vector>} The current editable feature.\n */\n feature: null,\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The temporary drawing layer\n- */\n- layer: null,\n-\n- /**\n- * Property: origin\n- * {<OpenLayers.Geometry.Point>} Location of the first mouse down\n+ * Property: point\n+ * {<OpenLayers.Geometry.Point>} The currently snapped vertex.\n */\n- origin: null,\n+ point: null,\n \n /**\n- * Constructor: OpenLayers.Handler.RegularPolygon\n- * Create a new regular polygon handler.\n+ * Constructor: OpenLayers.Control.Snapping\n+ * Creates a new snapping control. A control is constructed with an editable\n+ * layer and a set of configuration objects for target layers. While the\n+ * control is active, dragging vertices while drawing new features or\n+ * modifying existing features on the editable layer will engage\n+ * snapping to features on the target layers. Whether a vertex snaps to\n+ * a feature on a target layer depends on the target layer configuration.\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} The control that owns this handler\n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} An object with properties to be set on the handler.\n- * If the options.sides property is not specified, the number of sides\n- * will default to 4.\n+ * options - {Object} An object containing all configuration properties for\n+ * the control.\n *\n- * Named callbacks:\n- * create - Called when a sketch is first created. Callback called with\n- * the creation point geometry and sketch feature.\n- * done - Called when the sketch drawing is finished. The callback will\n- * recieve a single argument, the sketch geometry.\n- * cancel - Called when the handler is deactivated while drawing. The\n- * cancel callback will receive a geometry.\n+ * Valid options:\n+ * layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this\n+ * layer that are digitized or modified may have vertices snapped to\n+ * features from any of the target layers.\n+ * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for\n+ * configuring target layers. See valid properties of the target\n+ * objects below. If the items in the targets list are vector layers\n+ * (instead of configuration objects), the defaults from the <defaults>\n+ * property will apply. The editable layer itself may be a target\n+ * layer, allowing newly created or edited features to be snapped to\n+ * existing features from the same layer. If no targets are provided\n+ * the layer given in the constructor (as <layer>) will become the\n+ * initial target.\n+ * defaults - {Object} An object with default properties to be applied\n+ * to all target objects.\n+ * greedy - {Boolean} Snap to closest feature in first target layer that\n+ * applies. Default is true. If false, all features in all target\n+ * layers will be checked and the closest feature in all target layers\n+ * will be chosen. The greedy property determines if the order of the\n+ * target layers is significant. By default, the order of the target\n+ * layers is significant where layers earlier in the target layer list\n+ * have precedence over layers later in the list. Within a single\n+ * layer, the closest feature is always chosen for snapping. This\n+ * property only determines whether the search for a closer feature\n+ * continues after an eligible feature is found in a target layer.\n+ *\n+ * Valid target properties:\n+ * layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this\n+ * layer will be eligible to act as snapping target for the editable\n+ * layer.\n+ * tolerance - {Float} The distance (in pixels) at which snapping may occur.\n+ * Default is 10.\n+ * node - {Boolean} Snap to nodes (first or last point in a geometry) in\n+ * target layer. Default is true.\n+ * nodeTolerance - {Float} Optional distance at which snapping may occur\n+ * for nodes specifically. If none is provided, <tolerance> will be\n+ * used.\n+ * vertex - {Boolean} Snap to vertices in target layer. Default is true.\n+ * vertexTolerance - {Float} Optional distance at which snapping may occur\n+ * for vertices specifically. If none is provided, <tolerance> will be\n+ * used.\n+ * edge - {Boolean} Snap to edges in target layer. Default is true.\n+ * edgeTolerance - {Float} Optional distance at which snapping may occur\n+ * for edges specifically. If none is provided, <tolerance> will be\n+ * used.\n+ * filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if\n+ * feature is eligible for snapping. If filter evaluates to true for a\n+ * target feature a vertex may be snapped to the feature. \n+ * minResolution - {Number} If a minResolution is provided, snapping to this\n+ * target will only be considered if the map resolution is greater than\n+ * or equal to this value (the minResolution is inclusive). Default is\n+ * no minimum resolution limit.\n+ * maxResolution - {Number} If a maxResolution is provided, snapping to this\n+ * target will only be considered if the map resolution is strictly\n+ * less than this value (the maxResolution is exclusive). Default is\n+ * no maximum resolution limit.\n */\n- initialize: function(control, callbacks, options) {\n- if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n- this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});\n- }\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.options = options || {}; // TODO: this could be done by the super\n \n- OpenLayers.Handler.Drag.prototype.initialize.apply(this,\n- [control, callbacks, options]);\n- this.options = (options) ? options : {};\n- },\n+ // set the editable layer if provided\n+ if (this.options.layer) {\n+ this.setLayer(this.options.layer);\n+ }\n+ // configure target layers\n+ var defaults = OpenLayers.Util.extend({}, this.options.defaults);\n+ this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);\n+ this.setTargets(this.options.targets);\n+ if (this.targets.length === 0 && this.layer) {\n+ this.addTargetLayer(this.layer);\n+ }\n \n- /**\n- * APIMethod: setOptions\n- * \n- * Parameters:\n- * newOptions - {Object} \n- */\n- setOptions: function(newOptions) {\n- OpenLayers.Util.extend(this.options, newOptions);\n- OpenLayers.Util.extend(this, newOptions);\n+ this.geoToleranceCache = {};\n },\n \n /**\n- * APIMethod: activate\n- * Turn on the handler.\n+ * APIMethod: setLayer\n+ * Set the editable layer. Call the setLayer method if the editable layer\n+ * changes and the same control should be used on a new editable layer.\n+ * If the control is already active, it will be active after the new\n+ * layer is set.\n *\n- * Returns:\n- * {Boolean} The handler was successfully activated\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} The new editable layer.\n */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {\n- // create temporary vector layer for rendering geometry sketch\n- var options = OpenLayers.Util.extend({\n- displayInLayerSwitcher: false,\n- // indicate that the temp vector layer will never be out of range\n- // without this, resolution properties must be specified at the\n- // map-level for this temporary layer to init its resolutions\n- // correctly\n- calculateInRange: OpenLayers.Function.True,\n- wrapDateLine: this.citeCompliant\n- }, this.layerOptions);\n- this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n- this.map.addLayer(this.layer);\n- activated = true;\n+ setLayer: function(layer) {\n+ if (this.active) {\n+ this.deactivate();\n+ this.layer = layer;\n+ this.activate();\n+ } else {\n+ this.layer = layer;\n }\n- return activated;\n },\n \n /**\n- * APIMethod: deactivate\n- * Turn off the handler.\n+ * Method: setTargets\n+ * Set the targets for the snapping agent.\n *\n- * Returns:\n- * {Boolean} The handler was successfully deactivated\n+ * Parameters:\n+ * targets - {Array} An array of target configs or target layers.\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {\n- // call the cancel callback if mid-drawing\n- if (this.dragging) {\n- this.cancel();\n- }\n- // If a layer's map property is set to null, it means that that\n- // layer isn't added to the map. Since we ourself added the layer\n- // to the map in activate(), we can assume that if this.layer.map\n- // is null it means that the layer has been destroyed (as a result\n- // of map.destroy() for example.\n- if (this.layer.map != null) {\n- this.layer.destroy(false);\n- if (this.feature) {\n- this.feature.destroy();\n+ setTargets: function(targets) {\n+ this.targets = [];\n+ if (targets && targets.length) {\n+ var target;\n+ for (var i = 0, len = targets.length; i < len; ++i) {\n+ target = targets[i];\n+ if (target instanceof OpenLayers.Layer.Vector) {\n+ this.addTargetLayer(target);\n+ } else {\n+ this.addTarget(target);\n }\n }\n- this.layer = null;\n- this.feature = null;\n- deactivated = true;\n }\n- return deactivated;\n },\n \n /**\n- * Method: down\n- * Start drawing a new feature\n+ * Method: addTargetLayer\n+ * Add a target layer with the default target config.\n *\n * Parameters:\n- * evt - {Event} The drag start event\n+ * layer - {<OpenLayers.Layer.Vector>} A target layer.\n */\n- down: function(evt) {\n- this.fixedRadius = !!(this.radius);\n- var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n- this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n- // create the new polygon\n- if (!this.fixedRadius || this.irregular) {\n- // smallest radius should not be less one pixel in map units\n- // VML doesn't behave well with smaller\n- this.radius = this.map.getResolution();\n- }\n- if (this.persist) {\n- this.clear();\n- }\n- this.feature = new OpenLayers.Feature.Vector();\n- this.createGeometry();\n- this.callback(\"create\", [this.origin, this.feature]);\n- this.layer.addFeatures([this.feature], {\n- silent: true\n+ addTargetLayer: function(layer) {\n+ this.addTarget({\n+ layer: layer\n });\n- this.layer.drawFeature(this.feature, this.style);\n },\n \n /**\n- * Method: move\n- * Respond to drag move events\n+ * Method: addTarget\n+ * Add a configured target layer.\n *\n * Parameters:\n- * evt - {Evt} The move event\n+ * target - {Object} A target config.\n */\n- move: function(evt) {\n- var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n- var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n- if (this.irregular) {\n- var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;\n- this.radius = Math.max(this.map.getResolution() / 2, ry);\n- } else if (this.fixedRadius) {\n- this.origin = point;\n- } else {\n- this.calculateAngle(point, evt);\n- this.radius = Math.max(this.map.getResolution() / 2,\n- point.distanceTo(this.origin));\n- }\n- this.modifyGeometry();\n- if (this.irregular) {\n- var dx = point.x - this.origin.x;\n- var dy = point.y - this.origin.y;\n- var ratio;\n- if (dy == 0) {\n- ratio = dx / (this.radius * Math.sqrt(2));\n- } else {\n- ratio = dx / dy;\n- }\n- this.feature.geometry.resize(1, this.origin, ratio);\n- this.feature.geometry.move(dx / 2, dy / 2);\n- }\n- this.layer.drawFeature(this.feature, this.style);\n+ addTarget: function(target) {\n+ target = OpenLayers.Util.applyDefaults(target, this.defaults);\n+ target.nodeTolerance = target.nodeTolerance || target.tolerance;\n+ target.vertexTolerance = target.vertexTolerance || target.tolerance;\n+ target.edgeTolerance = target.edgeTolerance || target.tolerance;\n+ this.targets.push(target);\n },\n \n /**\n- * Method: up\n- * Finish drawing the feature\n+ * Method: removeTargetLayer\n+ * Remove a target layer.\n *\n * Parameters:\n- * evt - {Event} The mouse up event\n+ * layer - {<OpenLayers.Layer.Vector>} The target layer to remove.\n */\n- up: function(evt) {\n- this.finalize();\n- // the mouseup method of superclass doesn't call the\n- // \"done\" callback if there's been no move between\n- // down and up\n- if (this.start == this.last) {\n- this.callback(\"done\", [evt.xy]);\n+ removeTargetLayer: function(layer) {\n+ var target;\n+ for (var i = this.targets.length - 1; i >= 0; --i) {\n+ target = this.targets[i];\n+ if (target.layer === layer) {\n+ this.removeTarget(target);\n+ }\n }\n },\n \n /**\n- * Method: out\n- * Finish drawing the feature.\n+ * Method: removeTarget\n+ * Remove a target.\n *\n * Parameters:\n- * evt - {Event} The mouse out event\n+ * target - {Object} A target config.\n+ *\n+ * Returns:\n+ * {Array} The targets array.\n */\n- out: function(evt) {\n- this.finalize();\n+ removeTarget: function(target) {\n+ return OpenLayers.Util.removeItem(this.targets, target);\n },\n \n /**\n- * Method: createGeometry\n- * Create the new polygon geometry. This is called at the start of the\n- * drag and at any point during the drag if the number of sides\n- * changes.\n+ * APIMethod: activate\n+ * Activate the control. Activating the control registers listeners for\n+ * editing related events so that during feature creation and\n+ * modification, moving vertices will trigger snapping.\n */\n- createGeometry: function() {\n- this.angle = Math.PI * ((1 / this.sides) - (1 / 2));\n- if (this.snapAngle) {\n- this.angle += this.snapAngle * (Math.PI / 180);\n+ activate: function() {\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.on({\n+ sketchstarted: this.onSketchModified,\n+ sketchmodified: this.onSketchModified,\n+ vertexmodified: this.onVertexModified,\n+ scope: this\n+ });\n+ }\n }\n- this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(\n- this.origin, this.radius, this.sides, this.snapAngle\n- );\n+ return activated;\n },\n \n /**\n- * Method: modifyGeometry\n- * Modify the polygon geometry in place.\n+ * APIMethod: deactivate\n+ * Deactivate the control. Deactivating the control unregisters listeners\n+ * so feature editing may proceed without engaging the snapping agent.\n */\n- modifyGeometry: function() {\n- var angle, point;\n- var ring = this.feature.geometry.components[0];\n- // if the number of sides ever changes, create a new geometry\n- if (ring.components.length != (this.sides + 1)) {\n- this.createGeometry();\n- ring = this.feature.geometry.components[0];\n- }\n- for (var i = 0; i < this.sides; ++i) {\n- point = ring.components[i];\n- angle = this.angle + (i * 2 * Math.PI / this.sides);\n- point.x = this.origin.x + (this.radius * Math.cos(angle));\n- point.y = this.origin.y + (this.radius * Math.sin(angle));\n- point.clearBounds();\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.un({\n+ sketchstarted: this.onSketchModified,\n+ sketchmodified: this.onSketchModified,\n+ vertexmodified: this.onVertexModified,\n+ scope: this\n+ });\n+ }\n }\n+ this.feature = null;\n+ this.point = null;\n+ return deactivated;\n },\n \n /**\n- * Method: calculateAngle\n- * Calculate the angle based on settings.\n+ * Method: onSketchModified\n+ * Registered as a listener for the sketchmodified event on the editable\n+ * layer.\n *\n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- * evt - {Event}\n+ * event - {Object} The sketch modified event.\n */\n- calculateAngle: function(point, evt) {\n- var alpha = Math.atan2(point.y - this.origin.y,\n- point.x - this.origin.x);\n- if (this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {\n- var snapAngleRad = (Math.PI / 180) * this.snapAngle;\n- this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;\n- } else {\n- this.angle = alpha;\n- }\n+ onSketchModified: function(event) {\n+ this.feature = event.feature;\n+ this.considerSnapping(event.vertex, event.vertex);\n },\n \n /**\n- * APIMethod: cancel\n- * Finish the geometry and call the \"cancel\" callback.\n+ * Method: onVertexModified\n+ * Registered as a listener for the vertexmodified event on the editable\n+ * layer.\n+ *\n+ * Parameters:\n+ * event - {Object} The vertex modified event.\n */\n- cancel: function() {\n- // the polygon geometry gets cloned in the callback method\n- this.callback(\"cancel\", null);\n- this.finalize();\n+ onVertexModified: function(event) {\n+ this.feature = event.feature;\n+ var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);\n+ this.considerSnapping(\n+ event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)\n+ );\n },\n \n /**\n- * Method: finalize\n- * Finish the geometry and call the \"done\" callback.\n+ * Method: considerSnapping\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or\n+ * unsnapped).\n+ * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map\n+ * coords.\n */\n- finalize: function() {\n- this.origin = null;\n- this.radius = this.options.radius;\n+ considerSnapping: function(point, loc) {\n+ var best = {\n+ rank: Number.POSITIVE_INFINITY,\n+ dist: Number.POSITIVE_INFINITY,\n+ x: null,\n+ y: null\n+ };\n+ var snapped = false;\n+ var result, target;\n+ for (var i = 0, len = this.targets.length; i < len; ++i) {\n+ target = this.targets[i];\n+ result = this.testTarget(target, loc);\n+ if (result) {\n+ if (this.greedy) {\n+ best = result;\n+ best.target = target;\n+ snapped = true;\n+ break;\n+ } else {\n+ if ((result.rank < best.rank) ||\n+ (result.rank === best.rank && result.dist < best.dist)) {\n+ best = result;\n+ best.target = target;\n+ snapped = true;\n+ }\n+ }\n+ }\n+ }\n+ if (snapped) {\n+ var proceed = this.events.triggerEvent(\"beforesnap\", {\n+ point: point,\n+ x: best.x,\n+ y: best.y,\n+ distance: best.dist,\n+ layer: best.target.layer,\n+ snapType: this.precedence[best.rank]\n+ });\n+ if (proceed !== false) {\n+ point.x = best.x;\n+ point.y = best.y;\n+ this.point = point;\n+ this.events.triggerEvent(\"snap\", {\n+ point: point,\n+ snapType: this.precedence[best.rank],\n+ layer: best.target.layer,\n+ distance: best.dist\n+ });\n+ } else {\n+ snapped = false;\n+ }\n+ }\n+ if (this.point && !snapped) {\n+ point.x = loc.x;\n+ point.y = loc.y;\n+ this.point = null;\n+ this.events.triggerEvent(\"unsnap\", {\n+ point: point\n+ });\n+ }\n },\n \n /**\n- * APIMethod: clear\n- * Clear any rendered features on the temporary layer. This is called\n- * when the handler is deactivated, canceled, or done (unless persist\n- * is true).\n+ * Method: testTarget\n+ *\n+ * Parameters:\n+ * target - {Object} Object with target layer configuration.\n+ * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map\n+ * coords.\n+ *\n+ * Returns:\n+ * {Object} A result object with rank, dist, x, and y properties.\n+ * Returns null if candidate is not eligible for snapping.\n */\n- clear: function() {\n- if (this.layer) {\n- this.layer.renderer.clear();\n- this.layer.destroyFeatures();\n+ testTarget: function(target, loc) {\n+ var resolution = this.layer.map.getResolution();\n+ if (\"minResolution\" in target) {\n+ if (resolution < target.minResolution) {\n+ return null;\n+ }\n+ }\n+ if (\"maxResolution\" in target) {\n+ if (resolution >= target.maxResolution) {\n+ return null;\n+ }\n }\n+ var tolerance = {\n+ node: this.getGeoTolerance(target.nodeTolerance, resolution),\n+ vertex: this.getGeoTolerance(target.vertexTolerance, resolution),\n+ edge: this.getGeoTolerance(target.edgeTolerance, resolution)\n+ };\n+ // this could be cached if we don't support setting tolerance values directly\n+ var maxTolerance = Math.max(\n+ tolerance.node, tolerance.vertex, tolerance.edge\n+ );\n+ var result = {\n+ rank: Number.POSITIVE_INFINITY,\n+ dist: Number.POSITIVE_INFINITY\n+ };\n+ var eligible = false;\n+ var features = target.layer.features;\n+ var feature, type, vertices, vertex, closest, dist, found;\n+ var numTypes = this.precedence.length;\n+ var ll = new OpenLayers.LonLat(loc.x, loc.y);\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ if (feature !== this.feature && !feature._sketch &&\n+ feature.state !== OpenLayers.State.DELETE &&\n+ (!target.filter || target.filter.evaluate(feature))) {\n+ if (feature.atPoint(ll, maxTolerance, maxTolerance)) {\n+ for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) {\n+ type = this.precedence[j];\n+ if (target[type]) {\n+ if (type === \"edge\") {\n+ closest = feature.geometry.distanceTo(loc, {\n+ details: true\n+ });\n+ dist = closest.distance;\n+ if (dist <= tolerance[type] && dist < result.dist) {\n+ result = {\n+ rank: j,\n+ dist: dist,\n+ x: closest.x0,\n+ y: closest.y0 // closest coords on feature\n+ };\n+ eligible = true;\n+ // don't look for lower precedence types for this feature\n+ break;\n+ }\n+ } else {\n+ // look for nodes or vertices\n+ vertices = feature.geometry.getVertices(type === \"node\");\n+ found = false;\n+ for (var k = 0, klen = vertices.length; k < klen; ++k) {\n+ vertex = vertices[k];\n+ dist = vertex.distanceTo(loc);\n+ if (dist <= tolerance[type] &&\n+ (j < result.rank || (j === result.rank && dist < result.dist))) {\n+ result = {\n+ rank: j,\n+ dist: dist,\n+ x: vertex.x,\n+ y: vertex.y\n+ };\n+ eligible = true;\n+ found = true;\n+ }\n+ }\n+ if (found) {\n+ // don't look for lower precedence types for this feature\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return eligible ? result : null;\n },\n \n /**\n- * Method: callback\n- * Trigger the control's named callback with the given arguments\n- *\n+ * Method: getGeoTolerance\n+ * Calculate a tolerance in map units given a tolerance in pixels. This\n+ * takes advantage of the <geoToleranceCache> when the map resolution\n+ * has not changed.\n+ * \n * Parameters:\n- * name - {String} The key for the callback that is one of the properties\n- * of the handler's callbacks object.\n- * args - {Array} An array of arguments with which to call the callback\n- * (defined by the control).\n+ * tolerance - {Number} A tolerance value in pixels.\n+ * resolution - {Number} Map resolution.\n+ *\n+ * Returns:\n+ * {Number} A tolerance value in map units.\n */\n- callback: function(name, args) {\n- // override the callback method to always send the polygon geometry\n- if (this.callbacks[name]) {\n- this.callbacks[name].apply(this.control,\n- [this.feature.geometry.clone()]);\n+ getGeoTolerance: function(tolerance, resolution) {\n+ if (resolution !== this.resolution) {\n+ this.resolution = resolution;\n+ this.geoToleranceCache = {};\n }\n- // since sketch features are added to the temporary layer\n- // they must be cleared here if done or cancel\n- if (!this.persist && (name == \"done\" || name == \"cancel\")) {\n- this.clear();\n+ var geoTolerance = this.geoToleranceCache[tolerance];\n+ if (geoTolerance === undefined) {\n+ geoTolerance = tolerance * resolution;\n+ this.geoToleranceCache[tolerance] = geoTolerance;\n }\n+ return geoTolerance;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.RegularPolygon\"\n+ /**\n+ * Method: destroy\n+ * Clean up the control.\n+ */\n+ destroy: function() {\n+ if (this.active) {\n+ this.deactivate(); // TODO: this should be handled by the super\n+ }\n+ delete this.layer;\n+ delete this.targets;\n+ OpenLayers.Control.prototype.destroy.call(this);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Snapping\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Click.js\n+ OpenLayers/Control/WMTSGetFeatureInfo.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Handler/Hover.js\n+ * @requires OpenLayers/Request.js\n+ * @requires OpenLayers/Format/WMSGetFeatureInfo.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Click\n- * A handler for mouse clicks. The intention of this handler is to give\n- * controls more flexibility with handling clicks. Browsers trigger\n- * click events twice for a double-click. In addition, the mousedown,\n- * mousemove, mouseup sequence fires a click event. With this handler,\n- * controls can decide whether to ignore clicks associated with a double\n- * click. By setting a <pixelTolerance>, controls can also ignore clicks\n- * that include a drag. Create a new instance with the\n- * <OpenLayers.Handler.Click> constructor.\n- * \n+ * Class: OpenLayers.Control.WMTSGetFeatureInfo\n+ * The WMTSGetFeatureInfo control uses a WMTS query to get information about a \n+ * point on the map. The information may be in a display-friendly format \n+ * such as HTML, or a machine-friendly format such as GML, depending on the \n+ * server's capabilities and the client's configuration. This control \n+ * handles click or hover events, attempts to parse the results using an \n+ * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer\n+ * queried.\n+ *\n * Inherits from:\n- * - <OpenLayers.Handler> \n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n+\n /**\n- * APIProperty: delay\n- * {Number} Number of milliseconds between clicks before the event is\n- * considered a double-click.\n+ * APIProperty: hover\n+ * {Boolean} Send GetFeatureInfo requests when mouse stops moving.\n+ * Default is false.\n */\n- delay: 300,\n+ hover: false,\n \n /**\n- * APIProperty: single\n- * {Boolean} Handle single clicks. Default is true. If false, clicks\n- * will not be reported. If true, single-clicks will be reported.\n+ * Property: requestEncoding\n+ * {String} One of \"KVP\" or \"REST\". Only KVP encoding is supported at this \n+ * time.\n */\n- single: true,\n+ requestEncoding: \"KVP\",\n \n /**\n- * APIProperty: double\n- * {Boolean} Handle double-clicks. Default is false.\n+ * APIProperty: drillDown\n+ * {Boolean} Drill down over all WMTS layers in the map. When\n+ * using drillDown mode, hover is not possible. A getfeatureinfo event\n+ * will be fired for each layer queried.\n */\n- 'double': false,\n+ drillDown: false,\n \n /**\n- * APIProperty: pixelTolerance\n- * {Number} Maximum number of pixels between mouseup and mousedown for an\n- * event to be considered a click. Default is 0. If set to an\n- * integer value, clicks with a drag greater than the value will be\n- * ignored. This property can only be set when the handler is\n- * constructed.\n+ * APIProperty: maxFeatures\n+ * {Integer} Maximum number of features to return from a WMTS query. This\n+ * sets the feature_count parameter on WMTS GetFeatureInfo\n+ * requests.\n */\n- pixelTolerance: 0,\n+ maxFeatures: 10,\n \n- /**\n- * APIProperty: dblclickTolerance\n- * {Number} Maximum distance in pixels between clicks for a sequence of \n- * events to be considered a double click. Default is 13. If the\n- * distance between two clicks is greater than this value, a double-\n- * click will not be fired.\n+ /** APIProperty: clickCallback\n+ * {String} The click callback to register in the\n+ * {<OpenLayers.Handler.Click>} object created when the hover\n+ * option is set to false. Default is \"click\".\n */\n- dblclickTolerance: 13,\n+ clickCallback: \"click\",\n \n /**\n- * APIProperty: stopSingle\n- * {Boolean} Stop other listeners from being notified of clicks. Default\n- * is false. If true, any listeners registered before this one for \n- * click or rightclick events will not be notified.\n+ * Property: layers\n+ * {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info.\n+ * If omitted, all map WMTS layers will be considered.\n */\n- stopSingle: false,\n+ layers: null,\n \n /**\n- * APIProperty: stopDouble\n- * {Boolean} Stop other listeners from being notified of double-clicks.\n- * Default is false. If true, any click listeners registered before\n- * this one will not be notified of *any* double-click events.\n- * \n- * The one caveat with stopDouble is that given a map with two click\n- * handlers, one with stopDouble true and the other with stopSingle\n- * true, the stopSingle handler should be activated last to get\n- * uniform cross-browser performance. Since IE triggers one click\n- * with a dblclick and FF triggers two, if a stopSingle handler is\n- * activated first, all it gets in IE is a single click when the\n- * second handler stops propagation on the dblclick.\n+ * APIProperty: queryVisible\n+ * {Boolean} Filter out hidden layers when searching the map for layers to \n+ * query. Default is true.\n */\n- stopDouble: false,\n+ queryVisible: true,\n \n /**\n- * Property: timerId\n- * {Number} The id of the timeout waiting to clear the <delayedCall>.\n+ * Property: infoFormat\n+ * {String} The mimetype to request from the server\n */\n- timerId: null,\n+ infoFormat: 'text/html',\n \n /**\n- * Property: down\n- * {Object} Object that store relevant information about the last\n- * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives\n- * the average location of the mouse/touch event. Its 'touches'\n- * property records clientX/clientY of each touches.\n+ * Property: vendorParams\n+ * {Object} Additional parameters that will be added to the request, for\n+ * WMTS implementations that support them. This could e.g. look like\n+ * (start code)\n+ * {\n+ * radius: 5\n+ * }\n+ * (end)\n */\n- down: null,\n+ vendorParams: {},\n \n /**\n- * Property: last\n- * {Object} Object that store relevant information about the last\n- * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives\n- * the average location of the mouse/touch event. Its 'touches'\n- * property records clientX/clientY of each touches.\n+ * Property: format\n+ * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.\n+ * Default is <OpenLayers.Format.WMSGetFeatureInfo>.\n */\n- last: null,\n+ format: null,\n \n- /** \n- * Property: first\n- * {Object} When waiting for double clicks, this object will store \n- * information about the first click in a two click sequence.\n+ /**\n+ * Property: formatOptions\n+ * {Object} Optional properties to set on the format (if one is not provided\n+ * in the <format> property.\n */\n- first: null,\n+ formatOptions: null,\n \n /**\n- * Property: rightclickTimerId\n- * {Number} The id of the right mouse timeout waiting to clear the \n- * <delayedEvent>.\n+ * APIProperty: handlerOptions\n+ * {Object} Additional options for the handlers used by this control, e.g.\n+ * (start code)\n+ * {\n+ * \"click\": {delay: 100},\n+ * \"hover\": {delay: 300}\n+ * }\n+ * (end)\n */\n- rightclickTimerId: null,\n \n /**\n- * Constructor: OpenLayers.Handler.Click\n- * Create a new click handler.\n- * \n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handler's setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object with keys corresponding to callbacks\n- * that will be called by the handler. The callbacks should\n- * expect to recieve a single argument, the click event.\n- * Callbacks for 'click' and 'dblclick' are supported.\n- * options - {Object} Optional object whose properties will be set on the\n- * handler.\n+ * Property: handler\n+ * {Object} Reference to the <OpenLayers.Handler> for this control\n */\n+ handler: null,\n \n /**\n- * Method: touchstart\n- * Handle touchstart.\n- *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Property: hoverRequest\n+ * {<OpenLayers.Request>} contains the currently running hover request\n+ * (if any).\n */\n- touchstart: function(evt) {\n- this.startTouch();\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true;\n- },\n+ hoverRequest: null,\n \n- /**\n- * Method: touchmove\n- * Store position of last move, because touchend event can have\n- * an empty \"touches\" property.\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforegetfeatureinfo - Triggered before each request is sent.\n+ * The event object has an *xy* property with the position of the \n+ * mouse click or hover event that triggers the request and a *layer*\n+ * property referencing the layer about to be queried. If a listener\n+ * returns false, the request will not be issued.\n+ * getfeatureinfo - Triggered when a GetFeatureInfo response is received.\n+ * The event object has a *text* property with the body of the\n+ * response (String), a *features* property with an array of the\n+ * parsed features, an *xy* property with the position of the mouse\n+ * click or hover event that triggered the request, a *layer* property\n+ * referencing the layer queried and a *request* property with the \n+ * request itself. If drillDown is set to true, one event will be fired\n+ * for each layer queried.\n+ * exception - Triggered when a GetFeatureInfo request fails (with a \n+ * status other than 200) or whenparsing fails. Listeners will receive \n+ * an event with *request*, *xy*, and *layer* properties. In the case \n+ * of a parsing error, the event will also contain an *error* property.\n */\n- touchmove: function(evt) {\n- this.last = this.getEventInfo(evt);\n- return true;\n- },\n \n- /**\n- * Method: touchend\n- * Correctly set event xy property, and add lastTouches to have\n- * touches property from last touchstart or touchmove\n- *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ /** \n+ * Property: pending\n+ * {Number} The number of pending requests.\n */\n- touchend: function(evt) {\n- // touchstart may not have been allowed to propagate\n- if (this.down) {\n- evt.xy = this.last.xy;\n- evt.lastTouches = this.last.touches;\n- this.handleSingle(evt);\n- this.down = null;\n- }\n- return true;\n- },\n+ pending: 0,\n \n /**\n- * Method: mousedown\n- * Handle mousedown.\n+ * Constructor: <OpenLayers.Control.WMTSGetFeatureInfo>\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Parameters:\n+ * options - {Object} \n */\n- mousedown: function(evt) {\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true;\n- },\n+ initialize: function(options) {\n+ options = options || {};\n+ options.handlerOptions = options.handlerOptions || {};\n \n- /**\n- * Method: mouseup\n- * Handle mouseup. Installed to support collection of right mouse events.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n- */\n- mouseup: function(evt) {\n- var propagate = true;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n \n- // Collect right mouse clicks from the mouseup\n- // IE - ignores the second right click in mousedown so using\n- // mouseup instead\n- if (this.checkModifiers(evt) && this.control.handleRightClicks &&\n- OpenLayers.Event.isRightClick(evt)) {\n- propagate = this.rightclick(evt);\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.WMSGetFeatureInfo(\n+ options.formatOptions\n+ );\n }\n \n- return propagate;\n- },\n-\n- /**\n- * Method: rightclick\n- * Handle rightclick. For a dblrightclick, we get two clicks so we need \n- * to always register for dblrightclick to properly handle single \n- * clicks.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n- */\n- rightclick: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.rightclickTimerId != null) {\n- //Second click received before timeout this must be \n- // a double click\n- this.clearTimer();\n- this.callback('dblrightclick', [evt]);\n- return !this.stopDouble;\n- } else {\n- //Set the rightclickTimerId, send evt only if double is \n- // true else trigger single\n- var clickEvent = this['double'] ?\n- OpenLayers.Util.extend({}, evt) :\n- this.callback('rightclick', [evt]);\n-\n- var delayedRightCall = OpenLayers.Function.bind(\n- this.delayedRightCall,\n- this,\n- clickEvent\n- );\n- this.rightclickTimerId = window.setTimeout(\n- delayedRightCall, this.delay\n- );\n- }\n+ if (this.drillDown === true) {\n+ this.hover = false;\n }\n- return !this.stopSingle;\n- },\n \n- /**\n- * Method: delayedRightCall\n- * Sets <rightclickTimerId> to null. And optionally triggers the \n- * rightclick callback if evt is set.\n- */\n- delayedRightCall: function(evt) {\n- this.rightclickTimerId = null;\n- if (evt) {\n- this.callback('rightclick', [evt]);\n+ if (this.hover) {\n+ this.handler = new OpenLayers.Handler.Hover(\n+ this, {\n+ move: this.cancelHover,\n+ pause: this.getInfoForHover\n+ },\n+ OpenLayers.Util.extend(\n+ this.handlerOptions.hover || {}, {\n+ delay: 250\n+ }\n+ )\n+ );\n+ } else {\n+ var callbacks = {};\n+ callbacks[this.clickCallback] = this.getInfoForClick;\n+ this.handler = new OpenLayers.Handler.Click(\n+ this, callbacks, this.handlerOptions.click || {}\n+ );\n }\n },\n \n /**\n- * Method: click\n- * Handle click events from the browser. This is registered as a listener\n- * for click events and should not be called from other events in this\n- * handler.\n+ * Method: getInfoForClick \n+ * Called on click\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n */\n- click: function(evt) {\n- if (!this.last) {\n- this.last = this.getEventInfo(evt);\n- }\n- this.handleSingle(evt);\n- return !this.stopSingle;\n+ getInfoForClick: function(evt) {\n+ this.request(evt.xy, {});\n },\n \n /**\n- * Method: dblclick\n- * Handle dblclick. For a dblclick, we get two clicks in some browsers\n- * (FF) and one in others (IE). So we need to always register for\n- * dblclick to properly handle single clicks. This method is registered\n- * as a listener for the dblclick browser event. It should *not* be\n- * called by other methods in this handler.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Method: getInfoForHover\n+ * Pause callback for the hover handler\n+ *\n+ * Parameters:\n+ * evt - {Object}\n */\n- dblclick: function(evt) {\n- this.handleDouble(evt);\n- return !this.stopDouble;\n+ getInfoForHover: function(evt) {\n+ this.request(evt.xy, {\n+ hover: true\n+ });\n },\n \n- /** \n- * Method: handleDouble\n- * Handle double-click sequence.\n+ /**\n+ * Method: cancelHover\n+ * Cancel callback for the hover handler\n */\n- handleDouble: function(evt) {\n- if (this.passesDblclickTolerance(evt)) {\n- if (this[\"double\"]) {\n- this.callback(\"dblclick\", [evt]);\n+ cancelHover: function() {\n+ if (this.hoverRequest) {\n+ --this.pending;\n+ if (this.pending <= 0) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ this.pending = 0;\n }\n- // to prevent a dblclick from firing the click callback in IE\n- this.clearTimer();\n+ this.hoverRequest.abort();\n+ this.hoverRequest = null;\n }\n },\n \n- /** \n- * Method: handleSingle\n- * Handle single click sequence.\n+ /**\n+ * Method: findLayers\n+ * Internal method to get the layers, independent of whether we are\n+ * inspecting the map or using a client-provided array\n */\n- handleSingle: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.timerId != null) {\n- // already received a click\n- if (this.last.touches && this.last.touches.length === 1) {\n- // touch device, no dblclick event - this may be a double\n- if (this[\"double\"]) {\n- // on Android don't let the browser zoom on the page\n- OpenLayers.Event.preventDefault(evt);\n- }\n- this.handleDouble(evt);\n- }\n- // if we're not in a touch environment we clear the click timer\n- // if we've got a second touch, we'll get two touchend events\n- if (!this.last.touches || this.last.touches.length !== 2) {\n- this.clearTimer();\n+ findLayers: function() {\n+ var candidates = this.layers || this.map.layers;\n+ var layers = [];\n+ var layer;\n+ for (var i = candidates.length - 1; i >= 0; --i) {\n+ layer = candidates[i];\n+ if (layer instanceof OpenLayers.Layer.WMTS &&\n+ layer.requestEncoding === this.requestEncoding &&\n+ (!this.queryVisible || layer.getVisibility())) {\n+ layers.push(layer);\n+ if (!this.drillDown || this.hover) {\n+ break;\n }\n- } else {\n- // remember the first click info so we can compare to the second\n- this.first = this.getEventInfo(evt);\n- // set the timer, send evt only if single is true\n- //use a clone of the event object because it will no longer \n- //be a valid event object in IE in the timer callback\n- var clickEvent = this.single ?\n- OpenLayers.Util.extend({}, evt) : null;\n- this.queuePotentialClick(clickEvent);\n }\n }\n+ return layers;\n },\n \n- /** \n- * Method: queuePotentialClick\n- * This method is separated out largely to make testing easier (so we\n- * don't have to override window.setTimeout)\n+ /**\n+ * Method: buildRequestOptions\n+ * Build an object with the relevant options for the GetFeatureInfo request.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.WMTS>} A WMTS layer.\n+ * xy - {<OpenLayers.Pixel>} The position on the map where the \n+ * mouse event occurred.\n */\n- queuePotentialClick: function(evt) {\n- this.timerId = window.setTimeout(\n- OpenLayers.Function.bind(this.delayedCall, this, evt),\n- this.delay\n+ buildRequestOptions: function(layer, xy) {\n+ var loc = this.map.getLonLatFromPixel(xy);\n+ var getTileUrl = layer.getURL(\n+ new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat)\n );\n+ var params = OpenLayers.Util.getParameters(getTileUrl);\n+ var tileInfo = layer.getTileInfo(loc);\n+ OpenLayers.Util.extend(params, {\n+ service: \"WMTS\",\n+ version: layer.version,\n+ request: \"GetFeatureInfo\",\n+ infoFormat: this.infoFormat,\n+ i: tileInfo.i,\n+ j: tileInfo.j\n+ });\n+ OpenLayers.Util.applyDefaults(params, this.vendorParams);\n+ return {\n+ url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,\n+ params: OpenLayers.Util.upperCaseObject(params),\n+ callback: function(request) {\n+ this.handleResponse(xy, request, layer);\n+ },\n+ scope: this\n+ };\n },\n \n /**\n- * Method: passesTolerance\n- * Determine whether the event is within the optional pixel tolerance. Note\n- * that the pixel tolerance check only works if mousedown events get to\n- * the listeners registered here. If they are stopped by other elements,\n- * the <pixelTolerance> will have no effect here (this method will always\n- * return true).\n- *\n- * Returns:\n- * {Boolean} The click is within the pixel tolerance (if specified).\n+ * Method: request\n+ * Sends a GetFeatureInfo request to the WMTS\n+ * \n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event \n+ * occurred.\n+ * options - {Object} additional options for this method.\n+ * \n+ * Valid options:\n+ * - *hover* {Boolean} true if we do the request for the hover handler\n */\n- passesTolerance: function(evt) {\n- var passes = true;\n- if (this.pixelTolerance != null && this.down && this.down.xy) {\n- passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n- // for touch environments, we also enforce that all touches\n- // start and end within the given tolerance to be considered a click\n- if (passes && this.touch &&\n- this.down.touches.length === this.last.touches.length) {\n- // the touchend event doesn't come with touches, so we check\n- // down and last\n- for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n- if (this.getTouchDistance(\n- this.down.touches[i],\n- this.last.touches[i]\n- ) > this.pixelTolerance) {\n- passes = false;\n- break;\n+ request: function(xy, options) {\n+ options = options || {};\n+ var layers = this.findLayers();\n+ if (layers.length > 0) {\n+ var issue, layer;\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ layer = layers[i];\n+ issue = this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: xy,\n+ layer: layer\n+ });\n+ if (issue !== false) {\n+ ++this.pending;\n+ var requestOptions = this.buildRequestOptions(layer, xy);\n+ var request = OpenLayers.Request.GET(requestOptions);\n+ if (options.hover === true) {\n+ this.hoverRequest = request;\n }\n }\n }\n- }\n- return passes;\n- },\n-\n- /** \n- * Method: getTouchDistance\n- *\n- * Returns:\n- * {Boolean} The pixel displacement between two touches.\n- */\n- getTouchDistance: function(from, to) {\n- return Math.sqrt(\n- Math.pow(from.clientX - to.clientX, 2) +\n- Math.pow(from.clientY - to.clientY, 2)\n- );\n- },\n-\n- /**\n- * Method: passesDblclickTolerance\n- * Determine whether the event is within the optional double-cick pixel \n- * tolerance.\n- *\n- * Returns:\n- * {Boolean} The click is within the double-click pixel tolerance.\n- */\n- passesDblclickTolerance: function(evt) {\n- var passes = true;\n- if (this.down && this.first) {\n- passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;\n- }\n- return passes;\n- },\n-\n- /**\n- * Method: clearTimer\n- * Clear the timer and set <timerId> to null.\n- */\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null;\n- }\n- if (this.rightclickTimerId != null) {\n- window.clearTimeout(this.rightclickTimerId);\n- this.rightclickTimerId = null;\n+ if (this.pending > 0) {\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n+ }\n }\n },\n \n /**\n- * Method: delayedCall\n- * Sets <timerId> to null. And optionally triggers the click callback if\n- * evt is set.\n+ * Method: handleResponse\n+ * Handler for the GetFeatureInfo response.\n+ * \n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event \n+ * occurred.\n+ * request - {XMLHttpRequest} The request object.\n+ * layer - {<OpenLayers.Layer.WMTS>} The queried layer.\n */\n- delayedCall: function(evt) {\n- this.timerId = null;\n- if (evt) {\n- this.callback(\"click\", [evt]);\n+ handleResponse: function(xy, request, layer) {\n+ --this.pending;\n+ if (this.pending <= 0) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ this.pending = 0;\n }\n- },\n-\n- /**\n- * Method: getEventInfo\n- * This method allows us to store event information without storing the\n- * actual event. In touch devices (at least), the same event is \n- * modified between touchstart, touchmove, and touchend.\n- *\n- * Returns:\n- * {Object} An object with event related info.\n- */\n- getEventInfo: function(evt) {\n- var touches;\n- if (evt.touches) {\n- var len = evt.touches.length;\n- touches = new Array(len);\n- var touch;\n- for (var i = 0; i < len; i++) {\n- touch = evt.touches[i];\n- touches[i] = {\n- clientX: touch.olClientX,\n- clientY: touch.olClientY\n- };\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"exception\", {\n+ xy: xy,\n+ request: request,\n+ layer: layer\n+ });\n+ } else {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ var features, except;\n+ try {\n+ features = this.format.read(doc);\n+ } catch (error) {\n+ except = true;\n+ this.events.triggerEvent(\"exception\", {\n+ xy: xy,\n+ request: request,\n+ error: error,\n+ layer: layer\n+ });\n+ }\n+ if (!except) {\n+ this.events.triggerEvent(\"getfeatureinfo\", {\n+ text: request.responseText,\n+ features: features,\n+ request: request,\n+ xy: xy,\n+ layer: layer\n+ });\n }\n }\n- return {\n- xy: evt.xy,\n- touches: touches\n- };\n- },\n-\n- /**\n- * APIMethod: deactivate\n- * Deactivate the handler.\n- *\n- * Returns:\n- * {Boolean} The handler was successfully deactivated.\n- */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- this.down = null;\n- this.first = null;\n- this.last = null;\n- deactivated = true;\n- }\n- return deactivated;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Click\"\n+ CLASS_NAME: \"OpenLayers.Control.WMTSGetFeatureInfo\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Path.js\n+ OpenLayers/Control/ModifyFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Handler/Point.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Handler/Keyboard.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Path\n- * Handler to draw a path on the map. Path is displayed on mouse down,\n- * moves on mouse move, and is finished on mouse up.\n+ * Class: OpenLayers.Control.ModifyFeature\n+ * Control to modify features. When activated, a click renders the vertices\n+ * of a feature - these vertices can then be dragged. By default, the\n+ * delete key will delete the vertex under the mouse. New features are\n+ * added by dragging \"virtual vertices\" between vertices. Create a new\n+ * control with the <OpenLayers.Control.ModifyFeature> constructor.\n *\n- * Inherits from:\n- * - <OpenLayers.Handler.Point>\n+ * Inherits From:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {\n+OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: line\n- * {<OpenLayers.Feature.Vector>}\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, dragging vertices will continue even if the\n+ * mouse cursor leaves the map viewport. Default is false.\n */\n- line: null,\n+ documentDrag: false,\n \n /**\n- * APIProperty: maxVertices\n- * {Number} The maximum number of vertices which can be drawn by this\n- * handler. When the number of vertices reaches maxVertices, the\n- * geometry is automatically finalized. Default is null.\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict modification to a limited set of geometry\n+ * types, send a list of strings corresponding to the geometry class\n+ * names.\n */\n- maxVertices: null,\n+ geometryTypes: null,\n \n /**\n- * Property: doubleTouchTolerance\n- * {Number} Maximum number of pixels between two touches for\n- * the gesture to be considered a \"finalize feature\" action.\n- * Default is 20.\n+ * APIProperty: clickout\n+ * {Boolean} Unselect features when clicking outside any feature.\n+ * Default is true.\n */\n- doubleTouchTolerance: 20,\n+ clickout: true,\n \n /**\n- * Property: freehand\n- * {Boolean} In freehand mode, the handler starts the path on mouse down,\n- * adds a point for every mouse move, and finishes the path on mouse up.\n- * Outside of freehand mode, a point is added to the path on every mouse\n- * click and double-click finishes the path.\n+ * APIProperty: toggle\n+ * {Boolean} Unselect a selected feature on click.\n+ * Default is true.\n */\n- freehand: false,\n+ toggle: true,\n \n /**\n- * Property: freehandToggle\n- * {String} If set, freehandToggle is checked on mouse events and will set\n- * the freehand mode to the opposite of this.freehand. To disallow\n- * toggling between freehand and non-freehand mode, set freehandToggle to\n- * null. Acceptable toggle values are 'shiftKey', 'ctrlKey', and 'altKey'.\n+ * APIProperty: standalone\n+ * {Boolean} Set to true to create a control without SelectFeature\n+ * capabilities. Default is false. If standalone is true, to modify\n+ * a feature, call the <selectFeature> method with the target feature.\n+ * Note that you must call the <unselectFeature> method to finish\n+ * feature modification in standalone mode (before starting to modify\n+ * another feature).\n */\n- freehandToggle: 'shiftKey',\n+ standalone: false,\n \n /**\n- * Property: timerId\n- * {Integer} The timer used to test the double touch.\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>}\n */\n- timerId: null,\n+ layer: null,\n \n /**\n- * Property: redoStack\n- * {Array} Stack containing points removed with <undo>.\n+ * Property: feature\n+ * {<OpenLayers.Feature.Vector>} Feature currently available for modification.\n */\n- redoStack: null,\n+ feature: null,\n \n /**\n- * Constructor: OpenLayers.Handler.Path\n- * Create a new path hander\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that owns this handler\n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} An optional object with properties to be set on the\n- * handler\n- *\n- * Named callbacks:\n- * create - Called when a sketch is first created. Callback called with\n- * the creation point geometry and sketch feature.\n- * modify - Called with each move of a vertex with the vertex (point)\n- * geometry and the sketch feature.\n- * point - Called as each point is added. Receives the new point geometry.\n- * done - Called when the point drawing is finished. The callback will\n- * recieve a single argument, the linestring geometry.\n- * cancel - Called when the handler is deactivated while drawing. The\n- * cancel callback will receive a geometry.\n+ * Property: vertex\n+ * {<OpenLayers.Feature.Vector>} Vertex currently being modified.\n */\n+ vertex: null,\n \n /**\n- * Method: createFeature\n- * Add temporary geometries\n- *\n- * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new\n- * feature.\n+ * Property: vertices\n+ * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available\n+ * for dragging.\n */\n- createFeature: function(pixel) {\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- var geometry = new OpenLayers.Geometry.Point(\n- lonlat.lon, lonlat.lat\n- );\n- this.point = new OpenLayers.Feature.Vector(geometry);\n- this.line = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.LineString([this.point.geometry])\n- );\n- this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n- this.point.geometry.clearBounds();\n- this.layer.addFeatures([this.line, this.point], {\n- silent: true\n- });\n- },\n+ vertices: null,\n \n /**\n- * Method: destroyFeature\n- * Destroy temporary geometries\n- *\n- * Parameters:\n- * force - {Boolean} Destroy even if persist is true.\n+ * Property: virtualVertices\n+ * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle\n+ * of each edge.\n */\n- destroyFeature: function(force) {\n- OpenLayers.Handler.Point.prototype.destroyFeature.call(\n- this, force);\n- this.line = null;\n- },\n+ virtualVertices: null,\n \n /**\n- * Method: destroyPersistedFeature\n- * Destroy the persisted feature.\n+ * Property: handlers\n+ * {Object}\n */\n- destroyPersistedFeature: function() {\n- var layer = this.layer;\n- if (layer && layer.features.length > 2) {\n- this.layer.features[0].destroy();\n- }\n- },\n+ handlers: null,\n \n /**\n- * Method: removePoint\n- * Destroy the temporary point.\n+ * APIProperty: deleteCodes\n+ * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable\n+ * vertex deltion by keypress. If non-null, keypresses with codes\n+ * in this array will delete vertices under the mouse. Default\n+ * is 46 and 68, the 'delete' and lowercase 'd' keys.\n */\n- removePoint: function() {\n- if (this.point) {\n- this.layer.removeFeatures([this.point]);\n- }\n- },\n+ deleteCodes: null,\n \n /**\n- * Method: addPoint\n- * Add point to geometry. Send the point index to override\n- * the behavior of LinearRing that disregards adding duplicate points.\n- *\n- * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.\n+ * APIProperty: virtualStyle\n+ * {Object} A symbolizer to be used for virtual vertices.\n */\n- addPoint: function(pixel) {\n- this.layer.removeFeatures([this.point]);\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- this.point = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)\n- );\n- this.line.geometry.addComponent(\n- this.point.geometry, this.line.geometry.components.length\n- );\n- this.layer.addFeatures([this.point]);\n- this.callback(\"point\", [this.point.geometry, this.getGeometry()]);\n- this.callback(\"modify\", [this.point.geometry, this.getSketch()]);\n- this.drawFeature();\n- delete this.redoStack;\n- },\n+ virtualStyle: null,\n \n /**\n- * Method: insertXY\n- * Insert a point in the current sketch given x & y coordinates. The new\n- * point is inserted immediately before the most recently drawn point.\n- *\n- * Parameters:\n- * x - {Number} The x-coordinate of the point.\n- * y - {Number} The y-coordinate of the point.\n+ * APIProperty: vertexRenderIntent\n+ * {String} The renderIntent to use for vertices. If no <virtualStyle> is\n+ * provided, this renderIntent will also be used for virtual vertices, with\n+ * a fillOpacity and strokeOpacity of 0.3. Default is null, which means\n+ * that the layer's default style will be used for vertices.\n */\n- insertXY: function(x, y) {\n- this.line.geometry.addComponent(\n- new OpenLayers.Geometry.Point(x, y),\n- this.getCurrentPointIndex()\n- );\n- this.drawFeature();\n- delete this.redoStack;\n- },\n+ vertexRenderIntent: null,\n \n /**\n- * Method: insertDeltaXY\n- * Insert a point given offsets from the previously inserted point.\n- *\n- * Parameters:\n- * dx - {Number} The x-coordinate offset of the point.\n- * dy - {Number} The y-coordinate offset of the point.\n+ * APIProperty: mode\n+ * {Integer} Bitfields specifying the modification mode. Defaults to\n+ * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a\n+ * combination of options, use the | operator. For example, to allow\n+ * the control to both resize and rotate features, use the following\n+ * syntax\n+ * (code)\n+ * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |\n+ * OpenLayers.Control.ModifyFeature.ROTATE;\n+ * (end)\n */\n- insertDeltaXY: function(dx, dy) {\n- var previousIndex = this.getCurrentPointIndex() - 1;\n- var p0 = this.line.geometry.components[previousIndex];\n- if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {\n- this.insertXY(p0.x + dx, p0.y + dy);\n- }\n- },\n+ mode: null,\n \n /**\n- * Method: insertDirectionLength\n- * Insert a point in the current sketch given a direction and a length.\n- *\n- * Parameters:\n- * direction - {Number} Degrees clockwise from the positive x-axis.\n- * length - {Number} Distance from the previously drawn point.\n+ * APIProperty: createVertices\n+ * {Boolean} Create new vertices by dragging the virtual vertices\n+ * in the middle of each edge. Default is true.\n */\n- insertDirectionLength: function(direction, length) {\n- direction *= Math.PI / 180;\n- var dx = length * Math.cos(direction);\n- var dy = length * Math.sin(direction);\n- this.insertDeltaXY(dx, dy);\n- },\n+ createVertices: true,\n \n /**\n- * Method: insertDeflectionLength\n- * Insert a point in the current sketch given a deflection and a length.\n- * The deflection should be degrees clockwise from the previously \n- * digitized segment.\n- *\n- * Parameters:\n- * deflection - {Number} Degrees clockwise from the previous segment.\n- * length - {Number} Distance from the previously drawn point.\n+ * Property: modified\n+ * {Boolean} The currently selected feature has been modified.\n */\n- insertDeflectionLength: function(deflection, length) {\n- var previousIndex = this.getCurrentPointIndex() - 1;\n- if (previousIndex > 0) {\n- var p1 = this.line.geometry.components[previousIndex];\n- var p0 = this.line.geometry.components[previousIndex - 1];\n- var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);\n- this.insertDirectionLength(\n- (theta * 180 / Math.PI) + deflection, length\n- );\n- }\n- },\n+ modified: false,\n \n /**\n- * Method: getCurrentPointIndex\n- * \n- * Returns:\n- * {Number} The index of the most recently drawn point.\n+ * Property: radiusHandle\n+ * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.\n */\n- getCurrentPointIndex: function() {\n- return this.line.geometry.components.length - 1;\n- },\n+ radiusHandle: null,\n \n+ /**\n+ * Property: dragHandle\n+ * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.\n+ */\n+ dragHandle: null,\n \n /**\n- * Method: undo\n- * Remove the most recently added point in the sketch geometry.\n+ * APIProperty: onModificationStart \n+ * {Function} *Deprecated*. Register for \"beforefeaturemodified\" instead.\n+ * The \"beforefeaturemodified\" event is triggered on the layer before\n+ * any modification begins.\n *\n- * Returns: \n- * {Boolean} A point was removed.\n+ * Optional function to be called when a feature is selected\n+ * to be modified. The function should expect to be called with a\n+ * feature. This could be used for example to allow to lock the\n+ * feature on server-side.\n */\n- undo: function() {\n- var geometry = this.line.geometry;\n- var components = geometry.components;\n- var index = this.getCurrentPointIndex() - 1;\n- var target = components[index];\n- var undone = geometry.removeComponent(target);\n- if (undone) {\n- // On touch devices, set the current (\"mouse location\") point to\n- // match the last digitized point.\n- if (this.touch && index > 0) {\n- components = geometry.components; // safety\n- var lastpt = components[index - 1];\n- var curptidx = this.getCurrentPointIndex();\n- var curpt = components[curptidx];\n- curpt.x = lastpt.x;\n- curpt.y = lastpt.y;\n- }\n- if (!this.redoStack) {\n- this.redoStack = [];\n- }\n- this.redoStack.push(target);\n- this.drawFeature();\n- }\n- return undone;\n- },\n+ onModificationStart: function() {},\n \n /**\n- * Method: redo\n- * Reinsert the most recently removed point resulting from an <undo> call.\n- * The undo stack is deleted whenever a point is added by other means.\n+ * APIProperty: onModification\n+ * {Function} *Deprecated*. Register for \"featuremodified\" instead.\n+ * The \"featuremodified\" event is triggered on the layer with each\n+ * feature modification.\n *\n- * Returns: \n- * {Boolean} A point was added.\n+ * Optional function to be called when a feature has been\n+ * modified. The function should expect to be called with a feature.\n */\n- redo: function() {\n- var target = this.redoStack && this.redoStack.pop();\n- if (target) {\n- this.line.geometry.addComponent(target, this.getCurrentPointIndex());\n- this.drawFeature();\n- }\n- return !!target;\n- },\n+ onModification: function() {},\n \n /**\n- * Method: freehandMode\n- * Determine whether to behave in freehand mode or not.\n+ * APIProperty: onModificationEnd\n+ * {Function} *Deprecated*. Register for \"afterfeaturemodified\" instead.\n+ * The \"afterfeaturemodified\" event is triggered on the layer after\n+ * a feature has been modified.\n *\n- * Returns:\n- * {Boolean}\n+ * Optional function to be called when a feature is finished \n+ * being modified. The function should expect to be called with a\n+ * feature.\n */\n- freehandMode: function(evt) {\n- return (this.freehandToggle && evt[this.freehandToggle]) ?\n- !this.freehand : this.freehand;\n- },\n+ onModificationEnd: function() {},\n \n /**\n- * Method: modifyFeature\n- * Modify the existing geometry given the new point\n+ * Constructor: OpenLayers.Control.ModifyFeature\n+ * Create a new modify feature control.\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The updated pixel location for the latest\n- * point.\n- * drawing - {Boolean} Indicate if we're currently drawing.\n+ * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that\n+ * will be modified.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * control.\n */\n- modifyFeature: function(pixel, drawing) {\n- if (!this.line) {\n- this.createFeature(pixel);\n+ initialize: function(layer, options) {\n+ options = options || {};\n+ this.layer = layer;\n+ this.vertices = [];\n+ this.virtualVertices = [];\n+ this.virtualStyle = OpenLayers.Util.extend({},\n+ this.layer.style ||\n+ this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)\n+ );\n+ this.virtualStyle.fillOpacity = 0.3;\n+ this.virtualStyle.strokeOpacity = 0.3;\n+ this.deleteCodes = [46, 68];\n+ this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (!(OpenLayers.Util.isArray(this.deleteCodes))) {\n+ this.deleteCodes = [this.deleteCodes];\n }\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- this.point.geometry.x = lonlat.lon;\n- this.point.geometry.y = lonlat.lat;\n- this.callback(\"modify\", [this.point.geometry, this.getSketch(), drawing]);\n- this.point.geometry.clearBounds();\n- this.drawFeature();\n- },\n \n- /**\n- * Method: drawFeature\n- * Render geometries on the temporary layer.\n- */\n- drawFeature: function() {\n- this.layer.drawFeature(this.line, this.style);\n- this.layer.drawFeature(this.point, this.style);\n+ // configure the drag handler\n+ var dragCallbacks = {\n+ down: function(pixel) {\n+ this.vertex = null;\n+ var feature = this.layer.getFeatureFromEvent(\n+ this.handlers.drag.evt);\n+ if (feature) {\n+ this.dragStart(feature);\n+ } else if (this.clickout) {\n+ this._unselect = this.feature;\n+ }\n+ },\n+ move: function(pixel) {\n+ delete this._unselect;\n+ if (this.vertex) {\n+ this.dragVertex(this.vertex, pixel);\n+ }\n+ },\n+ up: function() {\n+ this.handlers.drag.stopDown = false;\n+ if (this._unselect) {\n+ this.unselectFeature(this._unselect);\n+ delete this._unselect;\n+ }\n+ },\n+ done: function(pixel) {\n+ if (this.vertex) {\n+ this.dragComplete(this.vertex);\n+ }\n+ }\n+ };\n+ var dragOptions = {\n+ documentDrag: this.documentDrag,\n+ stopDown: false\n+ };\n+\n+ // configure the keyboard handler\n+ var keyboardOptions = {\n+ keydown: this.handleKeypress\n+ };\n+ this.handlers = {\n+ keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),\n+ drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)\n+ };\n },\n \n /**\n- * Method: getSketch\n- * Return the sketch feature.\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>}\n+ * APIMethod: destroy\n+ * Take care of things that are not handled in superclass.\n */\n- getSketch: function() {\n- return this.line;\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.un({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ }\n+ this.layer = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, []);\n },\n \n /**\n- * Method: getGeometry\n- * Return the sketch geometry. If <multi> is true, this will return\n- * a multi-part geometry.\n- *\n+ * APIMethod: activate\n+ * Activate the control.\n+ * \n * Returns:\n- * {<OpenLayers.Geometry.LineString>}\n+ * {Boolean} Successfully activated the control.\n */\n- getGeometry: function() {\n- var geometry = this.line && this.line.geometry;\n- if (geometry && this.multi) {\n- geometry = new OpenLayers.Geometry.MultiLineString([geometry]);\n- }\n- return geometry;\n+ activate: function() {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ return (this.handlers.keyboard.activate() &&\n+ this.handlers.drag.activate() &&\n+ OpenLayers.Control.prototype.activate.apply(this, arguments));\n },\n \n /**\n- * method: touchstart\n- * handle touchstart.\n- *\n- * parameters:\n- * evt - {event} the browser event\n+ * APIMethod: deactivate\n+ * Deactivate the control.\n *\n- * returns:\n- * {boolean} allow event propagation\n+ * Returns: \n+ * {Boolean} Successfully deactivated the control.\n */\n- touchstart: function(evt) {\n- if (this.timerId &&\n- this.passesTolerance(this.lastTouchPx, evt.xy,\n- this.doubleTouchTolerance)) {\n- // double-tap, finalize the geometry\n- this.finishGeometry();\n- window.clearTimeout(this.timerId);\n- this.timerId = null;\n- return false;\n- } else {\n- if (this.timerId) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null;\n+ deactivate: function() {\n+ var deactivated = false;\n+ // the return from the controls is unimportant in this case\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.map.events.un({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ this.layer.removeFeatures(this.vertices, {\n+ silent: true\n+ });\n+ this.layer.removeFeatures(this.virtualVertices, {\n+ silent: true\n+ });\n+ this.vertices = [];\n+ this.handlers.drag.deactivate();\n+ this.handlers.keyboard.deactivate();\n+ var feature = this.feature;\n+ if (feature && feature.geometry && feature.layer) {\n+ this.unselectFeature(feature);\n }\n- this.timerId = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.timerId = null;\n- }, this), 300);\n- return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt);\n+ deactivated = true;\n }\n+ return deactivated;\n },\n \n /**\n- * Method: down\n- * Handle mousedown and touchstart. Add a new point to the geometry and\n- * render it. Return determines whether to propagate the event on the map.\n- * \n- * Parameters:\n- * evt - {Event} The browser event\n+ * Method: beforeSelectFeature\n+ * Called before a feature is selected.\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.\n */\n- down: function(evt) {\n- var stopDown = this.stopDown;\n- if (this.freehandMode(evt)) {\n- stopDown = true;\n- if (this.touch) {\n- this.modifyFeature(evt.xy, !!this.lastUp);\n- OpenLayers.Event.stop(evt);\n+ beforeSelectFeature: function(feature) {\n+ return this.layer.events.triggerEvent(\n+ \"beforefeaturemodified\", {\n+ feature: feature\n }\n- }\n- if (!this.touch && (!this.lastDown ||\n- !this.passesTolerance(this.lastDown, evt.xy,\n- this.pixelTolerance))) {\n- this.modifyFeature(evt.xy, !!this.lastUp);\n- }\n- this.mouseDown = true;\n- this.lastDown = evt.xy;\n- this.stoppedDown = stopDown;\n- return !stopDown;\n+ );\n },\n \n /**\n- * Method: move\n- * Handle mousemove and touchmove. Adjust the geometry and redraw.\n- * Return determines whether to propagate the event on the map.\n- * \n- * Parameters:\n- * evt - {Event} The browser event\n+ * APIMethod: selectFeature\n+ * Select a feature for modification in standalone mode. In non-standalone\n+ * mode, this method is called when a feature is selected by clicking.\n+ * Register a listener to the beforefeaturemodified event and return false\n+ * to prevent feature modification.\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} the selected feature.\n */\n- move: function(evt) {\n- if (this.stoppedDown && this.freehandMode(evt)) {\n- if (this.persist) {\n- this.destroyPersistedFeature();\n- }\n- if (this.maxVertices && this.line &&\n- this.line.geometry.components.length === this.maxVertices) {\n- this.removePoint();\n- this.finalize();\n- } else {\n- this.addPoint(evt.xy);\n+ selectFeature: function(feature) {\n+ if (this.feature === feature ||\n+ (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,\n+ feature.geometry.CLASS_NAME) == -1)) {\n+ return;\n+ }\n+ if (this.beforeSelectFeature(feature) !== false) {\n+ if (this.feature) {\n+ this.unselectFeature(this.feature);\n }\n- return false;\n+ this.feature = feature;\n+ this.layer.selectedFeatures.push(feature);\n+ this.layer.drawFeature(feature, 'select');\n+ this.modified = false;\n+ this.resetVertices();\n+ this.onModificationStart(this.feature);\n }\n- if (!this.touch && (!this.mouseDown || this.stoppedDown)) {\n- this.modifyFeature(evt.xy, !!this.lastUp);\n+ // keep track of geometry modifications\n+ var modified = feature.modified;\n+ if (feature.geometry && !(modified && modified.geometry)) {\n+ this._originalGeometry = feature.geometry.clone();\n }\n- return true;\n },\n \n /**\n- * Method: up\n- * Handle mouseup and touchend. Send the latest point in the geometry to\n- * the control. Return determines whether to propagate the event on the map.\n- * \n- * Parameters:\n- * evt - {Event} The browser event\n+ * APIMethod: unselectFeature\n+ * Called when the select feature control unselects a feature.\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The unselected feature.\n */\n- up: function(evt) {\n- if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {\n- if (this.stoppedDown && this.freehandMode(evt)) {\n- if (this.persist) {\n- this.destroyPersistedFeature();\n- }\n- this.removePoint();\n- this.finalize();\n- } else {\n- if (this.passesTolerance(this.lastDown, evt.xy,\n- this.pixelTolerance)) {\n- if (this.touch) {\n- this.modifyFeature(evt.xy);\n- }\n- if (this.lastUp == null && this.persist) {\n- this.destroyPersistedFeature();\n- }\n- this.addPoint(evt.xy);\n- this.lastUp = evt.xy;\n- if (this.line.geometry.components.length === this.maxVertices + 1) {\n- this.finishGeometry();\n- }\n- }\n- }\n+ unselectFeature: function(feature) {\n+ this.layer.removeFeatures(this.vertices, {\n+ silent: true\n+ });\n+ this.vertices = [];\n+ this.layer.destroyFeatures(this.virtualVertices, {\n+ silent: true\n+ });\n+ this.virtualVertices = [];\n+ if (this.dragHandle) {\n+ this.layer.destroyFeatures([this.dragHandle], {\n+ silent: true\n+ });\n+ delete this.dragHandle;\n }\n- this.stoppedDown = this.stopDown;\n- this.mouseDown = false;\n- return !this.stopUp;\n+ if (this.radiusHandle) {\n+ this.layer.destroyFeatures([this.radiusHandle], {\n+ silent: true\n+ });\n+ delete this.radiusHandle;\n+ }\n+ this.layer.drawFeature(this.feature, 'default');\n+ this.feature = null;\n+ OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);\n+ this.onModificationEnd(feature);\n+ this.layer.events.triggerEvent(\"afterfeaturemodified\", {\n+ feature: feature,\n+ modified: this.modified\n+ });\n+ this.modified = false;\n },\n \n- /**\n- * APIMethod: finishGeometry\n- * Finish the geometry and send it back to the control.\n- */\n- finishGeometry: function() {\n- var index = this.line.geometry.components.length - 1;\n- this.line.geometry.removeComponent(this.line.geometry.components[index]);\n- this.removePoint();\n- this.finalize();\n- },\n \n /**\n- * Method: dblclick \n- * Handle double-clicks.\n- * \n- * Parameters:\n- * evt - {Event} The browser event\n+ * Method: dragStart\n+ * Called by the drag handler before a feature is dragged. This method is\n+ * used to differentiate between points and vertices\n+ * of higher order geometries.\n *\n- * Returns: \n- * {Boolean} Allow event propagation\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be\n+ * dragged.\n */\n- dblclick: function(evt) {\n- if (!this.freehandMode(evt)) {\n- this.finishGeometry();\n+ dragStart: function(feature) {\n+ var isPoint = feature.geometry.CLASS_NAME ==\n+ 'OpenLayers.Geometry.Point';\n+ if (!this.standalone &&\n+ ((!feature._sketch && isPoint) || !feature._sketch)) {\n+ if (this.toggle && this.feature === feature) {\n+ // mark feature for unselection\n+ this._unselect = feature;\n+ }\n+ this.selectFeature(feature);\n+ }\n+ if (feature._sketch || isPoint) {\n+ // feature is a drag or virtual handle or point\n+ this.vertex = feature;\n+ this.handlers.drag.stopDown = true;\n }\n- return false;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Path\"\n-});\n-/* ======================================================================\n- OpenLayers/Handler/Polygon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Handler/Path.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Handler.Polygon\n- * Handler to draw a polygon on the map. Polygon is displayed on mouse down,\n- * moves on mouse move, and is finished on mouse up.\n- *\n- * Inherits from:\n- * - <OpenLayers.Handler.Path>\n- * - <OpenLayers.Handler>\n- */\n-OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {\n-\n- /** \n- * APIProperty: holeModifier\n- * {String} Key modifier to trigger hole digitizing. Acceptable values are\n- * \"altKey\", \"shiftKey\", or \"ctrlKey\". If not set, no hole digitizing\n- * will take place. Default is null.\n- */\n- holeModifier: null,\n-\n- /**\n- * Property: drawingHole\n- * {Boolean} Currently drawing an interior ring.\n- */\n- drawingHole: false,\n-\n- /**\n- * Property: polygon\n- * {<OpenLayers.Feature.Vector>}\n- */\n- polygon: null,\n-\n /**\n- * Constructor: OpenLayers.Handler.Polygon\n- * Create a Polygon Handler.\n+ * Method: dragVertex\n+ * Called by the drag handler with each drag move of a vertex.\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} The control that owns this handler\n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} An optional object with properties to be set on the\n- * handler\n- *\n- * Named callbacks:\n- * create - Called when a sketch is first created. Callback called with\n- * the creation point geometry and sketch feature.\n- * modify - Called with each move of a vertex with the vertex (point)\n- * geometry and the sketch feature.\n- * point - Called as each point is added. Receives the new point geometry.\n- * done - Called when the point drawing is finished. The callback will\n- * recieve a single argument, the polygon geometry.\n- * cancel - Called when the handler is deactivated while drawing. The\n- * cancel callback will receive a geometry.\n+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.\n+ * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.\n */\n+ dragVertex: function(vertex, pixel) {\n+ var pos = this.map.getLonLatFromViewPortPx(pixel);\n+ var geom = vertex.geometry;\n+ geom.move(pos.lon - geom.x, pos.lat - geom.y);\n+ this.modified = true;\n+ /**\n+ * Five cases:\n+ * 1) dragging a simple point\n+ * 2) dragging a virtual vertex\n+ * 3) dragging a drag handle\n+ * 4) dragging a real vertex\n+ * 5) dragging a radius handle\n+ */\n+ if (this.feature.geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ // dragging a simple point\n+ this.layer.events.triggerEvent(\"vertexmodified\", {\n+ vertex: vertex.geometry,\n+ feature: this.feature,\n+ pixel: pixel\n+ });\n+ } else {\n+ if (vertex._index) {\n+ // dragging a virtual vertex\n+ vertex.geometry.parent.addComponent(vertex.geometry,\n+ vertex._index);\n+ // move from virtual to real vertex\n+ delete vertex._index;\n+ OpenLayers.Util.removeItem(this.virtualVertices, vertex);\n+ this.vertices.push(vertex);\n+ } else if (vertex == this.dragHandle) {\n+ // dragging a drag handle\n+ this.layer.removeFeatures(this.vertices, {\n+ silent: true\n+ });\n+ this.vertices = [];\n+ if (this.radiusHandle) {\n+ this.layer.destroyFeatures([this.radiusHandle], {\n+ silent: true\n+ });\n+ this.radiusHandle = null;\n+ }\n+ } else if (vertex !== this.radiusHandle) {\n+ // dragging a real vertex\n+ this.layer.events.triggerEvent(\"vertexmodified\", {\n+ vertex: vertex.geometry,\n+ feature: this.feature,\n+ pixel: pixel\n+ });\n+ }\n+ // dragging a radius handle - no special treatment\n+ if (this.virtualVertices.length > 0) {\n+ this.layer.destroyFeatures(this.virtualVertices, {\n+ silent: true\n+ });\n+ this.virtualVertices = [];\n+ }\n+ this.layer.drawFeature(this.feature, this.standalone ? undefined :\n+ 'select');\n+ }\n+ // keep the vertex on top so it gets the mouseout after dragging\n+ // this should be removed in favor of an option to draw under or\n+ // maintain node z-index\n+ this.layer.drawFeature(vertex);\n+ },\n \n /**\n- * Method: createFeature\n- * Add temporary geometries\n+ * Method: dragComplete\n+ * Called by the drag handler when the feature dragging is complete.\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new\n- * feature.\n+ * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.\n */\n- createFeature: function(pixel) {\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- var geometry = new OpenLayers.Geometry.Point(\n- lonlat.lon, lonlat.lat\n- );\n- this.point = new OpenLayers.Feature.Vector(geometry);\n- this.line = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.LinearRing([this.point.geometry])\n- );\n- this.polygon = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Polygon([this.line.geometry])\n- );\n- this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n- this.point.geometry.clearBounds();\n- this.layer.addFeatures([this.polygon, this.point], {\n- silent: true\n+ dragComplete: function(vertex) {\n+ this.resetVertices();\n+ this.setFeatureState();\n+ this.onModification(this.feature);\n+ this.layer.events.triggerEvent(\"featuremodified\", {\n+ feature: this.feature\n });\n },\n \n /**\n- * Method: addPoint\n- * Add point to geometry.\n- *\n- * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The pixel location for the new point.\n+ * Method: setFeatureState\n+ * Called when the feature is modified. If the current state is not\n+ * INSERT or DELETE, the state is set to UPDATE.\n */\n- addPoint: function(pixel) {\n- if (!this.drawingHole && this.holeModifier &&\n- this.evt && this.evt[this.holeModifier]) {\n- var geometry = this.point.geometry;\n- var features = this.control.layer.features;\n- var candidate, polygon;\n- // look for intersections, last drawn gets priority\n- for (var i = features.length - 1; i >= 0; --i) {\n- candidate = features[i].geometry;\n- if ((candidate instanceof OpenLayers.Geometry.Polygon ||\n- candidate instanceof OpenLayers.Geometry.MultiPolygon) &&\n- candidate.intersects(geometry)) {\n- polygon = features[i];\n- this.control.layer.removeFeatures([polygon], {\n- silent: true\n- });\n- this.control.layer.events.registerPriority(\n- \"sketchcomplete\", this, this.finalizeInteriorRing\n- );\n- this.control.layer.events.registerPriority(\n- \"sketchmodified\", this, this.enforceTopology\n- );\n- polygon.geometry.addComponent(this.line.geometry);\n- this.polygon = polygon;\n- this.drawingHole = true;\n- break;\n- }\n+ setFeatureState: function() {\n+ if (this.feature.state != OpenLayers.State.INSERT &&\n+ this.feature.state != OpenLayers.State.DELETE) {\n+ this.feature.state = OpenLayers.State.UPDATE;\n+ if (this.modified && this._originalGeometry) {\n+ var feature = this.feature;\n+ feature.modified = OpenLayers.Util.extend(feature.modified, {\n+ geometry: this._originalGeometry\n+ });\n+ delete this._originalGeometry;\n }\n }\n- OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);\n },\n \n /**\n- * Method: getCurrentPointIndex\n- * \n- * Returns:\n- * {Number} The index of the most recently drawn point.\n+ * Method: resetVertices\n */\n- getCurrentPointIndex: function() {\n- return this.line.geometry.components.length - 2;\n+ resetVertices: function() {\n+ if (this.vertices.length > 0) {\n+ this.layer.removeFeatures(this.vertices, {\n+ silent: true\n+ });\n+ this.vertices = [];\n+ }\n+ if (this.virtualVertices.length > 0) {\n+ this.layer.removeFeatures(this.virtualVertices, {\n+ silent: true\n+ });\n+ this.virtualVertices = [];\n+ }\n+ if (this.dragHandle) {\n+ this.layer.destroyFeatures([this.dragHandle], {\n+ silent: true\n+ });\n+ this.dragHandle = null;\n+ }\n+ if (this.radiusHandle) {\n+ this.layer.destroyFeatures([this.radiusHandle], {\n+ silent: true\n+ });\n+ this.radiusHandle = null;\n+ }\n+ if (this.feature &&\n+ this.feature.geometry.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n+ if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {\n+ this.collectDragHandle();\n+ }\n+ if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |\n+ OpenLayers.Control.ModifyFeature.RESIZE))) {\n+ this.collectRadiusHandle();\n+ }\n+ if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {\n+ // Don't collect vertices when we're resizing\n+ if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {\n+ this.collectVertices();\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: enforceTopology\n- * Simple topology enforcement for drawing interior rings. Ensures vertices\n- * of interior rings are contained by exterior ring. Other topology \n- * rules are enforced in <finalizeInteriorRing> to allow drawing of \n- * rings that intersect only during the sketch (e.g. a \"C\" shaped ring\n- * that nearly encloses another ring).\n+ * Method: handleKeypress\n+ * Called by the feature handler on keypress. This is used to delete\n+ * vertices. If the <deleteCode> property is set, vertices will\n+ * be deleted when a feature is selected for modification and\n+ * the mouse is over a vertex.\n+ *\n+ * Parameters:\n+ * evt - {Event} Keypress event.\n */\n- enforceTopology: function(event) {\n- var point = event.vertex;\n- var components = this.line.geometry.components;\n- // ensure that vertices of interior ring are contained by exterior ring\n- if (!this.polygon.geometry.intersects(point)) {\n- var last = components[components.length - 3];\n- point.x = last.x;\n- point.y = last.y;\n+ handleKeypress: function(evt) {\n+ var code = evt.keyCode;\n+\n+ // check for delete key\n+ if (this.feature &&\n+ OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {\n+ var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);\n+ if (vertex &&\n+ OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&\n+ !this.handlers.drag.dragging && vertex.geometry.parent) {\n+ // remove the vertex\n+ vertex.geometry.parent.removeComponent(vertex.geometry);\n+ this.layer.events.triggerEvent(\"vertexremoved\", {\n+ vertex: vertex.geometry,\n+ feature: this.feature,\n+ pixel: evt.xy\n+ });\n+ this.layer.drawFeature(this.feature, this.standalone ?\n+ undefined : 'select');\n+ this.modified = true;\n+ this.resetVertices();\n+ this.setFeatureState();\n+ this.onModification(this.feature);\n+ this.layer.events.triggerEvent(\"featuremodified\", {\n+ feature: this.feature\n+ });\n+ }\n }\n },\n \n /**\n- * Method: finishGeometry\n- * Finish the geometry and send it back to the control.\n+ * Method: collectVertices\n+ * Collect the vertices from the modifiable feature's geometry and push\n+ * them on to the control's vertices array.\n */\n- finishGeometry: function() {\n- var index = this.line.geometry.components.length - 2;\n- this.line.geometry.removeComponent(this.line.geometry.components[index]);\n- this.removePoint();\n- this.finalize();\n- },\n+ collectVertices: function() {\n+ this.vertices = [];\n+ this.virtualVertices = [];\n+ var control = this;\n \n- /**\n- * Method: finalizeInteriorRing\n- * Enforces that new ring has some area and doesn't contain vertices of any\n- * other rings.\n- */\n- finalizeInteriorRing: function() {\n- var ring = this.line.geometry;\n- // ensure that ring has some area\n- var modified = (ring.getArea() !== 0);\n- if (modified) {\n- // ensure that new ring doesn't intersect any other rings\n- var rings = this.polygon.geometry.components;\n- for (var i = rings.length - 2; i >= 0; --i) {\n- if (ring.intersects(rings[i])) {\n- modified = false;\n- break;\n+ function collectComponentVertices(geometry) {\n+ var i, vertex, component, len;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ vertex = new OpenLayers.Feature.Vector(geometry);\n+ vertex._sketch = true;\n+ vertex.renderIntent = control.vertexRenderIntent;\n+ control.vertices.push(vertex);\n+ } else {\n+ var numVert = geometry.components.length;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ numVert -= 1;\n }\n- }\n- if (modified) {\n- // ensure that new ring doesn't contain any other rings\n- var target;\n- outer: for (var i = rings.length - 2; i > 0; --i) {\n- var points = rings[i].components;\n- for (var j = 0, jj = points.length; j < jj; ++j) {\n- if (ring.containsPoint(points[j])) {\n- modified = false;\n- break outer;\n+ for (i = 0; i < numVert; ++i) {\n+ component = geometry.components[i];\n+ if (component.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ vertex = new OpenLayers.Feature.Vector(component);\n+ vertex._sketch = true;\n+ vertex.renderIntent = control.vertexRenderIntent;\n+ control.vertices.push(vertex);\n+ } else {\n+ collectComponentVertices(component);\n+ }\n+ }\n+\n+ // add virtual vertices in the middle of each edge\n+ if (control.createVertices && geometry.CLASS_NAME != \"OpenLayers.Geometry.MultiPoint\") {\n+ for (i = 0, len = geometry.components.length; i < len - 1; ++i) {\n+ var prevVertex = geometry.components[i];\n+ var nextVertex = geometry.components[i + 1];\n+ if (prevVertex.CLASS_NAME == \"OpenLayers.Geometry.Point\" &&\n+ nextVertex.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ var x = (prevVertex.x + nextVertex.x) / 2;\n+ var y = (prevVertex.y + nextVertex.y) / 2;\n+ var point = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Point(x, y),\n+ null, control.virtualStyle\n+ );\n+ // set the virtual parent and intended index\n+ point.geometry.parent = geometry;\n+ point._index = i + 1;\n+ point._sketch = true;\n+ control.virtualVertices.push(point);\n }\n }\n }\n }\n }\n- if (modified) {\n- if (this.polygon.state !== OpenLayers.State.INSERT) {\n- this.polygon.state = OpenLayers.State.UPDATE;\n- }\n- } else {\n- this.polygon.geometry.removeComponent(ring);\n- }\n- this.restoreFeature();\n- return false;\n+ collectComponentVertices.call(this, this.feature.geometry);\n+ this.layer.addFeatures(this.virtualVertices, {\n+ silent: true\n+ });\n+ this.layer.addFeatures(this.vertices, {\n+ silent: true\n+ });\n },\n \n /**\n- * APIMethod: cancel\n- * Finish the geometry and call the \"cancel\" callback.\n+ * Method: collectDragHandle\n+ * Collect the drag handle for the selected geometry.\n */\n- cancel: function() {\n- if (this.drawingHole) {\n- this.polygon.geometry.removeComponent(this.line.geometry);\n- this.restoreFeature(true);\n- }\n- return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);\n+ collectDragHandle: function() {\n+ var geometry = this.feature.geometry;\n+ var center = geometry.getBounds().getCenterLonLat();\n+ var originGeometry = new OpenLayers.Geometry.Point(\n+ center.lon, center.lat\n+ );\n+ var origin = new OpenLayers.Feature.Vector(originGeometry);\n+ originGeometry.move = function(x, y) {\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ geometry.move(x, y);\n+ };\n+ origin._sketch = true;\n+ this.dragHandle = origin;\n+ this.dragHandle.renderIntent = this.vertexRenderIntent;\n+ this.layer.addFeatures([this.dragHandle], {\n+ silent: true\n+ });\n },\n \n /**\n- * Method: restoreFeature\n- * Move the feature from the sketch layer to the target layer.\n- *\n- * Properties: \n- * cancel - {Boolean} Cancel drawing. If falsey, the \"sketchcomplete\" event\n- * will be fired.\n+ * Method: collectRadiusHandle\n+ * Collect the radius handle for the selected geometry.\n */\n- restoreFeature: function(cancel) {\n- this.control.layer.events.unregister(\n- \"sketchcomplete\", this, this.finalizeInteriorRing\n+ collectRadiusHandle: function() {\n+ var geometry = this.feature.geometry;\n+ var bounds = geometry.getBounds();\n+ var center = bounds.getCenterLonLat();\n+ var originGeometry = new OpenLayers.Geometry.Point(\n+ center.lon, center.lat\n );\n- this.control.layer.events.unregister(\n- \"sketchmodified\", this, this.enforceTopology\n+ var radiusGeometry = new OpenLayers.Geometry.Point(\n+ bounds.right, bounds.bottom\n );\n- this.layer.removeFeatures([this.polygon], {\n- silent: true\n- });\n- this.control.layer.addFeatures([this.polygon], {\n+ var radius = new OpenLayers.Feature.Vector(radiusGeometry);\n+ var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);\n+ var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);\n+ var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);\n+\n+ radiusGeometry.move = function(x, y) {\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ var dx1 = this.x - originGeometry.x;\n+ var dy1 = this.y - originGeometry.y;\n+ var dx0 = dx1 - x;\n+ var dy0 = dy1 - y;\n+ if (rotate) {\n+ var a0 = Math.atan2(dy0, dx0);\n+ var a1 = Math.atan2(dy1, dx1);\n+ var angle = a1 - a0;\n+ angle *= 180 / Math.PI;\n+ geometry.rotate(angle, originGeometry);\n+ }\n+ if (resize) {\n+ var scale, ratio;\n+ // 'resize' together with 'reshape' implies that the aspect \n+ // ratio of the geometry will not be preserved whilst resizing \n+ if (reshape) {\n+ scale = dy1 / dy0;\n+ ratio = (dx1 / dx0) / scale;\n+ } else {\n+ var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));\n+ var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));\n+ scale = l1 / l0;\n+ }\n+ geometry.resize(scale, originGeometry, ratio);\n+ }\n+ };\n+ radius._sketch = true;\n+ this.radiusHandle = radius;\n+ this.radiusHandle.renderIntent = this.vertexRenderIntent;\n+ this.layer.addFeatures([this.radiusHandle], {\n silent: true\n });\n- this.drawingHole = false;\n- if (!cancel) {\n- // Re-trigger \"sketchcomplete\" so other listeners can do their\n- // business. While this is somewhat sloppy (if a listener is \n- // registered with registerPriority - not common - between the start\n- // and end of a single ring drawing - very uncommon - it will be \n- // called twice).\n- // TODO: In 3.0, collapse sketch handlers into geometry specific\n- // drawing controls.\n- this.control.layer.events.triggerEvent(\n- \"sketchcomplete\", {\n- feature: this.polygon\n- }\n- );\n- }\n },\n \n /**\n- * Method: destroyFeature\n- * Destroy temporary geometries\n+ * Method: setMap\n+ * Set the map property for the control and all handlers.\n *\n * Parameters:\n- * force - {Boolean} Destroy even if persist is true.\n+ * map - {<OpenLayers.Map>} The control's map.\n */\n- destroyFeature: function(force) {\n- OpenLayers.Handler.Path.prototype.destroyFeature.call(\n- this, force);\n- this.polygon = null;\n+ setMap: function(map) {\n+ this.handlers.drag.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n },\n \n /**\n- * Method: drawFeature\n- * Render geometries on the temporary layer.\n+ * Method: handleMapEvents\n+ * \n+ * Parameters:\n+ * evt - {Object}\n */\n- drawFeature: function() {\n- this.layer.drawFeature(this.polygon, this.style);\n- this.layer.drawFeature(this.point, this.style);\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop();\n+ }\n },\n \n /**\n- * Method: getSketch\n- * Return the sketch feature.\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>}\n+ * Method: moveLayerToTop\n+ * Moves the layer for this handler to the top, so mouse events can reach\n+ * it.\n */\n- getSketch: function() {\n- return this.polygon;\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n+ this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index);\n+\n },\n \n /**\n- * Method: getGeometry\n- * Return the sketch geometry. If <multi> is true, this will return\n- * a multi-part geometry.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Polygon>}\n+ * Method: moveLayerBack\n+ * Moves the layer back to the position determined by the map's layers\n+ * array.\n */\n- getGeometry: function() {\n- var geometry = this.polygon && this.polygon.geometry;\n- if (geometry && this.multi) {\n- geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE['Feature']) {\n+ this.layer.setZIndex(index);\n+ } else {\n+ this.map.setLayerZIndex(this.layer,\n+ this.map.getLayerIndex(this.layer));\n }\n- return geometry;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n+ CLASS_NAME: \"OpenLayers.Control.ModifyFeature\"\n });\n+\n+/**\n+ * Constant: RESHAPE\n+ * {Integer} Constant used to make the control work in reshape mode\n+ */\n+OpenLayers.Control.ModifyFeature.RESHAPE = 1;\n+/**\n+ * Constant: RESIZE\n+ * {Integer} Constant used to make the control work in resize mode\n+ */\n+OpenLayers.Control.ModifyFeature.RESIZE = 2;\n+/**\n+ * Constant: ROTATE\n+ * {Integer} Constant used to make the control work in rotate mode\n+ */\n+OpenLayers.Control.ModifyFeature.ROTATE = 4;\n+/**\n+ * Constant: DRAG\n+ * {Integer} Constant used to make the control work in drag mode\n+ */\n+OpenLayers.Control.ModifyFeature.DRAG = 8;\n /* ======================================================================\n- OpenLayers/Handler/MouseWheel.js\n+ OpenLayers/Control/ZoomBox.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Box.js\n */\n \n /**\n- * Class: OpenLayers.Handler.MouseWheel\n- * Handler for wheel up/down events.\n- * \n+ * Class: OpenLayers.Control.ZoomBox\n+ * The ZoomBox control enables zooming directly to a given extent, by drawing \n+ * a box on the map. The box is drawn by holding down shift, whilst dragging \n+ * the mouse.\n+ *\n * Inherits from:\n- * - <OpenLayers.Handler>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n- /** \n- * Property: wheelListener \n- * {function} \n- */\n- wheelListener: null,\n-\n+OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n /**\n- * Property: interval\n- * {Integer} In order to increase server performance, an interval (in \n- * milliseconds) can be set to reduce the number of up/down events \n- * called. If set, a new up/down event will not be set until the \n- * interval has passed. \n- * Defaults to 0, meaning no interval. \n+ * Property: type\n+ * {OpenLayers.Control.TYPE}\n */\n- interval: 0,\n+ type: OpenLayers.Control.TYPE_TOOL,\n \n /**\n- * Property: maxDelta\n- * {Integer} Maximum delta to collect before breaking from the current\n- * interval. In cumulative mode, this also limits the maximum delta\n- * returned from the handler. Default is Number.POSITIVE_INFINITY.\n+ * Property: out\n+ * {Boolean} Should the control be used for zooming out?\n */\n- maxDelta: Number.POSITIVE_INFINITY,\n+ out: false,\n \n /**\n- * Property: delta\n- * {Integer} When interval is set, delta collects the mousewheel z-deltas\n- * of the events that occur within the interval.\n- * See also the cumulative option\n+ * APIProperty: keyMask\n+ * {Integer} Zoom only occurs if the keyMask matches the combination of \n+ * keys down. Use bitwise operators and one or more of the\n+ * <OpenLayers.Handler> constants to construct a keyMask. Leave null if \n+ * not used mask. Default is null.\n */\n- delta: 0,\n+ keyMask: null,\n \n /**\n- * Property: cumulative\n- * {Boolean} When interval is set: true to collect all the mousewheel \n- * z-deltas, false to only record the delta direction (positive or\n- * negative)\n+ * APIProperty: alwaysZoom\n+ * {Boolean} Always zoom in/out when box drawn, even if the zoom level does\n+ * not change.\n */\n- cumulative: true,\n+ alwaysZoom: false,\n \n /**\n- * Constructor: OpenLayers.Handler.MouseWheel\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} \n- * callbacks - {Object} An object containing a single function to be\n- * called when the drag operation is finished.\n- * The callback should expect to recieve a single\n- * argument, the point geometry.\n- * options - {Object} \n+ * APIProperty: zoomOnClick\n+ * {Boolean} Should we zoom when no box was dragged, i.e. the user only\n+ * clicked? Default is true.\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.wheelListener = OpenLayers.Function.bindAsEventListener(\n- this.onWheelEvent, this\n- );\n- },\n+ zoomOnClick: true,\n \n /**\n- * Method: destroy\n+ * Method: draw\n */\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- this.wheelListener = null;\n+ draw: function() {\n+ this.handler = new OpenLayers.Handler.Box(this, {\n+ done: this.zoomBox\n+ }, {\n+ keyMask: this.keyMask\n+ });\n },\n \n /**\n- * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/\n- */\n-\n- /** \n- * Method: onWheelEvent\n- * Catch the wheel event and handle it xbrowserly\n- * \n+ * Method: zoomBox\n+ *\n * Parameters:\n- * e - {Event} \n+ * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}\n */\n- onWheelEvent: function(e) {\n-\n- // make sure we have a map and check keyboard modifiers\n- if (!this.map || !this.checkModifiers(e)) {\n- return;\n- }\n-\n- // Ride up the element's DOM hierarchy to determine if it or any of \n- // its ancestors was: \n- // * specifically marked as scrollable (CSS overflow property)\n- // * one of our layer divs or a div marked as scrollable\n- // ('olScrollable' CSS class)\n- // * the map div\n- //\n- var overScrollableDiv = false;\n- var allowScroll = false;\n- var overMapDiv = false;\n-\n- var elem = OpenLayers.Event.element(e);\n- while ((elem != null) && !overMapDiv && !overScrollableDiv) {\n-\n- if (!overScrollableDiv) {\n- try {\n- var overflow;\n- if (elem.currentStyle) {\n- overflow = elem.currentStyle[\"overflow\"];\n- } else {\n- var style =\n- document.defaultView.getComputedStyle(elem, null);\n- overflow = style.getPropertyValue(\"overflow\");\n- }\n- overScrollableDiv = (overflow &&\n- (overflow == \"auto\") || (overflow == \"scroll\"));\n- } catch (err) {\n- //sometimes when scrolling in a popup, this causes \n- // obscure browser error\n- }\n+ zoomBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var bounds,\n+ targetCenterPx = position.getCenterPixel();\n+ if (!this.out) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,\n+ maxXY.lon, maxXY.lat);\n+ } else {\n+ var pixWidth = position.right - position.left;\n+ var pixHeight = position.bottom - position.top;\n+ var zoomFactor = Math.min((this.map.size.h / pixHeight),\n+ (this.map.size.w / pixWidth));\n+ var extent = this.map.getExtent();\n+ var center = this.map.getLonLatFromPixel(targetCenterPx);\n+ var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor;\n+ var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor;\n+ var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor;\n+ var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor;\n+ bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);\n }\n-\n- if (!allowScroll) {\n- allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');\n- if (!allowScroll) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- // Are we in the layer div? Note that we have two cases\n- // here: one is to catch EventPane layers, which have a\n- // pane above the layer (layer.pane)\n- var layer = this.map.layers[i];\n- if (elem == layer.div || elem == layer.pane) {\n- allowScroll = true;\n- break;\n- }\n- }\n- }\n+ // always zoom in/out \n+ var lastZoom = this.map.getZoom(),\n+ size = this.map.getSize(),\n+ centerPx = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ },\n+ zoom = this.map.getZoomForExtent(bounds),\n+ oldRes = this.map.getResolution(),\n+ newRes = this.map.getResolutionForZoom(zoom);\n+ if (oldRes == newRes) {\n+ this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));\n+ } else {\n+ var zoomOriginPx = {\n+ x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /\n+ (oldRes - newRes),\n+ y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /\n+ (oldRes - newRes)\n+ };\n+ this.map.zoomTo(zoom, zoomOriginPx);\n }\n- overMapDiv = (elem == this.map.div);\n-\n- elem = elem.parentNode;\n- }\n-\n- // Logic below is the following:\n- //\n- // If we are over a scrollable div or not over the map div:\n- // * do nothing (let the browser handle scrolling)\n- //\n- // otherwise \n- // \n- // If we are over the layer div or a 'olScrollable' div:\n- // * zoom/in out\n- // then\n- // * kill event (so as not to also scroll the page after zooming)\n- //\n- // otherwise\n- //\n- // Kill the event (dont scroll the page if we wheel over the \n- // layerswitcher or the pan/zoom control)\n- //\n- if (!overScrollableDiv && overMapDiv) {\n- if (allowScroll) {\n- var delta = 0;\n-\n- if (e.wheelDelta) {\n- delta = e.wheelDelta;\n- if (delta % 160 === 0) {\n- // opera have steps of 160 instead of 120\n- delta = delta * 0.75;\n- }\n- delta = delta / 120;\n- } else if (e.detail) {\n- // detail in Firefox on OS X is 1/3 of Windows\n- // so force delta 1 / -1\n- delta = -(e.detail / Math.abs(e.detail));\n- }\n- this.delta += delta;\n-\n- window.clearTimeout(this._timeoutId);\n- if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n- // store e because window.event might change during delay\n- var evt = OpenLayers.Util.extend({}, e);\n- this._timeoutId = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.wheelZoom(evt);\n- }, this),\n- this.interval\n- );\n- } else {\n- this.wheelZoom(e);\n- }\n+ if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n+ this.map.zoomTo(lastZoom + (this.out ? -1 : 1));\n }\n- OpenLayers.Event.stop(e);\n- }\n- },\n-\n- /**\n- * Method: wheelZoom\n- * Given the wheel event, we carry out the appropriate zooming in or out,\n- * based on the 'wheelDelta' or 'detail' property of the event.\n- * \n- * Parameters:\n- * e - {Event}\n- */\n- wheelZoom: function(e) {\n- var delta = this.delta;\n- this.delta = 0;\n-\n- if (delta) {\n- e.xy = this.map.events.getMousePosition(e);\n- if (delta < 0) {\n- this.callback(\"down\",\n- [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);\n+ } else if (this.zoomOnClick) { // it's a pixel\n+ if (!this.out) {\n+ this.map.zoomTo(this.map.getZoom() + 1, position);\n } else {\n- this.callback(\"up\",\n- [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);\n+ this.map.zoomTo(this.map.getZoom() - 1, position);\n }\n }\n },\n \n- /**\n- * Method: activate \n- */\n- activate: function(evt) {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- //register mousewheel events specifically on the window and document\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n- return true;\n- } else {\n- return false;\n- }\n- },\n-\n- /**\n- * Method: deactivate \n- */\n- deactivate: function(evt) {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- // unregister mousewheel events specifically on the window and document\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n- return true;\n- } else {\n- return false;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+ CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Keyboard.js\n+ OpenLayers/Control/DragPan.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Drag.js\n */\n \n /**\n- * Class: OpenLayers.handler.Keyboard\n- * A handler for keyboard events. Create a new instance with the\n- * <OpenLayers.Handler.Keyboard> constructor.\n- * \n+ * Class: OpenLayers.Control.DragPan\n+ * The DragPan control pans the map with a drag of the mouse.\n+ *\n * Inherits from:\n- * - <OpenLayers.Handler> \n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {\n-\n- /* http://www.quirksmode.org/js/keys.html explains key x-browser\n- key handling quirks in pretty nice detail */\n+OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n- * Constant: KEY_EVENTS\n- * keydown, keypress, keyup\n+ * Property: type\n+ * {OpenLayers.Control.TYPES}\n */\n- KEY_EVENTS: [\"keydown\", \"keyup\"],\n+ type: OpenLayers.Control.TYPE_TOOL,\n \n- /** \n- * Property: eventListener\n- * {Function}\n+ /**\n+ * Property: panned\n+ * {Boolean} The map moved.\n */\n- eventListener: null,\n+ panned: false,\n \n /**\n- * Property: observeElement\n- * {DOMElement|String} The DOM element on which we listen for\n- * key events. Default to the document.\n+ * Property: interval\n+ * {Integer} The number of milliseconds that should ellapse before\n+ * panning the map again. Defaults to 0 milliseconds, which means that\n+ * no separate cycle is used for panning. In most cases you won't want\n+ * to change this value. For slow machines/devices larger values can be\n+ * tried out.\n */\n- observeElement: null,\n+ interval: 0,\n \n /**\n- * Constructor: OpenLayers.Handler.Keyboard\n- * Returns a new keyboard handler.\n- * \n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handlers setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object containing a single function to be\n- * called when the drag operation is finished. The callback should\n- * expect to recieve a single argument, the pixel location of the event.\n- * Callbacks for 'keydown', 'keypress', and 'keyup' are supported.\n- * options - {Object} Optional object whose properties will be set on the\n- * handler.\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, mouse dragging will continue even if the\n+ * mouse cursor leaves the map viewport. Default is false.\n+ */\n+ documentDrag: false,\n+\n+ /**\n+ * Property: kinetic\n+ * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.\n+ */\n+ kinetic: null,\n+\n+ /**\n+ * APIProperty: enableKinetic\n+ * {Boolean} Set this option to enable \"kinetic dragging\". Can be\n+ * set to true or to an object. If set to an object this\n+ * object will be passed to the {<OpenLayers.Kinetic>}\n+ * constructor. Defaults to true.\n+ * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is\n+ * included in your build config.\n+ */\n+ enableKinetic: true,\n+\n+ /**\n+ * APIProperty: kineticInterval\n+ * {Integer} Interval in milliseconds between 2 steps in the \"kinetic\n+ * scrolling\". Applies only if enableKinetic is set. Defaults\n+ * to 10 milliseconds.\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- // cache the bound event listener method so it can be unobserved later\n- this.eventListener = OpenLayers.Function.bindAsEventListener(\n- this.handleKeyEvent, this\n- );\n- },\n+ kineticInterval: 10,\n+\n \n /**\n- * Method: destroy\n+ * Method: draw\n+ * Creates a Drag handler, using <panMap> and\n+ * <panMapDone> as callbacks.\n */\n- destroy: function() {\n- this.deactivate();\n- this.eventListener = null;\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ draw: function() {\n+ if (this.enableKinetic && OpenLayers.Kinetic) {\n+ var config = {\n+ interval: this.kineticInterval\n+ };\n+ if (typeof this.enableKinetic === \"object\") {\n+ config = OpenLayers.Util.extend(config, this.enableKinetic);\n+ }\n+ this.kinetic = new OpenLayers.Kinetic(config);\n+ }\n+ this.handler = new OpenLayers.Handler.Drag(this, {\n+ \"move\": this.panMap,\n+ \"done\": this.panMapDone,\n+ \"down\": this.panMapStart\n+ }, {\n+ interval: this.interval,\n+ documentDrag: this.documentDrag\n+ });\n },\n \n /**\n- * Method: activate\n+ * Method: panMapStart\n */\n- activate: function() {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.observeElement = this.observeElement || document;\n- for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {\n- OpenLayers.Event.observe(\n- this.observeElement, this.KEY_EVENTS[i], this.eventListener);\n- }\n- return true;\n- } else {\n- return false;\n+ panMapStart: function() {\n+ if (this.kinetic) {\n+ this.kinetic.begin();\n }\n },\n \n /**\n- * Method: deactivate\n+ * Method: panMap\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {\n- OpenLayers.Event.stopObserving(\n- this.observeElement, this.KEY_EVENTS[i], this.eventListener);\n- }\n- deactivated = true;\n+ panMap: function(xy) {\n+ if (this.kinetic) {\n+ this.kinetic.update(xy);\n }\n- return deactivated;\n+ this.panned = true;\n+ this.map.pan(\n+ this.handler.last.x - xy.x,\n+ this.handler.last.y - xy.y, {\n+ dragging: true,\n+ animate: false\n+ }\n+ );\n },\n \n /**\n- * Method: handleKeyEvent \n+ * Method: panMapDone\n+ * Finish the panning operation. Only call setCenter (through <panMap>)\n+ * if the map has actually been moved.\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n */\n- handleKeyEvent: function(evt) {\n- if (this.checkModifiers(evt)) {\n- this.callback(evt.type, [evt]);\n+ panMapDone: function(xy) {\n+ if (this.panned) {\n+ var res = null;\n+ if (this.kinetic) {\n+ res = this.kinetic.end(xy);\n+ }\n+ this.map.pan(\n+ this.handler.last.x - xy.x,\n+ this.handler.last.y - xy.y, {\n+ dragging: !!res,\n+ animate: false\n+ }\n+ );\n+ if (res) {\n+ var self = this;\n+ this.kinetic.move(res, function(x, y, end) {\n+ self.map.pan(x, y, {\n+ dragging: !end,\n+ animate: false\n+ });\n+ });\n+ }\n+ this.panned = false;\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Keyboard\"\n+ CLASS_NAME: \"OpenLayers.Control.DragPan\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Hover.js\n+ OpenLayers/Control/Navigation.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Control/ZoomBox.js\n+ * @requires OpenLayers/Control/DragPan.js\n+ * @requires OpenLayers/Handler/MouseWheel.js\n+ * @requires OpenLayers/Handler/Click.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Hover\n- * The hover handler is to be used to emulate mouseovers on objects\n- * on the map that aren't DOM elements. For example one can use\n- * this handler to send WMS/GetFeatureInfo requests as the user\n- * moves the mouve over the map.\n+ * Class: OpenLayers.Control.Navigation\n+ * The navigation control handles map browsing with mouse events (dragging,\n+ * double-clicking, and scrolling the wheel). Create a new navigation \n+ * control with the <OpenLayers.Control.Navigation> control. \n * \n- * Inherits from:\n- * - <OpenLayers.Handler> \n+ * Note that this control is added to the map by default (if no controls \n+ * array is sent in the options object to the <OpenLayers.Map> \n+ * constructor).\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * Property: dragPan\n+ * {<OpenLayers.Control.DragPan>} \n+ */\n+ dragPan: null,\n \n /**\n- * APIProperty: delay\n- * {Integer} - Number of milliseconds between mousemoves before\n- * the event is considered a hover. Default is 500.\n+ * APIProperty: dragPanOptions\n+ * {Object} Options passed to the DragPan control.\n */\n- delay: 500,\n+ dragPanOptions: null,\n \n /**\n- * APIProperty: pixelTolerance\n- * {Integer} - Maximum number of pixels between mousemoves for\n- * an event to be considered a hover. Default is null.\n+ * Property: pinchZoom\n+ * {<OpenLayers.Control.PinchZoom>}\n */\n- pixelTolerance: null,\n+ pinchZoom: null,\n \n /**\n- * APIProperty: stopMove\n- * {Boolean} - Stop other listeners from being notified on mousemoves.\n- * Default is false.\n+ * APIProperty: pinchZoomOptions\n+ * {Object} Options passed to the PinchZoom control.\n */\n- stopMove: false,\n+ pinchZoomOptions: null,\n \n /**\n- * Property: px\n- * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed\n- * in pixels.\n+ * APIProperty: documentDrag\n+ * {Boolean} Allow panning of the map by dragging outside map viewport.\n+ * Default is false.\n */\n- px: null,\n+ documentDrag: false,\n+\n+ /** \n+ * Property: zoomBox\n+ * {<OpenLayers.Control.ZoomBox>}\n+ */\n+ zoomBox: null,\n \n /**\n- * Property: timerId\n- * {Number} - The id of the timer.\n+ * APIProperty: zoomBoxEnabled\n+ * {Boolean} Whether the user can draw a box to zoom\n */\n- timerId: null,\n+ zoomBoxEnabled: true,\n \n /**\n- * Constructor: OpenLayers.Handler.Hover\n- * Construct a hover handler.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that initialized this\n- * handler. The control is assumed to have a valid map property; that\n- * map is used in the handler's own setMap method.\n- * callbacks - {Object} An object with keys corresponding to callbacks\n- * that will be called by the handler. The callbacks should\n- * expect to receive a single argument, the event. Callbacks for\n- * 'move', the mouse is moving, and 'pause', the mouse is pausing,\n- * are supported.\n- * options - {Object} An optional object whose properties will be set on\n- * the handler.\n+ * APIProperty: zoomWheelEnabled\n+ * {Boolean} Whether the mousewheel should zoom the map\n */\n+ zoomWheelEnabled: true,\n \n /**\n- * Method: mousemove\n- * Called when the mouse moves on the map.\n- *\n- * Parameters:\n- * evt - {<OpenLayers.Event>}\n- *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Property: mouseWheelOptions\n+ * {Object} Options passed to the MouseWheel control (only useful if\n+ * <zoomWheelEnabled> is set to true). Default is no options for maps\n+ * with fractionalZoom set to true, otherwise\n+ * {cumulative: false, interval: 50, maxDelta: 6} \n */\n- mousemove: function(evt) {\n- if (this.passesTolerance(evt.xy)) {\n- this.clearTimer();\n- this.callback('move', [evt]);\n- this.px = evt.xy;\n- // clone the evt so original properties can be accessed even\n- // if the browser deletes them during the delay\n- evt = OpenLayers.Util.extend({}, evt);\n- this.timerId = window.setTimeout(\n- OpenLayers.Function.bind(this.delayedCall, this, evt),\n- this.delay\n- );\n- }\n- return !this.stopMove;\n- },\n+ mouseWheelOptions: null,\n \n /**\n- * Method: mouseout\n- * Called when the mouse goes out of the map.\n- *\n- * Parameters:\n- * evt - {<OpenLayers.Event>}\n- *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * APIProperty: handleRightClicks\n+ * {Boolean} Whether or not to handle right clicks. Default is false.\n */\n- mouseout: function(evt) {\n- if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- this.clearTimer();\n- this.callback('move', [evt]);\n- }\n- return true;\n- },\n+ handleRightClicks: false,\n \n /**\n- * Method: passesTolerance\n- * Determine whether the mouse move is within the optional pixel tolerance.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- *\n- * Returns:\n- * {Boolean} The mouse move is within the pixel tolerance.\n+ * APIProperty: zoomBoxKeyMask\n+ * {Integer} <OpenLayers.Handler> key code of the key, which has to be\n+ * pressed, while drawing the zoom box with the mouse on the screen. \n+ * You should probably set handleRightClicks to true if you use this\n+ * with MOD_CTRL, to disable the context menu for machines which use\n+ * CTRL-Click as a right click.\n+ * Default: <OpenLayers.Handler.MOD_SHIFT>\n */\n- passesTolerance: function(px) {\n- var passes = true;\n- if (this.pixelTolerance && this.px) {\n- var dpx = Math.sqrt(\n- Math.pow(this.px.x - px.x, 2) +\n- Math.pow(this.px.y - px.y, 2)\n- );\n- if (dpx < this.pixelTolerance) {\n- passes = false;\n- }\n- }\n- return passes;\n- },\n+ zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n \n /**\n- * Method: clearTimer\n- * Clear the timer and set <timerId> to null.\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null;\n- }\n- },\n+ autoActivate: true,\n \n /**\n- * Method: delayedCall\n- * Triggers pause callback.\n- *\n+ * Constructor: OpenLayers.Control.Navigation\n+ * Create a new navigation control\n+ * \n * Parameters:\n- * evt - {<OpenLayers.Event>}\n+ * options - {Object} An optional object whose properties will be set on\n+ * the control\n */\n- delayedCall: function(evt) {\n- this.callback('pause', [evt]);\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the handler.\n- *\n- * Returns:\n- * {Boolean} The handler was successfully deactivated.\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- deactivated = true;\n- }\n- return deactivated;\n- },\n+ destroy: function() {\n+ this.deactivate();\n \n- CLASS_NAME: \"OpenLayers.Handler.Hover\"\n-});\n-/* ======================================================================\n- OpenLayers/Handler/Pinch.js\n- ====================================================================== */\n+ if (this.dragPan) {\n+ this.dragPan.destroy();\n+ }\n+ this.dragPan = null;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ if (this.zoomBox) {\n+ this.zoomBox.destroy();\n+ }\n+ this.zoomBox = null;\n \n-/**\n- * @requires OpenLayers/Handler.js\n- */\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy();\n+ }\n+ this.pinchZoom = null;\n \n-/**\n- * Class: OpenLayers.Handler.Pinch\n- * The pinch handler is used to deal with sequences of browser events related\n- * to pinch gestures. The handler is used by controls that want to know\n- * when a pinch sequence begins, when a pinch is happening, and when it has\n- * finished.\n- *\n- * Controls that use the pinch handler typically construct it with callbacks\n- * for 'start', 'move', and 'done'. Callbacks for these keys are\n- * called when the pinch begins, with each change, and when the pinch is\n- * done.\n- *\n- * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Handler>\n- */\n-OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n \n /**\n- * Property: started\n- * {Boolean} When a touchstart event is received, we want to record it,\n- * but not set 'pinching' until the touchmove get started after\n- * starting.\n+ * Method: activate\n */\n- started: false,\n+ activate: function() {\n+ this.dragPan.activate();\n+ if (this.zoomWheelEnabled) {\n+ this.handlers.wheel.activate();\n+ }\n+ this.handlers.click.activate();\n+ if (this.zoomBoxEnabled) {\n+ this.zoomBox.activate();\n+ }\n+ if (this.pinchZoom) {\n+ this.pinchZoom.activate();\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments);\n+ },\n \n /**\n- * Property: stopDown\n- * {Boolean} Stop propagation of touchstart events from getting to\n- * listeners on the same element. Default is false.\n+ * Method: deactivate\n */\n- stopDown: false,\n+ deactivate: function() {\n+ if (this.pinchZoom) {\n+ this.pinchZoom.deactivate();\n+ }\n+ this.zoomBox.deactivate();\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.handlers.wheel.deactivate();\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ },\n \n /**\n- * Property: pinching\n- * {Boolean}\n+ * Method: draw\n */\n- pinching: false,\n+ draw: function() {\n+ // disable right mouse context menu for support of right click events\n+ if (this.handleRightClicks) {\n+ this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;\n+ }\n+\n+ var clickCallbacks = {\n+ 'click': this.defaultClick,\n+ 'dblclick': this.defaultDblClick,\n+ 'dblrightclick': this.defaultDblRightClick\n+ };\n+ var clickOptions = {\n+ 'double': true,\n+ 'stopDouble': true\n+ };\n+ this.handlers.click = new OpenLayers.Handler.Click(\n+ this, clickCallbacks, clickOptions\n+ );\n+ this.dragPan = new OpenLayers.Control.DragPan(\n+ OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions)\n+ );\n+ this.zoomBox = new OpenLayers.Control.ZoomBox({\n+ map: this.map,\n+ keyMask: this.zoomBoxKeyMask\n+ });\n+ this.dragPan.draw();\n+ this.zoomBox.draw();\n+ var wheelOptions = this.map.fractionalZoom ? {} : {\n+ cumulative: false,\n+ interval: 50,\n+ maxDelta: 6\n+ };\n+ this.handlers.wheel = new OpenLayers.Handler.MouseWheel(\n+ this, {\n+ up: this.wheelUp,\n+ down: this.wheelDown\n+ },\n+ OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)\n+ );\n+ if (OpenLayers.Control.PinchZoom) {\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(\n+ OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions));\n+ }\n+ },\n \n /**\n- * Property: last\n- * {Object} Object that store informations related to pinch last touch.\n+ * Method: defaultClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n */\n- last: null,\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut();\n+ }\n+ },\n \n /**\n- * Property: start\n- * {Object} Object that store informations related to pinch touchstart.\n+ * Method: defaultDblClick \n+ * \n+ * Parameters:\n+ * evt - {Event} \n */\n- start: null,\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy);\n+ },\n \n /**\n- * Constructor: OpenLayers.Handler.Pinch\n- * Returns OpenLayers.Handler.Pinch\n- *\n+ * Method: defaultDblRightClick \n+ * \n * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handlers setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object containing functions to be called when\n- * the pinch operation start, change, or is finished. The callbacks\n- * should expect to receive an object argument, which contains\n- * information about scale, distance, and position of touch points.\n- * options - {Object}\n+ * evt - {Event} \n */\n+ defaultDblRightClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom - 1, evt.xy);\n+ },\n \n /**\n- * Method: touchstart\n- * Handle touchstart events\n+ * Method: wheelChange \n *\n * Parameters:\n * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * deltaZ - {Integer}\n */\n- touchstart: function(evt) {\n- var propagate = true;\n- this.pinching = false;\n- if (OpenLayers.Event.isMultiTouch(evt)) {\n- this.started = true;\n- this.last = this.start = {\n- distance: this.getDistance(evt.touches),\n- delta: 0,\n- scale: 1\n- };\n- this.callback(\"start\", [evt, this.start]);\n- propagate = !this.stopDown;\n- } else if (this.started) {\n- // Some webkit versions send fake single-touch events during\n- // multitouch, which cause the drag handler to trigger\n- return false;\n- } else {\n- this.started = false;\n- this.start = null;\n- this.last = null;\n+ wheelChange: function(evt, deltaZ) {\n+ if (!this.map.fractionalZoom) {\n+ deltaZ = Math.round(deltaZ);\n }\n- // prevent document dragging\n- OpenLayers.Event.preventDefault(evt);\n- return propagate;\n+ var currentZoom = this.map.getZoom(),\n+ newZoom = currentZoom + deltaZ;\n+ newZoom = Math.max(newZoom, 0);\n+ newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n+ if (newZoom === currentZoom) {\n+ return;\n+ }\n+ this.map.zoomTo(newZoom, evt.xy);\n },\n \n- /**\n- * Method: touchmove\n- * Handle touchmove events\n- *\n+ /** \n+ * Method: wheelUp\n+ * User spun scroll wheel up\n+ * \n * Parameters:\n * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * delta - {Integer}\n */\n- touchmove: function(evt) {\n- if (this.started && OpenLayers.Event.isMultiTouch(evt)) {\n- this.pinching = true;\n- var current = this.getPinchData(evt);\n- this.callback(\"move\", [evt, current]);\n- this.last = current;\n- // prevent document dragging\n- OpenLayers.Event.stop(evt);\n- } else if (this.started) {\n- // Some webkit versions send fake single-touch events during\n- // multitouch, which cause the drag handler to trigger\n- return false;\n- }\n- return true;\n+ wheelUp: function(evt, delta) {\n+ this.wheelChange(evt, delta || 1);\n },\n \n- /**\n- * Method: touchend\n- * Handle touchend events\n- *\n+ /** \n+ * Method: wheelDown\n+ * User spun scroll wheel down\n+ * \n * Parameters:\n * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * delta - {Integer}\n */\n- touchend: function(evt) {\n- if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {\n- this.started = false;\n- this.pinching = false;\n- this.callback(\"done\", [evt, this.start, this.last]);\n- this.start = null;\n- this.last = null;\n- return false;\n- }\n- return true;\n+ wheelDown: function(evt, delta) {\n+ this.wheelChange(evt, delta || -1);\n },\n \n /**\n- * Method: activate\n- * Activate the handler.\n- *\n- * Returns:\n- * {Boolean} The handler was successfully activated.\n+ * Method: disableZoomBox\n */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.pinching = false;\n- activated = true;\n- }\n- return activated;\n+ disableZoomBox: function() {\n+ this.zoomBoxEnabled = false;\n+ this.zoomBox.deactivate();\n },\n \n /**\n- * Method: deactivate\n- * Deactivate the handler.\n- *\n- * Returns:\n- * {Boolean} The handler was successfully deactivated.\n+ * Method: enableZoomBox\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.started = false;\n- this.pinching = false;\n- this.start = null;\n- this.last = null;\n- deactivated = true;\n+ enableZoomBox: function() {\n+ this.zoomBoxEnabled = true;\n+ if (this.active) {\n+ this.zoomBox.activate();\n }\n- return deactivated;\n },\n \n /**\n- * Method: getDistance\n- * Get the distance in pixels between two touches.\n- *\n- * Parameters:\n- * touches - {Array(Object)}\n- *\n- * Returns:\n- * {Number} The distance in pixels.\n+ * Method: disableZoomWheel\n */\n- getDistance: function(touches) {\n- var t0 = touches[0];\n- var t1 = touches[1];\n- return Math.sqrt(\n- Math.pow(t0.olClientX - t1.olClientX, 2) +\n- Math.pow(t0.olClientY - t1.olClientY, 2)\n- );\n- },\n \n+ disableZoomWheel: function() {\n+ this.zoomWheelEnabled = false;\n+ this.handlers.wheel.deactivate();\n+ },\n \n /**\n- * Method: getPinchData\n- * Get informations about the pinch event.\n- *\n- * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Object} Object that contains data about the current pinch.\n+ * Method: enableZoomWheel\n */\n- getPinchData: function(evt) {\n- var distance = this.getDistance(evt.touches);\n- var scale = distance / this.start.distance;\n- return {\n- distance: distance,\n- delta: this.last.distance - distance,\n- scale: scale\n- };\n+\n+ enableZoomWheel: function() {\n+ this.zoomWheelEnabled = true;\n+ if (this.active) {\n+ this.handlers.wheel.activate();\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Pinch\"\n+ CLASS_NAME: \"OpenLayers.Control.Navigation\"\n });\n-\n /* ======================================================================\n- OpenLayers/Handler/Box.js\n+ OpenLayers/Control/CacheWrite.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n- * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Request.js\n+ * @requires OpenLayers/Console.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Box\n- * Handler for dragging a rectangle across the map. Box is displayed \n- * on mouse down, moves on mouse move, and is finished on mouse up.\n+ * Class: OpenLayers.Control.CacheWrite\n+ * A control for caching image tiles in the browser's local storage. The\n+ * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached\n+ * tile images.\n+ *\n+ * Note: Before using this control on any layer that is not your own, make sure\n+ * that the terms of service of the tile provider allow local storage of tiles.\n *\n * Inherits from:\n- * - <OpenLayers.Handler> \n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n- * Property: dragHandler \n- * {<OpenLayers.Handler.Drag>} \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * To register events in the constructor, configure <eventListeners>.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * cachefull - Triggered when the cache is full. Listeners receive an\n+ * object with a tile property as first argument. The tile references\n+ * the tile that couldn't be cached.\n */\n- dragHandler: null,\n \n /**\n- * APIProperty: boxDivClassName\n- * {String} The CSS class to use for drawing the box. Default is\n- * olHandlerBoxZoomBox\n+ * APIProperty: eventListeners\n+ * {Object} Object with event listeners, keyed by event name. An optional\n+ * scope property defines the scope that listeners will be executed in.\n */\n- boxDivClassName: 'olHandlerBoxZoomBox',\n \n /**\n- * Property: boxOffsets\n- * {Object} Caches box offsets from css. This is used by the getBoxOffsets\n- * method.\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching\n+ * will be enabled for these layers only, otherwise for all cacheable\n+ * layers.\n */\n- boxOffsets: null,\n+ layers: null,\n \n /**\n- * Constructor: OpenLayers.Handler.Box\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} \n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} \n- *\n- * Named callbacks:\n- * start - Called when the box drag operation starts.\n- * done - Called when the box drag operation is finished.\n- * The callback should expect to receive a single argument, the box \n- * bounds or a pixel. If the box dragging didn't span more than a 5 \n- * pixel distance, a pixel will be returned instead of a bounds object.\n+ * APIProperty: imageFormat\n+ * {String} The image format used for caching. The default is \"image/png\".\n+ * Supported formats depend on the user agent. If an unsupported\n+ * <imageFormat> is provided, \"image/png\" will be used. For aerial\n+ * imagery, \"image/jpeg\" is recommended.\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.dragHandler = new OpenLayers.Handler.Drag(\n- this, {\n- down: this.startBox,\n- move: this.moveBox,\n- out: this.removeBox,\n- up: this.endBox\n- }, {\n- keyMask: this.keyMask\n- }\n- );\n- },\n+ imageFormat: \"image/png\",\n \n /**\n- * Method: destroy\n+ * Property: quotaRegEx\n+ * {RegExp}\n */\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.destroy();\n- this.dragHandler = null;\n- }\n- },\n+ quotaRegEx: (/quota/i),\n \n /**\n+ * Constructor: OpenLayers.Control.CacheWrite\n+ *\n+ * Parameters:\n+ * options - {Object} Object with API properties for this control.\n+ */\n+\n+ /** \n * Method: setMap\n+ * Set the map property for the control. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n */\n setMap: function(map) {\n- OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ var i, layers = this.layers || map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.addLayer({\n+ layer: layers[i]\n+ });\n+ }\n+ if (!this.layers) {\n+ map.events.on({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ });\n }\n },\n \n /**\n- * Method: startBox\n+ * Method: addLayer\n+ * Adds a layer to the control. Once added, tiles requested for this layer\n+ * will be cached.\n *\n * Parameters:\n- * xy - {<OpenLayers.Pixel>}\n+ * evt - {Object} Object with a layer property referencing an\n+ * <OpenLayers.Layer> instance\n */\n- startBox: function(xy) {\n- this.callback(\"start\", []);\n- this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {\n- x: -9999,\n- y: -9999\n+ addLayer: function(evt) {\n+ evt.layer.events.on({\n+ tileloadstart: this.makeSameOrigin,\n+ tileloaded: this.onTileLoaded,\n+ scope: this\n });\n- this.zoomBox.className = this.boxDivClassName;\n- this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n-\n- this.map.viewPortDiv.appendChild(this.zoomBox);\n-\n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv, \"olDrawBox\"\n- );\n },\n \n /**\n- * Method: moveBox\n+ * Method: removeLayer\n+ * Removes a layer from the control. Once removed, tiles requested for this\n+ * layer will no longer be cached.\n+ *\n+ * Parameters:\n+ * evt - {Object} Object with a layer property referencing an\n+ * <OpenLayers.Layer> instance\n */\n- moveBox: function(xy) {\n- var startX = this.dragHandler.start.x;\n- var startY = this.dragHandler.start.y;\n- var deltaX = Math.abs(startX - xy.x);\n- var deltaY = Math.abs(startY - xy.y);\n-\n- var offset = this.getBoxOffsets();\n- this.zoomBox.style.width = (deltaX + offset.width + 1) + \"px\";\n- this.zoomBox.style.height = (deltaY + offset.height + 1) + \"px\";\n- this.zoomBox.style.left = (xy.x < startX ?\n- startX - deltaX - offset.left : startX - offset.left) + \"px\";\n- this.zoomBox.style.top = (xy.y < startY ?\n- startY - deltaY - offset.top : startY - offset.top) + \"px\";\n+ removeLayer: function(evt) {\n+ evt.layer.events.un({\n+ tileloadstart: this.makeSameOrigin,\n+ tileloaded: this.onTileLoaded,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: endBox\n+ * Method: makeSameOrigin\n+ * If the tile does not have CORS image loading enabled and is from a\n+ * different origin, use OpenLayers.ProxyHost to make it a same origin url.\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>}\n */\n- endBox: function(end) {\n- var result;\n- if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||\n- Math.abs(this.dragHandler.start.y - end.y) > 5) {\n- var start = this.dragHandler.start;\n- var top = Math.min(start.y, end.y);\n- var bottom = Math.max(start.y, end.y);\n- var left = Math.min(start.x, end.x);\n- var right = Math.max(start.x, end.x);\n- result = new OpenLayers.Bounds(left, bottom, right, top);\n- } else {\n- result = this.dragHandler.start.clone(); // i.e. OL.Pixel\n+ makeSameOrigin: function(evt) {\n+ if (this.active) {\n+ var tile = evt.tile;\n+ if (tile instanceof OpenLayers.Tile.Image &&\n+ !tile.crossOriginKeyword &&\n+ tile.url.substr(0, 5) !== \"data:\") {\n+ var sameOriginUrl = OpenLayers.Request.makeSameOrigin(\n+ tile.url, OpenLayers.ProxyHost\n+ );\n+ OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url;\n+ tile.url = sameOriginUrl;\n+ }\n }\n- this.removeBox();\n-\n- this.callback(\"done\", [result]);\n- },\n-\n- /**\n- * Method: removeBox\n- * Remove the zoombox from the screen and nullify our reference to it.\n- */\n- removeBox: function() {\n- this.map.viewPortDiv.removeChild(this.zoomBox);\n- this.zoomBox = null;\n- this.boxOffsets = null;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDrawBox\"\n- );\n-\n },\n \n /**\n- * Method: activate\n+ * Method: onTileLoaded\n+ * Decides whether a tile can be cached and calls the cache method.\n+ *\n+ * Parameters:\n+ * evt - {Event}\n */\n- activate: function() {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragHandler.activate();\n- return true;\n- } else {\n- return false;\n+ onTileLoaded: function(evt) {\n+ if (this.active && !evt.aborted &&\n+ evt.tile instanceof OpenLayers.Tile.Image &&\n+ evt.tile.url.substr(0, 5) !== 'data:') {\n+ this.cache({\n+ tile: evt.tile\n+ });\n+ delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url];\n }\n },\n \n /**\n- * Method: deactivate\n+ * Method: cache\n+ * Adds a tile to the cache. When the cache is full, the \"cachefull\" event\n+ * is triggered.\n+ *\n+ * Parameters:\n+ * obj - {Object} Object with a tile property, tile being the\n+ * <OpenLayers.Tile.Image> with the data to add to the cache\n */\n- deactivate: function() {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- if (this.dragHandler.deactivate()) {\n- if (this.zoomBox) {\n- this.removeBox();\n+ cache: function(obj) {\n+ if (window.localStorage) {\n+ var tile = obj.tile;\n+ try {\n+ var canvasContext = tile.getCanvasContext();\n+ if (canvasContext) {\n+ var urlMap = OpenLayers.Control.CacheWrite.urlMap;\n+ var url = urlMap[tile.url] || tile.url;\n+ window.localStorage.setItem(\n+ \"olCache_\" + url,\n+ canvasContext.canvas.toDataURL(this.imageFormat)\n+ );\n+ }\n+ } catch (e) {\n+ // local storage full or CORS violation\n+ var reason = e.name || e.message;\n+ if (reason && this.quotaRegEx.test(reason)) {\n+ this.events.triggerEvent(\"cachefull\", {\n+ tile: tile\n+ });\n+ } else {\n+ OpenLayers.Console.error(e.toString());\n }\n }\n- return true;\n- } else {\n- return false;\n }\n },\n \n /**\n- * Method: getBoxOffsets\n- * Determines border offsets for a box, according to the box model.\n- * \n- * Returns:\n- * {Object} an object with the following offsets:\n- * - left\n- * - right\n- * - top\n- * - bottom\n- * - width\n- * - height\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n */\n- getBoxOffsets: function() {\n- if (!this.boxOffsets) {\n- // Determine the box model. If the testDiv's clientWidth is 3, then\n- // the borders are outside and we are dealing with the w3c box\n- // model. Otherwise, the browser uses the traditional box model and\n- // the borders are inside the box bounds, leaving us with a\n- // clientWidth of 1.\n- var testDiv = document.createElement(\"div\");\n- //testDiv.style.visibility = \"hidden\";\n- testDiv.style.position = \"absolute\";\n- testDiv.style.border = \"1px solid black\";\n- testDiv.style.width = \"3px\";\n- document.body.appendChild(testDiv);\n- var w3cBoxModel = testDiv.clientWidth == 3;\n- document.body.removeChild(testDiv);\n-\n- var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n- \"border-left-width\"));\n- var right = parseInt(OpenLayers.Element.getStyle(\n- this.zoomBox, \"border-right-width\"));\n- var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n- \"border-top-width\"));\n- var bottom = parseInt(OpenLayers.Element.getStyle(\n- this.zoomBox, \"border-bottom-width\"));\n- this.boxOffsets = {\n- left: left,\n- right: right,\n- top: top,\n- bottom: bottom,\n- width: w3cBoxModel === false ? left + right : 0,\n- height: w3cBoxModel === false ? top + bottom : 0\n- };\n+ destroy: function() {\n+ if (this.layers || this.map) {\n+ var i, layers = this.layers || this.map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.removeLayer({\n+ layer: layers[i]\n+ });\n+ }\n }\n- return this.boxOffsets;\n+ if (this.map) {\n+ this.map.events.un({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ });\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Box\"\n+ CLASS_NAME: \"OpenLayers.Control.CacheWrite\"\n });\n+\n+/**\n+ * APIFunction: OpenLayers.Control.CacheWrite.clearCache\n+ * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.\n+ */\n+OpenLayers.Control.CacheWrite.clearCache = function() {\n+ if (!window.localStorage) {\n+ return;\n+ }\n+ var i, key;\n+ for (i = window.localStorage.length - 1; i >= 0; --i) {\n+ key = window.localStorage.key(i);\n+ if (key.substr(0, 8) === \"olCache_\") {\n+ window.localStorage.removeItem(key);\n+ }\n+ }\n+};\n+\n+/**\n+ * Property: OpenLayers.Control.CacheWrite.urlMap\n+ * {Object} Mapping of same origin urls to cache url keys. Entries will be\n+ * deleted as soon as a tile was cached.\n+ */\n+OpenLayers.Control.CacheWrite.urlMap = {};\n+\n+\n /* ======================================================================\n- OpenLayers/Marker/Box.js\n+ OpenLayers/Control/NavigationHistory.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Marker.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Control/Button.js\n */\n \n /**\n- * Class: OpenLayers.Marker.Box\n+ * Class: OpenLayers.Control.NavigationHistory\n+ * A navigation history control. This is a meta-control, that creates two\n+ * dependent controls: <previous> and <next>. Call the trigger method\n+ * on the <previous> and <next> controls to restore previous and next\n+ * history states. The previous and next controls will become active\n+ * when there are available states to restore and will become deactive\n+ * when there are no states to restore.\n *\n * Inherits from:\n- * - <OpenLayers.Marker> \n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {\n+OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {\n \n- /** \n- * Property: bounds \n- * {<OpenLayers.Bounds>} \n+ /**\n+ * Property: type\n+ * {String} Note that this control is not intended to be added directly\n+ * to a control panel. Instead, add the sub-controls previous and\n+ * next. These sub-controls are button type controls that activate\n+ * and deactivate themselves. If this parent control is added to\n+ * a panel, it will act as a toggle.\n */\n- bounds: null,\n+ type: OpenLayers.Control.TYPE_TOGGLE,\n \n- /** \n- * Property: div \n- * {DOMElement} \n+ /**\n+ * APIProperty: previous\n+ * {<OpenLayers.Control>} A button type control whose trigger method restores\n+ * the previous state managed by this control.\n */\n- div: null,\n+ previous: null,\n \n- /** \n- * Constructor: OpenLayers.Marker.Box\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * borderColor - {String} \n- * borderWidth - {int} \n+ /**\n+ * APIProperty: previousOptions\n+ * {Object} Set this property on the options argument of the constructor\n+ * to set optional properties on the <previous> control.\n */\n- initialize: function(bounds, borderColor, borderWidth) {\n- this.bounds = bounds;\n- this.div = OpenLayers.Util.createDiv();\n- this.div.style.overflow = 'hidden';\n- this.events = new OpenLayers.Events(this, this.div);\n- this.setBorder(borderColor, borderWidth);\n- },\n+ previousOptions: null,\n \n /**\n- * Method: destroy \n+ * APIProperty: next\n+ * {<OpenLayers.Control>} A button type control whose trigger method restores\n+ * the next state managed by this control.\n */\n- destroy: function() {\n-\n- this.bounds = null;\n- this.div = null;\n-\n- OpenLayers.Marker.prototype.destroy.apply(this, arguments);\n- },\n+ next: null,\n \n- /** \n- * Method: setBorder\n- * Allow the user to change the box's color and border width\n- * \n- * Parameters:\n- * color - {String} Default is \"red\"\n- * width - {int} Default is 2\n+ /**\n+ * APIProperty: nextOptions\n+ * {Object} Set this property on the options argument of the constructor\n+ * to set optional properties on the <next> control.\n */\n- setBorder: function(color, width) {\n- if (!color) {\n- color = \"red\";\n- }\n- if (!width) {\n- width = 2;\n- }\n- this.div.style.border = width + \"px solid \" + color;\n- },\n+ nextOptions: null,\n \n- /** \n- * Method: draw\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>} \n- * sz - {<OpenLayers.Size>} \n- * \n- * Returns: \n- * {DOMElement} A new DOM Image with this marker's icon set at the \n- * location passed-in\n+ /**\n+ * APIProperty: limit\n+ * {Integer} Optional limit on the number of history items to retain. If\n+ * null, there is no limit. Default is 50.\n */\n- draw: function(px, sz) {\n- OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);\n- return this.div;\n- },\n+ limit: 50,\n \n /**\n- * Method: onScreen\n- * \n- * Rreturn:\n- * {Boolean} Whether or not the marker is currently visible on screen.\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n- onScreen: function() {\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsBounds(this.bounds, true, true);\n- }\n- return onScreen;\n- },\n+ autoActivate: true,\n \n /**\n- * Method: display\n- * Hide or show the icon\n- * \n- * Parameters:\n- * display - {Boolean} \n+ * Property: clearOnDeactivate\n+ * {Boolean} Clear the history when the control is deactivated. Default\n+ * is false.\n */\n- display: function(display) {\n- this.div.style.display = (display) ? \"\" : \"none\";\n- },\n-\n- CLASS_NAME: \"OpenLayers.Marker.Box\"\n-});\n-\n-/* ======================================================================\n- OpenLayers/Strategy/Cluster.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Strategy.js\n- */\n+ clearOnDeactivate: false,\n \n-/**\n- * Class: OpenLayers.Strategy.Cluster\n- * Strategy for vector feature clustering.\n- *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n- */\n-OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {\n+ /**\n+ * Property: registry\n+ * {Object} An object with keys corresponding to event types. Values\n+ * are functions that return an object representing the current state.\n+ */\n+ registry: null,\n \n /**\n- * APIProperty: distance\n- * {Integer} Pixel distance between features that should be considered a\n- * single cluster. Default is 20 pixels.\n+ * Property: nextStack\n+ * {Array} Array of items in the history.\n */\n- distance: 20,\n+ nextStack: null,\n \n /**\n- * APIProperty: threshold\n- * {Integer} Optional threshold below which original features will be\n- * added to the layer instead of clusters. For example, a threshold\n- * of 3 would mean that any time there are 2 or fewer features in\n- * a cluster, those features will be added directly to the layer instead\n- * of a cluster representing those features. Default is null (which is\n- * equivalent to 1 - meaning that clusters may contain just one feature).\n+ * Property: previousStack\n+ * {Array} List of items in the history. First item represents the current\n+ * state.\n */\n- threshold: null,\n+ previousStack: null,\n \n /**\n- * Property: features\n- * {Array(<OpenLayers.Feature.Vector>)} Cached features.\n+ * Property: listeners\n+ * {Object} An object containing properties corresponding to event types.\n+ * This object is used to configure the control and is modified on\n+ * construction.\n */\n- features: null,\n+ listeners: null,\n \n /**\n- * Property: clusters\n- * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.\n+ * Property: restoring\n+ * {Boolean} Currently restoring a history state. This is set to true\n+ * before calling restore and set to false after restore returns.\n */\n- clusters: null,\n+ restoring: false,\n \n /**\n- * Property: clustering\n- * {Boolean} The strategy is currently clustering features.\n+ * Constructor: OpenLayers.Control.NavigationHistory \n+ * \n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n */\n- clustering: false,\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+\n+ this.registry = OpenLayers.Util.extend({\n+ \"moveend\": this.getState\n+ }, this.registry);\n+\n+ var previousOptions = {\n+ trigger: OpenLayers.Function.bind(this.previousTrigger, this),\n+ displayClass: this.displayClass + \" \" + this.displayClass + \"Previous\"\n+ };\n+ OpenLayers.Util.extend(previousOptions, this.previousOptions);\n+ this.previous = new OpenLayers.Control.Button(previousOptions);\n+\n+ var nextOptions = {\n+ trigger: OpenLayers.Function.bind(this.nextTrigger, this),\n+ displayClass: this.displayClass + \" \" + this.displayClass + \"Next\"\n+ };\n+ OpenLayers.Util.extend(nextOptions, this.nextOptions);\n+ this.next = new OpenLayers.Control.Button(nextOptions);\n+\n+ this.clear();\n+ },\n \n /**\n- * Property: resolution\n- * {Float} The resolution (map units per pixel) of the current cluster set.\n+ * Method: onPreviousChange\n+ * Called when the previous history stack changes.\n+ *\n+ * Parameters:\n+ * state - {Object} An object representing the state to be restored\n+ * if previous is triggered again or null if no previous states remain.\n+ * length - {Integer} The number of remaining previous states that can\n+ * be restored.\n */\n- resolution: null,\n+ onPreviousChange: function(state, length) {\n+ if (state && !this.previous.active) {\n+ this.previous.activate();\n+ } else if (!state && this.previous.active) {\n+ this.previous.deactivate();\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Strategy.Cluster\n- * Create a new clustering strategy.\n+ * Method: onNextChange\n+ * Called when the next history stack changes.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * state - {Object} An object representing the state to be restored\n+ * if next is triggered again or null if no next states remain.\n+ * length - {Integer} The number of remaining next states that can\n+ * be restored.\n */\n+ onNextChange: function(state, length) {\n+ if (state && !this.next.active) {\n+ this.next.activate();\n+ } else if (!state && this.next.active) {\n+ this.next.deactivate();\n+ }\n+ },\n \n /**\n- * APIMethod: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully activated.\n+ * APIMethod: destroy\n+ * Destroy the control.\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- \"beforefeaturesadded\": this.cacheFeatures,\n- \"featuresremoved\": this.clearCache,\n- \"moveend\": this.cluster,\n- scope: this\n- });\n+ destroy: function() {\n+ OpenLayers.Control.prototype.destroy.apply(this);\n+ this.previous.destroy();\n+ this.next.destroy();\n+ this.deactivate();\n+ for (var prop in this) {\n+ this[prop] = null;\n }\n- return activated;\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * Set the map property for the control and <previous> and <next> child\n+ * controls.\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ this.map = map;\n+ this.next.setMap(map);\n+ this.previous.setMap(map);\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n- * \n+ * Method: draw\n+ * Called when the control is added to the map.\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ this.next.draw();\n+ this.previous.draw();\n+ },\n+\n+ /**\n+ * Method: previousTrigger\n+ * Restore the previous state. If no items are in the previous history\n+ * stack, this has no effect.\n+ *\n * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * {Object} Item representing state that was restored. Undefined if no\n+ * items are in the previous history stack.\n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.clearCache();\n- this.layer.events.un({\n- \"beforefeaturesadded\": this.cacheFeatures,\n- \"featuresremoved\": this.clearCache,\n- \"moveend\": this.cluster,\n- scope: this\n- });\n+ previousTrigger: function() {\n+ var current = this.previousStack.shift();\n+ var state = this.previousStack.shift();\n+ if (state != undefined) {\n+ this.nextStack.unshift(current);\n+ this.previousStack.unshift(state);\n+ this.restoring = true;\n+ this.restore(state);\n+ this.restoring = false;\n+ this.onNextChange(this.nextStack[0], this.nextStack.length);\n+ this.onPreviousChange(\n+ this.previousStack[1], this.previousStack.length - 1\n+ );\n+ } else {\n+ this.previousStack.unshift(current);\n }\n- return deactivated;\n+ return state;\n },\n \n /**\n- * Method: cacheFeatures\n- * Cache features before they are added to the layer.\n+ * APIMethod: nextTrigger\n+ * Restore the next state. If no items are in the next history\n+ * stack, this has no effect. The next history stack is populated\n+ * as states are restored from the previous history stack.\n *\n- * Parameters:\n- * event - {Object} The event that this was listening for. This will come\n- * with a batch of features to be clustered.\n- * \n * Returns:\n- * {Boolean} False to stop features from being added to the layer.\n+ * {Object} Item representing state that was restored. Undefined if no\n+ * items are in the next history stack.\n */\n- cacheFeatures: function(event) {\n- var propagate = true;\n- if (!this.clustering) {\n- this.clearCache();\n- this.features = event.features;\n- this.cluster();\n- propagate = false;\n+ nextTrigger: function() {\n+ var state = this.nextStack.shift();\n+ if (state != undefined) {\n+ this.previousStack.unshift(state);\n+ this.restoring = true;\n+ this.restore(state);\n+ this.restoring = false;\n+ this.onNextChange(this.nextStack[0], this.nextStack.length);\n+ this.onPreviousChange(\n+ this.previousStack[1], this.previousStack.length - 1\n+ );\n }\n- return propagate;\n+ return state;\n },\n \n /**\n- * Method: clearCache\n- * Clear out the cached features.\n+ * APIMethod: clear\n+ * Clear history.\n */\n- clearCache: function() {\n- if (!this.clustering) {\n- this.features = null;\n- }\n+ clear: function() {\n+ this.previousStack = [];\n+ this.previous.deactivate();\n+ this.nextStack = [];\n+ this.next.deactivate();\n },\n \n /**\n- * Method: cluster\n- * Cluster features based on some threshold distance.\n+ * Method: getState\n+ * Get the current state and return it.\n+ *\n+ * Returns:\n+ * {Object} An object representing the current state.\n+ */\n+ getState: function() {\n+ return {\n+ center: this.map.getCenter(),\n+ resolution: this.map.getResolution(),\n+ projection: this.map.getProjectionObject(),\n+ units: this.map.getProjectionObject().getUnits() ||\n+ this.map.units || this.map.baseLayer.units\n+ };\n+ },\n+\n+ /**\n+ * Method: restore\n+ * Update the state with the given object.\n *\n * Parameters:\n- * event - {Object} The event received when cluster is called as a\n- * result of a moveend event.\n+ * state - {Object} An object representing the state to restore.\n */\n- cluster: function(event) {\n- if ((!event || event.zoomChanged) && this.features) {\n- var resolution = this.layer.map.getResolution();\n- if (resolution != this.resolution || !this.clustersExist()) {\n- this.resolution = resolution;\n- var clusters = [];\n- var feature, clustered, cluster;\n- for (var i = 0; i < this.features.length; ++i) {\n- feature = this.features[i];\n- if (feature.geometry) {\n- clustered = false;\n- for (var j = clusters.length - 1; j >= 0; --j) {\n- cluster = clusters[j];\n- if (this.shouldCluster(cluster, feature)) {\n- this.addToCluster(cluster, feature);\n- clustered = true;\n- break;\n- }\n- }\n- if (!clustered) {\n- clusters.push(this.createCluster(this.features[i]));\n- }\n- }\n- }\n- this.clustering = true;\n- this.layer.removeAllFeatures();\n- this.clustering = false;\n- if (clusters.length > 0) {\n- if (this.threshold > 1) {\n- var clone = clusters.slice();\n- clusters = [];\n- var candidate;\n- for (var i = 0, len = clone.length; i < len; ++i) {\n- candidate = clone[i];\n- if (candidate.attributes.count < this.threshold) {\n- Array.prototype.push.apply(clusters, candidate.cluster);\n- } else {\n- clusters.push(candidate);\n- }\n- }\n- }\n- this.clustering = true;\n- // A legitimate feature addition could occur during this\n- // addFeatures call. For clustering to behave well, features\n- // should be removed from a layer before requesting a new batch.\n- this.layer.addFeatures(clusters);\n- this.clustering = false;\n- }\n- this.clusters = clusters;\n- }\n+ restore: function(state) {\n+ var center, zoom;\n+ if (this.map.getProjectionObject() == state.projection) {\n+ zoom = this.map.getZoomForResolution(state.resolution);\n+ center = state.center;\n+ } else {\n+ center = state.center.clone();\n+ center.transform(state.projection, this.map.getProjectionObject());\n+ var sourceUnits = state.units;\n+ var targetUnits = this.map.getProjectionObject().getUnits() ||\n+ this.map.units || this.map.baseLayer.units;\n+ var resolutionFactor = sourceUnits && targetUnits ?\n+ OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n+ zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution);\n }\n+ this.map.setCenter(center, zoom);\n },\n \n /**\n- * Method: clustersExist\n- * Determine whether calculated clusters are already on the layer.\n- *\n- * Returns:\n- * {Boolean} The calculated clusters are already on the layer.\n+ * Method: setListeners\n+ * Sets functions to be registered in the listeners object.\n */\n- clustersExist: function() {\n- var exist = false;\n- if (this.clusters && this.clusters.length > 0 &&\n- this.clusters.length == this.layer.features.length) {\n- exist = true;\n- for (var i = 0; i < this.clusters.length; ++i) {\n- if (this.clusters[i] != this.layer.features[i]) {\n- exist = false;\n- break;\n+ setListeners: function() {\n+ this.listeners = {};\n+ for (var type in this.registry) {\n+ this.listeners[type] = OpenLayers.Function.bind(function() {\n+ if (!this.restoring) {\n+ var state = this.registry[type].apply(this, arguments);\n+ this.previousStack.unshift(state);\n+ if (this.previousStack.length > 1) {\n+ this.onPreviousChange(\n+ this.previousStack[1], this.previousStack.length - 1\n+ );\n+ }\n+ if (this.previousStack.length > (this.limit + 1)) {\n+ this.previousStack.pop();\n+ }\n+ if (this.nextStack.length > 0) {\n+ this.nextStack = [];\n+ this.onNextChange(null, 0);\n+ }\n }\n- }\n+ return true;\n+ }, this);\n }\n- return exist;\n },\n \n /**\n- * Method: shouldCluster\n- * Determine whether to include a feature in a given cluster.\n- *\n- * Parameters:\n- * cluster - {<OpenLayers.Feature.Vector>} A cluster.\n- * feature - {<OpenLayers.Feature.Vector>} A feature.\n+ * APIMethod: activate\n+ * Activate the control. This registers any listeners.\n *\n * Returns:\n- * {Boolean} The feature should be included in the cluster.\n+ * {Boolean} Control successfully activated.\n */\n- shouldCluster: function(cluster, feature) {\n- var cc = cluster.geometry.getBounds().getCenterLonLat();\n- var fc = feature.geometry.getBounds().getCenterLonLat();\n- var distance = (\n- Math.sqrt(\n- Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)\n- ) / this.resolution\n- );\n- return (distance <= this.distance);\n+ activate: function() {\n+ var activated = false;\n+ if (this.map) {\n+ if (OpenLayers.Control.prototype.activate.apply(this)) {\n+ if (this.listeners == null) {\n+ this.setListeners();\n+ }\n+ for (var type in this.listeners) {\n+ this.map.events.register(type, this, this.listeners[type]);\n+ }\n+ activated = true;\n+ if (this.previousStack.length == 0) {\n+ this.initStack();\n+ }\n+ }\n+ }\n+ return activated;\n },\n \n /**\n- * Method: addToCluster\n- * Add a feature to a cluster.\n- *\n- * Parameters:\n- * cluster - {<OpenLayers.Feature.Vector>} A cluster.\n- * feature - {<OpenLayers.Feature.Vector>} A feature.\n+ * Method: initStack\n+ * Called after the control is activated if the previous history stack is\n+ * empty.\n */\n- addToCluster: function(cluster, feature) {\n- cluster.cluster.push(feature);\n- cluster.attributes.count += 1;\n+ initStack: function() {\n+ if (this.map.getCenter()) {\n+ this.listeners.moveend();\n+ }\n },\n \n /**\n- * Method: createCluster\n- * Given a feature, create a cluster.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n+ * APIMethod: deactivate\n+ * Deactivate the control. This unregisters any listeners.\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>} A cluster.\n+ * {Boolean} Control successfully deactivated.\n */\n- createCluster: function(feature) {\n- var center = feature.geometry.getBounds().getCenterLonLat();\n- var cluster = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Point(center.lon, center.lat), {\n- count: 1\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (this.map) {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this)) {\n+ for (var type in this.listeners) {\n+ this.map.events.unregister(\n+ type, this, this.listeners[type]\n+ );\n+ }\n+ if (this.clearOnDeactivate) {\n+ this.clear();\n+ }\n+ deactivated = true;\n }\n- );\n- cluster.cluster = [feature];\n- return cluster;\n+ }\n+ return deactivated;\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.Cluster\"\n+ CLASS_NAME: \"OpenLayers.Control.NavigationHistory\"\n });\n+\n /* ======================================================================\n- OpenLayers/Strategy/Paging.js\n+ OpenLayers/Control/WMSGetFeatureInfo.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Handler/Hover.js\n+ * @requires OpenLayers/Request.js\n+ * @requires OpenLayers/Format/WMSGetFeatureInfo.js\n */\n \n /**\n- * Class: OpenLayers.Strategy.Paging\n- * Strategy for vector feature paging\n+ * Class: OpenLayers.Control.WMSGetFeatureInfo\n+ * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The\n+ * information may be in a display-friendly format such as HTML, or a machine-friendly format such\n+ * as GML, depending on the server's capabilities and the client's configuration. This control\n+ * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and\n+ * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an\n+ * array of features if it successfully read the response.\n *\n * Inherits from:\n- * - <OpenLayers.Strategy>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {\n+OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: features\n- * {Array(<OpenLayers.Feature.Vector>)} Cached features.\n+ * APIProperty: hover\n+ * {Boolean} Send GetFeatureInfo requests when mouse stops moving.\n+ * Default is false.\n */\n- features: null,\n+ hover: false,\n \n /**\n- * Property: length\n- * {Integer} Number of features per page. Default is 10.\n+ * APIProperty: drillDown\n+ * {Boolean} Drill down over all WMS layers in the map. When\n+ * using drillDown mode, hover is not possible, and an infoFormat that\n+ * returns parseable features is required. Default is false.\n */\n- length: 10,\n+ drillDown: false,\n \n /**\n- * Property: num\n- * {Integer} The currently displayed page number.\n+ * APIProperty: maxFeatures\n+ * {Integer} Maximum number of features to return from a WMS query. This\n+ * sets the feature_count parameter on WMS GetFeatureInfo\n+ * requests.\n */\n- num: null,\n+ maxFeatures: 10,\n \n /**\n- * Property: paging\n- * {Boolean} The strategy is currently changing pages.\n+ * APIProperty: clickCallback\n+ * {String} The click callback to register in the\n+ * {<OpenLayers.Handler.Click>} object created when the hover\n+ * option is set to false. Default is \"click\".\n */\n- paging: false,\n+ clickCallback: \"click\",\n \n /**\n- * Constructor: OpenLayers.Strategy.Paging\n- * Create a new paging strategy.\n+ * APIProperty: output\n+ * {String} Either \"features\" or \"object\". When triggering a getfeatureinfo\n+ * request should we pass on an array of features or an object with with\n+ * a \"features\" property and other properties (such as the url of the\n+ * WMS). Default is \"features\".\n+ */\n+ output: \"features\",\n+\n+ /**\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info.\n+ * If omitted, all map WMS layers with a url that matches this <url> or\n+ * <layerUrls> will be considered.\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: queryVisible\n+ * {Boolean} If true, filter out hidden layers when searching the map for\n+ * layers to query. Default is false.\n+ */\n+ queryVisible: false,\n+\n+ /**\n+ * APIProperty: url\n+ * {String} The URL of the WMS service to use. If not provided, the url\n+ * of the first eligible layer will be used.\n+ */\n+ url: null,\n+\n+ /**\n+ * APIProperty: layerUrls\n+ * {Array(String)} Optional list of urls for layers that should be queried.\n+ * This can be used when the layer url differs from the url used for\n+ * making GetFeatureInfo requests (in the case of a layer using cached\n+ * tiles).\n+ */\n+ layerUrls: null,\n+\n+ /**\n+ * APIProperty: infoFormat\n+ * {String} The mimetype to request from the server. If you are using\n+ * drillDown mode and have multiple servers that do not share a common\n+ * infoFormat, you can override the control's infoFormat by providing an\n+ * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).\n+ */\n+ infoFormat: 'text/html',\n+\n+ /**\n+ * APIProperty: vendorParams\n+ * {Object} Additional parameters that will be added to the request, for\n+ * WMS implementations that support them. This could e.g. look like\n+ * (start code)\n+ * {\n+ * radius: 5\n+ * }\n+ * (end)\n+ */\n+ vendorParams: {},\n+\n+ /**\n+ * APIProperty: format\n+ * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.\n+ * Default is <OpenLayers.Format.WMSGetFeatureInfo>.\n+ */\n+ format: null,\n+\n+ /**\n+ * APIProperty: formatOptions\n+ * {Object} Optional properties to set on the format (if one is not provided\n+ * in the <format> property.\n+ */\n+ formatOptions: null,\n+\n+ /**\n+ * APIProperty: handlerOptions\n+ * {Object} Additional options for the handlers used by this control, e.g.\n+ * (start code)\n+ * {\n+ * \"click\": {delay: 100},\n+ * \"hover\": {delay: 300}\n+ * }\n+ * (end)\n+ */\n+\n+ /**\n+ * Property: handler\n+ * {Object} Reference to the <OpenLayers.Handler> for this control\n+ */\n+ handler: null,\n+\n+ /**\n+ * Property: hoverRequest\n+ * {<OpenLayers.Request>} contains the currently running hover request\n+ * (if any).\n+ */\n+ hoverRequest: null,\n+\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforegetfeatureinfo - Triggered before the request is sent.\n+ * The event object has an *xy* property with the position of the\n+ * mouse click or hover event that triggers the request.\n+ * nogetfeatureinfo - no queryable layers were found.\n+ * getfeatureinfo - Triggered when a GetFeatureInfo response is received.\n+ * The event object has a *text* property with the body of the\n+ * response (String), a *features* property with an array of the\n+ * parsed features, an *xy* property with the position of the mouse\n+ * click or hover event that triggered the request, and a *request*\n+ * property with the request itself. If drillDown is set to true and\n+ * multiple requests were issued to collect feature info from all\n+ * layers, *text* and *request* will only contain the response body\n+ * and request object of the last request.\n */\n \n /**\n- * APIMethod: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully activated.\n+ * Constructor: <OpenLayers.Control.WMSGetFeatureInfo>\n+ *\n+ * Parameters:\n+ * options - {Object}\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- \"beforefeaturesadded\": this.cacheFeatures,\n- scope: this\n- });\n+ initialize: function(options) {\n+ options = options || {};\n+ options.handlerOptions = options.handlerOptions || {};\n+\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.WMSGetFeatureInfo(\n+ options.formatOptions\n+ );\n+ }\n+\n+ if (this.drillDown === true) {\n+ this.hover = false;\n+ }\n+\n+ if (this.hover) {\n+ this.handler = new OpenLayers.Handler.Hover(\n+ this, {\n+ 'move': this.cancelHover,\n+ 'pause': this.getInfoForHover\n+ },\n+ OpenLayers.Util.extend(this.handlerOptions.hover || {}, {\n+ 'delay': 250\n+ }));\n+ } else {\n+ var callbacks = {};\n+ callbacks[this.clickCallback] = this.getInfoForClick;\n+ this.handler = new OpenLayers.Handler.Click(\n+ this, callbacks, this.handlerOptions.click || {});\n }\n- return activated;\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * Method: getInfoForClick\n+ * Called on click\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>}\n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.clearCache();\n- this.layer.events.un({\n- \"beforefeaturesadded\": this.cacheFeatures,\n- scope: this\n- });\n- }\n- return deactivated;\n+ getInfoForClick: function(evt) {\n+ this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: evt.xy\n+ });\n+ // Set the cursor to \"wait\" to tell the user we're working on their\n+ // click.\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n+ this.request(evt.xy, {});\n },\n \n /**\n- * Method: cacheFeatures\n- * Cache features before they are added to the layer.\n+ * Method: getInfoForHover\n+ * Pause callback for the hover handler\n *\n * Parameters:\n- * event - {Object} The event that this was listening for. This will come\n- * with a batch of features to be paged.\n+ * evt - {Object}\n */\n- cacheFeatures: function(event) {\n- if (!this.paging) {\n- this.clearCache();\n- this.features = event.features;\n- this.pageNext(event);\n+ getInfoForHover: function(evt) {\n+ this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: evt.xy\n+ });\n+ this.request(evt.xy, {\n+ hover: true\n+ });\n+ },\n+\n+ /**\n+ * Method: cancelHover\n+ * Cancel callback for the hover handler\n+ */\n+ cancelHover: function() {\n+ if (this.hoverRequest) {\n+ this.hoverRequest.abort();\n+ this.hoverRequest = null;\n }\n },\n \n /**\n- * Method: clearCache\n- * Clear out the cached features. This destroys features, assuming\n- * nothing else has a reference.\n+ * Method: findLayers\n+ * Internal method to get the layers, independent of whether we are\n+ * inspecting the map or using a client-provided array\n */\n- clearCache: function() {\n- if (this.features) {\n- for (var i = 0; i < this.features.length; ++i) {\n- this.features[i].destroy();\n+ findLayers: function() {\n+\n+ var candidates = this.layers || this.map.layers;\n+ var layers = [];\n+ var layer, url;\n+ for (var i = candidates.length - 1; i >= 0; --i) {\n+ layer = candidates[i];\n+ if (layer instanceof OpenLayers.Layer.WMS &&\n+ (!this.queryVisible || layer.getVisibility())) {\n+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n+ // if the control was not configured with a url, set it\n+ // to the first layer url\n+ if (this.drillDown === false && !this.url) {\n+ this.url = url;\n+ }\n+ if (this.drillDown === true || this.urlMatches(url)) {\n+ layers.push(layer);\n+ }\n }\n }\n- this.features = null;\n- this.num = null;\n+ return layers;\n },\n \n /**\n- * APIMethod: pageCount\n- * Get the total count of pages given the current cache of features.\n+ * Method: urlMatches\n+ * Test to see if the provided url matches either the control <url> or one\n+ * of the <layerUrls>.\n+ *\n+ * Parameters:\n+ * url - {String} The url to test.\n *\n * Returns:\n- * {Integer} The page count.\n+ * {Boolean} The provided url matches the control <url> or one of the\n+ * <layerUrls>.\n */\n- pageCount: function() {\n- var numFeatures = this.features ? this.features.length : 0;\n- return Math.ceil(numFeatures / this.length);\n+ urlMatches: function(url) {\n+ var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);\n+ if (!matches && this.layerUrls) {\n+ for (var i = 0, len = this.layerUrls.length; i < len; ++i) {\n+ if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {\n+ matches = true;\n+ break;\n+ }\n+ }\n+ }\n+ return matches;\n },\n \n /**\n- * APIMethod: pageNum\n- * Get the zero based page number.\n+ * Method: buildWMSOptions\n+ * Build an object with the relevant WMS options for the GetFeatureInfo request\n *\n- * Returns:\n- * {Integer} The current page number being displayed.\n+ * Parameters:\n+ * url - {String} The url to be used for sending the request\n+ * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers\n+ * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse\n+ * event occurred.\n+ * format - {String} The format from the corresponding GetMap request\n */\n- pageNum: function() {\n- return this.num;\n+ buildWMSOptions: function(url, layers, clickPosition, format) {\n+ var layerNames = [],\n+ styleNames = [];\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ if (layers[i].params.LAYERS != null) {\n+ layerNames = layerNames.concat(layers[i].params.LAYERS);\n+ styleNames = styleNames.concat(this.getStyleNames(layers[i]));\n+ }\n+ }\n+ var firstLayer = layers[0];\n+ // use the firstLayer's projection if it matches the map projection -\n+ // this assumes that all layers will be available in this projection\n+ var projection = this.map.getProjection();\n+ var layerProj = firstLayer.projection;\n+ if (layerProj && layerProj.equals(this.map.getProjectionObject())) {\n+ projection = layerProj.getCode();\n+ }\n+ var params = OpenLayers.Util.extend({\n+ service: \"WMS\",\n+ version: firstLayer.params.VERSION,\n+ request: \"GetFeatureInfo\",\n+ exceptions: firstLayer.params.EXCEPTIONS,\n+ bbox: this.map.getExtent().toBBOX(null,\n+ firstLayer.reverseAxisOrder()),\n+ feature_count: this.maxFeatures,\n+ height: this.map.getSize().h,\n+ width: this.map.getSize().w,\n+ format: format,\n+ info_format: firstLayer.params.INFO_FORMAT || this.infoFormat\n+ }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? {\n+ crs: projection,\n+ i: parseInt(clickPosition.x),\n+ j: parseInt(clickPosition.y)\n+ } : {\n+ srs: projection,\n+ x: parseInt(clickPosition.x),\n+ y: parseInt(clickPosition.y)\n+ });\n+ if (layerNames.length != 0) {\n+ params = OpenLayers.Util.extend({\n+ layers: layerNames,\n+ query_layers: layerNames,\n+ styles: styleNames\n+ }, params);\n+ }\n+ OpenLayers.Util.applyDefaults(params, this.vendorParams);\n+ return {\n+ url: url,\n+ params: OpenLayers.Util.upperCaseObject(params),\n+ callback: function(request) {\n+ this.handleResponse(clickPosition, request, url);\n+ },\n+ scope: this\n+ };\n },\n \n /**\n- * APIMethod: pageLength\n- * Gets or sets page length.\n+ * Method: getStyleNames\n+ * Gets the STYLES parameter for the layer. Make sure the STYLES parameter\n+ * matches the LAYERS parameter\n *\n * Parameters:\n- * newLength - {Integer} Optional length to be set.\n+ * layer - {<OpenLayers.Layer.WMS>}\n *\n * Returns:\n- * {Integer} The length of a page (number of features per page).\n+ * {Array(String)} The STYLES parameter\n */\n- pageLength: function(newLength) {\n- if (newLength && newLength > 0) {\n- this.length = newLength;\n+ getStyleNames: function(layer) {\n+ // in the event of a WMS layer bundling multiple layers but not\n+ // specifying styles,we need the same number of commas to specify\n+ // the default style for each of the layers. We can't just leave it\n+ // blank as we may be including other layers that do specify styles.\n+ var styleNames;\n+ if (layer.params.STYLES) {\n+ styleNames = layer.params.STYLES;\n+ } else {\n+ if (OpenLayers.Util.isArray(layer.params.LAYERS)) {\n+ styleNames = new Array(layer.params.LAYERS.length);\n+ } else { // Assume it's a String\n+ styleNames = layer.params.LAYERS.replace(/[^,]/g, \"\");\n+ }\n }\n- return this.length;\n+ return styleNames;\n },\n \n /**\n- * APIMethod: pageNext\n- * Display the next page of features.\n+ * Method: request\n+ * Sends a GetFeatureInfo request to the WMS\n *\n- * Returns:\n- * {Boolean} A new page was displayed.\n+ * Parameters:\n+ * clickPosition - {<OpenLayers.Pixel>} The position on the map where the\n+ * mouse event occurred.\n+ * options - {Object} additional options for this method.\n+ *\n+ * Valid options:\n+ * - *hover* {Boolean} true if we do the request for the hover handler\n */\n- pageNext: function(event) {\n- var changed = false;\n- if (this.features) {\n- if (this.num === null) {\n- this.num = -1;\n+ request: function(clickPosition, options) {\n+ var layers = this.findLayers();\n+ if (layers.length == 0) {\n+ this.events.triggerEvent(\"nogetfeatureinfo\");\n+ // Reset the cursor.\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ return;\n+ }\n+\n+ options = options || {};\n+ if (this.drillDown === false) {\n+ var wmsOptions = this.buildWMSOptions(this.url, layers,\n+ clickPosition, layers[0].params.FORMAT);\n+ var request = OpenLayers.Request.GET(wmsOptions);\n+\n+ if (options.hover === true) {\n+ this.hoverRequest = request;\n+ }\n+ } else {\n+ this._requestCount = 0;\n+ this._numRequests = 0;\n+ this.features = [];\n+ // group according to service url to combine requests\n+ var services = {},\n+ url;\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ var service, found = false;\n+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n+ if (url in services) {\n+ services[url].push(layer);\n+ } else {\n+ this._numRequests++;\n+ services[url] = [layer];\n+ }\n+ }\n+ var layers;\n+ for (var url in services) {\n+ layers = services[url];\n+ var wmsOptions = this.buildWMSOptions(url, layers,\n+ clickPosition, layers[0].params.FORMAT);\n+ OpenLayers.Request.GET(wmsOptions);\n }\n- var start = (this.num + 1) * this.length;\n- changed = this.page(start, event);\n }\n- return changed;\n },\n \n /**\n- * APIMethod: pagePrevious\n- * Display the previous page of features.\n+ * Method: triggerGetFeatureInfo\n+ * Trigger the getfeatureinfo event when all is done\n *\n- * Returns:\n- * {Boolean} A new page was displayed.\n+ * Parameters:\n+ * request - {XMLHttpRequest} The request object\n+ * xy - {<OpenLayers.Pixel>} The position on the map where the\n+ * mouse event occurred.\n+ * features - {Array(<OpenLayers.Feature.Vector>)} or\n+ * {Array({Object}) when output is \"object\". The object has a url and a\n+ * features property which contains an array of features.\n */\n- pagePrevious: function() {\n- var changed = false;\n- if (this.features) {\n- if (this.num === null) {\n- this.num = this.pageCount();\n- }\n- var start = (this.num - 1) * this.length;\n- changed = this.page(start);\n- }\n- return changed;\n+ triggerGetFeatureInfo: function(request, xy, features) {\n+ this.events.triggerEvent(\"getfeatureinfo\", {\n+ text: request.responseText,\n+ features: features,\n+ request: request,\n+ xy: xy\n+ });\n+\n+ // Reset the cursor.\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n },\n \n /**\n- * Method: page\n- * Display the page starting at the given index from the cache.\n+ * Method: handleResponse\n+ * Handler for the GetFeatureInfo response.\n *\n- * Returns:\n- * {Boolean} A new page was displayed.\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The position on the map where the\n+ * mouse event occurred.\n+ * request - {XMLHttpRequest} The request object.\n+ * url - {String} The url which was used for this request.\n */\n- page: function(start, event) {\n- var changed = false;\n- if (this.features) {\n- if (start >= 0 && start < this.features.length) {\n- var num = Math.floor(start / this.length);\n- if (num != this.num) {\n- this.paging = true;\n- var features = this.features.slice(start, start + this.length);\n- this.layer.removeFeatures(this.layer.features);\n- this.num = num;\n- // modify the event if any\n- if (event && event.features) {\n- // this.was called by an event listener\n- event.features = features;\n- } else {\n- // this was called directly on the strategy\n- this.layer.addFeatures(features);\n- }\n- this.paging = false;\n- changed = true;\n- }\n+ handleResponse: function(xy, request, url) {\n+\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ var features = this.format.read(doc);\n+ if (this.drillDown === false) {\n+ this.triggerGetFeatureInfo(request, xy, features);\n+ } else {\n+ this._requestCount++;\n+ if (this.output === \"object\") {\n+ this._features = (this._features || []).concat({\n+ url: url,\n+ features: features\n+ });\n+ } else {\n+ this._features = (this._features || []).concat(features);\n+ }\n+ if (this._requestCount === this._numRequests) {\n+ this.triggerGetFeatureInfo(request, xy, this._features.concat());\n+ delete this._features;\n+ delete this._requestCount;\n+ delete this._numRequests;\n }\n }\n- return changed;\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.Paging\"\n+ CLASS_NAME: \"OpenLayers.Control.WMSGetFeatureInfo\"\n });\n /* ======================================================================\n- OpenLayers/Strategy/Filter.js\n+ OpenLayers/Control/DragFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Strategy.js\n- * @requires OpenLayers/Filter.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Handler/Feature.js\n */\n \n /**\n- * Class: OpenLayers.Strategy.Filter\n- * Strategy for limiting features that get added to a layer by \n- * evaluating a filter. The strategy maintains a cache of\n- * all features until removeFeatures is called on the layer.\n+ * Class: OpenLayers.Control.DragFeature\n+ * The DragFeature control moves a feature with a drag of the mouse. Create a\n+ * new control with the <OpenLayers.Control.DragFeature> constructor.\n *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n+ * Inherits From:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {\n+OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * APIProperty: filter\n- * {<OpenLayers.Filter>} Filter for limiting features sent to the layer.\n- * Use the <setFilter> method to update this filter after construction.\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict dragging to a limited set of geometry types,\n+ * send a list of strings corresponding to the geometry class names.\n */\n- filter: null,\n+ geometryTypes: null,\n \n /**\n- * Property: cache\n- * {Array(<OpenLayers.Feature.Vector>)} List of currently cached\n- * features.\n+ * APIProperty: onStart\n+ * {Function} Define this function if you want to know when a drag starts.\n+ * The function should expect to receive two arguments: the feature\n+ * that is about to be dragged and the pixel location of the mouse.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be\n+ * dragged.\n+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n */\n- cache: null,\n+ onStart: function(feature, pixel) {},\n \n /**\n- * Property: caching\n- * {Boolean} The filter is currently caching features.\n+ * APIProperty: onDrag\n+ * {Function} Define this function if you want to know about each move of a\n+ * feature. The function should expect to receive two arguments: the\n+ * feature that is being dragged and the pixel location of the mouse.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n */\n- caching: false,\n+ onDrag: function(feature, pixel) {},\n \n /**\n- * Constructor: OpenLayers.Strategy.Filter\n- * Create a new filter strategy.\n+ * APIProperty: onComplete\n+ * {Function} Define this function if you want to know when a feature is\n+ * done dragging. The function should expect to receive two arguments:\n+ * the feature that is being dragged and the pixel location of the\n+ * mouse.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n+ * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n */\n+ onComplete: function(feature, pixel) {},\n \n /**\n- * APIMethod: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- * By default, this strategy automatically activates itself when a layer\n- * is added to a map.\n+ * APIProperty: onEnter\n+ * {Function} Define this function if you want to know when the mouse\n+ * goes over a feature and thereby makes this feature a candidate\n+ * for dragging.\n *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that is ready\n+ * to be dragged.\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.cache = [];\n- this.layer.events.on({\n- \"beforefeaturesadded\": this.handleAdd,\n- \"beforefeaturesremoved\": this.handleRemove,\n- scope: this\n- });\n- }\n- return activated;\n- },\n+ onEnter: function(feature) {},\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the strategy. Clear the feature cache.\n+ * APIProperty: onLeave\n+ * {Function} Define this function if you want to know when the mouse\n+ * goes out of the feature that was dragged.\n *\n- * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n */\n- deactivate: function() {\n- this.cache = null;\n- if (this.layer && this.layer.events) {\n- this.layer.events.un({\n- \"beforefeaturesadded\": this.handleAdd,\n- \"beforefeaturesremoved\": this.handleRemove,\n- scope: this\n- });\n- }\n- return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);\n- },\n+ onLeave: function(feature) {},\n \n /**\n- * Method: handleAdd\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, mouse dragging will continue even if the\n+ * mouse cursor leaves the map viewport. Default is false.\n */\n- handleAdd: function(event) {\n- if (!this.caching && this.filter) {\n- var features = event.features;\n- event.features = [];\n- var feature;\n- for (var i = 0, ii = features.length; i < ii; ++i) {\n- feature = features[i];\n- if (this.filter.evaluate(feature)) {\n- event.features.push(feature);\n- } else {\n- this.cache.push(feature);\n- }\n- }\n- }\n- },\n+ documentDrag: false,\n \n /**\n- * Method: handleRemove\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>}\n */\n- handleRemove: function(event) {\n- if (!this.caching) {\n- this.cache = [];\n- }\n- },\n+ layer: null,\n \n- /** \n- * APIMethod: setFilter\n- * Update the filter for this strategy. This will re-evaluate\n- * any features on the layer and in the cache. Only features\n- * for which filter.evalute(feature) returns true will be\n- * added to the layer. Others will be cached by the strategy.\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>} A filter for evaluating features.\n+ /**\n+ * Property: feature\n+ * {<OpenLayers.Feature.Vector>}\n */\n- setFilter: function(filter) {\n- this.filter = filter;\n- var previousCache = this.cache;\n- this.cache = [];\n- // look through layer for features to remove from layer\n- this.handleAdd({\n- features: this.layer.features\n- });\n- // cache now contains features to remove from layer\n- if (this.cache.length > 0) {\n- this.caching = true;\n- this.layer.removeFeatures(this.cache.slice());\n- this.caching = false;\n- }\n- // now look through previous cache for features to add to layer\n- if (previousCache.length > 0) {\n- var event = {\n- features: previousCache\n- };\n- this.handleAdd(event);\n- if (event.features.length > 0) {\n- // event has features to add to layer\n- this.caching = true;\n- this.layer.addFeatures(event.features);\n- this.caching = false;\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Strategy.Filter\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Strategy/Refresh.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ feature: null,\n \n-/**\n- * @requires OpenLayers/Strategy.js\n- */\n+ /**\n+ * Property: dragCallbacks\n+ * {Object} The functions that are sent to the drag handler for callback.\n+ */\n+ dragCallbacks: {},\n \n-/**\n- * Class: OpenLayers.Strategy.Refresh\n- * A strategy that refreshes the layer. By default the strategy waits for a\n- * call to <refresh> before refreshing. By configuring the strategy with \n- * the <interval> option, refreshing can take place automatically.\n- *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n- */\n-OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {\n+ /**\n+ * Property: featureCallbacks\n+ * {Object} The functions that are sent to the feature handler for callback.\n+ */\n+ featureCallbacks: {},\n \n /**\n- * Property: force\n- * {Boolean} Force a refresh on the layer. Default is false.\n+ * Property: lastPixel\n+ * {<OpenLayers.Pixel>}\n */\n- force: false,\n+ lastPixel: null,\n \n /**\n- * Property: interval\n- * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed \n- * every N milliseconds.\n+ * Constructor: OpenLayers.Control.DragFeature\n+ * Create a new control to drag features.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be\n+ * dragged.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * control.\n */\n- interval: 0,\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.layer = layer;\n+ this.handlers = {\n+ drag: new OpenLayers.Handler.Drag(\n+ this, OpenLayers.Util.extend({\n+ down: this.downFeature,\n+ move: this.moveFeature,\n+ up: this.upFeature,\n+ out: this.cancel,\n+ done: this.doneDragging\n+ }, this.dragCallbacks), {\n+ documentDrag: this.documentDrag\n+ }\n+ ),\n+ feature: new OpenLayers.Handler.Feature(\n+ this, this.layer, OpenLayers.Util.extend({\n+ // 'click' and 'clickout' callback are for the mobile\n+ // support: no 'over' or 'out' in touch based browsers.\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature,\n+ over: this.overFeature,\n+ out: this.outFeature\n+ }, this.featureCallbacks), {\n+ geometryTypes: this.geometryTypes\n+ }\n+ )\n+ };\n+ },\n \n /**\n- * Property: timer\n- * {Number} The id of the timer.\n+ * Method: clickFeature\n+ * Called when the feature handler detects a click-in on a feature.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n */\n- timer: null,\n+ clickFeature: function(feature) {\n+ if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {\n+ this.handlers.drag.dragstart(this.handlers.feature.evt);\n+ // to let the events propagate to the feature handler (click callback)\n+ this.handlers.drag.stopDown = false;\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Strategy.Refresh\n- * Create a new Refresh strategy.\n+ * Method: clickoutFeature\n+ * Called when the feature handler detects a click-out on a feature.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ clickoutFeature: function(feature) {\n+ if (this.handlers.feature.touch && this.over) {\n+ this.outFeature(feature);\n+ this.handlers.drag.stopDown = true;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Take care of things that are not handled in superclass\n */\n+ destroy: function() {\n+ this.layer = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, []);\n+ },\n \n /**\n * APIMethod: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n+ * Activate the control and the feature handler.\n * \n * Returns:\n- * {Boolean} True if the strategy was successfully activated.\n+ * {Boolean} Successfully activated the control and feature handler.\n */\n activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- if (this.layer.visibility === true) {\n- this.start();\n- }\n- this.layer.events.on({\n- \"visibilitychanged\": this.reset,\n- scope: this\n- });\n- }\n- return activated;\n+ return (this.handlers.feature.activate() &&\n+ OpenLayers.Control.prototype.activate.apply(this, arguments));\n },\n \n /**\n * APIMethod: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n+ * Deactivate the control and all handlers.\n * \n * Returns:\n- * {Boolean} True if the strategy was successfully deactivated.\n+ * {Boolean} Successfully deactivated the control.\n */\n deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.stop();\n- this.layer.events.un({\n- \"visibilitychanged\": this.reset,\n- scope: this\n- });\n- }\n- return deactivated;\n+ // the return from the handlers is unimportant in this case\n+ this.handlers.drag.deactivate();\n+ this.handlers.feature.deactivate();\n+ this.feature = null;\n+ this.dragging = false;\n+ this.lastPixel = null;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, this.displayClass + \"Over\"\n+ );\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n },\n \n /**\n- * Method: reset\n- * Start or cancel the refresh interval depending on the visibility of \n- * the layer.\n+ * Method: overFeature\n+ * Called when the feature handler detects a mouse-over on a feature.\n+ * This activates the drag handler.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The selected feature.\n+ *\n+ * Returns:\n+ * {Boolean} Successfully activated the drag handler.\n */\n- reset: function() {\n- if (this.layer.visibility === true) {\n- this.start();\n+ overFeature: function(feature) {\n+ var activated = false;\n+ if (!this.handlers.drag.dragging) {\n+ this.feature = feature;\n+ this.handlers.drag.activate();\n+ activated = true;\n+ this.over = true;\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n+ this.onEnter(feature);\n } else {\n- this.stop();\n+ if (this.feature.id == feature.id) {\n+ this.over = true;\n+ } else {\n+ this.over = false;\n+ }\n }\n+ return activated;\n },\n \n /**\n- * Method: start\n- * Start the refresh interval. \n+ * Method: downFeature\n+ * Called when the drag handler detects a mouse-down.\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n */\n- start: function() {\n- if (this.interval && typeof this.interval === \"number\" &&\n- this.interval > 0) {\n+ downFeature: function(pixel) {\n+ this.lastPixel = pixel;\n+ this.onStart(this.feature, pixel);\n+ },\n \n- this.timer = window.setInterval(\n- OpenLayers.Function.bind(this.refresh, this),\n- this.interval);\n+ /**\n+ * Method: moveFeature\n+ * Called when the drag handler detects a mouse-move. Also calls the\n+ * optional onDrag method.\n+ * \n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n+ */\n+ moveFeature: function(pixel) {\n+ var res = this.map.getResolution();\n+ this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),\n+ res * (this.lastPixel.y - pixel.y));\n+ this.layer.drawFeature(this.feature);\n+ this.lastPixel = pixel;\n+ this.onDrag(this.feature, pixel);\n+ },\n+\n+ /**\n+ * Method: upFeature\n+ * Called when the drag handler detects a mouse-up.\n+ * \n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n+ */\n+ upFeature: function(pixel) {\n+ if (!this.over) {\n+ this.handlers.drag.deactivate();\n }\n },\n \n /**\n- * APIMethod: refresh\n- * Tell the strategy to refresh which will refresh the layer.\n+ * Method: doneDragging\n+ * Called when the drag handler is done dragging.\n+ *\n+ * Parameters:\n+ * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event\n+ * came from a mouseout, this may not be in the map viewport.\n */\n- refresh: function() {\n- if (this.layer && this.layer.refresh &&\n- typeof this.layer.refresh == \"function\") {\n+ doneDragging: function(pixel) {\n+ this.onComplete(this.feature, pixel);\n+ },\n \n- this.layer.refresh({\n- force: this.force\n- });\n+ /**\n+ * Method: outFeature\n+ * Called when the feature handler detects a mouse-out on a feature.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.\n+ */\n+ outFeature: function(feature) {\n+ if (!this.handlers.drag.dragging) {\n+ this.over = false;\n+ this.handlers.drag.deactivate();\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, this.displayClass + \"Over\"\n+ );\n+ this.onLeave(feature);\n+ this.feature = null;\n+ } else {\n+ if (this.feature.id == feature.id) {\n+ this.over = false;\n+ }\n }\n },\n \n /**\n- * Method: stop\n- * Cancels the refresh interval. \n+ * Method: cancel\n+ * Called when the drag handler detects a mouse-out (from the map viewport).\n */\n- stop: function() {\n- if (this.timer !== null) {\n- window.clearInterval(this.timer);\n- this.timer = null;\n- }\n+ cancel: function() {\n+ this.handlers.drag.deactivate();\n+ this.over = false;\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.Refresh\"\n+ /**\n+ * Method: setMap\n+ * Set the map property for the control and all handlers.\n+ *\n+ * Parameters: \n+ * map - {<OpenLayers.Map>} The control's map.\n+ */\n+ setMap: function(map) {\n+ this.handlers.drag.setMap(map);\n+ this.handlers.feature.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.DragFeature\"\n });\n /* ======================================================================\n- OpenLayers/Strategy/Save.js\n+ OpenLayers/Control/ArgParser.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Control.js\n */\n \n /**\n- * Class: OpenLayers.Strategy.Save\n- * A strategy that commits newly created or modified features. By default\n- * the strategy waits for a call to <save> before persisting changes. By\n- * configuring the strategy with the <auto> option, changes can be saved\n- * automatically.\n+ * Class: OpenLayers.Control.ArgParser\n+ * The ArgParser control adds location bar query string parsing functionality \n+ * to an OpenLayers Map.\n+ * When added to a Map control, on a page load/refresh, the Map will \n+ * automatically take the href string and parse it for lon, lat, zoom, and \n+ * layers information. \n *\n * Inherits from:\n- * - <OpenLayers.Strategy>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {\n+OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the strategy object.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * strategy.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types:\n- * start - Triggered before saving\n- * success - Triggered after a successful transaction\n- * fail - Triggered after a failed transaction\n- * \n+ * Property: center\n+ * {<OpenLayers.LonLat>}\n */\n+ center: null,\n \n- /** \n- * Property: events\n- * {<OpenLayers.Events>} Events instance for triggering this protocol\n- * events.\n+ /**\n+ * Property: zoom\n+ * {int}\n */\n- events: null,\n+ zoom: null,\n \n /**\n- * APIProperty: auto\n- * {Boolean | Number} Auto-save. Default is false. If true, features will be\n- * saved immediately after being added to the layer and with each\n- * modification or deletion. If auto is a number, features will be\n- * saved on an interval provided by the value (in seconds).\n+ * Property: layers\n+ * {String} Each character represents the state of the corresponding layer \n+ * on the map.\n */\n- auto: false,\n+ layers: null,\n \n- /**\n- * Property: timer\n- * {Number} The id of the timer.\n+ /** \n+ * APIProperty: displayProjection\n+ * {<OpenLayers.Projection>} Requires proj4js support. \n+ * Projection used when reading the coordinates from the URL. This will\n+ * reproject the map coordinates from the URL into the map's\n+ * projection.\n+ *\n+ * If you are using this functionality, be aware that any permalink\n+ * which is added to the map will determine the coordinate type which\n+ * is read from the URL, which means you should not add permalinks with\n+ * different displayProjections to the same map. \n */\n- timer: null,\n+ displayProjection: null,\n \n /**\n- * Constructor: OpenLayers.Strategy.Save\n- * Create a new Save strategy.\n+ * Constructor: OpenLayers.Control.ArgParser\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * options - {Object}\n */\n- initialize: function(options) {\n- OpenLayers.Strategy.prototype.initialize.apply(this, [options]);\n- this.events = new OpenLayers.Events(this);\n- },\n \n /**\n- * APIMethod: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully activated.\n+ * Method: getParameters\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- if (this.auto) {\n- if (typeof this.auto === \"number\") {\n- this.timer = window.setInterval(\n- OpenLayers.Function.bind(this.save, this),\n- this.auto * 1000\n- );\n- } else {\n- this.layer.events.on({\n- \"featureadded\": this.triggerSave,\n- \"afterfeaturemodified\": this.triggerSave,\n- scope: this\n- });\n- }\n- }\n+ getParameters: function(url) {\n+ url = url || window.location.href;\n+ var parameters = OpenLayers.Util.getParameters(url);\n+\n+ // If we have an anchor in the url use it to split the url\n+ var index = url.indexOf('#');\n+ if (index > 0) {\n+ // create an url to parse on the getParameters\n+ url = '?' + url.substring(index + 1, url.length);\n+\n+ OpenLayers.Util.extend(parameters,\n+ OpenLayers.Util.getParameters(url));\n }\n- return activated;\n+ return parameters;\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n+ * Method: setMap\n+ * Set the map property for the control. \n * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.auto) {\n- if (typeof this.auto === \"number\") {\n- window.clearInterval(this.timer);\n- } else {\n- this.layer.events.un({\n- \"featureadded\": this.triggerSave,\n- \"afterfeaturemodified\": this.triggerSave,\n- scope: this\n- });\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+\n+ //make sure we dont already have an arg parser attached\n+ for (var i = 0, len = this.map.controls.length; i < len; i++) {\n+ var control = this.map.controls[i];\n+ if ((control != this) &&\n+ (control.CLASS_NAME == \"OpenLayers.Control.ArgParser\")) {\n+\n+ // If a second argparser is added to the map, then we \n+ // override the displayProjection to be the one added to the\n+ // map. \n+ if (control.displayProjection != this.displayProjection) {\n+ this.displayProjection = control.displayProjection;\n }\n+\n+ break;\n }\n }\n- return deactivated;\n- },\n+ if (i == this.map.controls.length) {\n \n- /**\n- * Method: triggerSave\n- * Registered as a listener. Calls save if a feature has insert, update,\n- * or delete state.\n- *\n- * Parameters:\n- * event - {Object} The event this function is listening for.\n- */\n- triggerSave: function(event) {\n- var feature = event.feature;\n- if (feature.state === OpenLayers.State.INSERT ||\n- feature.state === OpenLayers.State.UPDATE ||\n- feature.state === OpenLayers.State.DELETE) {\n- this.save([event.feature]);\n+ var args = this.getParameters();\n+ // Be careful to set layer first, to not trigger unnecessary layer loads\n+ if (args.layers) {\n+ this.layers = args.layers;\n+\n+ // when we add a new layer, set its visibility \n+ this.map.events.register('addlayer', this,\n+ this.configureLayers);\n+ this.configureLayers();\n+ }\n+ if (args.lat && args.lon) {\n+ this.center = new OpenLayers.LonLat(parseFloat(args.lon),\n+ parseFloat(args.lat));\n+ if (args.zoom) {\n+ this.zoom = parseFloat(args.zoom);\n+ }\n+\n+ // when we add a new baselayer to see when we can set the center\n+ this.map.events.register('changebaselayer', this,\n+ this.setCenter);\n+ this.setCenter();\n+ }\n }\n },\n \n- /**\n- * APIMethod: save\n- * Tell the layer protocol to commit unsaved features. If the layer\n- * projection differs from the map projection, features will be\n- * transformed into the layer projection before being committed.\n- *\n- * Parameters:\n- * features - {Array} Features to be saved. If null, then default is all\n- * features in the layer. Features are assumed to be in the map\n- * projection.\n+ /** \n+ * Method: setCenter\n+ * As soon as a baseLayer has been loaded, we center and zoom\n+ * ...and remove the handler.\n */\n- save: function(features) {\n- if (!features) {\n- features = this.layer.features;\n- }\n- this.events.triggerEvent(\"start\", {\n- features: features\n- });\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var len = features.length;\n- var clones = new Array(len);\n- var orig, clone;\n- for (var i = 0; i < len; ++i) {\n- orig = features[i];\n- clone = orig.clone();\n- clone.fid = orig.fid;\n- clone.state = orig.state;\n- if (orig.url) {\n- clone.url = orig.url;\n- }\n- clone._original = orig;\n- clone.geometry.transform(local, remote);\n- clones[i] = clone;\n+ setCenter: function() {\n+\n+ if (this.map.baseLayer) {\n+ //dont need to listen for this one anymore\n+ this.map.events.unregister('changebaselayer', this,\n+ this.setCenter);\n+\n+ if (this.displayProjection) {\n+ this.center.transform(this.displayProjection,\n+ this.map.getProjectionObject());\n }\n- features = clones;\n+\n+ this.map.setCenter(this.center, this.zoom);\n }\n- this.layer.protocol.commit(features, {\n- callback: this.onCommit,\n- scope: this\n- });\n },\n \n- /**\n- * Method: onCommit\n- * Called after protocol commit.\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} A response object.\n+ /** \n+ * Method: configureLayers\n+ * As soon as all the layers are loaded, cycle through them and \n+ * hide or show them. \n */\n- onCommit: function(response) {\n- var evt = {\n- \"response\": response\n- };\n- if (response.success()) {\n- var features = response.reqFeatures;\n- // deal with inserts, updates, and deletes\n- var state, feature;\n- var destroys = [];\n- var insertIds = response.insertIds || [];\n- var j = 0;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- // if projection was different, we may be dealing with clones\n- feature = feature._original || feature;\n- state = feature.state;\n- if (state) {\n- if (state == OpenLayers.State.DELETE) {\n- destroys.push(feature);\n- } else if (state == OpenLayers.State.INSERT) {\n- feature.fid = insertIds[j];\n- ++j;\n- }\n- feature.state = null;\n- }\n- }\n+ configureLayers: function() {\n \n- if (destroys.length > 0) {\n- this.layer.destroyFeatures(destroys);\n- }\n+ if (this.layers.length == this.map.layers.length) {\n+ this.map.events.unregister('addlayer', this, this.configureLayers);\n \n- this.events.triggerEvent(\"success\", evt);\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n \n- } else {\n- this.events.triggerEvent(\"fail\", evt);\n+ var layer = this.map.layers[i];\n+ var c = this.layers.charAt(i);\n+\n+ if (c == \"B\") {\n+ this.map.setBaseLayer(layer);\n+ } else if ((c == \"T\") || (c == \"F\")) {\n+ layer.setVisibility(c == \"T\");\n+ }\n+ }\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.Save\"\n+ CLASS_NAME: \"OpenLayers.Control.ArgParser\"\n });\n /* ======================================================================\n- OpenLayers/Strategy/Fixed.js\n+ OpenLayers/Control/Permalink.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Control/ArgParser.js\n+ * @requires OpenLayers/Lang.js\n */\n \n /**\n- * Class: OpenLayers.Strategy.Fixed\n- * A simple strategy that requests features once and never requests new data.\n- *\n+ * Class: OpenLayers.Control.Permalink\n+ * The Permalink control is hyperlink that will return the user to the \n+ * current map view. By default it is drawn in the lower right corner of the\n+ * map. The href is updated as the map is zoomed, panned and whilst layers\n+ * are switched.\n+ * \n * Inherits from:\n- * - <OpenLayers.Strategy>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n+OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * APIProperty: preload\n- * {Boolean} Load data before layer made visible. Enabling this may result\n- * in considerable overhead if your application loads many data layers\n- * that are not visible by default. Default is false.\n+ * APIProperty: argParserClass\n+ * {Class} The ArgParser control class (not instance) to use with this\n+ * control.\n */\n- preload: false,\n+ argParserClass: OpenLayers.Control.ArgParser,\n \n- /**\n- * Constructor: OpenLayers.Strategy.Fixed\n- * Create a new Fixed strategy.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ /** \n+ * Property: element \n+ * {DOMElement}\n+ */\n+ element: null,\n+\n+ /** \n+ * APIProperty: anchor\n+ * {Boolean} This option changes 3 things:\n+ * the character '#' is used in place of the character '?',\n+ * the window.href is updated if no element is provided.\n+ * When this option is set to true it's not recommend to provide\n+ * a base without provide an element.\n+ */\n+ anchor: false,\n+\n+ /** \n+ * APIProperty: base\n+ * {String}\n+ */\n+ base: '',\n+\n+ /** \n+ * APIProperty: displayProjection\n+ * {<OpenLayers.Projection>} Requires proj4js support. Projection used\n+ * when creating the coordinates in the link. This will reproject the\n+ * map coordinates into display coordinates. If you are using this\n+ * functionality, the permalink which is last added to the map will\n+ * determine the coordinate type which is read from the URL, which\n+ * means you should not add permalinks with different\n+ * displayProjections to the same map. \n */\n+ displayProjection: null,\n \n /**\n- * Method: activate\n- * Activate the strategy: load data or add listener to load when visible\n+ * Constructor: OpenLayers.Control.Permalink\n *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n+ * Parameters: \n+ * element - {DOMElement} \n+ * base - {String} \n+ * options - {Object} options to the control.\n+ *\n+ * Or for anchor:\n+ * options - {Object} options to the control.\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- \"refresh\": this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load();\n- } else {\n- this.layer.events.on({\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n+ initialize: function(element, base, options) {\n+ if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) {\n+ options = element;\n+ this.base = document.location.href;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (this.element != null) {\n+ this.element = OpenLayers.Util.getElement(this.element);\n }\n+ } else {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.element = OpenLayers.Util.getElement(element);\n+ this.base = base || document.location.href;\n }\n- return activated;\n },\n \n /**\n- * Method: deactivate\n- * Deactivate the strategy. Undo what is done in <activate>.\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ if (this.element && this.element.parentNode == this.div) {\n+ this.div.removeChild(this.element);\n+ this.element = null;\n+ }\n+ if (this.map) {\n+ this.map.events.unregister('moveend', this, this.updateLink);\n+ }\n+\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: setMap\n+ * Set the map property for the control. \n * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- \"refresh\": this.load,\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+\n+ //make sure we have an arg parser attached\n+ for (var i = 0, len = this.map.controls.length; i < len; i++) {\n+ var control = this.map.controls[i];\n+ if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {\n+\n+ // If a permalink is added to the map, and an ArgParser already\n+ // exists, we override the displayProjection to be the one\n+ // on the permalink. \n+ if (control.displayProjection != this.displayProjection) {\n+ this.displayProjection = control.displayProjection;\n+ }\n+\n+ break;\n+ }\n }\n- return deactivated;\n+ if (i == this.map.controls.length) {\n+ this.map.addControl(new this.argParserClass({\n+ 'displayProjection': this.displayProjection\n+ }));\n+ }\n+\n },\n \n /**\n- * Method: load\n- * Tells protocol to load data and unhooks the visibilitychanged event\n+ * Method: draw\n *\n- * Parameters:\n- * options - {Object} options to pass to protocol read.\n+ * Returns:\n+ * {DOMElement}\n */\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- \"visibilitychanged\": this.load,\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+\n+ if (!this.element && !this.anchor) {\n+ this.element = document.createElement(\"a\");\n+ this.element.innerHTML = OpenLayers.i18n(\"Permalink\");\n+ this.element.href = \"\";\n+ this.div.appendChild(this.element);\n+ }\n+ this.map.events.on({\n+ 'moveend': this.updateLink,\n+ 'changelayer': this.updateLink,\n+ 'changebaselayer': this.updateLink,\n scope: this\n });\n+\n+ // Make it so there is at least a link even though the map may not have\n+ // moved yet.\n+ this.updateLink();\n+\n+ return this.div;\n },\n \n /**\n- * Method: merge\n- * Add all features to the layer.\n- * If the layer projection differs from the map projection, features\n- * will be transformed from the layer projection to the map projection.\n- *\n+ * Method: updateLink \n+ */\n+ updateLink: function() {\n+ var separator = this.anchor ? '#' : '?';\n+ var href = this.base;\n+ var anchor = null;\n+ if (href.indexOf(\"#\") != -1 && this.anchor == false) {\n+ anchor = href.substring(href.indexOf(\"#\"), href.length);\n+ }\n+ if (href.indexOf(separator) != -1) {\n+ href = href.substring(0, href.indexOf(separator));\n+ }\n+ var splits = href.split(\"#\");\n+ href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams());\n+ if (anchor) {\n+ href += anchor;\n+ }\n+ if (this.anchor && !this.element) {\n+ window.location.href = href;\n+ } else {\n+ this.element.href = href;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: createParams\n+ * Creates the parameters that need to be encoded into the permalink url.\n+ * \n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object passed\n- * by the protocol.\n+ * center - {<OpenLayers.LonLat>} center to encode in the permalink.\n+ * Defaults to the current map center.\n+ * zoom - {Integer} zoom level to encode in the permalink. Defaults to the\n+ * current map zoom level.\n+ * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.\n+ * Defaults to the current map layers.\n+ * \n+ * Returns:\n+ * {Object} Hash of parameters that will be url-encoded into the\n+ * permalink.\n */\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local);\n- }\n+ createParams: function(center, zoom, layers) {\n+ center = center || this.map.getCenter();\n+\n+ var params = OpenLayers.Util.getParameters(this.base);\n+\n+ // If there's still no center, map is not initialized yet. \n+ // Break out of this function, and simply return the params from the\n+ // base link.\n+ if (center) {\n+\n+ //zoom\n+ params.zoom = zoom || this.map.getZoom();\n+\n+ //lon,lat\n+ var lat = center.lat;\n+ var lon = center.lon;\n+\n+ if (this.displayProjection) {\n+ var mapPosition = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ },\n+ this.map.getProjectionObject(),\n+ this.displayProjection);\n+ lon = mapPosition.x;\n+ lat = mapPosition.y;\n+ }\n+ params.lat = Math.round(lat * 100000) / 100000;\n+ params.lon = Math.round(lon * 100000) / 100000;\n+\n+ //layers \n+ layers = layers || this.map.layers;\n+ params.layers = '';\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+\n+ if (layer.isBaseLayer) {\n+ params.layers += (layer == this.map.baseLayer) ? \"B\" : \"0\";\n+ } else {\n+ params.layers += (layer.getVisibility()) ? \"T\" : \"F\";\n }\n }\n- layer.addFeatures(features);\n }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- });\n+\n+ return params;\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n+ CLASS_NAME: \"OpenLayers.Control.Permalink\"\n });\n /* ======================================================================\n- OpenLayers/Strategy/BBOX.js\n+ OpenLayers/Control/DrawFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Strategy.js\n- * @requires OpenLayers/Filter/Spatial.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Feature/Vector.js\n */\n \n /**\n- * Class: OpenLayers.Strategy.BBOX\n- * A simple strategy that reads new features when the viewport invalidates\n- * some bounds.\n+ * Class: OpenLayers.Control.DrawFeature\n+ * The DrawFeature control draws point, line or polygon features on a vector\n+ * layer when active.\n *\n * Inherits from:\n- * - <OpenLayers.Strategy>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: bounds\n- * {<OpenLayers.Bounds>} The current data bounds (in the same projection\n- * as the layer - not always the same projection as the map).\n- */\n- bounds: null,\n-\n- /** \n- * Property: resolution \n- * {Float} The current data resolution. \n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>}\n */\n- resolution: null,\n+ layer: null,\n \n /**\n- * APIProperty: ratio\n- * {Float} The ratio of the data bounds to the viewport bounds (in each\n- * dimension). Default is 2.\n+ * Property: callbacks\n+ * {Object} The functions that are sent to the handler for callback\n */\n- ratio: 2,\n+ callbacks: null,\n \n /** \n- * Property: resFactor \n- * {Float} Optional factor used to determine when previously requested \n- * features are invalid. If set, the resFactor will be compared to the\n- * resolution of the previous request to the current map resolution.\n- * If resFactor > (old / new) and 1/resFactor < (old / new). If you\n- * set a resFactor of 1, data will be requested every time the\n- * resolution changes. If you set a resFactor of 3, data will be\n- * requested if the old resolution is 3 times the new, or if the new is\n- * 3 times the old. If the old bounds do not contain the new bounds\n- * new data will always be requested (with or without considering\n- * resFactor). \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * featureadded - Triggered when a feature is added\n */\n- resFactor: null,\n \n /**\n- * Property: response\n- * {<OpenLayers.Protocol.Response>} The protocol response object returned\n- * by the layer protocol.\n+ * APIProperty: multi\n+ * {Boolean} Cast features to multi-part geometries before passing to the\n+ * layer. Default is false.\n */\n- response: null,\n+ multi: false,\n \n /**\n- * Constructor: OpenLayers.Strategy.BBOX\n- * Create a new BBOX strategy.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * APIProperty: featureAdded\n+ * {Function} Called after each feature is added\n */\n+ featureAdded: function() {},\n \n /**\n- * Method: activate\n- * Set up strategy with regard to reading new batches of remote data.\n+ * APIProperty: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Control.DrawFeature\n * \n- * Returns:\n- * {Boolean} The strategy was successfully activated.\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} \n+ * handler - {<OpenLayers.Handler>} \n+ * options - {Object} \n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- \"moveend\": this.update,\n- \"refresh\": this.update,\n- \"visibilitychanged\": this.update,\n- scope: this\n- });\n- this.update();\n+ initialize: function(layer, handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.callbacks = OpenLayers.Util.extend({\n+ done: this.drawFeature,\n+ modify: function(vertex, feature) {\n+ this.layer.events.triggerEvent(\n+ \"sketchmodified\", {\n+ vertex: vertex,\n+ feature: feature\n+ }\n+ );\n+ },\n+ create: function(vertex, feature) {\n+ this.layer.events.triggerEvent(\n+ \"sketchstarted\", {\n+ vertex: vertex,\n+ feature: feature\n+ }\n+ );\n+ }\n+ },\n+ this.callbacks\n+ );\n+ this.layer = layer;\n+ this.handlerOptions = this.handlerOptions || {};\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n+ this.handlerOptions.layerOptions, {\n+ renderers: layer.renderers,\n+ rendererOptions: layer.rendererOptions\n+ }\n+ );\n+ if (!(\"multi\" in this.handlerOptions)) {\n+ this.handlerOptions.multi = this.multi;\n }\n- return activated;\n+ var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;\n+ if (sketchStyle) {\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n+ this.handlerOptions.layerOptions, {\n+ styleMap: new OpenLayers.StyleMap({\n+ \"default\": sketchStyle\n+ })\n+ }\n+ );\n+ }\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions);\n },\n \n /**\n- * Method: deactivate\n- * Tear down strategy with regard to reading new batches of remote data.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * Method: drawFeature\n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- \"moveend\": this.update,\n- \"refresh\": this.update,\n- \"visibilitychanged\": this.update,\n- scope: this\n+ drawFeature: function(geometry) {\n+ var feature = new OpenLayers.Feature.Vector(geometry);\n+ var proceed = this.layer.events.triggerEvent(\n+ \"sketchcomplete\", {\n+ feature: feature\n+ }\n+ );\n+ if (proceed !== false) {\n+ feature.state = OpenLayers.State.INSERT;\n+ this.layer.addFeatures([feature]);\n+ this.featureAdded(feature);\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n });\n }\n- return deactivated;\n },\n \n /**\n- * Method: update\n- * Callback function called on \"moveend\" or \"refresh\" layer events.\n+ * APIMethod: insertXY\n+ * Insert a point in the current sketch given x & y coordinates.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will determine\n- * the behaviour of this Strategy\n- *\n- * Valid options include:\n- * force - {Boolean} if true, new data must be unconditionally read.\n- * noAbort - {Boolean} if true, do not abort previous requests.\n+ * x - {Number} The x-coordinate of the point.\n+ * y - {Number} The y-coordinate of the point.\n */\n- update: function(options) {\n- var mapBounds = this.getMapBounds();\n- if (mapBounds !== null && ((options && options.force) ||\n- (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {\n- this.calculateBounds(mapBounds);\n- this.resolution = this.layer.map.getResolution();\n- this.triggerRead(options);\n+ insertXY: function(x, y) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertXY(x, y);\n }\n },\n \n /**\n- * Method: getMapBounds\n- * Get the map bounds expressed in the same projection as this layer.\n+ * APIMethod: insertDeltaXY\n+ * Insert a point given offsets from the previously inserted point.\n *\n- * Returns:\n- * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.\n+ * Parameters:\n+ * dx - {Number} The x-coordinate offset of the point.\n+ * dy - {Number} The y-coordinate offset of the point.\n */\n- getMapBounds: function() {\n- if (this.layer.map === null) {\n- return null;\n- }\n- var bounds = this.layer.map.getExtent();\n- if (bounds && !this.layer.projection.equals(\n- this.layer.map.getProjectionObject())) {\n- bounds = bounds.clone().transform(\n- this.layer.map.getProjectionObject(), this.layer.projection\n- );\n+ insertDeltaXY: function(dx, dy) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeltaXY(dx, dy);\n }\n- return bounds;\n },\n \n /**\n- * Method: invalidBounds\n- * Determine whether the previously requested set of features is invalid. \n- * This occurs when the new map bounds do not contain the previously \n- * requested bounds. In addition, if <resFactor> is set, it will be \n- * considered.\n+ * APIMethod: insertDirectionLength\n+ * Insert a point in the current sketch given a direction and a length.\n *\n * Parameters:\n- * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n- * retrieved from the map object if not provided\n- *\n- * Returns:\n- * {Boolean} \n+ * direction - {Number} Degrees clockwise from the positive x-axis.\n+ * length - {Number} Distance from the previously drawn point.\n */\n- invalidBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds();\n- }\n- var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n- if (!invalid && this.resFactor) {\n- var ratio = this.resolution / this.layer.map.getResolution();\n- invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));\n+ insertDirectionLength: function(direction, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDirectionLength(direction, length);\n }\n- return invalid;\n },\n \n /**\n- * Method: calculateBounds\n+ * APIMethod: insertDeflectionLength\n+ * Insert a point in the current sketch given a deflection and a length.\n+ * The deflection should be degrees clockwise from the previously \n+ * digitized segment.\n *\n * Parameters:\n- * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n- * retrieved from the map object if not provided\n+ * deflection - {Number} Degrees clockwise from the previous segment.\n+ * length - {Number} Distance from the previously drawn point.\n */\n- calculateBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds();\n+ insertDeflectionLength: function(deflection, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeflectionLength(deflection, length);\n }\n- var center = mapBounds.getCenterLonLat();\n- var dataWidth = mapBounds.getWidth() * this.ratio;\n- var dataHeight = mapBounds.getHeight() * this.ratio;\n- this.bounds = new OpenLayers.Bounds(\n- center.lon - (dataWidth / 2),\n- center.lat - (dataHeight / 2),\n- center.lon + (dataWidth / 2),\n- center.lat + (dataHeight / 2)\n- );\n },\n \n /**\n- * Method: triggerRead\n+ * APIMethod: undo\n+ * Remove the most recently added point in the current sketch geometry.\n *\n- * Parameters:\n- * options - {Object} Additional options for the protocol's read method \n- * (optional)\n+ * Returns: \n+ * {Boolean} An edit was undone.\n+ */\n+ undo: function() {\n+ return this.handler.undo && this.handler.undo();\n+ },\n+\n+ /**\n+ * APIMethod: redo\n+ * Reinsert the most recently removed point resulting from an <undo> call.\n+ * The undo stack is deleted whenever a point is added by other means.\n *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} The protocol response object\n- * returned by the layer protocol.\n+ * Returns: \n+ * {Boolean} An edit was redone.\n */\n- triggerRead: function(options) {\n- if (this.response && !(options && options.noAbort === true)) {\n- this.layer.protocol.abort(this.response);\n- this.layer.events.triggerEvent(\"loadend\");\n- }\n- var evt = {\n- filter: this.createFilter()\n- };\n- this.layer.events.triggerEvent(\"loadstart\", evt);\n- this.response = this.layer.protocol.read(\n- OpenLayers.Util.applyDefaults({\n- filter: evt.filter,\n- callback: this.merge,\n- scope: this\n- }, options));\n+ redo: function() {\n+ return this.handler.redo && this.handler.redo();\n },\n \n /**\n- * Method: createFilter\n- * Creates a spatial BBOX filter. If the layer that this strategy belongs\n- * to has a filter property, this filter will be combined with the BBOX \n- * filter.\n- * \n- * Returns\n- * {<OpenLayers.Filter>} The filter object.\n+ * APIMethod: finishSketch\n+ * Finishes the sketch without including the currently drawn point.\n+ * This method can be called to terminate drawing programmatically\n+ * instead of waiting for the user to end the sketch.\n */\n- createFilter: function() {\n- var filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- value: this.bounds,\n- projection: this.layer.projection\n- });\n- if (this.layer.filter) {\n- filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.layer.filter, filter]\n- });\n- }\n- return filter;\n+ finishSketch: function() {\n+ this.handler.finishGeometry();\n },\n \n /**\n- * Method: merge\n- * Given a list of features, determine which ones to add to the layer.\n- * If the layer projection differs from the map projection, features\n- * will be transformed from the layer projection to the map projection.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object passed\n- * by the protocol.\n+ * APIMethod: cancel\n+ * Cancel the current sketch. This removes the current sketch and keeps\n+ * the drawing control active.\n */\n- merge: function(resp) {\n- this.layer.destroyFeatures();\n- if (resp.success()) {\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local);\n- }\n- }\n- }\n- this.layer.addFeatures(features);\n- }\n- } else {\n- this.bounds = null;\n- }\n- this.response = null;\n- this.layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- });\n+ cancel: function() {\n+ this.handler.cancel();\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n+ CLASS_NAME: \"OpenLayers.Control.DrawFeature\"\n });\n /* ======================================================================\n- OpenLayers/Control/Panel.js\n+ OpenLayers/Control/EditingToolbar.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Control/Panel.js\n+ * @requires OpenLayers/Control/Navigation.js\n+ * @requires OpenLayers/Control/DrawFeature.js\n+ * @requires OpenLayers/Handler/Point.js\n+ * @requires OpenLayers/Handler/Path.js\n+ * @requires OpenLayers/Handler/Polygon.js\n */\n \n /**\n- * Class: OpenLayers.Control.Panel\n- * The Panel control is a container for other controls. With it toolbars\n- * may be composed.\n- *\n+ * Class: OpenLayers.Control.EditingToolbar \n+ * The EditingToolbar is a panel of 4 controls to draw polygons, lines, \n+ * points, or to navigate the map by panning. By default it appears in the \n+ * upper right corner of the map.\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Control.Panel>\n */\n-OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n- /**\n- * Property: controls\n- * {Array(<OpenLayers.Control>)}\n- */\n- controls: null,\n+OpenLayers.Control.EditingToolbar = OpenLayers.Class(\n+ OpenLayers.Control.Panel, {\n \n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n- */\n- autoActivate: true,\n+ /**\n+ * APIProperty: citeCompliant\n+ * {Boolean} If set to true, coordinates of features drawn in a map extent\n+ * crossing the date line won't exceed the world bounds. Default is false.\n+ */\n+ citeCompliant: false,\n \n- /** \n- * APIProperty: defaultControl\n- * {<OpenLayers.Control>} The control which is activated when the control is\n- * activated (turned on), which also happens at instantiation.\n- * If <saveState> is true, <defaultControl> will be nullified after the\n- * first activation of the panel.\n- */\n- defaultControl: null,\n+ /**\n+ * Constructor: OpenLayers.Control.EditingToolbar\n+ * Create an editing toolbar for a given layer. \n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} \n+ * options - {Object} \n+ */\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n \n- /**\n- * APIProperty: saveState\n- * {Boolean} If set to true, the active state of this panel's controls will\n- * be stored on panel deactivation, and restored on reactivation. Default\n- * is false.\n- */\n- saveState: false,\n+ this.addControls(\n+ [new OpenLayers.Control.Navigation()]\n+ );\n+ var controls = [\n+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {\n+ displayClass: 'olControlDrawFeaturePoint',\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n+ }\n+ }),\n+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {\n+ displayClass: 'olControlDrawFeaturePath',\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n+ }\n+ }),\n+ new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {\n+ displayClass: 'olControlDrawFeaturePolygon',\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n+ }\n+ })\n+ ];\n+ this.addControls(controls);\n+ },\n \n- /**\n- * APIProperty: allowDepress\n- * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n- * be deactivated by clicking the icon that represents them. Default \n- * is false.\n- */\n- allowDepress: false,\n+ /**\n+ * Method: draw\n+ * calls the default draw, and then activates mouse defaults.\n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n+ if (this.defaultControl === null) {\n+ this.defaultControl = this.controls[0];\n+ }\n+ return div;\n+ },\n \n- /**\n- * Property: activeState\n- * {Object} stores the active state of this panel's controls.\n- */\n- activeState: null,\n+ CLASS_NAME: \"OpenLayers.Control.EditingToolbar\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Control/Attribution.js\n+ ====================================================================== */\n \n- /**\n- * Constructor: OpenLayers.Control.Panel\n- * Create a new control panel.\n- *\n- * Each control in the panel is represented by an icon. When clicking \n- * on an icon, the <activateControl> method is called.\n- *\n- * Specific properties for controls on a panel:\n- * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n- * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n- * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n- * title - {string} Text displayed when mouse is over the icon that \n- * represents the control. \n- *\n- * The <OpenLayers.Control.type> of a control determines the behavior when\n- * clicking its icon:\n- * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n- * controls of this type in the same panel are deactivated. This is\n- * the default type.\n- * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n- * toggled.\n- * <OpenLayers.Control.TYPE_BUTTON> - The\n- * <OpenLayers.Control.Button.trigger> method of the control is called,\n- * but its active state is not changed.\n- *\n- * If a control is <OpenLayers.Control.active>, it will be drawn with the\n- * olControl[Name]ItemActive class, otherwise with the\n- * olControl[Name]ItemInactive class.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n- */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.controls = [];\n- this.activeState = {};\n- },\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n- ctl = this.controls[i];\n- if (ctl.events) {\n- ctl.events.un({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n- }\n- ctl.panel_div = null;\n- }\n- this.activeState = null;\n- },\n+/**\n+ * @requires OpenLayers/Control.js\n+ */\n \n- /**\n- * APIMethod: activate\n- */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- if (control === this.defaultControl ||\n- (this.saveState && this.activeState[control.id])) {\n- control.activate();\n- }\n- }\n- if (this.saveState === true) {\n- this.defaultControl = null;\n- }\n- this.redraw();\n- return true;\n- } else {\n- return false;\n- }\n- },\n+/**\n+ * Class: OpenLayers.Control.Attribution\n+ * The attribution control adds attribution from layers to the map display. \n+ * It uses 'attribution' property of each layer.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Attribution =\n+ OpenLayers.Class(OpenLayers.Control, {\n \n- /**\n- * APIMethod: deactivate\n- */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- this.activeState[control.id] = control.deactivate();\n- }\n- this.redraw();\n- return true;\n- } else {\n- return false;\n- }\n- },\n+ /**\n+ * APIProperty: separator\n+ * {String} String used to separate layers.\n+ */\n+ separator: \", \",\n \n- /**\n- * Method: draw\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick);\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n- }\n- this.addControlsToMap(this.controls);\n- return this.div;\n- },\n+ /**\n+ * APIProperty: template\n+ * {String} Template for the attribution. This has to include the substring\n+ * \"${layers}\", which will be replaced by the layer specific\n+ * attributions, separated by <separator>. The default is \"${layers}\".\n+ */\n+ template: \"${layers}\",\n \n- /**\n- * Method: redraw\n- */\n- redraw: function() {\n- for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n- this.div.removeChild(this.div.childNodes[i]);\n- }\n- this.div.innerHTML = \"\";\n- if (this.active) {\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.div.appendChild(this.controls[i].panel_div);\n- }\n- }\n- },\n+ /**\n+ * Constructor: OpenLayers.Control.Attribution \n+ * \n+ * Parameters:\n+ * options - {Object} Options for control.\n+ */\n \n- /**\n- * APIMethod: activateControl\n- * This method is called when the user click on the icon representing a \n- * control in the panel.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>}\n- */\n- activateControl: function(control) {\n- if (!this.active) {\n- return false;\n- }\n- if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n- control.trigger();\n- return;\n- }\n- if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n- if (control.active) {\n- control.deactivate();\n- } else {\n- control.activate();\n- }\n- return;\n- }\n- if (this.allowDepress && control.active) {\n- control.deactivate();\n- } else {\n- var c;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- c = this.controls[i];\n- if (c != control &&\n- (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n- c.deactivate();\n+ /** \n+ * Method: destroy\n+ * Destroy control.\n+ */\n+ destroy: function() {\n+ this.map.events.un({\n+ \"removelayer\": this.updateAttribution,\n+ \"addlayer\": this.updateAttribution,\n+ \"changelayer\": this.updateAttribution,\n+ \"changebaselayer\": this.updateAttribution,\n+ scope: this\n+ });\n+\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: draw\n+ * Initialize control.\n+ * \n+ * Returns: \n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+\n+ this.map.events.on({\n+ 'changebaselayer': this.updateAttribution,\n+ 'changelayer': this.updateAttribution,\n+ 'addlayer': this.updateAttribution,\n+ 'removelayer': this.updateAttribution,\n+ scope: this\n+ });\n+ this.updateAttribution();\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: updateAttribution\n+ * Update attribution string.\n+ */\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ // add attribution only if attribution text is unique\n+ if (OpenLayers.Util.indexOf(\n+ attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution);\n+ }\n+ }\n }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ });\n }\n- control.activate();\n- }\n- },\n+ },\n \n- /**\n- * APIMethod: addControls\n- * To build a toolbar, you add a set of controls to it. addControls\n- * lets you add a single control or a list of controls to the \n- * Control Panel.\n- *\n- * Parameters:\n- * controls - {<OpenLayers.Control>} Controls to add in the panel.\n- */\n- addControls: function(controls) {\n- if (!(OpenLayers.Util.isArray(controls))) {\n- controls = [controls];\n- }\n- this.controls = this.controls.concat(controls);\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Control/PinchZoom.js\n+ ====================================================================== */\n \n- for (var i = 0, len = controls.length; i < len; i++) {\n- var control = controls[i],\n- element = this.createControlMarkup(control);\n- OpenLayers.Element.addClass(element,\n- control.displayClass + \"ItemInactive\");\n- OpenLayers.Element.addClass(element, \"olButton\");\n- if (control.title != \"\" && !element.title) {\n- element.title = control.title;\n- }\n- control.panel_div = element;\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (this.map) { // map.addControl() has already been called on the panel\n- this.addControlsToMap(controls);\n- this.redraw();\n- }\n- },\n+/**\n+ * @requires OpenLayers/Handler/Pinch.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.PinchZoom\n+ *\n+ * Inherits:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * Property: type\n+ * {OpenLayers.Control.TYPES}\n+ */\n+ type: OpenLayers.Control.TYPE_TOOL,\n \n /**\n- * APIMethod: createControlMarkup\n- * This function just creates a div for the control. If specific HTML\n- * markup is needed this function can be overridden in specific classes,\n- * or at panel instantiation time:\n- *\n- * Example:\n- * (code)\n- * var panel = new OpenLayers.Control.Panel({\n- * defaultControl: control,\n- * // ovverride createControlMarkup to create actual buttons\n- * // including texts wrapped into span elements.\n- * createControlMarkup: function(control) {\n- * var button = document.createElement('button'),\n- * span = document.createElement('span');\n- * if (control.text) {\n- * span.innerHTML = control.text;\n- * }\n- * return button;\n- * }\n- * });\n- * (end)\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control to create the HTML\n- * markup for.\n- *\n- * Returns:\n- * {DOMElement} The markup.\n+ * Property: pinchOrigin\n+ * {Object} Cached object representing the pinch start (in pixels).\n */\n- createControlMarkup: function(control) {\n- return document.createElement(\"div\");\n- },\n+ pinchOrigin: null,\n \n /**\n- * Method: addControlsToMap\n- * Only for internal use in draw() and addControls() methods.\n- *\n- * Parameters:\n- * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n+ * Property: currentCenter\n+ * {Object} Cached object representing the latest pinch center (in pixels).\n */\n- addControlsToMap: function(controls) {\n- var control;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- control = controls[i];\n- if (control.autoActivate === true) {\n- control.autoActivate = false;\n- this.map.addControl(control);\n- control.autoActivate = true;\n- } else {\n- this.map.addControl(control);\n- control.deactivate();\n- }\n- control.events.on({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n- }\n- },\n+ currentCenter: null,\n \n /**\n- * Method: iconOn\n- * Internal use, for use only with \"controls[i].events.on/un\".\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n- iconOn: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n- d.className = d.className.replace(re, \"$1Active\");\n- },\n+ autoActivate: true,\n \n /**\n- * Method: iconOff\n- * Internal use, for use only with \"controls[i].events.on/un\".\n+ * APIProperty: preserveCenter\n+ * {Boolean} Set this to true if you don't want the map center to change\n+ * while pinching. For example you may want to set preserveCenter to\n+ * true when the user location is being watched and you want to preserve\n+ * the user location at the center of the map even if he zooms in or\n+ * out using pinch. This property's value can be changed any time on an\n+ * existing instance. Default is false.\n */\n- iconOff: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n- d.className = d.className.replace(re, \"$1Inactive\");\n- },\n+ preserveCenter: false,\n \n /**\n- * Method: onButtonClick\n+ * APIProperty: handlerOptions\n+ * {Object} Used to set non-default properties on the pinch handler\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Control.PinchZoom\n+ * Create a control for zooming with pinch gestures. This works on devices\n+ * with multi-touch support.\n *\n * Parameters:\n- * evt - {Event}\n+ * options - {Object} An optional object whose properties will be set on\n+ * the control\n */\n- onButtonClick: function(evt) {\n- var controls = this.controls,\n- button = evt.buttonElement;\n- for (var i = controls.length - 1; i >= 0; --i) {\n- if (controls[i].panel_div === button) {\n- this.activateControl(controls[i]);\n- break;\n- }\n- }\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ this.handler = new OpenLayers.Handler.Pinch(this, {\n+ start: this.pinchStart,\n+ move: this.pinchMove,\n+ done: this.pinchDone\n+ }, this.handlerOptions);\n },\n \n /**\n- * APIMethod: getControlsBy\n- * Get a list of controls with properties matching the given criteria.\n+ * Method: pinchStart\n *\n * Parameters:\n- * property - {String} A control property to be matched.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(control[property]) evaluates to true, the control will be\n- * included in the array returned. If no controls are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n- * An empty array is returned if no matches are found.\n+ * evt - {Event}\n+ * pinchData - {Object} pinch data object related to the current touchmove\n+ * of the pinch gesture. This give us the current scale of the pinch.\n */\n- getControlsBy: function(property, match) {\n- var test = (typeof match.test == \"function\");\n- var found = OpenLayers.Array.filter(this.controls, function(item) {\n- return item[property] == match || (test && match.test(item[property]));\n- });\n- return found;\n+ pinchStart: function(evt, pinchData) {\n+ var xy = (this.preserveCenter) ?\n+ this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;\n+ this.pinchOrigin = xy;\n+ this.currentCenter = xy;\n },\n \n /**\n- * APIMethod: getControlsByName\n- * Get a list of contorls with names matching the given name.\n+ * Method: pinchMove\n *\n * Parameters:\n- * match - {String | Object} A control name. The name can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * name.test(control.name) evaluates to true, the control will be included\n- * in the list of controls returned. If no controls are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n- * An empty array is returned if no matches are found.\n+ * evt - {Event}\n+ * pinchData - {Object} pinch data object related to the current touchmove\n+ * of the pinch gesture. This give us the current scale of the pinch.\n */\n- getControlsByName: function(match) {\n- return this.getControlsBy(\"name\", match);\n+ pinchMove: function(evt, pinchData) {\n+ var scale = pinchData.scale;\n+ var containerOrigin = this.map.layerContainerOriginPx;\n+ var pinchOrigin = this.pinchOrigin;\n+ var current = (this.preserveCenter) ?\n+ this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;\n+\n+ var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));\n+ var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));\n+\n+ this.map.applyTransform(dx, dy, scale);\n+ this.currentCenter = current;\n },\n \n /**\n- * APIMethod: getControlsByClass\n- * Get a list of controls of a given type (CLASS_NAME).\n+ * Method: pinchDone\n *\n * Parameters:\n- * match - {String | Object} A control class name. The type can also be a\n- * regular expression literal or object. In addition, it can be any\n- * object with a method named test. For reqular expressions or other,\n- * if type.test(control.CLASS_NAME) evaluates to true, the control will\n- * be included in the list of controls returned. If no controls are\n- * found, an empty array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n- * An empty array is returned if no matches are found.\n+ * evt - {Event}\n+ * start - {Object} pinch data object related to the touchstart event that\n+ * started the pinch gesture.\n+ * last - {Object} pinch data object related to the last touchmove event\n+ * of the pinch gesture. This give us the final scale of the pinch.\n */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\n+ pinchDone: function(evt, start, last) {\n+ this.map.applyTransform();\n+ var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);\n+ if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {\n+ var resolution = this.map.getResolutionForZoom(zoom);\n+\n+ var location = this.map.getLonLatFromPixel(this.pinchOrigin);\n+ var zoomPixel = this.currentCenter;\n+ var size = this.map.getSize();\n+\n+ location.lon += resolution * ((size.w / 2) - zoomPixel.x);\n+ location.lat -= resolution * ((size.h / 2) - zoomPixel.y);\n+\n+ // Force a reflow before calling setCenter. This is to work\n+ // around an issue occuring in iOS.\n+ //\n+ // See https://github.com/openlayers/openlayers/pull/351.\n+ //\n+ // Without a reflow setting the layer container div's top left\n+ // style properties to \"0px\" - as done in Map.moveTo when zoom\n+ // is changed - won't actually correctly reposition the layer\n+ // container div.\n+ //\n+ // Also, we need to use a statement that the Google Closure\n+ // compiler won't optimize away.\n+ this.map.div.clientWidth = this.map.div.clientWidth;\n+\n+ this.map.setCenter(location, zoom);\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Panel\"\n-});\n+ CLASS_NAME: \"OpenLayers.Control.PinchZoom\"\n \n+});\n /* ======================================================================\n- OpenLayers/Control/ZoomBox.js\n+ OpenLayers/Control/KeyboardDefaults.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Box.js\n+ * @requires OpenLayers/Handler/Keyboard.js\n+ * @requires OpenLayers/Events.js\n */\n \n /**\n- * Class: OpenLayers.Control.ZoomBox\n- * The ZoomBox control enables zooming directly to a given extent, by drawing \n- * a box on the map. The box is drawn by holding down shift, whilst dragging \n- * the mouse.\n+ * Class: OpenLayers.Control.KeyboardDefaults\n+ * The KeyboardDefaults control adds panning and zooming functions, controlled\n+ * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page\n+ * Down/Home/End scroll by three quarters of a page.\n+ * \n+ * This control has no visible appearance.\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n- /**\n- * Property: type\n- * {OpenLayers.Control.TYPE}\n- */\n- type: OpenLayers.Control.TYPE_TOOL,\n+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: out\n- * {Boolean} Should the control be used for zooming out?\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n- out: false,\n+ autoActivate: true,\n \n /**\n- * APIProperty: keyMask\n- * {Integer} Zoom only occurs if the keyMask matches the combination of \n- * keys down. Use bitwise operators and one or more of the\n- * <OpenLayers.Handler> constants to construct a keyMask. Leave null if \n- * not used mask. Default is null.\n+ * APIProperty: slideFactor\n+ * Pixels to slide by.\n */\n- keyMask: null,\n+ slideFactor: 75,\n \n /**\n- * APIProperty: alwaysZoom\n- * {Boolean} Always zoom in/out when box drawn, even if the zoom level does\n- * not change.\n+ * APIProperty: observeElement\n+ * {DOMelement|String} The DOM element to handle keys for. You\n+ * can use the map div here, to have the navigation keys\n+ * work when the map div has the focus. If undefined the\n+ * document is used.\n */\n- alwaysZoom: false,\n+ observeElement: null,\n \n /**\n- * APIProperty: zoomOnClick\n- * {Boolean} Should we zoom when no box was dragged, i.e. the user only\n- * clicked? Default is true.\n+ * Constructor: OpenLayers.Control.KeyboardDefaults\n */\n- zoomOnClick: true,\n \n /**\n * Method: draw\n+ * Create handler.\n */\n draw: function() {\n- this.handler = new OpenLayers.Handler.Box(this, {\n- done: this.zoomBox\n+ var observeElement = this.observeElement || document;\n+ this.handler = new OpenLayers.Handler.Keyboard(this, {\n+ \"keydown\": this.defaultKeyPress\n }, {\n- keyMask: this.keyMask\n+ observeElement: observeElement\n });\n },\n \n /**\n- * Method: zoomBox\n+ * Method: defaultKeyPress\n+ * When handling the key event, we only use evt.keyCode. This holds \n+ * some drawbacks, though we get around them below. When interpretting\n+ * the keycodes below (including the comments associated with them),\n+ * consult the URL below. For instance, the Safari browser returns\n+ * \"IE keycodes\", and so is supported by any keycode labeled \"IE\".\n+ * \n+ * Very informative URL:\n+ * http://unixpapa.com/js/key.html\n *\n * Parameters:\n- * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}\n+ * evt - {Event} \n */\n- zoomBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var bounds,\n- targetCenterPx = position.getCenterPixel();\n- if (!this.out) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,\n- maxXY.lon, maxXY.lat);\n- } else {\n- var pixWidth = position.right - position.left;\n- var pixHeight = position.bottom - position.top;\n- var zoomFactor = Math.min((this.map.size.h / pixHeight),\n- (this.map.size.w / pixWidth));\n- var extent = this.map.getExtent();\n- var center = this.map.getLonLatFromPixel(targetCenterPx);\n- var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor;\n- var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor;\n- var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor;\n- var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor;\n- bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);\n- }\n- // always zoom in/out \n- var lastZoom = this.map.getZoom(),\n- size = this.map.getSize(),\n- centerPx = {\n- x: size.w / 2,\n- y: size.h / 2\n- },\n- zoom = this.map.getZoomForExtent(bounds),\n- oldRes = this.map.getResolution(),\n- newRes = this.map.getResolutionForZoom(zoom);\n- if (oldRes == newRes) {\n- this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));\n- } else {\n- var zoomOriginPx = {\n- x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /\n- (oldRes - newRes),\n- y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /\n- (oldRes - newRes)\n- };\n- this.map.zoomTo(zoom, zoomOriginPx);\n- }\n- if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n- this.map.zoomTo(lastZoom + (this.out ? -1 : 1));\n- }\n- } else if (this.zoomOnClick) { // it's a pixel\n- if (!this.out) {\n- this.map.zoomTo(this.map.getZoom() + 1, position);\n- } else {\n- this.map.zoomTo(this.map.getZoom() - 1, position);\n- }\n+ defaultKeyPress: function(evt) {\n+ var size, handled = true;\n+\n+ var target = OpenLayers.Event.element(evt);\n+ if (target &&\n+ (target.tagName == 'INPUT' ||\n+ target.tagName == 'TEXTAREA' ||\n+ target.tagName == 'SELECT')) {\n+ return;\n+ }\n+\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_LEFT:\n+ this.map.pan(-this.slideFactor, 0);\n+ break;\n+ case OpenLayers.Event.KEY_RIGHT:\n+ this.map.pan(this.slideFactor, 0);\n+ break;\n+ case OpenLayers.Event.KEY_UP:\n+ this.map.pan(0, -this.slideFactor);\n+ break;\n+ case OpenLayers.Event.KEY_DOWN:\n+ this.map.pan(0, this.slideFactor);\n+ break;\n+\n+ case 33: // Page Up. Same in all browsers.\n+ size = this.map.getSize();\n+ this.map.pan(0, -0.75 * size.h);\n+ break;\n+ case 34: // Page Down. Same in all browsers.\n+ size = this.map.getSize();\n+ this.map.pan(0, 0.75 * size.h);\n+ break;\n+ case 35: // End. Same in all browsers.\n+ size = this.map.getSize();\n+ this.map.pan(0.75 * size.w, 0);\n+ break;\n+ case 36: // Home. Same in all browsers.\n+ size = this.map.getSize();\n+ this.map.pan(-0.75 * size.w, 0);\n+ break;\n+\n+ case 43: // +/= (ASCII), keypad + (ASCII, Opera)\n+ case 61: // +/= (Mozilla, Opera, some ASCII)\n+ case 187: // +/= (IE)\n+ case 107: // keypad + (IE, Mozilla)\n+ this.map.zoomIn();\n+ break;\n+ case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)\n+ case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)\n+ case 189: // -/_ (IE)\n+ case 95: // -/_ (some ASCII)\n+ this.map.zoomOut();\n+ break;\n+ default:\n+ handled = false;\n+ }\n+ if (handled) {\n+ // prevent browser default not to move the page\n+ // when moving the page with the keyboard\n+ OpenLayers.Event.stop(evt);\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n+ CLASS_NAME: \"OpenLayers.Control.KeyboardDefaults\"\n });\n /* ======================================================================\n- OpenLayers/Control/DragPan.js\n+ OpenLayers/Control/TransformFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Control/DragFeature.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/Point.js\n */\n \n /**\n- * Class: OpenLayers.Control.DragPan\n- * The DragPan control pans the map with a drag of the mouse.\n+ * Class: OpenLayers.Control.TransformFeature\n+ * Control to transform features with a standard transformation box.\n *\n- * Inherits from:\n+ * Inherits From:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n- * Property: type\n- * {OpenLayers.Control.TYPES}\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforesetfeature - Triggered before a feature is set for\n+ * tranformation. The feature will not be set if a listener returns\n+ * false. Listeners receive a *feature* property, with the feature\n+ * that will be set for transformation. Listeners are allowed to\n+ * set the control's *scale*, *ratio* and *rotation* properties,\n+ * which will set the initial scale, ratio and rotation of the\n+ * feature, like the <setFeature> method's initialParams argument.\n+ * setfeature - Triggered when a feature is set for tranformation.\n+ * Listeners receive a *feature* property, with the feature that\n+ * is now set for transformation.\n+ * beforetransform - Triggered while dragging, before a feature is\n+ * transformed. The feature will not be transformed if a listener\n+ * returns false (but the box still will). Listeners receive one or\n+ * more of *center*, *scale*, *ratio* and *rotation*. The *center*\n+ * property is an <OpenLayers.Geometry.Point> object with the new\n+ * center of the transformed feature, the others are Floats with the\n+ * scale, ratio or rotation change since the last transformation.\n+ * transform - Triggered while dragging, when a feature is transformed.\n+ * Listeners receive an event object with one or more of *center*,\n+ * scale*, *ratio* and *rotation*. The *center* property is an\n+ * <OpenLayers.Geometry.Point> object with the new center of the\n+ * transformed feature, the others are Floats with the scale, ratio\n+ * or rotation change of the feature since the last transformation.\n+ * transformcomplete - Triggered after dragging. Listeners receive\n+ * an event object with the transformed *feature*.\n */\n- type: OpenLayers.Control.TYPE_TOOL,\n \n /**\n- * Property: panned\n- * {Boolean} The map moved.\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict transformation to a limited set of geometry\n+ * types, send a list of strings corresponding to the geometry class\n+ * names.\n */\n- panned: false,\n+ geometryTypes: null,\n \n /**\n- * Property: interval\n- * {Integer} The number of milliseconds that should ellapse before\n- * panning the map again. Defaults to 0 milliseconds, which means that\n- * no separate cycle is used for panning. In most cases you won't want\n- * to change this value. For slow machines/devices larger values can be\n- * tried out.\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>}\n */\n- interval: 0,\n+ layer: null,\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, mouse dragging will continue even if the\n- * mouse cursor leaves the map viewport. Default is false.\n+ * APIProperty: preserveAspectRatio\n+ * {Boolean} set to true to not change the feature's aspect ratio.\n */\n- documentDrag: false,\n+ preserveAspectRatio: false,\n \n /**\n- * Property: kinetic\n- * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.\n+ * APIProperty: rotate\n+ * {Boolean} set to false if rotation should be disabled. Default is true.\n+ * To be passed with the constructor or set when the control is not\n+ * active.\n */\n- kinetic: null,\n+ rotate: true,\n \n /**\n- * APIProperty: enableKinetic\n- * {Boolean} Set this option to enable \"kinetic dragging\". Can be\n- * set to true or to an object. If set to an object this\n- * object will be passed to the {<OpenLayers.Kinetic>}\n- * constructor. Defaults to true.\n- * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is\n- * included in your build config.\n+ * APIProperty: feature\n+ * {<OpenLayers.Feature.Vector>} Feature currently available for\n+ * transformation. Read-only, use <setFeature> to set it manually.\n */\n- enableKinetic: true,\n+ feature: null,\n \n /**\n- * APIProperty: kineticInterval\n- * {Integer} Interval in milliseconds between 2 steps in the \"kinetic\n- * scrolling\". Applies only if enableKinetic is set. Defaults\n- * to 10 milliseconds.\n+ * APIProperty: renderIntent\n+ * {String|Object} Render intent for the transformation box and\n+ * handles. A symbolizer object can also be provided here.\n */\n- kineticInterval: 10,\n-\n+ renderIntent: \"temporary\",\n \n /**\n- * Method: draw\n- * Creates a Drag handler, using <panMap> and\n- * <panMapDone> as callbacks.\n+ * APIProperty: rotationHandleSymbolizer\n+ * {Object|String} Optional. A custom symbolizer for the rotation handles.\n+ * A render intent can also be provided here. Defaults to\n+ * (code)\n+ * {\n+ * stroke: false,\n+ * pointRadius: 10,\n+ * fillOpacity: 0,\n+ * cursor: \"pointer\"\n+ * }\n+ * (end)\n */\n- draw: function() {\n- if (this.enableKinetic && OpenLayers.Kinetic) {\n- var config = {\n- interval: this.kineticInterval\n- };\n- if (typeof this.enableKinetic === \"object\") {\n- config = OpenLayers.Util.extend(config, this.enableKinetic);\n- }\n- this.kinetic = new OpenLayers.Kinetic(config);\n- }\n- this.handler = new OpenLayers.Handler.Drag(this, {\n- \"move\": this.panMap,\n- \"done\": this.panMapDone,\n- \"down\": this.panMapStart\n- }, {\n- interval: this.interval,\n- documentDrag: this.documentDrag\n- });\n- },\n+ rotationHandleSymbolizer: null,\n \n /**\n- * Method: panMapStart\n+ * APIProperty: box\n+ * {<OpenLayers.Feature.Vector>} The transformation box rectangle.\n+ * Read-only.\n */\n- panMapStart: function() {\n- if (this.kinetic) {\n- this.kinetic.begin();\n- }\n- },\n+ box: null,\n \n /**\n- * Method: panMap\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n+ * APIProperty: center\n+ * {<OpenLayers.Geometry.Point>} The center of the feature bounds.\n+ * Read-only.\n */\n- panMap: function(xy) {\n- if (this.kinetic) {\n- this.kinetic.update(xy);\n- }\n- this.panned = true;\n- this.map.pan(\n- this.handler.last.x - xy.x,\n- this.handler.last.y - xy.y, {\n- dragging: true,\n- animate: false\n- }\n- );\n- },\n+ center: null,\n \n /**\n- * Method: panMapDone\n- * Finish the panning operation. Only call setCenter (through <panMap>)\n- * if the map has actually been moved.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n- */\n- panMapDone: function(xy) {\n- if (this.panned) {\n- var res = null;\n- if (this.kinetic) {\n- res = this.kinetic.end(xy);\n- }\n- this.map.pan(\n- this.handler.last.x - xy.x,\n- this.handler.last.y - xy.y, {\n- dragging: !!res,\n- animate: false\n- }\n- );\n- if (res) {\n- var self = this;\n- this.kinetic.move(res, function(x, y, end) {\n- self.map.pan(x, y, {\n- dragging: !end,\n- animate: false\n- });\n- });\n- }\n- this.panned = false;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control.DragPan\"\n-});\n-/* ======================================================================\n- OpenLayers/Control/Navigation.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Control/ZoomBox.js\n- * @requires OpenLayers/Control/DragPan.js\n- * @requires OpenLayers/Handler/MouseWheel.js\n- * @requires OpenLayers/Handler/Click.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control.Navigation\n- * The navigation control handles map browsing with mouse events (dragging,\n- * double-clicking, and scrolling the wheel). Create a new navigation \n- * control with the <OpenLayers.Control.Navigation> control. \n- * \n- * Note that this control is added to the map by default (if no controls \n- * array is sent in the options object to the <OpenLayers.Map> \n- * constructor).\n- * \n- * Inherits:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * Property: dragPan\n- * {<OpenLayers.Control.DragPan>} \n+ * APIProperty: scale\n+ * {Float} The scale of the feature, relative to the scale the time the\n+ * feature was set. Read-only, except for *beforesetfeature*\n+ * listeners.\n */\n- dragPan: null,\n+ scale: 1,\n \n /**\n- * APIProperty: dragPanOptions\n- * {Object} Options passed to the DragPan control.\n+ * APIProperty: ratio\n+ * {Float} The ratio of the feature relative to the ratio the time the\n+ * feature was set. Read-only, except for *beforesetfeature*\n+ * listeners.\n */\n- dragPanOptions: null,\n+ ratio: 1,\n \n /**\n- * Property: pinchZoom\n- * {<OpenLayers.Control.PinchZoom>}\n+ * Property: rotation\n+ * {Integer} the current rotation angle of the box. Read-only, except for\n+ * *beforesetfeature* listeners.\n */\n- pinchZoom: null,\n+ rotation: 0,\n \n /**\n- * APIProperty: pinchZoomOptions\n- * {Object} Options passed to the PinchZoom control.\n+ * APIProperty: handles\n+ * {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available\n+ * for scaling/resizing. Numbered counterclockwise, starting from the\n+ * southwest corner. Read-only.\n */\n- pinchZoomOptions: null,\n+ handles: null,\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} Allow panning of the map by dragging outside map viewport.\n- * Default is false.\n- */\n- documentDrag: false,\n-\n- /** \n- * Property: zoomBox\n- * {<OpenLayers.Control.ZoomBox>}\n+ * APIProperty: rotationHandles\n+ * {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently\n+ * available for rotating. Numbered counterclockwise, starting from\n+ * the southwest corner. Read-only.\n */\n- zoomBox: null,\n+ rotationHandles: null,\n \n /**\n- * APIProperty: zoomBoxEnabled\n- * {Boolean} Whether the user can draw a box to zoom\n+ * Property: dragControl\n+ * {<OpenLayers.Control.DragFeature>}\n */\n- zoomBoxEnabled: true,\n+ dragControl: null,\n \n /**\n- * APIProperty: zoomWheelEnabled\n- * {Boolean} Whether the mousewheel should zoom the map\n+ * APIProperty: irregular\n+ * {Boolean} Make scaling/resizing work irregularly. If true then\n+ * dragging a handle causes the feature to resize in the direction\n+ * of movement. If false then the feature resizes symetrically\n+ * about it's center.\n */\n- zoomWheelEnabled: true,\n+ irregular: false,\n \n /**\n- * Property: mouseWheelOptions\n- * {Object} Options passed to the MouseWheel control (only useful if\n- * <zoomWheelEnabled> is set to true). Default is no options for maps\n- * with fractionalZoom set to true, otherwise\n- * {cumulative: false, interval: 50, maxDelta: 6} \n+ * Constructor: OpenLayers.Control.TransformFeature\n+ * Create a new transform feature control.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that\n+ * will be transformed.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * control.\n */\n- mouseWheelOptions: null,\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n \n- /**\n- * APIProperty: handleRightClicks\n- * {Boolean} Whether or not to handle right clicks. Default is false.\n- */\n- handleRightClicks: false,\n+ this.layer = layer;\n+\n+ if (!this.rotationHandleSymbolizer) {\n+ this.rotationHandleSymbolizer = {\n+ stroke: false,\n+ pointRadius: 10,\n+ fillOpacity: 0,\n+ cursor: \"pointer\"\n+ };\n+ }\n+\n+ this.createBox();\n+ this.createControl();\n+ },\n \n /**\n- * APIProperty: zoomBoxKeyMask\n- * {Integer} <OpenLayers.Handler> key code of the key, which has to be\n- * pressed, while drawing the zoom box with the mouse on the screen. \n- * You should probably set handleRightClicks to true if you use this\n- * with MOD_CTRL, to disable the context menu for machines which use\n- * CTRL-Click as a right click.\n- * Default: <OpenLayers.Handler.MOD_SHIFT>\n+ * APIMethod: activate\n+ * Activates the control.\n */\n- zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.dragControl.activate();\n+ this.layer.addFeatures([this.box]);\n+ this.rotate && this.layer.addFeatures(this.rotationHandles);\n+ this.layer.addFeatures(this.handles);\n+ activated = true;\n+ }\n+ return activated;\n+ },\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * APIMethod: deactivate\n+ * Deactivates the control.\n */\n- autoActivate: true,\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.layer.removeFeatures(this.handles);\n+ this.rotate && this.layer.removeFeatures(this.rotationHandles);\n+ this.layer.removeFeatures([this.box]);\n+ this.dragControl.deactivate();\n+ deactivated = true;\n+ }\n+ return deactivated;\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.Navigation\n- * Create a new navigation control\n+ * Method: setMap\n * \n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * the control\n+ * map - {<OpenLayers.Map>}\n */\n- initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ setMap: function(map) {\n+ this.dragControl.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n },\n \n /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ * APIMethod: setFeature\n+ * Place the transformation box on a feature and start transforming it.\n+ * If the control is not active, it will be activated.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * initialParams - {Object} Initial values for rotation, scale or ratio.\n+ * Setting a rotation value here will cause the transformation box to\n+ * start rotated. Setting a scale or ratio will not affect the\n+ * transormation box, but applications may use this to keep track of\n+ * scale and ratio of a feature across multiple transforms.\n */\n- destroy: function() {\n- this.deactivate();\n+ setFeature: function(feature, initialParams) {\n+ initialParams = OpenLayers.Util.applyDefaults(initialParams, {\n+ rotation: 0,\n+ scale: 1,\n+ ratio: 1\n+ });\n \n- if (this.dragPan) {\n- this.dragPan.destroy();\n- }\n- this.dragPan = null;\n+ var oldRotation = this.rotation;\n+ var oldCenter = this.center;\n+ OpenLayers.Util.extend(this, initialParams);\n \n- if (this.zoomBox) {\n- this.zoomBox.destroy();\n+ var cont = this.events.triggerEvent(\"beforesetfeature\", {\n+ feature: feature\n+ });\n+ if (cont === false) {\n+ return;\n }\n- this.zoomBox = null;\n \n- if (this.pinchZoom) {\n- this.pinchZoom.destroy();\n- }\n- this.pinchZoom = null;\n+ this.feature = feature;\n+ this.activate();\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- },\n+ this._setfeature = true;\n \n- /**\n- * Method: activate\n- */\n- activate: function() {\n- this.dragPan.activate();\n- if (this.zoomWheelEnabled) {\n- this.handlers.wheel.activate();\n- }\n- this.handlers.click.activate();\n- if (this.zoomBoxEnabled) {\n- this.zoomBox.activate();\n- }\n- if (this.pinchZoom) {\n- this.pinchZoom.activate();\n+ var featureBounds = this.feature.geometry.getBounds();\n+ this.box.move(featureBounds.getCenterLonLat());\n+ this.box.geometry.rotate(-oldRotation, oldCenter);\n+ this._angle = 0;\n+\n+ var ll;\n+ if (this.rotation) {\n+ var geom = feature.geometry.clone();\n+ geom.rotate(-this.rotation, this.center);\n+ var box = new OpenLayers.Feature.Vector(\n+ geom.getBounds().toGeometry());\n+ box.geometry.rotate(this.rotation, this.center);\n+ this.box.geometry.rotate(this.rotation, this.center);\n+ this.box.move(box.geometry.getBounds().getCenterLonLat());\n+ var llGeom = box.geometry.components[0].components[0];\n+ ll = llGeom.getBounds().getCenterLonLat();\n+ } else {\n+ ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments);\n+ this.handles[0].move(ll);\n+\n+ delete this._setfeature;\n+\n+ this.events.triggerEvent(\"setfeature\", {\n+ feature: feature\n+ });\n },\n \n /**\n- * Method: deactivate\n+ * APIMethod: unsetFeature\n+ * Remove the transformation box off any feature.\n+ * If the control is active, it will be deactivated first.\n */\n- deactivate: function() {\n- if (this.pinchZoom) {\n- this.pinchZoom.deactivate();\n+ unsetFeature: function() {\n+ if (this.active) {\n+ this.deactivate();\n+ } else {\n+ this.feature = null;\n+ this.rotation = 0;\n+ this.scale = 1;\n+ this.ratio = 1;\n }\n- this.zoomBox.deactivate();\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.handlers.wheel.deactivate();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n },\n \n /**\n- * Method: draw\n+ * Method: createBox\n+ * Creates the box with all handles and transformation handles.\n */\n- draw: function() {\n- // disable right mouse context menu for support of right click events\n- if (this.handleRightClicks) {\n- this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;\n- }\n+ createBox: function() {\n+ var control = this;\n \n- var clickCallbacks = {\n- 'click': this.defaultClick,\n- 'dblclick': this.defaultDblClick,\n- 'dblrightclick': this.defaultDblRightClick\n+ this.center = new OpenLayers.Geometry.Point(0, 0);\n+ this.box = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.LineString([\n+ new OpenLayers.Geometry.Point(-1, -1),\n+ new OpenLayers.Geometry.Point(0, -1),\n+ new OpenLayers.Geometry.Point(1, -1),\n+ new OpenLayers.Geometry.Point(1, 0),\n+ new OpenLayers.Geometry.Point(1, 1),\n+ new OpenLayers.Geometry.Point(0, 1),\n+ new OpenLayers.Geometry.Point(-1, 1),\n+ new OpenLayers.Geometry.Point(-1, 0),\n+ new OpenLayers.Geometry.Point(-1, -1)\n+ ]), null,\n+ typeof this.renderIntent == \"string\" ? null : this.renderIntent\n+ );\n+\n+ // Override for box move - make sure that the center gets updated\n+ this.box.geometry.move = function(x, y) {\n+ control._moving = true;\n+ OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);\n+ control.center.move(x, y);\n+ delete control._moving;\n };\n- var clickOptions = {\n- 'double': true,\n- 'stopDouble': true\n+\n+ // Overrides for vertex move, resize and rotate - make sure that\n+ // handle and rotationHandle geometries are also moved, resized and\n+ // rotated.\n+ var vertexMoveFn = function(x, y) {\n+ OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.move(x, y);\n+ this._handle.geometry.move(x, y);\n };\n- this.handlers.click = new OpenLayers.Handler.Click(\n- this, clickCallbacks, clickOptions\n- );\n- this.dragPan = new OpenLayers.Control.DragPan(\n- OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions)\n- );\n- this.zoomBox = new OpenLayers.Control.ZoomBox({\n- map: this.map,\n- keyMask: this.zoomBoxKeyMask\n- });\n- this.dragPan.draw();\n- this.zoomBox.draw();\n- var wheelOptions = this.map.fractionalZoom ? {} : {\n- cumulative: false,\n- interval: 50,\n- maxDelta: 6\n+ var vertexResizeFn = function(scale, center, ratio) {\n+ OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.resize(\n+ scale, center, ratio);\n+ this._handle.geometry.resize(scale, center, ratio);\n+ };\n+ var vertexRotateFn = function(angle, center) {\n+ OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.rotate(\n+ angle, center);\n+ this._handle.geometry.rotate(angle, center);\n };\n- this.handlers.wheel = new OpenLayers.Handler.MouseWheel(\n- this, {\n- up: this.wheelUp,\n- down: this.wheelDown\n- },\n- OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)\n- );\n- if (OpenLayers.Control.PinchZoom) {\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(\n- OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions));\n- }\n- },\n \n- /**\n- * Method: defaultClick\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut();\n- }\n- },\n+ // Override for handle move - make sure that the box and other handles\n+ // are updated, and finally transform the feature.\n+ var handleMoveFn = function(x, y) {\n+ var oldX = this.x,\n+ oldY = this.y;\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ if (control._moving) {\n+ return;\n+ }\n+ var evt = control.dragControl.handlers.drag.evt;\n+ var preserveAspectRatio = !control._setfeature &&\n+ control.preserveAspectRatio;\n+ var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);\n+ var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);\n+ var centerGeometry = control.center;\n+ this.rotate(-control.rotation, centerGeometry);\n+ oldGeom.rotate(-control.rotation, centerGeometry);\n+ var dx1 = this.x - centerGeometry.x;\n+ var dy1 = this.y - centerGeometry.y;\n+ var dx0 = dx1 - (this.x - oldGeom.x);\n+ var dy0 = dy1 - (this.y - oldGeom.y);\n+ if (control.irregular && !control._setfeature) {\n+ dx1 -= (this.x - oldGeom.x) / 2;\n+ dy1 -= (this.y - oldGeom.y) / 2;\n+ }\n+ this.x = oldX;\n+ this.y = oldY;\n+ var scale, ratio = 1;\n+ if (reshape) {\n+ scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;\n+ ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;\n+ } else {\n+ var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));\n+ var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));\n+ scale = l1 / l0;\n+ }\n \n- /**\n- * Method: defaultDblClick \n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy);\n- },\n+ // rotate the box to 0 before resizing - saves us some\n+ // calculations and is inexpensive because we don't drawFeature.\n+ control._moving = true;\n+ control.box.geometry.rotate(-control.rotation, centerGeometry);\n+ delete control._moving;\n \n- /**\n- * Method: defaultDblRightClick \n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- defaultDblRightClick: function(evt) {\n- this.map.zoomTo(this.map.zoom - 1, evt.xy);\n- },\n+ control.box.geometry.resize(scale, centerGeometry, ratio);\n+ control.box.geometry.rotate(control.rotation, centerGeometry);\n+ control.transformFeature({\n+ scale: scale,\n+ ratio: ratio\n+ });\n+ if (control.irregular && !control._setfeature) {\n+ var newCenter = centerGeometry.clone();\n+ newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX);\n+ newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY);\n+ control.box.geometry.move(this.x - oldX, this.y - oldY);\n+ control.transformFeature({\n+ center: newCenter\n+ });\n+ }\n+ };\n \n- /**\n- * Method: wheelChange \n- *\n- * Parameters:\n- * evt - {Event}\n- * deltaZ - {Integer}\n- */\n- wheelChange: function(evt, deltaZ) {\n- if (!this.map.fractionalZoom) {\n- deltaZ = Math.round(deltaZ);\n- }\n- var currentZoom = this.map.getZoom(),\n- newZoom = currentZoom + deltaZ;\n- newZoom = Math.max(newZoom, 0);\n- newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n- if (newZoom === currentZoom) {\n- return;\n- }\n- this.map.zoomTo(newZoom, evt.xy);\n- },\n+ // Override for rotation handle move - make sure that the box and\n+ // other handles are updated, and finally transform the feature.\n+ var rotationHandleMoveFn = function(x, y) {\n+ var oldX = this.x,\n+ oldY = this.y;\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ if (control._moving) {\n+ return;\n+ }\n+ var evt = control.dragControl.handlers.drag.evt;\n+ var constrain = (evt && evt.shiftKey) ? 45 : 1;\n+ var centerGeometry = control.center;\n+ var dx1 = this.x - centerGeometry.x;\n+ var dy1 = this.y - centerGeometry.y;\n+ var dx0 = dx1 - x;\n+ var dy0 = dy1 - y;\n+ this.x = oldX;\n+ this.y = oldY;\n+ var a0 = Math.atan2(dy0, dx0);\n+ var a1 = Math.atan2(dy1, dx1);\n+ var angle = a1 - a0;\n+ angle *= 180 / Math.PI;\n+ control._angle = (control._angle + angle) % 360;\n+ var diff = control.rotation % constrain;\n+ if (Math.abs(control._angle) >= constrain || diff !== 0) {\n+ angle = Math.round(control._angle / constrain) * constrain -\n+ diff;\n+ control._angle = 0;\n+ control.box.geometry.rotate(angle, centerGeometry);\n+ control.transformFeature({\n+ rotation: angle\n+ });\n+ }\n+ };\n \n- /** \n- * Method: wheelUp\n- * User spun scroll wheel up\n- * \n- * Parameters:\n- * evt - {Event}\n- * delta - {Integer}\n- */\n- wheelUp: function(evt, delta) {\n- this.wheelChange(evt, delta || 1);\n- },\n+ var handles = new Array(8);\n+ var rotationHandles = new Array(4);\n+ var geom, handle, rotationHandle;\n+ var positions = [\"sw\", \"s\", \"se\", \"e\", \"ne\", \"n\", \"nw\", \"w\"];\n+ for (var i = 0; i < 8; ++i) {\n+ geom = this.box.geometry.components[i];\n+ handle = new OpenLayers.Feature.Vector(geom.clone(), {\n+ role: positions[i] + \"-resize\"\n+ }, typeof this.renderIntent == \"string\" ? null :\n+ this.renderIntent);\n+ if (i % 2 == 0) {\n+ rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {\n+ role: positions[i] + \"-rotate\"\n+ }, typeof this.rotationHandleSymbolizer == \"string\" ?\n+ null : this.rotationHandleSymbolizer);\n+ rotationHandle.geometry.move = rotationHandleMoveFn;\n+ geom._rotationHandle = rotationHandle;\n+ rotationHandles[i / 2] = rotationHandle;\n+ }\n+ geom.move = vertexMoveFn;\n+ geom.resize = vertexResizeFn;\n+ geom.rotate = vertexRotateFn;\n+ handle.geometry.move = handleMoveFn;\n+ geom._handle = handle;\n+ handles[i] = handle;\n+ }\n \n- /** \n- * Method: wheelDown\n- * User spun scroll wheel down\n- * \n- * Parameters:\n- * evt - {Event}\n- * delta - {Integer}\n- */\n- wheelDown: function(evt, delta) {\n- this.wheelChange(evt, delta || -1);\n+ this.rotationHandles = rotationHandles;\n+ this.handles = handles;\n },\n \n /**\n- * Method: disableZoomBox\n+ * Method: createControl\n+ * Creates a DragFeature control for this control.\n */\n- disableZoomBox: function() {\n- this.zoomBoxEnabled = false;\n- this.zoomBox.deactivate();\n+ createControl: function() {\n+ var control = this;\n+ this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {\n+ documentDrag: true,\n+ // avoid moving the feature itself - move the box instead\n+ moveFeature: function(pixel) {\n+ if (this.feature === control.feature) {\n+ this.feature = control.box;\n+ }\n+ OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,\n+ arguments);\n+ },\n+ // transform while dragging\n+ onDrag: function(feature, pixel) {\n+ if (feature === control.box) {\n+ control.transformFeature({\n+ center: control.center\n+ });\n+ }\n+ },\n+ // set a new feature\n+ onStart: function(feature, pixel) {\n+ var eligible = !control.geometryTypes ||\n+ OpenLayers.Util.indexOf(control.geometryTypes,\n+ feature.geometry.CLASS_NAME) !== -1;\n+ var i = OpenLayers.Util.indexOf(control.handles, feature);\n+ i += OpenLayers.Util.indexOf(control.rotationHandles,\n+ feature);\n+ if (feature !== control.feature && feature !== control.box &&\n+ i == -2 && eligible) {\n+ control.setFeature(feature);\n+ }\n+ },\n+ onComplete: function(feature, pixel) {\n+ control.events.triggerEvent(\"transformcomplete\", {\n+ feature: control.feature\n+ });\n+ }\n+ });\n },\n \n /**\n- * Method: enableZoomBox\n+ * Method: drawHandles\n+ * Draws the handles to match the box.\n */\n- enableZoomBox: function() {\n- this.zoomBoxEnabled = true;\n- if (this.active) {\n- this.zoomBox.activate();\n+ drawHandles: function() {\n+ var layer = this.layer;\n+ for (var i = 0; i < 8; ++i) {\n+ if (this.rotate && i % 2 === 0) {\n+ layer.drawFeature(this.rotationHandles[i / 2],\n+ this.rotationHandleSymbolizer);\n+ }\n+ layer.drawFeature(this.handles[i], this.renderIntent);\n }\n },\n \n /**\n- * Method: disableZoomWheel\n+ * Method: transformFeature\n+ * Transforms the feature.\n+ * \n+ * Parameters:\n+ * mods - {Object} An object with optional scale, ratio, rotation and\n+ * center properties.\n */\n+ transformFeature: function(mods) {\n+ if (!this._setfeature) {\n+ this.scale *= (mods.scale || 1);\n+ this.ratio *= (mods.ratio || 1);\n+ var oldRotation = this.rotation;\n+ this.rotation = (this.rotation + (mods.rotation || 0)) % 360;\n \n- disableZoomWheel: function() {\n- this.zoomWheelEnabled = false;\n- this.handlers.wheel.deactivate();\n+ if (this.events.triggerEvent(\"beforetransform\", mods) !== false) {\n+ var feature = this.feature;\n+ var geom = feature.geometry;\n+ var center = this.center;\n+ geom.rotate(-oldRotation, center);\n+ if (mods.scale || mods.ratio) {\n+ geom.resize(mods.scale, center, mods.ratio);\n+ } else if (mods.center) {\n+ feature.move(mods.center.getBounds().getCenterLonLat());\n+ }\n+ geom.rotate(this.rotation, center);\n+ this.layer.drawFeature(feature);\n+ feature.toState(OpenLayers.State.UPDATE);\n+ this.events.triggerEvent(\"transform\", mods);\n+ }\n+ }\n+ this.layer.drawFeature(this.box, this.renderIntent);\n+ this.drawHandles();\n },\n \n /**\n- * Method: enableZoomWheel\n+ * APIMethod: destroy\n+ * Take care of things that are not handled in superclass.\n */\n-\n- enableZoomWheel: function() {\n- this.zoomWheelEnabled = true;\n- if (this.active) {\n- this.handlers.wheel.activate();\n+ destroy: function() {\n+ var geom;\n+ for (var i = 0; i < 8; ++i) {\n+ geom = this.box.geometry.components[i];\n+ geom._handle.destroy();\n+ geom._handle = null;\n+ geom._rotationHandle && geom._rotationHandle.destroy();\n+ geom._rotationHandle = null;\n }\n+ this.center = null;\n+ this.feature = null;\n+ this.handles = null;\n+ this.rotationHandleSymbolizer = null;\n+ this.rotationHandles = null;\n+ this.box.destroy();\n+ this.box = null;\n+ this.layer = null;\n+ this.dragControl.destroy();\n+ this.dragControl = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Navigation\"\n+ CLASS_NAME: \"OpenLayers.Control.TransformFeature\"\n });\n /* ======================================================================\n OpenLayers/Control/NavToolbar.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -67343,2003 +62248,2492 @@\n }\n return div;\n },\n \n CLASS_NAME: \"OpenLayers.Control.NavToolbar\"\n });\n /* ======================================================================\n- OpenLayers/Control/CacheRead.js\n+ OpenLayers/Control/ScaleLine.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/Control.js\n */\n \n /**\n- * Class: OpenLayers.Control.CacheRead\n- * A control for using image tiles cached with <OpenLayers.Control.CacheWrite>\n- * from the browser's local storage.\n- *\n+ * Class: OpenLayers.Control.ScaleLine\n+ * The ScaleLine displays a small line indicator representing the current \n+ * map scale on the map. By default it is drawn in the lower left corner of\n+ * the map.\n+ * \n * Inherits from:\n * - <OpenLayers.Control>\n+ * \n+ * Is a very close copy of:\n+ * - <OpenLayers.Control.Scale>\n */\n-OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * APIProperty: fetchEvent\n- * {String} The layer event to listen to for replacing remote resource tile\n- * URLs with cached data URIs. Supported values are \"tileerror\" (try\n- * remote first, fall back to cached) and \"tileloadstart\" (try cache\n- * first, fall back to remote). Default is \"tileloadstart\".\n- *\n- * Note that \"tileerror\" will not work for CORS enabled images (see\n- * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers\n- * configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in\n- * <OpenLayers.Layer.Grid.tileOptions>.\n+ * Property: maxWidth\n+ * {Integer} Maximum width of the scale line in pixels. Default is 100.\n */\n- fetchEvent: \"tileloadstart\",\n+ maxWidth: 100,\n \n /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these\n- * layers will receive tiles from the cache.\n+ * Property: topOutUnits\n+ * {String} Units for zoomed out on top bar. Default is km.\n */\n- layers: null,\n+ topOutUnits: \"km\",\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * Property: topInUnits\n+ * {String} Units for zoomed in on top bar. Default is m.\n */\n- autoActivate: true,\n+ topInUnits: \"m\",\n \n /**\n- * Constructor: OpenLayers.Control.CacheRead\n- *\n- * Parameters:\n- * options - {Object} Object with API properties for this control\n+ * Property: bottomOutUnits\n+ * {String} Units for zoomed out on bottom bar. Default is mi.\n */\n+ bottomOutUnits: \"mi\",\n \n- /** \n- * Method: setMap\n- * Set the map property for the control. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n+ /**\n+ * Property: bottomInUnits\n+ * {String} Units for zoomed in on bottom bar. Default is ft.\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- var i, layers = this.layers || map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.addLayer({\n- layer: layers[i]\n- });\n- }\n- if (!this.layers) {\n- map.events.on({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n- scope: this\n- });\n- }\n- },\n+ bottomInUnits: \"ft\",\n \n /**\n- * Method: addLayer\n- * Adds a layer to the control. Once added, tiles requested for this layer\n- * will be cached.\n- *\n- * Parameters:\n- * evt - {Object} Object with a layer property referencing an\n- * <OpenLayers.Layer> instance\n+ * Property: eTop\n+ * {DOMElement}\n */\n- addLayer: function(evt) {\n- evt.layer.events.register(this.fetchEvent, this, this.fetch);\n- },\n+ eTop: null,\n \n /**\n- * Method: removeLayer\n- * Removes a layer from the control. Once removed, tiles requested for this\n- * layer will no longer be cached.\n- *\n- * Parameters:\n- * evt - {Object} Object with a layer property referencing an\n- * <OpenLayers.Layer> instance\n+ * Property: eBottom\n+ * {DOMElement}\n */\n- removeLayer: function(evt) {\n- evt.layer.events.unregister(this.fetchEvent, this, this.fetch);\n- },\n+ eBottom: null,\n \n /**\n- * Method: fetch\n- * Listener to the <fetchEvent> event. Replaces a tile's url with a data\n- * URI from the cache.\n- *\n+ * APIProperty: geodesic\n+ * {Boolean} Use geodesic measurement. Default is false. The recommended\n+ * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to\n+ * true, the scale will be calculated based on the horizontal size of the\n+ * pixel in the center of the map viewport.\n+ */\n+ geodesic: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.ScaleLine\n+ * Create a new scale line control.\n+ * \n * Parameters:\n- * evt - {Object} Event object with a tile property.\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n */\n- fetch: function(evt) {\n- if (this.active && window.localStorage &&\n- evt.tile instanceof OpenLayers.Tile.Image) {\n- var tile = evt.tile,\n- url = tile.url;\n- // deal with modified tile urls when both CacheWrite and CacheRead\n- // are active\n- if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost &&\n- url.indexOf(OpenLayers.ProxyHost) === 0) {\n- url = OpenLayers.Control.CacheWrite.urlMap[url];\n+\n+ /**\n+ * Method: draw\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.eTop) {\n+ // stick in the top bar\n+ this.eTop = document.createElement(\"div\");\n+ this.eTop.className = this.displayClass + \"Top\";\n+ var theLen = this.topInUnits.length;\n+ this.div.appendChild(this.eTop);\n+ if ((this.topOutUnits == \"\") || (this.topInUnits == \"\")) {\n+ this.eTop.style.visibility = \"hidden\";\n+ } else {\n+ this.eTop.style.visibility = \"visible\";\n }\n- var dataURI = window.localStorage.getItem(\"olCache_\" + url);\n- if (dataURI) {\n- tile.url = dataURI;\n- if (evt.type === \"tileerror\") {\n- tile.setImgSrc(dataURI);\n- }\n+\n+ // and the bottom bar\n+ this.eBottom = document.createElement(\"div\");\n+ this.eBottom.className = this.displayClass + \"Bottom\";\n+ this.div.appendChild(this.eBottom);\n+ if ((this.bottomOutUnits == \"\") || (this.bottomInUnits == \"\")) {\n+ this.eBottom.style.visibility = \"hidden\";\n+ } else {\n+ this.eBottom.style.visibility = \"visible\";\n }\n }\n+ this.map.events.register('moveend', this, this.update);\n+ this.update();\n+ return this.div;\n+ },\n+\n+ /** \n+ * Method: getBarLen\n+ * Given a number, round it down to the nearest 1,2,5 times a power of 10.\n+ * That seems a fairly useful set of number groups to use.\n+ * \n+ * Parameters:\n+ * maxLen - {float} the number we're rounding down from\n+ * \n+ * Returns:\n+ * {Float} the rounded number (less than or equal to maxLen)\n+ */\n+ getBarLen: function(maxLen) {\n+ // nearest power of 10 lower than maxLen\n+ var digits = parseInt(Math.log(maxLen) / Math.log(10));\n+ var pow10 = Math.pow(10, digits);\n+\n+ // ok, find first character\n+ var firstChar = parseInt(maxLen / pow10);\n+\n+ // right, put it into the correct bracket\n+ var barLen;\n+ if (firstChar > 5) {\n+ barLen = 5;\n+ } else if (firstChar > 2) {\n+ barLen = 2;\n+ } else {\n+ barLen = 1;\n+ }\n+\n+ // scale it up the correct power of 10\n+ return barLen * pow10;\n },\n \n /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ * Method: update\n+ * Update the size of the bars, and the labels they contain.\n */\n- destroy: function() {\n- if (this.layers || this.map) {\n- var i, layers = this.layers || this.map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.removeLayer({\n- layer: layers[i]\n- });\n- }\n+ update: function() {\n+ var res = this.map.getResolution();\n+ if (!res) {\n+ return;\n }\n- if (this.map) {\n- this.map.events.un({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n- scope: this\n- });\n+\n+ var curMapUnits = this.map.getUnits();\n+ var inches = OpenLayers.INCHES_PER_UNIT;\n+\n+ // convert maxWidth to map units\n+ var maxSizeData = this.maxWidth * res * inches[curMapUnits];\n+ var geodesicRatio = 1;\n+ if (this.geodesic === true) {\n+ var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||\n+ 0.000001) * this.maxWidth;\n+ var maxSizeKilometers = maxSizeData / inches[\"km\"];\n+ geodesicRatio = maxSizeGeodesic / maxSizeKilometers;\n+ maxSizeData *= geodesicRatio;\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+\n+ // decide whether to use large or small scale units \n+ var topUnits;\n+ var bottomUnits;\n+ if (maxSizeData > 100000) {\n+ topUnits = this.topOutUnits;\n+ bottomUnits = this.bottomOutUnits;\n+ } else {\n+ topUnits = this.topInUnits;\n+ bottomUnits = this.bottomInUnits;\n+ }\n+\n+ // and to map units units\n+ var topMax = maxSizeData / inches[topUnits];\n+ var bottomMax = maxSizeData / inches[bottomUnits];\n+\n+ // now trim this down to useful block length\n+ var topRounded = this.getBarLen(topMax);\n+ var bottomRounded = this.getBarLen(bottomMax);\n+\n+ // and back to display units\n+ topMax = topRounded / inches[curMapUnits] * inches[topUnits];\n+ bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];\n+\n+ // and to pixel units\n+ var topPx = topMax / res / geodesicRatio;\n+ var bottomPx = bottomMax / res / geodesicRatio;\n+\n+ // now set the pixel widths\n+ // and the values inside them\n+\n+ if (this.eBottom.style.visibility == \"visible\") {\n+ this.eBottom.style.width = Math.round(bottomPx) + \"px\";\n+ this.eBottom.innerHTML = bottomRounded + \" \" + bottomUnits;\n+ }\n+\n+ if (this.eTop.style.visibility == \"visible\") {\n+ this.eTop.style.width = Math.round(topPx) + \"px\";\n+ this.eTop.innerHTML = topRounded + \" \" + topUnits;\n+ }\n+\n },\n \n- CLASS_NAME: \"OpenLayers.Control.CacheRead\"\n+ CLASS_NAME: \"OpenLayers.Control.ScaleLine\"\n });\n+\n /* ======================================================================\n- OpenLayers/Control/ModifyFeature.js\n+ OpenLayers/Control/OverviewMap.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/**\n+/** \n * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/BaseTypes.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Map.js\n+ * @requires OpenLayers/Handler/Click.js\n * @requires OpenLayers/Handler/Drag.js\n- * @requires OpenLayers/Handler/Keyboard.js\n */\n \n /**\n- * Class: OpenLayers.Control.ModifyFeature\n- * Control to modify features. When activated, a click renders the vertices\n- * of a feature - these vertices can then be dragged. By default, the\n- * delete key will delete the vertex under the mouse. New features are\n- * added by dragging \"virtual vertices\" between vertices. Create a new\n- * control with the <OpenLayers.Control.ModifyFeature> constructor.\n+ * Class: OpenLayers.Control.OverviewMap\n+ * The OverMap control creates a small overview map, useful to display the \n+ * extent of a zoomed map and your main map and provide additional \n+ * navigation options to the User. By default the overview map is drawn in\n+ * the lower right corner of the main map. Create a new overview map with the\n+ * <OpenLayers.Control.OverviewMap> constructor.\n *\n- * Inherits From:\n+ * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, dragging vertices will continue even if the\n- * mouse cursor leaves the map viewport. Default is false.\n+ * Property: element\n+ * {DOMElement} The DOM element that contains the overview map\n */\n- documentDrag: false,\n+ element: null,\n \n /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict modification to a limited set of geometry\n- * types, send a list of strings corresponding to the geometry class\n- * names.\n+ * APIProperty: ovmap\n+ * {<OpenLayers.Map>} A reference to the overview map itself.\n */\n- geometryTypes: null,\n+ ovmap: null,\n \n /**\n- * APIProperty: clickout\n- * {Boolean} Unselect features when clicking outside any feature.\n- * Default is true.\n+ * APIProperty: size\n+ * {<OpenLayers.Size>} The overvew map size in pixels. Note that this is\n+ * the size of the map itself - the element that contains the map (default\n+ * class name olControlOverviewMapElement) may have padding or other style\n+ * attributes added via CSS.\n */\n- clickout: true,\n+ size: {\n+ w: 180,\n+ h: 90\n+ },\n \n /**\n- * APIProperty: toggle\n- * {Boolean} Unselect a selected feature on click.\n- * Default is true.\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.\n+ * If none are sent at construction, the base layer for the main map is used.\n */\n- toggle: true,\n+ layers: null,\n \n /**\n- * APIProperty: standalone\n- * {Boolean} Set to true to create a control without SelectFeature\n- * capabilities. Default is false. If standalone is true, to modify\n- * a feature, call the <selectFeature> method with the target feature.\n- * Note that you must call the <unselectFeature> method to finish\n- * feature modification in standalone mode (before starting to modify\n- * another feature).\n+ * APIProperty: minRectSize\n+ * {Integer} The minimum width or height (in pixels) of the extent\n+ * rectangle on the overview map. When the extent rectangle reaches\n+ * this size, it will be replaced depending on the value of the\n+ * <minRectDisplayClass> property. Default is 15 pixels.\n */\n- standalone: false,\n+ minRectSize: 15,\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>}\n+ * APIProperty: minRectDisplayClass\n+ * {String} Replacement style class name for the extent rectangle when\n+ * <minRectSize> is reached. This string will be suffixed on to the\n+ * displayClass. Default is \"RectReplacement\".\n+ *\n+ * Example CSS declaration:\n+ * (code)\n+ * .olControlOverviewMapRectReplacement {\n+ * overflow: hidden;\n+ * cursor: move;\n+ * background-image: url(\"img/overview_replacement.gif\");\n+ * background-repeat: no-repeat;\n+ * background-position: center;\n+ * }\n+ * (end)\n */\n- layer: null,\n+ minRectDisplayClass: \"RectReplacement\",\n \n /**\n- * Property: feature\n- * {<OpenLayers.Feature.Vector>} Feature currently available for modification.\n+ * APIProperty: minRatio\n+ * {Float} The ratio of the overview map resolution to the main map\n+ * resolution at which to zoom farther out on the overview map.\n */\n- feature: null,\n+ minRatio: 8,\n \n /**\n- * Property: vertex\n- * {<OpenLayers.Feature.Vector>} Vertex currently being modified.\n+ * APIProperty: maxRatio\n+ * {Float} The ratio of the overview map resolution to the main map\n+ * resolution at which to zoom farther in on the overview map.\n */\n- vertex: null,\n+ maxRatio: 32,\n \n /**\n- * Property: vertices\n- * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available\n- * for dragging.\n+ * APIProperty: mapOptions\n+ * {Object} An object containing any non-default properties to be sent to\n+ * the overview map's map constructor. These should include any\n+ * non-default options that the main map was constructed with.\n */\n- vertices: null,\n+ mapOptions: null,\n \n /**\n- * Property: virtualVertices\n- * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle\n- * of each edge.\n+ * APIProperty: autoPan\n+ * {Boolean} Always pan the overview map, so the extent marker remains in\n+ * the center. Default is false. If true, when you drag the extent\n+ * marker, the overview map will update itself so the marker returns\n+ * to the center.\n */\n- virtualVertices: null,\n+ autoPan: false,\n \n /**\n * Property: handlers\n * {Object}\n */\n handlers: null,\n \n /**\n- * APIProperty: deleteCodes\n- * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable\n- * vertex deltion by keypress. If non-null, keypresses with codes\n- * in this array will delete vertices under the mouse. Default\n- * is 46 and 68, the 'delete' and lowercase 'd' keys.\n+ * Property: resolutionFactor\n+ * {Object}\n */\n- deleteCodes: null,\n+ resolutionFactor: 1,\n \n /**\n- * APIProperty: virtualStyle\n- * {Object} A symbolizer to be used for virtual vertices.\n+ * APIProperty: maximized\n+ * {Boolean} Start as maximized (visible). Defaults to false.\n */\n- virtualStyle: null,\n+ maximized: false,\n \n /**\n- * APIProperty: vertexRenderIntent\n- * {String} The renderIntent to use for vertices. If no <virtualStyle> is\n- * provided, this renderIntent will also be used for virtual vertices, with\n- * a fillOpacity and strokeOpacity of 0.3. Default is null, which means\n- * that the layer's default style will be used for vertices.\n+ * APIProperty: maximizeTitle\n+ * {String} This property is used for showing a tooltip over the \n+ * maximize div. Defaults to \"\" (no title).\n */\n- vertexRenderIntent: null,\n+ maximizeTitle: \"\",\n \n /**\n- * APIProperty: mode\n- * {Integer} Bitfields specifying the modification mode. Defaults to\n- * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a\n- * combination of options, use the | operator. For example, to allow\n- * the control to both resize and rotate features, use the following\n- * syntax\n- * (code)\n- * control.mode = OpenLayers.Control.ModifyFeature.RESIZE |\n- * OpenLayers.Control.ModifyFeature.ROTATE;\n- * (end)\n+ * APIProperty: minimizeTitle\n+ * {String} This property is used for showing a tooltip over the \n+ * minimize div. Defaults to \"\" (no title).\n */\n- mode: null,\n+ minimizeTitle: \"\",\n \n /**\n- * APIProperty: createVertices\n- * {Boolean} Create new vertices by dragging the virtual vertices\n- * in the middle of each edge. Default is true.\n+ * Constructor: OpenLayers.Control.OverviewMap\n+ * Create a new overview map\n+ *\n+ * Parameters:\n+ * options - {Object} Properties of this object will be set on the overview\n+ * map object. Note, to set options on the map object contained in this\n+ * control, set <mapOptions> as one of the options properties.\n */\n- createVertices: true,\n+ initialize: function(options) {\n+ this.layers = [];\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ },\n \n /**\n- * Property: modified\n- * {Boolean} The currently selected feature has been modified.\n+ * APIMethod: destroy\n+ * Deconstruct the control\n */\n- modified: false,\n+ destroy: function() {\n+ if (!this.mapDiv) { // we've already been destroyed\n+ return;\n+ }\n+ if (this.handlers.click) {\n+ this.handlers.click.destroy();\n+ }\n+ if (this.handlers.drag) {\n+ this.handlers.drag.destroy();\n+ }\n \n- /**\n- * Property: radiusHandle\n- * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature.\n- */\n- radiusHandle: null,\n+ this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);\n+ this.extentRectangle = null;\n \n- /**\n- * Property: dragHandle\n- * {<OpenLayers.Feature.Vector>} A handle for dragging a feature.\n- */\n- dragHandle: null,\n+ if (this.rectEvents) {\n+ this.rectEvents.destroy();\n+ this.rectEvents = null;\n+ }\n \n- /**\n- * APIProperty: onModificationStart \n- * {Function} *Deprecated*. Register for \"beforefeaturemodified\" instead.\n- * The \"beforefeaturemodified\" event is triggered on the layer before\n- * any modification begins.\n- *\n- * Optional function to be called when a feature is selected\n- * to be modified. The function should expect to be called with a\n- * feature. This could be used for example to allow to lock the\n- * feature on server-side.\n- */\n- onModificationStart: function() {},\n+ if (this.ovmap) {\n+ this.ovmap.destroy();\n+ this.ovmap = null;\n+ }\n \n- /**\n- * APIProperty: onModification\n- * {Function} *Deprecated*. Register for \"featuremodified\" instead.\n- * The \"featuremodified\" event is triggered on the layer with each\n- * feature modification.\n- *\n- * Optional function to be called when a feature has been\n- * modified. The function should expect to be called with a feature.\n- */\n- onModification: function() {},\n+ this.element.removeChild(this.mapDiv);\n+ this.mapDiv = null;\n \n- /**\n- * APIProperty: onModificationEnd\n- * {Function} *Deprecated*. Register for \"afterfeaturemodified\" instead.\n- * The \"afterfeaturemodified\" event is triggered on the layer after\n- * a feature has been modified.\n- *\n- * Optional function to be called when a feature is finished \n- * being modified. The function should expect to be called with a\n- * feature.\n- */\n- onModificationEnd: function() {},\n+ this.div.removeChild(this.element);\n+ this.element = null;\n \n- /**\n- * Constructor: OpenLayers.Control.ModifyFeature\n- * Create a new modify feature control.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that\n- * will be modified.\n- * options - {Object} Optional object whose properties will be set on the\n- * control.\n- */\n- initialize: function(layer, options) {\n- options = options || {};\n- this.layer = layer;\n- this.vertices = [];\n- this.virtualVertices = [];\n- this.virtualStyle = OpenLayers.Util.extend({},\n- this.layer.style ||\n- this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)\n- );\n- this.virtualStyle.fillOpacity = 0.3;\n- this.virtualStyle.strokeOpacity = 0.3;\n- this.deleteCodes = [46, 68];\n- this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (!(OpenLayers.Util.isArray(this.deleteCodes))) {\n- this.deleteCodes = [this.deleteCodes];\n+ if (this.maximizeDiv) {\n+ this.div.removeChild(this.maximizeDiv);\n+ this.maximizeDiv = null;\n }\n \n- // configure the drag handler\n- var dragCallbacks = {\n- down: function(pixel) {\n- this.vertex = null;\n- var feature = this.layer.getFeatureFromEvent(\n- this.handlers.drag.evt);\n- if (feature) {\n- this.dragStart(feature);\n- } else if (this.clickout) {\n- this._unselect = this.feature;\n- }\n- },\n- move: function(pixel) {\n- delete this._unselect;\n- if (this.vertex) {\n- this.dragVertex(this.vertex, pixel);\n- }\n- },\n- up: function() {\n- this.handlers.drag.stopDown = false;\n- if (this._unselect) {\n- this.unselectFeature(this._unselect);\n- delete this._unselect;\n- }\n- },\n- done: function(pixel) {\n- if (this.vertex) {\n- this.dragComplete(this.vertex);\n- }\n- }\n- };\n- var dragOptions = {\n- documentDrag: this.documentDrag,\n- stopDown: false\n- };\n+ if (this.minimizeDiv) {\n+ this.div.removeChild(this.minimizeDiv);\n+ this.minimizeDiv = null;\n+ }\n \n- // configure the keyboard handler\n- var keyboardOptions = {\n- keydown: this.handleKeypress\n- };\n- this.handlers = {\n- keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions),\n- drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions)\n- };\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ moveend: this.update,\n+ changebaselayer: this.baseLayerDraw,\n+ scope: this\n+ });\n+\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * APIMethod: destroy\n- * Take care of things that are not handled in superclass.\n+ * Method: draw\n+ * Render the control in the browser.\n */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.un({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n- });\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.layers.length === 0) {\n+ if (this.map.baseLayer) {\n+ var layer = this.map.baseLayer.clone();\n+ this.layers = [layer];\n+ } else {\n+ this.map.events.register(\"changebaselayer\", this, this.baseLayerDraw);\n+ return this.div;\n+ }\n+ }\n+\n+ // create overview map DOM elements\n+ this.element = document.createElement('div');\n+ this.element.className = this.displayClass + 'Element';\n+ this.element.style.display = 'none';\n+\n+ this.mapDiv = document.createElement('div');\n+ this.mapDiv.style.width = this.size.w + 'px';\n+ this.mapDiv.style.height = this.size.h + 'px';\n+ this.mapDiv.style.position = 'relative';\n+ this.mapDiv.style.overflow = 'hidden';\n+ this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');\n+\n+ this.extentRectangle = document.createElement('div');\n+ this.extentRectangle.style.position = 'absolute';\n+ this.extentRectangle.style.zIndex = 1000; //HACK\n+ this.extentRectangle.className = this.displayClass + 'ExtentRectangle';\n+\n+ this.element.appendChild(this.mapDiv);\n+\n+ this.div.appendChild(this.element);\n+\n+ // Optionally add min/max buttons if the control will go in the\n+ // map viewport.\n+ if (!this.outsideViewport) {\n+ this.div.className += \" \" + this.displayClass + 'Container';\n+ // maximize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ this.displayClass + 'MaximizeButton',\n+ null,\n+ null,\n+ img,\n+ 'absolute');\n+ this.maximizeDiv.style.display = 'none';\n+ this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton';\n+ if (this.maximizeTitle) {\n+ this.maximizeDiv.title = this.maximizeTitle;\n+ }\n+ this.div.appendChild(this.maximizeDiv);\n+\n+ // minimize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ 'OpenLayers_Control_minimizeDiv',\n+ null,\n+ null,\n+ img,\n+ 'absolute');\n+ this.minimizeDiv.style.display = 'none';\n+ this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton';\n+ if (this.minimizeTitle) {\n+ this.minimizeDiv.title = this.minimizeTitle;\n+ }\n+ this.div.appendChild(this.minimizeDiv);\n+ this.minimizeControl();\n+ } else {\n+ // show the overview map\n+ this.element.style.display = '';\n+ }\n+ if (this.map.getExtent()) {\n+ this.update();\n }\n- this.layer = null;\n- OpenLayers.Control.prototype.destroy.apply(this, []);\n- },\n \n- /**\n- * APIMethod: activate\n- * Activate the control.\n- * \n- * Returns:\n- * {Boolean} Successfully activated the control.\n- */\n- activate: function() {\n- this.moveLayerToTop();\n this.map.events.on({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n+ buttonclick: this.onButtonClick,\n+ moveend: this.update,\n scope: this\n });\n- return (this.handlers.keyboard.activate() &&\n- this.handlers.drag.activate() &&\n- OpenLayers.Control.prototype.activate.apply(this, arguments));\n+\n+ if (this.maximized) {\n+ this.maximizeControl();\n+ }\n+ return this.div;\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the control.\n- *\n- * Returns: \n- * {Boolean} Successfully deactivated the control.\n+ * Method: baseLayerDraw\n+ * Draw the base layer - called if unable to complete in the initial draw\n */\n- deactivate: function() {\n- var deactivated = false;\n- // the return from the controls is unimportant in this case\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.map.events.un({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n- });\n- this.layer.removeFeatures(this.vertices, {\n- silent: true\n- });\n- this.layer.removeFeatures(this.virtualVertices, {\n- silent: true\n- });\n- this.vertices = [];\n- this.handlers.drag.deactivate();\n- this.handlers.keyboard.deactivate();\n- var feature = this.feature;\n- if (feature && feature.geometry && feature.layer) {\n- this.unselectFeature(feature);\n- }\n- deactivated = true;\n- }\n- return deactivated;\n+ baseLayerDraw: function() {\n+ this.draw();\n+ this.map.events.unregister(\"changebaselayer\", this, this.baseLayerDraw);\n },\n \n /**\n- * Method: beforeSelectFeature\n- * Called before a feature is selected.\n+ * Method: rectDrag\n+ * Handle extent rectangle drag\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected.\n+ * px - {<OpenLayers.Pixel>} The pixel location of the drag.\n */\n- beforeSelectFeature: function(feature) {\n- return this.layer.events.triggerEvent(\n- \"beforefeaturemodified\", {\n- feature: feature\n- }\n- );\n+ rectDrag: function(px) {\n+ var deltaX = this.handlers.drag.last.x - px.x;\n+ var deltaY = this.handlers.drag.last.y - px.y;\n+ if (deltaX != 0 || deltaY != 0) {\n+ var rectTop = this.rectPxBounds.top;\n+ var rectLeft = this.rectPxBounds.left;\n+ var rectHeight = Math.abs(this.rectPxBounds.getHeight());\n+ var rectWidth = this.rectPxBounds.getWidth();\n+ // don't allow dragging off of parent element\n+ var newTop = Math.max(0, (rectTop - deltaY));\n+ newTop = Math.min(newTop,\n+ this.ovmap.size.h - this.hComp - rectHeight);\n+ var newLeft = Math.max(0, (rectLeft - deltaX));\n+ newLeft = Math.min(newLeft,\n+ this.ovmap.size.w - this.wComp - rectWidth);\n+ this.setRectPxBounds(new OpenLayers.Bounds(newLeft,\n+ newTop + rectHeight,\n+ newLeft + rectWidth,\n+ newTop));\n+ }\n },\n \n /**\n- * APIMethod: selectFeature\n- * Select a feature for modification in standalone mode. In non-standalone\n- * mode, this method is called when a feature is selected by clicking.\n- * Register a listener to the beforefeaturemodified event and return false\n- * to prevent feature modification.\n+ * Method: mapDivClick\n+ * Handle browser events\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} the selected feature.\n+ * evt - {<OpenLayers.Event>} evt\n */\n- selectFeature: function(feature) {\n- if (this.feature === feature ||\n- (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes,\n- feature.geometry.CLASS_NAME) == -1)) {\n- return;\n- }\n- if (this.beforeSelectFeature(feature) !== false) {\n- if (this.feature) {\n- this.unselectFeature(this.feature);\n- }\n- this.feature = feature;\n- this.layer.selectedFeatures.push(feature);\n- this.layer.drawFeature(feature, 'select');\n- this.modified = false;\n- this.resetVertices();\n- this.onModificationStart(this.feature);\n- }\n- // keep track of geometry modifications\n- var modified = feature.modified;\n- if (feature.geometry && !(modified && modified.geometry)) {\n- this._originalGeometry = feature.geometry.clone();\n- }\n+ mapDivClick: function(evt) {\n+ var pxCenter = this.rectPxBounds.getCenterPixel();\n+ var deltaX = evt.xy.x - pxCenter.x;\n+ var deltaY = evt.xy.y - pxCenter.y;\n+ var top = this.rectPxBounds.top;\n+ var left = this.rectPxBounds.left;\n+ var height = Math.abs(this.rectPxBounds.getHeight());\n+ var width = this.rectPxBounds.getWidth();\n+ var newTop = Math.max(0, (top + deltaY));\n+ newTop = Math.min(newTop, this.ovmap.size.h - height);\n+ var newLeft = Math.max(0, (left + deltaX));\n+ newLeft = Math.min(newLeft, this.ovmap.size.w - width);\n+ this.setRectPxBounds(new OpenLayers.Bounds(newLeft,\n+ newTop + height,\n+ newLeft + width,\n+ newTop));\n+ this.updateMapToRect();\n },\n \n /**\n- * APIMethod: unselectFeature\n- * Called when the select feature control unselects a feature.\n+ * Method: onButtonClick\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The unselected feature.\n+ * evt - {Event}\n */\n- unselectFeature: function(feature) {\n- this.layer.removeFeatures(this.vertices, {\n- silent: true\n- });\n- this.vertices = [];\n- this.layer.destroyFeatures(this.virtualVertices, {\n- silent: true\n- });\n- this.virtualVertices = [];\n- if (this.dragHandle) {\n- this.layer.destroyFeatures([this.dragHandle], {\n- silent: true\n- });\n- delete this.dragHandle;\n- }\n- if (this.radiusHandle) {\n- this.layer.destroyFeatures([this.radiusHandle], {\n- silent: true\n- });\n- delete this.radiusHandle;\n+ onButtonClick: function(evt) {\n+ if (evt.buttonElement === this.minimizeDiv) {\n+ this.minimizeControl();\n+ } else if (evt.buttonElement === this.maximizeDiv) {\n+ this.maximizeControl();\n }\n- this.layer.drawFeature(this.feature, 'default');\n- this.feature = null;\n- OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature);\n- this.onModificationEnd(feature);\n- this.layer.events.triggerEvent(\"afterfeaturemodified\", {\n- feature: feature,\n- modified: this.modified\n- });\n- this.modified = false;\n },\n \n-\n /**\n- * Method: dragStart\n- * Called by the drag handler before a feature is dragged. This method is\n- * used to differentiate between points and vertices\n- * of higher order geometries.\n+ * Method: maximizeControl\n+ * Unhide the control. Called when the control is in the map viewport.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be\n- * dragged.\n+ * e - {<OpenLayers.Event>}\n */\n- dragStart: function(feature) {\n- var isPoint = feature.geometry.CLASS_NAME ==\n- 'OpenLayers.Geometry.Point';\n- if (!this.standalone &&\n- ((!feature._sketch && isPoint) || !feature._sketch)) {\n- if (this.toggle && this.feature === feature) {\n- // mark feature for unselection\n- this._unselect = feature;\n- }\n- this.selectFeature(feature);\n- }\n- if (feature._sketch || isPoint) {\n- // feature is a drag or virtual handle or point\n- this.vertex = feature;\n- this.handlers.drag.stopDown = true;\n+ maximizeControl: function(e) {\n+ this.element.style.display = '';\n+ this.showToggle(false);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n }\n },\n \n /**\n- * Method: dragVertex\n- * Called by the drag handler with each drag move of a vertex.\n- *\n+ * Method: minimizeControl\n+ * Hide all the contents of the control, shrink the size, \n+ * add the maximize icon\n+ * \n * Parameters:\n- * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.\n- * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event.\n+ * e - {<OpenLayers.Event>}\n */\n- dragVertex: function(vertex, pixel) {\n- var pos = this.map.getLonLatFromViewPortPx(pixel);\n- var geom = vertex.geometry;\n- geom.move(pos.lon - geom.x, pos.lat - geom.y);\n- this.modified = true;\n- /**\n- * Five cases:\n- * 1) dragging a simple point\n- * 2) dragging a virtual vertex\n- * 3) dragging a drag handle\n- * 4) dragging a real vertex\n- * 5) dragging a radius handle\n- */\n- if (this.feature.geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- // dragging a simple point\n- this.layer.events.triggerEvent(\"vertexmodified\", {\n- vertex: vertex.geometry,\n- feature: this.feature,\n- pixel: pixel\n- });\n- } else {\n- if (vertex._index) {\n- // dragging a virtual vertex\n- vertex.geometry.parent.addComponent(vertex.geometry,\n- vertex._index);\n- // move from virtual to real vertex\n- delete vertex._index;\n- OpenLayers.Util.removeItem(this.virtualVertices, vertex);\n- this.vertices.push(vertex);\n- } else if (vertex == this.dragHandle) {\n- // dragging a drag handle\n- this.layer.removeFeatures(this.vertices, {\n- silent: true\n- });\n- this.vertices = [];\n- if (this.radiusHandle) {\n- this.layer.destroyFeatures([this.radiusHandle], {\n- silent: true\n- });\n- this.radiusHandle = null;\n- }\n- } else if (vertex !== this.radiusHandle) {\n- // dragging a real vertex\n- this.layer.events.triggerEvent(\"vertexmodified\", {\n- vertex: vertex.geometry,\n- feature: this.feature,\n- pixel: pixel\n- });\n- }\n- // dragging a radius handle - no special treatment\n- if (this.virtualVertices.length > 0) {\n- this.layer.destroyFeatures(this.virtualVertices, {\n- silent: true\n- });\n- this.virtualVertices = [];\n- }\n- this.layer.drawFeature(this.feature, this.standalone ? undefined :\n- 'select');\n+ minimizeControl: function(e) {\n+ this.element.style.display = 'none';\n+ this.showToggle(true);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n }\n- // keep the vertex on top so it gets the mouseout after dragging\n- // this should be removed in favor of an option to draw under or\n- // maintain node z-index\n- this.layer.drawFeature(vertex);\n },\n \n /**\n- * Method: dragComplete\n- * Called by the drag handler when the feature dragging is complete.\n+ * Method: showToggle\n+ * Hide/Show the toggle depending on whether the control is minimized\n *\n * Parameters:\n- * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged.\n+ * minimize - {Boolean} \n */\n- dragComplete: function(vertex) {\n- this.resetVertices();\n- this.setFeatureState();\n- this.onModification(this.feature);\n- this.layer.events.triggerEvent(\"featuremodified\", {\n- feature: this.feature\n- });\n+ showToggle: function(minimize) {\n+ if (this.maximizeDiv) {\n+ this.maximizeDiv.style.display = minimize ? '' : 'none';\n+ }\n+ if (this.minimizeDiv) {\n+ this.minimizeDiv.style.display = minimize ? 'none' : '';\n+ }\n },\n \n /**\n- * Method: setFeatureState\n- * Called when the feature is modified. If the current state is not\n- * INSERT or DELETE, the state is set to UPDATE.\n+ * Method: update\n+ * Update the overview map after layers move.\n */\n- setFeatureState: function() {\n- if (this.feature.state != OpenLayers.State.INSERT &&\n- this.feature.state != OpenLayers.State.DELETE) {\n- this.feature.state = OpenLayers.State.UPDATE;\n- if (this.modified && this._originalGeometry) {\n- var feature = this.feature;\n- feature.modified = OpenLayers.Util.extend(feature.modified, {\n- geometry: this._originalGeometry\n- });\n- delete this._originalGeometry;\n- }\n+ update: function() {\n+ if (this.ovmap == null) {\n+ this.createMap();\n+ }\n+\n+ if (this.autoPan || !this.isSuitableOverview()) {\n+ this.updateOverview();\n }\n+\n+ // update extent rectangle\n+ this.updateRectToMap();\n },\n \n /**\n- * Method: resetVertices\n+ * Method: isSuitableOverview\n+ * Determines if the overview map is suitable given the extent and\n+ * resolution of the main map.\n */\n- resetVertices: function() {\n- if (this.vertices.length > 0) {\n- this.layer.removeFeatures(this.vertices, {\n- silent: true\n- });\n- this.vertices = [];\n- }\n- if (this.virtualVertices.length > 0) {\n- this.layer.removeFeatures(this.virtualVertices, {\n- silent: true\n- });\n- this.virtualVertices = [];\n- }\n- if (this.dragHandle) {\n- this.layer.destroyFeatures([this.dragHandle], {\n- silent: true\n- });\n- this.dragHandle = null;\n- }\n- if (this.radiusHandle) {\n- this.layer.destroyFeatures([this.radiusHandle], {\n- silent: true\n- });\n- this.radiusHandle = null;\n- }\n- if (this.feature &&\n- this.feature.geometry.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n- if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {\n- this.collectDragHandle();\n- }\n- if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |\n- OpenLayers.Control.ModifyFeature.RESIZE))) {\n- this.collectRadiusHandle();\n- }\n- if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {\n- // Don't collect vertices when we're resizing\n- if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {\n- this.collectVertices();\n- }\n- }\n+ isSuitableOverview: function() {\n+ var mapExtent = this.map.getExtent();\n+ var maxExtent = this.map.getMaxExtent();\n+ var testExtent = new OpenLayers.Bounds(\n+ Math.max(mapExtent.left, maxExtent.left),\n+ Math.max(mapExtent.bottom, maxExtent.bottom),\n+ Math.min(mapExtent.right, maxExtent.right),\n+ Math.min(mapExtent.top, maxExtent.top));\n+\n+ if (this.ovmap.getProjection() != this.map.getProjection()) {\n+ testExtent = testExtent.transform(\n+ this.map.getProjectionObject(),\n+ this.ovmap.getProjectionObject());\n }\n+\n+ var resRatio = this.ovmap.getResolution() / this.map.getResolution();\n+ return ((resRatio > this.minRatio) &&\n+ (resRatio <= this.maxRatio) &&\n+ (this.ovmap.getExtent().containsBounds(testExtent)));\n },\n \n /**\n- * Method: handleKeypress\n- * Called by the feature handler on keypress. This is used to delete\n- * vertices. If the <deleteCode> property is set, vertices will\n- * be deleted when a feature is selected for modification and\n- * the mouse is over a vertex.\n- *\n- * Parameters:\n- * evt - {Event} Keypress event.\n+ * Method updateOverview\n+ * Called by <update> if <isSuitableOverview> returns true\n */\n- handleKeypress: function(evt) {\n- var code = evt.keyCode;\n-\n- // check for delete key\n- if (this.feature &&\n- OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {\n- var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt);\n- if (vertex &&\n- OpenLayers.Util.indexOf(this.vertices, vertex) != -1 &&\n- !this.handlers.drag.dragging && vertex.geometry.parent) {\n- // remove the vertex\n- vertex.geometry.parent.removeComponent(vertex.geometry);\n- this.layer.events.triggerEvent(\"vertexremoved\", {\n- vertex: vertex.geometry,\n- feature: this.feature,\n- pixel: evt.xy\n- });\n- this.layer.drawFeature(this.feature, this.standalone ?\n- undefined : 'select');\n- this.modified = true;\n- this.resetVertices();\n- this.setFeatureState();\n- this.onModification(this.feature);\n- this.layer.events.triggerEvent(\"featuremodified\", {\n- feature: this.feature\n- });\n- }\n+ updateOverview: function() {\n+ var mapRes = this.map.getResolution();\n+ var targetRes = this.ovmap.getResolution();\n+ var resRatio = targetRes / mapRes;\n+ if (resRatio > this.maxRatio) {\n+ // zoom in overview map\n+ targetRes = this.minRatio * mapRes;\n+ } else if (resRatio <= this.minRatio) {\n+ // zoom out overview map\n+ targetRes = this.maxRatio * mapRes;\n+ }\n+ var center;\n+ if (this.ovmap.getProjection() != this.map.getProjection()) {\n+ center = this.map.center.clone();\n+ center.transform(this.map.getProjectionObject(),\n+ this.ovmap.getProjectionObject());\n+ } else {\n+ center = this.map.center;\n }\n+ this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(\n+ targetRes * this.resolutionFactor));\n+ this.updateRectToMap();\n },\n \n /**\n- * Method: collectVertices\n- * Collect the vertices from the modifiable feature's geometry and push\n- * them on to the control's vertices array.\n+ * Method: createMap\n+ * Construct the map that this control contains\n */\n- collectVertices: function() {\n- this.vertices = [];\n- this.virtualVertices = [];\n- var control = this;\n+ createMap: function() {\n+ // create the overview map\n+ var options = OpenLayers.Util.extend({\n+ controls: [],\n+ maxResolution: 'auto',\n+ fallThrough: false\n+ }, this.mapOptions);\n+ this.ovmap = new OpenLayers.Map(this.mapDiv, options);\n+ this.ovmap.viewPortDiv.appendChild(this.extentRectangle);\n \n- function collectComponentVertices(geometry) {\n- var i, vertex, component, len;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- vertex = new OpenLayers.Feature.Vector(geometry);\n- vertex._sketch = true;\n- vertex.renderIntent = control.vertexRenderIntent;\n- control.vertices.push(vertex);\n- } else {\n- var numVert = geometry.components.length;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- numVert -= 1;\n- }\n- for (i = 0; i < numVert; ++i) {\n- component = geometry.components[i];\n- if (component.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- vertex = new OpenLayers.Feature.Vector(component);\n- vertex._sketch = true;\n- vertex.renderIntent = control.vertexRenderIntent;\n- control.vertices.push(vertex);\n- } else {\n- collectComponentVertices(component);\n- }\n- }\n+ // prevent ovmap from being destroyed when the page unloads, because\n+ // the OverviewMap control has to do this (and does it).\n+ OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);\n \n- // add virtual vertices in the middle of each edge\n- if (control.createVertices && geometry.CLASS_NAME != \"OpenLayers.Geometry.MultiPoint\") {\n- for (i = 0, len = geometry.components.length; i < len - 1; ++i) {\n- var prevVertex = geometry.components[i];\n- var nextVertex = geometry.components[i + 1];\n- if (prevVertex.CLASS_NAME == \"OpenLayers.Geometry.Point\" &&\n- nextVertex.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- var x = (prevVertex.x + nextVertex.x) / 2;\n- var y = (prevVertex.y + nextVertex.y) / 2;\n- var point = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Point(x, y),\n- null, control.virtualStyle\n- );\n- // set the virtual parent and intended index\n- point.geometry.parent = geometry;\n- point._index = i + 1;\n- point._sketch = true;\n- control.virtualVertices.push(point);\n- }\n- }\n- }\n+ this.ovmap.addLayers(this.layers);\n+ this.ovmap.zoomToMaxExtent();\n+ // check extent rectangle border width\n+ this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n+ 'border-left-width')) +\n+ parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n+ 'border-right-width'));\n+ this.wComp = (this.wComp) ? this.wComp : 2;\n+ this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n+ 'border-top-width')) +\n+ parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n+ 'border-bottom-width'));\n+ this.hComp = (this.hComp) ? this.hComp : 2;\n+\n+ this.handlers.drag = new OpenLayers.Handler.Drag(\n+ this, {\n+ move: this.rectDrag,\n+ done: this.updateMapToRect\n+ }, {\n+ map: this.ovmap\n+ }\n+ );\n+ this.handlers.click = new OpenLayers.Handler.Click(\n+ this, {\n+ \"click\": this.mapDivClick\n+ }, {\n+ \"single\": true,\n+ \"double\": false,\n+ \"stopSingle\": true,\n+ \"stopDouble\": true,\n+ \"pixelTolerance\": 1,\n+ map: this.ovmap\n+ }\n+ );\n+ this.handlers.click.activate();\n+\n+ this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,\n+ null, true);\n+ this.rectEvents.register(\"mouseover\", this, function(e) {\n+ if (!this.handlers.drag.active && !this.map.dragging) {\n+ this.handlers.drag.activate();\n }\n- }\n- collectComponentVertices.call(this, this.feature.geometry);\n- this.layer.addFeatures(this.virtualVertices, {\n- silent: true\n });\n- this.layer.addFeatures(this.vertices, {\n- silent: true\n+ this.rectEvents.register(\"mouseout\", this, function(e) {\n+ if (!this.handlers.drag.dragging) {\n+ this.handlers.drag.deactivate();\n+ }\n });\n+\n+ if (this.ovmap.getProjection() != this.map.getProjection()) {\n+ var sourceUnits = this.map.getProjectionObject().getUnits() ||\n+ this.map.units || this.map.baseLayer.units;\n+ var targetUnits = this.ovmap.getProjectionObject().getUnits() ||\n+ this.ovmap.units || this.ovmap.baseLayer.units;\n+ this.resolutionFactor = sourceUnits && targetUnits ?\n+ OpenLayers.INCHES_PER_UNIT[sourceUnits] /\n+ OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n+ }\n },\n \n /**\n- * Method: collectDragHandle\n- * Collect the drag handle for the selected geometry.\n+ * Method: updateRectToMap\n+ * Updates the extent rectangle position and size to match the map extent\n */\n- collectDragHandle: function() {\n- var geometry = this.feature.geometry;\n- var center = geometry.getBounds().getCenterLonLat();\n- var originGeometry = new OpenLayers.Geometry.Point(\n- center.lon, center.lat\n- );\n- var origin = new OpenLayers.Feature.Vector(originGeometry);\n- originGeometry.move = function(x, y) {\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- geometry.move(x, y);\n- };\n- origin._sketch = true;\n- this.dragHandle = origin;\n- this.dragHandle.renderIntent = this.vertexRenderIntent;\n- this.layer.addFeatures([this.dragHandle], {\n- silent: true\n- });\n+ updateRectToMap: function() {\n+ // If the projections differ we need to reproject\n+ var bounds;\n+ if (this.ovmap.getProjection() != this.map.getProjection()) {\n+ bounds = this.map.getExtent().transform(\n+ this.map.getProjectionObject(),\n+ this.ovmap.getProjectionObject());\n+ } else {\n+ bounds = this.map.getExtent();\n+ }\n+ var pxBounds = this.getRectBoundsFromMapBounds(bounds);\n+ if (pxBounds) {\n+ this.setRectPxBounds(pxBounds);\n+ }\n },\n \n /**\n- * Method: collectRadiusHandle\n- * Collect the radius handle for the selected geometry.\n+ * Method: updateMapToRect\n+ * Updates the map extent to match the extent rectangle position and size\n */\n- collectRadiusHandle: function() {\n- var geometry = this.feature.geometry;\n- var bounds = geometry.getBounds();\n- var center = bounds.getCenterLonLat();\n- var originGeometry = new OpenLayers.Geometry.Point(\n- center.lon, center.lat\n- );\n- var radiusGeometry = new OpenLayers.Geometry.Point(\n- bounds.right, bounds.bottom\n- );\n- var radius = new OpenLayers.Feature.Vector(radiusGeometry);\n- var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);\n- var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);\n- var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);\n-\n- radiusGeometry.move = function(x, y) {\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- var dx1 = this.x - originGeometry.x;\n- var dy1 = this.y - originGeometry.y;\n- var dx0 = dx1 - x;\n- var dy0 = dy1 - y;\n- if (rotate) {\n- var a0 = Math.atan2(dy0, dx0);\n- var a1 = Math.atan2(dy1, dx1);\n- var angle = a1 - a0;\n- angle *= 180 / Math.PI;\n- geometry.rotate(angle, originGeometry);\n- }\n- if (resize) {\n- var scale, ratio;\n- // 'resize' together with 'reshape' implies that the aspect \n- // ratio of the geometry will not be preserved whilst resizing \n- if (reshape) {\n- scale = dy1 / dy0;\n- ratio = (dx1 / dx0) / scale;\n- } else {\n- var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));\n- var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));\n- scale = l1 / l0;\n- }\n- geometry.resize(scale, originGeometry, ratio);\n- }\n- };\n- radius._sketch = true;\n- this.radiusHandle = radius;\n- this.radiusHandle.renderIntent = this.vertexRenderIntent;\n- this.layer.addFeatures([this.radiusHandle], {\n- silent: true\n- });\n+ updateMapToRect: function() {\n+ var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);\n+ if (this.ovmap.getProjection() != this.map.getProjection()) {\n+ lonLatBounds = lonLatBounds.transform(\n+ this.ovmap.getProjectionObject(),\n+ this.map.getProjectionObject());\n+ }\n+ this.map.panTo(lonLatBounds.getCenterLonLat());\n },\n \n /**\n- * Method: setMap\n- * Set the map property for the control and all handlers.\n+ * Method: setRectPxBounds\n+ * Set extent rectangle pixel bounds.\n *\n * Parameters:\n- * map - {<OpenLayers.Map>} The control's map.\n+ * pxBounds - {<OpenLayers.Bounds>}\n */\n- setMap: function(map) {\n- this.handlers.drag.setMap(map);\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ setRectPxBounds: function(pxBounds) {\n+ var top = Math.max(pxBounds.top, 0);\n+ var left = Math.max(pxBounds.left, 0);\n+ var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),\n+ this.ovmap.size.h - this.hComp);\n+ var right = Math.min(pxBounds.left + pxBounds.getWidth(),\n+ this.ovmap.size.w - this.wComp);\n+ var width = Math.max(right - left, 0);\n+ var height = Math.max(bottom - top, 0);\n+ if (width < this.minRectSize || height < this.minRectSize) {\n+ this.extentRectangle.className = this.displayClass +\n+ this.minRectDisplayClass;\n+ var rLeft = left + (width / 2) - (this.minRectSize / 2);\n+ var rTop = top + (height / 2) - (this.minRectSize / 2);\n+ this.extentRectangle.style.top = Math.round(rTop) + 'px';\n+ this.extentRectangle.style.left = Math.round(rLeft) + 'px';\n+ this.extentRectangle.style.height = this.minRectSize + 'px';\n+ this.extentRectangle.style.width = this.minRectSize + 'px';\n+ } else {\n+ this.extentRectangle.className = this.displayClass +\n+ 'ExtentRectangle';\n+ this.extentRectangle.style.top = Math.round(top) + 'px';\n+ this.extentRectangle.style.left = Math.round(left) + 'px';\n+ this.extentRectangle.style.height = Math.round(height) + 'px';\n+ this.extentRectangle.style.width = Math.round(width) + 'px';\n+ }\n+ this.rectPxBounds = new OpenLayers.Bounds(\n+ Math.round(left), Math.round(bottom),\n+ Math.round(right), Math.round(top)\n+ );\n },\n \n /**\n- * Method: handleMapEvents\n- * \n+ * Method: getRectBoundsFromMapBounds\n+ * Get the rect bounds from the map bounds.\n+ *\n * Parameters:\n- * evt - {Object}\n+ * lonLatBounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent\n+ * translated into pixel bounds for the overview map\n */\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop();\n+ getRectBoundsFromMapBounds: function(lonLatBounds) {\n+ var leftBottomPx = this.getOverviewPxFromLonLat({\n+ lon: lonLatBounds.left,\n+ lat: lonLatBounds.bottom\n+ });\n+ var rightTopPx = this.getOverviewPxFromLonLat({\n+ lon: lonLatBounds.right,\n+ lat: lonLatBounds.top\n+ });\n+ var bounds = null;\n+ if (leftBottomPx && rightTopPx) {\n+ bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,\n+ rightTopPx.x, rightTopPx.y);\n }\n+ return bounds;\n },\n \n /**\n- * Method: moveLayerToTop\n- * Moves the layer for this handler to the top, so mouse events can reach\n- * it.\n+ * Method: getMapBoundsFromRectBounds\n+ * Get the map bounds from the rect bounds.\n+ *\n+ * Parameters:\n+ * pxBounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds\n+ * translated into lon/lat bounds for the overview map\n */\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n- this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index);\n+ getMapBoundsFromRectBounds: function(pxBounds) {\n+ var leftBottomLonLat = this.getLonLatFromOverviewPx({\n+ x: pxBounds.left,\n+ y: pxBounds.bottom\n+ });\n+ var rightTopLonLat = this.getLonLatFromOverviewPx({\n+ x: pxBounds.right,\n+ y: pxBounds.top\n+ });\n+ return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,\n+ rightTopLonLat.lon, rightTopLonLat.lat);\n+ },\n+\n+ /**\n+ * Method: getLonLatFromOverviewPx\n+ * Get a map location from a pixel location\n+ *\n+ * Parameters:\n+ * overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or\n+ * an object with a\n+ * 'x' and 'y' properties.\n+ *\n+ * Returns:\n+ * {Object} Location which is the passed-in overview map\n+ * OpenLayers.Pixel, translated into lon/lat by the overview\n+ * map. An object with a 'lon' and 'lat' properties.\n+ */\n+ getLonLatFromOverviewPx: function(overviewMapPx) {\n+ var size = this.ovmap.size;\n+ var res = this.ovmap.getResolution();\n+ var center = this.ovmap.getExtent().getCenterLonLat();\n+\n+ var deltaX = overviewMapPx.x - (size.w / 2);\n+ var deltaY = overviewMapPx.y - (size.h / 2);\n \n+ return {\n+ lon: center.lon + deltaX * res,\n+ lat: center.lat - deltaY * res\n+ };\n },\n \n /**\n- * Method: moveLayerBack\n- * Moves the layer back to the position determined by the map's layers\n- * array.\n+ * Method: getOverviewPxFromLonLat\n+ * Get a pixel location from a map location\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ *\n+ * Returns:\n+ * {Object} Location which is the passed-in OpenLayers.LonLat, \n+ * translated into overview map pixels\n */\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE['Feature']) {\n- this.layer.setZIndex(index);\n- } else {\n- this.map.setLayerZIndex(this.layer,\n- this.map.getLayerIndex(this.layer));\n+ getOverviewPxFromLonLat: function(lonlat) {\n+ var res = this.ovmap.getResolution();\n+ var extent = this.ovmap.getExtent();\n+ if (extent) {\n+ return {\n+ x: Math.round(1 / res * (lonlat.lon - extent.left)),\n+ y: Math.round(1 / res * (extent.top - lonlat.lat))\n+ };\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ModifyFeature\"\n+ CLASS_NAME: 'OpenLayers.Control.OverviewMap'\n });\n-\n-/**\n- * Constant: RESHAPE\n- * {Integer} Constant used to make the control work in reshape mode\n- */\n-OpenLayers.Control.ModifyFeature.RESHAPE = 1;\n-/**\n- * Constant: RESIZE\n- * {Integer} Constant used to make the control work in resize mode\n- */\n-OpenLayers.Control.ModifyFeature.RESIZE = 2;\n-/**\n- * Constant: ROTATE\n- * {Integer} Constant used to make the control work in rotate mode\n- */\n-OpenLayers.Control.ModifyFeature.ROTATE = 4;\n-/**\n- * Constant: DRAG\n- * {Integer} Constant used to make the control work in drag mode\n- */\n-OpenLayers.Control.ModifyFeature.DRAG = 8;\n /* ======================================================================\n- OpenLayers/Control/DrawFeature.js\n+ OpenLayers/Control/Geolocate.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Control.DrawFeature\n- * The DrawFeature control draws point, line or polygon features on a vector\n- * layer when active.\n+ * Class: OpenLayers.Control.Geolocate\n+ * The Geolocate control wraps w3c geolocation API into control that can be\n+ * bound to a map, and generate events on location update\n+ *\n+ * To use this control requires to load the proj4js library if the projection\n+ * of the map is not EPSG:4326 or EPSG:900913.\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n-\n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>}\n- */\n- layer: null,\n-\n- /**\n- * Property: callbacks\n- * {Object} The functions that are sent to the handler for callback\n- */\n- callbacks: null,\n+OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n * APIProperty: events\n * {<OpenLayers.Events>} Events instance for listeners and triggering\n * control specific events.\n *\n * Register a listener for a particular event with the following syntax:\n * (code)\n * control.events.register(type, obj, listener);\n * (end)\n *\n * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * featureadded - Triggered when a feature is added\n+ * locationupdated - Triggered when browser return a new position. Listeners will \n+ * receive an object with a 'position' property which is the browser.geolocation.position\n+ * native object, as well as a 'point' property which is the location transformed in the \n+ * current map projection.\n+ * locationfailed - Triggered when geolocation has failed\n+ * locationuncapable - Triggered when control is activated on a browser\n+ * which doesn't support geolocation\n */\n \n /**\n- * APIProperty: multi\n- * {Boolean} Cast features to multi-part geometries before passing to the\n- * layer. Default is false.\n+ * Property: geolocation\n+ * {Object} The geolocation engine, as a property to be possibly mocked.\n+ * This is set lazily to avoid a memory leak in IE9.\n */\n- multi: false,\n+ geolocation: null,\n \n /**\n- * APIProperty: featureAdded\n- * {Function} Called after each feature is added\n+ * Property: available\n+ * {Boolean} The navigator.geolocation object is available.\n */\n- featureAdded: function() {},\n+ available: ('geolocation' in navigator),\n \n /**\n- * APIProperty: handlerOptions\n- * {Object} Used to set non-default properties on the control's handler\n+ * APIProperty: bind\n+ * {Boolean} If true, map center will be set on location update.\n */\n+ bind: true,\n \n /**\n- * Constructor: OpenLayers.Control.DrawFeature\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} \n- * handler - {<OpenLayers.Handler>} \n- * options - {Object} \n+ * APIProperty: watch\n+ * {Boolean} If true, position will be update regularly.\n */\n- initialize: function(layer, handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.callbacks = OpenLayers.Util.extend({\n- done: this.drawFeature,\n- modify: function(vertex, feature) {\n- this.layer.events.triggerEvent(\n- \"sketchmodified\", {\n- vertex: vertex,\n- feature: feature\n- }\n- );\n- },\n- create: function(vertex, feature) {\n- this.layer.events.triggerEvent(\n- \"sketchstarted\", {\n- vertex: vertex,\n- feature: feature\n- }\n- );\n- }\n- },\n- this.callbacks\n- );\n- this.layer = layer;\n- this.handlerOptions = this.handlerOptions || {};\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n- this.handlerOptions.layerOptions, {\n- renderers: layer.renderers,\n- rendererOptions: layer.rendererOptions\n- }\n- );\n- if (!(\"multi\" in this.handlerOptions)) {\n- this.handlerOptions.multi = this.multi;\n- }\n- var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;\n- if (sketchStyle) {\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n- this.handlerOptions.layerOptions, {\n- styleMap: new OpenLayers.StyleMap({\n- \"default\": sketchStyle\n- })\n- }\n- );\n- }\n- this.handler = new handler(this, this.callbacks, this.handlerOptions);\n- },\n+ watch: false,\n \n /**\n- * Method: drawFeature\n+ * APIProperty: geolocationOptions\n+ * {Object} Options to pass to the navigator's geolocation API. See\n+ * <http://dev.w3.org/geo/api/spec-source.html>. No specific\n+ * option is passed to the geolocation API by default.\n */\n- drawFeature: function(geometry) {\n- var feature = new OpenLayers.Feature.Vector(geometry);\n- var proceed = this.layer.events.triggerEvent(\n- \"sketchcomplete\", {\n- feature: feature\n- }\n- );\n- if (proceed !== false) {\n- feature.state = OpenLayers.State.INSERT;\n- this.layer.addFeatures([feature]);\n- this.featureAdded(feature);\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- }\n- },\n+ geolocationOptions: null,\n \n /**\n- * APIMethod: insertXY\n- * Insert a point in the current sketch given x & y coordinates.\n+ * Constructor: OpenLayers.Control.Geolocate\n+ * Create a new control to deal with browser geolocation API\n *\n- * Parameters:\n- * x - {Number} The x-coordinate of the point.\n- * y - {Number} The y-coordinate of the point.\n */\n- insertXY: function(x, y) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertXY(x, y);\n- }\n- },\n \n /**\n- * APIMethod: insertDeltaXY\n- * Insert a point given offsets from the previously inserted point.\n- *\n- * Parameters:\n- * dx - {Number} The x-coordinate offset of the point.\n- * dy - {Number} The y-coordinate offset of the point.\n+ * Method: destroy\n */\n- insertDeltaXY: function(dx, dy) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDeltaXY(dx, dy);\n- }\n+ destroy: function() {\n+ this.deactivate();\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * APIMethod: insertDirectionLength\n- * Insert a point in the current sketch given a direction and a length.\n+ * Method: activate\n+ * Activates the control.\n *\n- * Parameters:\n- * direction - {Number} Degrees clockwise from the positive x-axis.\n- * length - {Number} Distance from the previously drawn point.\n+ * Returns:\n+ * {Boolean} The control was effectively activated.\n */\n- insertDirectionLength: function(direction, length) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDirectionLength(direction, length);\n+ activate: function() {\n+ if (this.available && !this.geolocation) {\n+ // set lazily to avoid IE9 memory leak\n+ this.geolocation = navigator.geolocation;\n+ }\n+ if (!this.geolocation) {\n+ this.events.triggerEvent(\"locationuncapable\");\n+ return false;\n+ }\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ if (this.watch) {\n+ this.watchId = this.geolocation.watchPosition(\n+ OpenLayers.Function.bind(this.geolocate, this),\n+ OpenLayers.Function.bind(this.failure, this),\n+ this.geolocationOptions\n+ );\n+ } else {\n+ this.getCurrentLocation();\n+ }\n+ return true;\n }\n+ return false;\n },\n \n /**\n- * APIMethod: insertDeflectionLength\n- * Insert a point in the current sketch given a deflection and a length.\n- * The deflection should be degrees clockwise from the previously \n- * digitized segment.\n+ * Method: deactivate\n+ * Deactivates the control.\n *\n- * Parameters:\n- * deflection - {Number} Degrees clockwise from the previous segment.\n- * length - {Number} Distance from the previously drawn point.\n+ * Returns:\n+ * {Boolean} The control was effectively deactivated.\n */\n- insertDeflectionLength: function(deflection, length) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDeflectionLength(deflection, length);\n+ deactivate: function() {\n+ if (this.active && this.watchId !== null) {\n+ this.geolocation.clearWatch(this.watchId);\n }\n+ return OpenLayers.Control.prototype.deactivate.apply(\n+ this, arguments\n+ );\n },\n \n /**\n- * APIMethod: undo\n- * Remove the most recently added point in the current sketch geometry.\n+ * Method: geolocate\n+ * Activates the control.\n *\n- * Returns: \n- * {Boolean} An edit was undone.\n */\n- undo: function() {\n- return this.handler.undo && this.handler.undo();\n+ geolocate: function(position) {\n+ var center = new OpenLayers.LonLat(\n+ position.coords.longitude,\n+ position.coords.latitude\n+ ).transform(\n+ new OpenLayers.Projection(\"EPSG:4326\"),\n+ this.map.getProjectionObject()\n+ );\n+ if (this.bind) {\n+ this.map.setCenter(center);\n+ }\n+ this.events.triggerEvent(\"locationupdated\", {\n+ position: position,\n+ point: new OpenLayers.Geometry.Point(\n+ center.lon, center.lat\n+ )\n+ });\n },\n \n /**\n- * APIMethod: redo\n- * Reinsert the most recently removed point resulting from an <undo> call.\n- * The undo stack is deleted whenever a point is added by other means.\n+ * APIMethod: getCurrentLocation\n *\n- * Returns: \n- * {Boolean} An edit was redone.\n- */\n- redo: function() {\n- return this.handler.redo && this.handler.redo();\n- },\n-\n- /**\n- * APIMethod: finishSketch\n- * Finishes the sketch without including the currently drawn point.\n- * This method can be called to terminate drawing programmatically\n- * instead of waiting for the user to end the sketch.\n+ * Returns:\n+ * {Boolean} Returns true if a event will be fired (successfull\n+ * registration)\n */\n- finishSketch: function() {\n- this.handler.finishGeometry();\n+ getCurrentLocation: function() {\n+ if (!this.active || this.watch) {\n+ return false;\n+ }\n+ this.geolocation.getCurrentPosition(\n+ OpenLayers.Function.bind(this.geolocate, this),\n+ OpenLayers.Function.bind(this.failure, this),\n+ this.geolocationOptions\n+ );\n+ return true;\n },\n \n /**\n- * APIMethod: cancel\n- * Cancel the current sketch. This removes the current sketch and keeps\n- * the drawing control active.\n+ * Method: failure\n+ * method called on browser's geolocation failure\n+ *\n */\n- cancel: function() {\n- this.handler.cancel();\n+ failure: function(error) {\n+ this.events.triggerEvent(\"locationfailed\", {\n+ error: error\n+ });\n },\n \n- CLASS_NAME: \"OpenLayers.Control.DrawFeature\"\n+ CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n });\n /* ======================================================================\n- OpenLayers/Control/ArgParser.js\n+ OpenLayers/Control/Graticule.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Rule.js\n+ * @requires OpenLayers/StyleMap.js\n+ * @requires OpenLayers/Layer/Vector.js\n */\n \n /**\n- * Class: OpenLayers.Control.ArgParser\n- * The ArgParser control adds location bar query string parsing functionality \n- * to an OpenLayers Map.\n- * When added to a Map control, on a page load/refresh, the Map will \n- * automatically take the href string and parse it for lon, lat, zoom, and \n- * layers information. \n- *\n+ * Class: OpenLayers.Control.Graticule\n+ * The Graticule displays a grid of latitude/longitude lines reprojected on\n+ * the map. \n+ * \n * Inherits from:\n * - <OpenLayers.Control>\n+ * \n */\n-OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: center\n- * {<OpenLayers.LonLat>}\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true. \n */\n- center: null,\n+ autoActivate: true,\n \n /**\n- * Property: zoom\n- * {int}\n+ * APIProperty: intervals\n+ * {Array(Float)} A list of possible graticule widths in degrees.\n */\n- zoom: null,\n+ intervals: [45, 30, 20, 10, 5, 2, 1,\n+ 0.5, 0.2, 0.1, 0.05, 0.01,\n+ 0.005, 0.002, 0.001\n+ ],\n \n /**\n- * Property: layers\n- * {String} Each character represents the state of the corresponding layer \n- * on the map.\n+ * APIProperty: displayInLayerSwitcher\n+ * {Boolean} Allows the Graticule control to be switched on and off by \n+ * LayerSwitcher control. Defaults is true.\n */\n- layers: null,\n+ displayInLayerSwitcher: true,\n \n- /** \n- * APIProperty: displayProjection\n- * {<OpenLayers.Projection>} Requires proj4js support. \n- * Projection used when reading the coordinates from the URL. This will\n- * reproject the map coordinates from the URL into the map's\n- * projection.\n- *\n- * If you are using this functionality, be aware that any permalink\n- * which is added to the map will determine the coordinate type which\n- * is read from the URL, which means you should not add permalinks with\n- * different displayProjections to the same map. \n+ /**\n+ * APIProperty: visible\n+ * {Boolean} should the graticule be initially visible (default=true)\n */\n- displayProjection: null,\n+ visible: true,\n \n /**\n- * Constructor: OpenLayers.Control.ArgParser\n- *\n- * Parameters:\n- * options - {Object}\n+ * APIProperty: numPoints\n+ * {Integer} The number of points to use in each graticule line. Higher\n+ * numbers result in a smoother curve for projected maps \n */\n+ numPoints: 50,\n \n /**\n- * Method: getParameters\n+ * APIProperty: targetSize\n+ * {Integer} The maximum size of the grid in pixels on the map\n */\n- getParameters: function(url) {\n- url = url || window.location.href;\n- var parameters = OpenLayers.Util.getParameters(url);\n+ targetSize: 200,\n \n- // If we have an anchor in the url use it to split the url\n- var index = url.indexOf('#');\n- if (index > 0) {\n- // create an url to parse on the getParameters\n- url = '?' + url.substring(index + 1, url.length);\n+ /**\n+ * APIProperty: layerName\n+ * {String} The name to be displayed in the layer switcher, default is set \n+ * by {<OpenLayers.Lang>}.\n+ */\n+ layerName: null,\n \n- OpenLayers.Util.extend(parameters,\n- OpenLayers.Util.getParameters(url));\n- }\n- return parameters;\n+ /**\n+ * APIProperty: labelled\n+ * {Boolean} Should the graticule lines be labelled?. default=true\n+ */\n+ labelled: true,\n+\n+ /**\n+ * APIProperty: labelFormat\n+ * {String} the format of the labels, default = 'dm'. See\n+ * <OpenLayers.Util.getFormattedLonLat> for other options.\n+ */\n+ labelFormat: 'dm',\n+\n+ /**\n+ * APIProperty: lineSymbolizer\n+ * {symbolizer} the symbolizer used to render lines\n+ */\n+ lineSymbolizer: {\n+ strokeColor: \"#333\",\n+ strokeWidth: 1,\n+ strokeOpacity: 0.5\n },\n \n /**\n- * Method: setMap\n- * Set the map property for the control. \n+ * APIProperty: labelSymbolizer\n+ * {symbolizer} the symbolizer used to render labels\n+ */\n+ labelSymbolizer: {},\n+\n+ /**\n+ * Property: gratLayer\n+ * {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on\n+ */\n+ gratLayer: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Graticule\n+ * Create a new graticule control to display a grid of latitude longitude\n+ * lines.\n * \n * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n-\n- //make sure we dont already have an arg parser attached\n- for (var i = 0, len = this.map.controls.length; i < len; i++) {\n- var control = this.map.controls[i];\n- if ((control != this) &&\n- (control.CLASS_NAME == \"OpenLayers.Control.ArgParser\")) {\n+ initialize: function(options) {\n+ options = options || {};\n+ options.layerName = options.layerName || OpenLayers.i18n(\"Graticule\");\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n \n- // If a second argparser is added to the map, then we \n- // override the displayProjection to be the one added to the\n- // map. \n- if (control.displayProjection != this.displayProjection) {\n- this.displayProjection = control.displayProjection;\n- }\n+ this.labelSymbolizer.stroke = false;\n+ this.labelSymbolizer.fill = false;\n+ this.labelSymbolizer.label = \"${label}\";\n+ this.labelSymbolizer.labelAlign = \"${labelAlign}\";\n+ this.labelSymbolizer.labelXOffset = \"${xOffset}\";\n+ this.labelSymbolizer.labelYOffset = \"${yOffset}\";\n+ },\n \n- break;\n- }\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.deactivate();\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.gratLayer) {\n+ this.gratLayer.destroy();\n+ this.gratLayer = null;\n }\n- if (i == this.map.controls.length) {\n-\n- var args = this.getParameters();\n- // Be careful to set layer first, to not trigger unnecessary layer loads\n- if (args.layers) {\n- this.layers = args.layers;\n+ },\n \n- // when we add a new layer, set its visibility \n- this.map.events.register('addlayer', this,\n- this.configureLayers);\n- this.configureLayers();\n- }\n- if (args.lat && args.lon) {\n- this.center = new OpenLayers.LonLat(parseFloat(args.lon),\n- parseFloat(args.lat));\n- if (args.zoom) {\n- this.zoom = parseFloat(args.zoom);\n- }\n+ /**\n+ * Method: draw\n+ *\n+ * initializes the graticule layer and does the initial update\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.gratLayer) {\n+ var gratStyle = new OpenLayers.Style({}, {\n+ rules: [new OpenLayers.Rule({\n+ 'symbolizer': {\n+ \"Point\": this.labelSymbolizer,\n+ \"Line\": this.lineSymbolizer\n+ }\n+ })]\n+ });\n+ this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {\n+ styleMap: new OpenLayers.StyleMap({\n+ 'default': gratStyle\n+ }),\n+ visibility: this.visible,\n+ displayInLayerSwitcher: this.displayInLayerSwitcher\n+ });\n+ }\n+ return this.div;\n+ },\n \n- // when we add a new baselayer to see when we can set the center\n- this.map.events.register('changebaselayer', this,\n- this.setCenter);\n- this.setCenter();\n- }\n+ /**\n+ * APIMethod: activate\n+ */\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.map.addLayer(this.gratLayer);\n+ this.map.events.register('moveend', this, this.update);\n+ this.update();\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n- /** \n- * Method: setCenter\n- * As soon as a baseLayer has been loaded, we center and zoom\n- * ...and remove the handler.\n+ /**\n+ * APIMethod: deactivate\n */\n- setCenter: function() {\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.map.events.unregister('moveend', this, this.update);\n+ this.map.removeLayer(this.gratLayer);\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+ /**\n+ * Method: update\n+ *\n+ * calculates the grid to be displayed and actually draws it\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ update: function() {\n+ //wait for the map to be initialized before proceeding\n+ var mapBounds = this.map.getExtent();\n+ if (!mapBounds) {\n+ return;\n+ }\n \n- if (this.map.baseLayer) {\n- //dont need to listen for this one anymore\n- this.map.events.unregister('changebaselayer', this,\n- this.setCenter);\n+ //clear out the old grid\n+ this.gratLayer.destroyFeatures();\n \n- if (this.displayProjection) {\n- this.center.transform(this.displayProjection,\n- this.map.getProjectionObject());\n- }\n+ //get the projection objects required\n+ var llProj = new OpenLayers.Projection(\"EPSG:4326\");\n+ var mapProj = this.map.getProjectionObject();\n+ var mapRes = this.map.getResolution();\n \n- this.map.setCenter(this.center, this.zoom);\n+ //if the map is in lon/lat, then the lines are straight and only one\n+ //point is required\n+ if (mapProj.proj && mapProj.proj.projName == \"longlat\") {\n+ this.numPoints = 1;\n }\n- },\n \n- /** \n- * Method: configureLayers\n- * As soon as all the layers are loaded, cycle through them and \n- * hide or show them. \n- */\n- configureLayers: function() {\n+ //get the map center in EPSG:4326\n+ var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y\n+ var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);\n+ OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);\n \n- if (this.layers.length == this.map.layers.length) {\n- this.map.events.unregister('addlayer', this, this.configureLayers);\n+ /* This block of code determines the lon/lat interval to use for the\n+ * grid by calculating the diagonal size of one grid cell at the map\n+ * center. Iterates through the intervals array until the diagonal\n+ * length is less than the targetSize option.\n+ */\n+ //find lat/lon interval that results in a grid of less than the target size\n+ var testSq = this.targetSize * mapRes;\n+ testSq *= testSq; //compare squares rather than doing a square root to save time\n+ var llInterval;\n+ for (var i = 0; i < this.intervals.length; ++i) {\n+ llInterval = this.intervals[i]; //could do this for both x and y??\n+ var delta = llInterval / 2;\n+ var p1 = mapCenterLL.offset({\n+ x: -delta,\n+ y: -delta\n+ }); //test coords in EPSG:4326 space\n+ var p2 = mapCenterLL.offset({\n+ x: delta,\n+ y: delta\n+ });\n+ OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection\n+ OpenLayers.Projection.transform(p2, llProj, mapProj);\n+ var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);\n+ if (distSq <= testSq) {\n+ break;\n+ }\n+ }\n+ //alert(llInterval);\n \n- for (var i = 0, len = this.layers.length; i < len; i++) {\n+ //round the LL center to an even number based on the interval\n+ mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval;\n+ mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval;\n+ //TODO adjust for minutses/seconds?\n \n- var layer = this.map.layers[i];\n- var c = this.layers.charAt(i);\n+ /* The following 2 blocks calculate the nodes of the grid along a \n+ * line of constant longitude (then latitiude) running through the\n+ * center of the map until it reaches the map edge. The calculation\n+ * goes from the center in both directions to the edge.\n+ */\n+ //get the central longitude line, increment the latitude\n+ var iter = 0;\n+ var centerLonPoints = [mapCenterLL.clone()];\n+ var newPoint = mapCenterLL.clone();\n+ var mapXY;\n+ do {\n+ newPoint = newPoint.offset({\n+ x: 0,\n+ y: llInterval\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLonPoints.unshift(newPoint);\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: 0,\n+ y: -llInterval\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLonPoints.push(newPoint);\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n \n- if (c == \"B\") {\n- this.map.setBaseLayer(layer);\n- } else if ((c == \"T\") || (c == \"F\")) {\n- layer.setVisibility(c == \"T\");\n+ //get the central latitude line, increment the longitude\n+ iter = 0;\n+ var centerLatPoints = [mapCenterLL.clone()];\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: -llInterval,\n+ y: 0\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLatPoints.unshift(newPoint);\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: llInterval,\n+ y: 0\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLatPoints.push(newPoint);\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n+\n+ //now generate a line for each node in the central lat and lon lines\n+ //first loop over constant longitude\n+ var lines = [];\n+ for (var i = 0; i < centerLatPoints.length; ++i) {\n+ var lon = centerLatPoints[i].x;\n+ var pointList = [];\n+ var labelPoint = null;\n+ var latEnd = Math.min(centerLonPoints[0].y, 90);\n+ var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);\n+ var latDelta = (latEnd - latStart) / this.numPoints;\n+ var lat = latStart;\n+ for (var j = 0; j <= this.numPoints; ++j) {\n+ var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n+ gridPoint.transform(llProj, mapProj);\n+ pointList.push(gridPoint);\n+ lat += latDelta;\n+ if (gridPoint.y >= mapBounds.bottom && !labelPoint) {\n+ labelPoint = gridPoint;\n+ }\n+ }\n+ if (this.labelled) {\n+ //keep track of when this grid line crosses the map bounds to set\n+ //the label position\n+ //labels along the bottom, add 10 pixel offset up into the map\n+ //TODO add option for labels on top\n+ var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom);\n+ var labelAttrs = {\n+ value: lon,\n+ label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, \"lon\", this.labelFormat) : \"\",\n+ labelAlign: \"cb\",\n+ xOffset: 0,\n+ yOffset: 2\n+ };\n+ this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));\n+ }\n+ var geom = new OpenLayers.Geometry.LineString(pointList);\n+ lines.push(new OpenLayers.Feature.Vector(geom));\n+ }\n+\n+ //now draw the lines of constant latitude\n+ for (var j = 0; j < centerLonPoints.length; ++j) {\n+ lat = centerLonPoints[j].y;\n+ if (lat < -90 || lat > 90) { //latitudes only valid between -90 and 90\n+ continue;\n+ }\n+ var pointList = [];\n+ var lonStart = centerLatPoints[0].x;\n+ var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;\n+ var lonDelta = (lonEnd - lonStart) / this.numPoints;\n+ var lon = lonStart;\n+ var labelPoint = null;\n+ for (var i = 0; i <= this.numPoints; ++i) {\n+ var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n+ gridPoint.transform(llProj, mapProj);\n+ pointList.push(gridPoint);\n+ lon += lonDelta;\n+ if (gridPoint.x < mapBounds.right) {\n+ labelPoint = gridPoint;\n }\n }\n+ if (this.labelled) {\n+ //keep track of when this grid line crosses the map bounds to set\n+ //the label position\n+ //labels along the right, 30 pixel offset left into the map\n+ //TODO add option for labels on left\n+ var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);\n+ var labelAttrs = {\n+ value: lat,\n+ label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, \"lat\", this.labelFormat) : \"\",\n+ labelAlign: \"rb\",\n+ xOffset: -2,\n+ yOffset: 2\n+ };\n+ this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));\n+ }\n+ var geom = new OpenLayers.Geometry.LineString(pointList);\n+ lines.push(new OpenLayers.Feature.Vector(geom));\n }\n+ this.gratLayer.addFeatures(lines);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ArgParser\"\n+ CLASS_NAME: \"OpenLayers.Control.Graticule\"\n });\n+\n /* ======================================================================\n- OpenLayers/Control/Geolocate.js\n+ OpenLayers/Control/MousePosition.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Control.Geolocate\n- * The Geolocate control wraps w3c geolocation API into control that can be\n- * bound to a map, and generate events on location update\n+ * Class: OpenLayers.Control.MousePosition\n+ * The MousePosition control displays geographic coordinates of the mouse\n+ * pointer, as it is moved about the map.\n *\n- * To use this control requires to load the proj4js library if the projection\n- * of the map is not EPSG:4326 or EPSG:900913.\n+ * You can use the <prefix>- or <suffix>-properties to provide more information\n+ * about the displayed coordinates to the user:\n+ *\n+ * (code)\n+ * var mousePositionCtrl = new OpenLayers.Control.MousePosition({\n+ * prefix: '<a target=\"_blank\" ' +\n+ * 'href=\"http://spatialreference.org/ref/epsg/4326/\">' +\n+ * 'EPSG:4326</a> coordinates: '\n+ * }\n+ * );\n+ * (end code)\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {\n \n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * locationupdated - Triggered when browser return a new position. Listeners will \n- * receive an object with a 'position' property which is the browser.geolocation.position\n- * native object, as well as a 'point' property which is the location transformed in the \n- * current map projection.\n- * locationfailed - Triggered when geolocation has failed\n- * locationuncapable - Triggered when control is activated on a browser\n- * which doesn't support geolocation\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n+ autoActivate: true,\n \n /**\n- * Property: geolocation\n- * {Object} The geolocation engine, as a property to be possibly mocked.\n- * This is set lazily to avoid a memory leak in IE9.\n+ * Property: element\n+ * {DOMElement}\n */\n- geolocation: null,\n+ element: null,\n \n /**\n- * Property: available\n- * {Boolean} The navigator.geolocation object is available.\n+ * APIProperty: prefix\n+ * {String} A string to be prepended to the current pointers coordinates\n+ * when it is rendered. Defaults to the empty string ''.\n */\n- available: ('geolocation' in navigator),\n+ prefix: '',\n \n /**\n- * APIProperty: bind\n- * {Boolean} If true, map center will be set on location update.\n+ * APIProperty: separator\n+ * {String} A string to be used to seperate the two coordinates from each\n+ * other. Defaults to the string ', ', which will result in a\n+ * rendered coordinate of e.g. '42.12, 21.22'.\n */\n- bind: true,\n+ separator: ', ',\n \n /**\n- * APIProperty: watch\n- * {Boolean} If true, position will be update regularly.\n+ * APIProperty: suffix\n+ * {String} A string to be appended to the current pointers coordinates\n+ * when it is rendered. Defaults to the empty string ''.\n */\n- watch: false,\n+ suffix: '',\n \n /**\n- * APIProperty: geolocationOptions\n- * {Object} Options to pass to the navigator's geolocation API. See\n- * <http://dev.w3.org/geo/api/spec-source.html>. No specific\n- * option is passed to the geolocation API by default.\n+ * APIProperty: numDigits\n+ * {Integer} The number of digits each coordinate shall have when being\n+ * rendered, Defaults to 5.\n */\n- geolocationOptions: null,\n+ numDigits: 5,\n \n /**\n- * Constructor: OpenLayers.Control.Geolocate\n- * Create a new control to deal with browser geolocation API\n+ * APIProperty: granularity\n+ * {Integer}\n+ */\n+ granularity: 10,\n+\n+ /**\n+ * APIProperty: emptyString\n+ * {String} Set this to some value to set when the mouse is outside the\n+ * map.\n+ */\n+ emptyString: null,\n+\n+ /**\n+ * Property: lastXy\n+ * {<OpenLayers.Pixel>}\n+ */\n+ lastXy: null,\n+\n+ /**\n+ * APIProperty: displayProjection\n+ * {<OpenLayers.Projection>} The projection in which the mouse position is\n+ * displayed.\n+ */\n+ displayProjection: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.MousePosition\n *\n+ * Parameters:\n+ * options - {Object} Options for control.\n */\n \n /**\n * Method: destroy\n */\n destroy: function() {\n this.deactivate();\n OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: activate\n- * Activates the control.\n- *\n- * Returns:\n- * {Boolean} The control was effectively activated.\n+ * APIMethod: activate\n */\n activate: function() {\n- if (this.available && !this.geolocation) {\n- // set lazily to avoid IE9 memory leak\n- this.geolocation = navigator.geolocation;\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.map.events.register('mousemove', this, this.redraw);\n+ this.map.events.register('mouseout', this, this.reset);\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n }\n- if (!this.geolocation) {\n- this.events.triggerEvent(\"locationuncapable\");\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ */\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.map.events.unregister('mousemove', this, this.redraw);\n+ this.map.events.unregister('mouseout', this, this.reset);\n+ this.element.innerHTML = \"\";\n+ return true;\n+ } else {\n return false;\n }\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- if (this.watch) {\n- this.watchId = this.geolocation.watchPosition(\n- OpenLayers.Function.bind(this.geolocate, this),\n- OpenLayers.Function.bind(this.failure, this),\n- this.geolocationOptions\n- );\n- } else {\n- this.getCurrentLocation();\n+ },\n+\n+ /**\n+ * Method: draw\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+\n+ if (!this.element) {\n+ this.div.left = \"\";\n+ this.div.top = \"\";\n+ this.element = this.div;\n+ }\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: redraw\n+ */\n+ redraw: function(evt) {\n+\n+ var lonLat;\n+\n+ if (evt == null) {\n+ this.reset();\n+ return;\n+ } else {\n+ if (this.lastXy == null ||\n+ Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||\n+ Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) {\n+ this.lastXy = evt.xy;\n+ return;\n }\n- return true;\n+\n+ lonLat = this.map.getLonLatFromPixel(evt.xy);\n+ if (!lonLat) {\n+ // map has not yet been properly initialized\n+ return;\n+ }\n+ if (this.displayProjection) {\n+ lonLat.transform(this.map.getProjectionObject(),\n+ this.displayProjection);\n+ }\n+ this.lastXy = evt.xy;\n+\n+ }\n+\n+ var newHtml = this.formatOutput(lonLat);\n+\n+ if (newHtml != this.element.innerHTML) {\n+ this.element.innerHTML = newHtml;\n }\n- return false;\n },\n \n /**\n- * Method: deactivate\n- * Deactivates the control.\n+ * Method: reset\n+ */\n+ reset: function(evt) {\n+ if (this.emptyString != null) {\n+ this.element.innerHTML = this.emptyString;\n+ }\n+ },\n+\n+ /**\n+ * Method: formatOutput\n+ * Override to provide custom display output\n *\n- * Returns:\n- * {Boolean} The control was effectively deactivated.\n+ * Parameters:\n+ * lonLat - {<OpenLayers.LonLat>} Location to display\n */\n- deactivate: function() {\n- if (this.active && this.watchId !== null) {\n- this.geolocation.clearWatch(this.watchId);\n+ formatOutput: function(lonLat) {\n+ var digits = parseInt(this.numDigits);\n+ var newHtml =\n+ this.prefix +\n+ lonLat.lon.toFixed(digits) +\n+ this.separator +\n+ lonLat.lat.toFixed(digits) +\n+ this.suffix;\n+ return newHtml;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.MousePosition\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/PanZoom.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.PanZoom\n+ * The PanZoom is a visible control, composed of a\n+ * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By\n+ * default it is drawn in the upper left corner of the map.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * APIProperty: slideFactor\n+ * {Integer} Number of pixels by which we'll pan the map in any direction \n+ * on clicking the arrow buttons. If you want to pan by some ratio\n+ * of the map dimensions, use <slideRatio> instead.\n+ */\n+ slideFactor: 50,\n+\n+ /** \n+ * APIProperty: slideRatio\n+ * {Number} The fraction of map width/height by which we'll pan the map \n+ * on clicking the arrow buttons. Default is null. If set, will\n+ * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up\n+ * button will pan up half the map height. \n+ */\n+ slideRatio: null,\n+\n+ /** \n+ * Property: buttons\n+ * {Array(DOMElement)} Array of Button Divs \n+ */\n+ buttons: null,\n+\n+ /** \n+ * Property: position\n+ * {<OpenLayers.Pixel>} \n+ */\n+ position: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.PanZoom\n+ * \n+ * Parameters:\n+ * options - {Object}\n+ */\n+ initialize: function(options) {\n+ this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,\n+ OpenLayers.Control.PanZoom.Y);\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n }\n- return OpenLayers.Control.prototype.deactivate.apply(\n- this, arguments\n- );\n+ this.removeButtons();\n+ this.buttons = null;\n+ this.position = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /** \n+ * Method: setMap\n+ *\n+ * Properties:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n },\n \n /**\n- * Method: geolocate\n- * Activates the control.\n+ * Method: draw\n *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>} \n+ * \n+ * Returns:\n+ * {DOMElement} A reference to the container div for the PanZoom control.\n */\n- geolocate: function(position) {\n- var center = new OpenLayers.LonLat(\n- position.coords.longitude,\n- position.coords.latitude\n- ).transform(\n- new OpenLayers.Projection(\"EPSG:4326\"),\n- this.map.getProjectionObject()\n- );\n- if (this.bind) {\n- this.map.setCenter(center);\n+ draw: function(px) {\n+ // initialize our internal div\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ px = this.position;\n+\n+ // place the controls\n+ this.buttons = [];\n+\n+ var sz = {\n+ w: 18,\n+ h: 18\n+ };\n+ var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);\n+\n+ this._addButton(\"panup\", \"north-mini.png\", centered, sz);\n+ px.y = centered.y + sz.h;\n+ this._addButton(\"panleft\", \"west-mini.png\", px, sz);\n+ this._addButton(\"panright\", \"east-mini.png\", px.add(sz.w, 0), sz);\n+ this._addButton(\"pandown\", \"south-mini.png\",\n+ centered.add(0, sz.h * 2), sz);\n+ this._addButton(\"zoomin\", \"zoom-plus-mini.png\",\n+ centered.add(0, sz.h * 3 + 5), sz);\n+ this._addButton(\"zoomworld\", \"zoom-world-mini.png\",\n+ centered.add(0, sz.h * 4 + 5), sz);\n+ this._addButton(\"zoomout\", \"zoom-minus-mini.png\",\n+ centered.add(0, sz.h * 5 + 5), sz);\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: _addButton\n+ * \n+ * Parameters:\n+ * id - {String} \n+ * img - {String} \n+ * xy - {<OpenLayers.Pixel>} \n+ * sz - {<OpenLayers.Size>} \n+ * \n+ * Returns:\n+ * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the\n+ * image of the button, and has all the proper event handlers set.\n+ */\n+ _addButton: function(id, img, xy, sz) {\n+ var imgLocation = OpenLayers.Util.getImageLocation(img);\n+ var btn = OpenLayers.Util.createAlphaImageDiv(\n+ this.id + \"_\" + id,\n+ xy, sz, imgLocation, \"absolute\");\n+ btn.style.cursor = \"pointer\";\n+ //we want to add the outer div\n+ this.div.appendChild(btn);\n+ btn.action = id;\n+ btn.className = \"olButton\";\n+\n+ //we want to remember/reference the outer div\n+ this.buttons.push(btn);\n+ return btn;\n+ },\n+\n+ /**\n+ * Method: _removeButton\n+ * \n+ * Parameters:\n+ * btn - {Object}\n+ */\n+ _removeButton: function(btn) {\n+ this.div.removeChild(btn);\n+ OpenLayers.Util.removeItem(this.buttons, btn);\n+ },\n+\n+ /**\n+ * Method: removeButtons\n+ */\n+ removeButtons: function() {\n+ for (var i = this.buttons.length - 1; i >= 0; --i) {\n+ this._removeButton(this.buttons[i]);\n }\n- this.events.triggerEvent(\"locationupdated\", {\n- position: position,\n- point: new OpenLayers.Geometry.Point(\n- center.lon, center.lat\n- )\n- });\n },\n \n /**\n- * APIMethod: getCurrentLocation\n+ * Method: onButtonClick\n *\n- * Returns:\n- * {Boolean} Returns true if a event will be fired (successfull\n- * registration)\n+ * Parameters:\n+ * evt - {Event}\n */\n- getCurrentLocation: function() {\n- if (!this.active || this.watch) {\n- return false;\n+ onButtonClick: function(evt) {\n+ var btn = evt.buttonElement;\n+ switch (btn.action) {\n+ case \"panup\":\n+ this.map.pan(0, -this.getSlideFactor(\"h\"));\n+ break;\n+ case \"pandown\":\n+ this.map.pan(0, this.getSlideFactor(\"h\"));\n+ break;\n+ case \"panleft\":\n+ this.map.pan(-this.getSlideFactor(\"w\"), 0);\n+ break;\n+ case \"panright\":\n+ this.map.pan(this.getSlideFactor(\"w\"), 0);\n+ break;\n+ case \"zoomin\":\n+ this.map.zoomIn();\n+ break;\n+ case \"zoomout\":\n+ this.map.zoomOut();\n+ break;\n+ case \"zoomworld\":\n+ this.map.zoomToMaxExtent();\n+ break;\n }\n- this.geolocation.getCurrentPosition(\n- OpenLayers.Function.bind(this.geolocate, this),\n- OpenLayers.Function.bind(this.failure, this),\n- this.geolocationOptions\n- );\n- return true;\n },\n \n /**\n- * Method: failure\n- * method called on browser's geolocation failure\n+ * Method: getSlideFactor\n+ *\n+ * Parameters:\n+ * dim - {String} \"w\" or \"h\" (for width or height).\n *\n+ * Returns:\n+ * {Number} The slide factor for panning in the requested direction.\n */\n- failure: function(error) {\n- this.events.triggerEvent(\"locationfailed\", {\n- error: error\n- });\n+ getSlideFactor: function(dim) {\n+ return this.slideRatio ?\n+ this.map.getSize()[dim] * this.slideRatio :\n+ this.slideFactor;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n+ CLASS_NAME: \"OpenLayers.Control.PanZoom\"\n });\n+\n+/**\n+ * Constant: X\n+ * {Integer}\n+ */\n+OpenLayers.Control.PanZoom.X = 4;\n+\n+/**\n+ * Constant: Y\n+ * {Integer}\n+ */\n+OpenLayers.Control.PanZoom.Y = 4;\n /* ======================================================================\n- OpenLayers/Control/CacheWrite.js\n+ OpenLayers/Control/PanZoomBar.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Request.js\n- * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Control/PanZoom.js\n */\n \n /**\n- * Class: OpenLayers.Control.CacheWrite\n- * A control for caching image tiles in the browser's local storage. The\n- * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached\n- * tile images.\n- *\n- * Note: Before using this control on any layer that is not your own, make sure\n- * that the terms of service of the tile provider allow local storage of tiles.\n+ * Class: OpenLayers.Control.PanZoomBar\n+ * The PanZoomBar is a visible control composed of a\n+ * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>. \n+ * By default it is displayed in the upper left corner of the map as 4\n+ * directional arrows above a vertical slider.\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Control.PanZoom>\n */\n-OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {\n \n /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * To register events in the constructor, configure <eventListeners>.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * cachefull - Triggered when the cache is full. Listeners receive an\n- * object with a tile property as first argument. The tile references\n- * the tile that couldn't be cached.\n+ * APIProperty: zoomStopWidth\n+ */\n+ zoomStopWidth: 18,\n+\n+ /** \n+ * APIProperty: zoomStopHeight\n+ */\n+ zoomStopHeight: 11,\n+\n+ /** \n+ * Property: slider\n+ */\n+ slider: null,\n+\n+ /** \n+ * Property: sliderEvents\n+ * {<OpenLayers.Events>}\n+ */\n+ sliderEvents: null,\n+\n+ /** \n+ * Property: zoombarDiv\n+ * {DOMElement}\n+ */\n+ zoombarDiv: null,\n+\n+ /** \n+ * APIProperty: zoomWorldIcon\n+ * {Boolean}\n */\n+ zoomWorldIcon: false,\n \n /**\n- * APIProperty: eventListeners\n- * {Object} Object with event listeners, keyed by event name. An optional\n- * scope property defines the scope that listeners will be executed in.\n+ * APIProperty: panIcons\n+ * {Boolean} Set this property to false not to display the pan icons. If\n+ * false the zoom world icon is placed under the zoom bar. Defaults to\n+ * true.\n */\n+ panIcons: true,\n \n /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching\n- * will be enabled for these layers only, otherwise for all cacheable\n- * layers.\n+ * APIProperty: forceFixedZoomLevel\n+ * {Boolean} Force a fixed zoom level even though the map has \n+ * fractionalZoom\n */\n- layers: null,\n+ forceFixedZoomLevel: false,\n \n /**\n- * APIProperty: imageFormat\n- * {String} The image format used for caching. The default is \"image/png\".\n- * Supported formats depend on the user agent. If an unsupported\n- * <imageFormat> is provided, \"image/png\" will be used. For aerial\n- * imagery, \"image/jpeg\" is recommended.\n+ * Property: mouseDragStart\n+ * {<OpenLayers.Pixel>}\n */\n- imageFormat: \"image/png\",\n+ mouseDragStart: null,\n \n /**\n- * Property: quotaRegEx\n- * {RegExp}\n+ * Property: deltaY\n+ * {Number} The cumulative vertical pixel offset during a zoom bar drag.\n */\n- quotaRegEx: (/quota/i),\n+ deltaY: null,\n \n /**\n- * Constructor: OpenLayers.Control.CacheWrite\n- *\n- * Parameters:\n- * options - {Object} Object with API properties for this control.\n+ * Property: zoomStart\n+ * {<OpenLayers.Pixel>}\n */\n+ zoomStart: null,\n \n- /** \n+ /**\n+ * Constructor: OpenLayers.Control.PanZoomBar\n+ */\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+\n+ this._removeZoomBar();\n+\n+ this.map.events.un({\n+ \"changebaselayer\": this.redraw,\n+ \"updatesize\": this.redraw,\n+ scope: this\n+ });\n+\n+ OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);\n+\n+ delete this.mouseDragStart;\n+ delete this.zoomStart;\n+ },\n+\n+ /**\n * Method: setMap\n- * Set the map property for the control. \n * \n * Parameters:\n * map - {<OpenLayers.Map>} \n */\n setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- var i, layers = this.layers || map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.addLayer({\n- layer: layers[i]\n- });\n- }\n- if (!this.layers) {\n- map.events.on({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n- scope: this\n- });\n+ OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);\n+ this.map.events.on({\n+ \"changebaselayer\": this.redraw,\n+ \"updatesize\": this.redraw,\n+ scope: this\n+ });\n+ },\n+\n+ /** \n+ * Method: redraw\n+ * clear the div and start over.\n+ */\n+ redraw: function() {\n+ if (this.div != null) {\n+ this.removeButtons();\n+ this._removeZoomBar();\n }\n+ this.draw();\n },\n \n /**\n- * Method: addLayer\n- * Adds a layer to the control. Once added, tiles requested for this layer\n- * will be cached.\n+ * Method: draw \n *\n * Parameters:\n- * evt - {Object} Object with a layer property referencing an\n- * <OpenLayers.Layer> instance\n+ * px - {<OpenLayers.Pixel>} \n */\n- addLayer: function(evt) {\n- evt.layer.events.on({\n- tileloadstart: this.makeSameOrigin,\n- tileloaded: this.onTileLoaded,\n- scope: this\n+ draw: function(px) {\n+ // initialize our internal div\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ px = this.position.clone();\n+\n+ // place the controls\n+ this.buttons = [];\n+\n+ var sz = {\n+ w: 18,\n+ h: 18\n+ };\n+ if (this.panIcons) {\n+ var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);\n+ var wposition = sz.w;\n+\n+ if (this.zoomWorldIcon) {\n+ centered = new OpenLayers.Pixel(px.x + sz.w, px.y);\n+ }\n+\n+ this._addButton(\"panup\", \"north-mini.png\", centered, sz);\n+ px.y = centered.y + sz.h;\n+ this._addButton(\"panleft\", \"west-mini.png\", px, sz);\n+ if (this.zoomWorldIcon) {\n+ this._addButton(\"zoomworld\", \"zoom-world-mini.png\", px.add(sz.w, 0), sz);\n+\n+ wposition *= 2;\n+ }\n+ this._addButton(\"panright\", \"east-mini.png\", px.add(wposition, 0), sz);\n+ this._addButton(\"pandown\", \"south-mini.png\", centered.add(0, sz.h * 2), sz);\n+ this._addButton(\"zoomin\", \"zoom-plus-mini.png\", centered.add(0, sz.h * 3 + 5), sz);\n+ centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5));\n+ this._addButton(\"zoomout\", \"zoom-minus-mini.png\", centered, sz);\n+ } else {\n+ this._addButton(\"zoomin\", \"zoom-plus-mini.png\", px, sz);\n+ centered = this._addZoomBar(px.add(0, sz.h));\n+ this._addButton(\"zoomout\", \"zoom-minus-mini.png\", centered, sz);\n+ if (this.zoomWorldIcon) {\n+ centered = centered.add(0, sz.h + 3);\n+ this._addButton(\"zoomworld\", \"zoom-world-mini.png\", centered, sz);\n+ }\n+ }\n+ return this.div;\n+ },\n+\n+ /** \n+ * Method: _addZoomBar\n+ * \n+ * Parameters:\n+ * centered - {<OpenLayers.Pixel>} where zoombar drawing is to start.\n+ */\n+ _addZoomBar: function(centered) {\n+ var imgLocation = OpenLayers.Util.getImageLocation(\"slider.png\");\n+ var id = this.id + \"_\" + this.map.id;\n+ var minZoom = this.map.getMinZoom();\n+ var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();\n+ var slider = OpenLayers.Util.createAlphaImageDiv(id,\n+ centered.add(-1, zoomsToEnd * this.zoomStopHeight), {\n+ w: 20,\n+ h: 9\n+ },\n+ imgLocation,\n+ \"absolute\");\n+ slider.style.cursor = \"move\";\n+ this.slider = slider;\n+\n+ this.sliderEvents = new OpenLayers.Events(this, slider, null, true, {\n+ includeXY: true\n+ });\n+ this.sliderEvents.on({\n+ \"touchstart\": this.zoomBarDown,\n+ \"touchmove\": this.zoomBarDrag,\n+ \"touchend\": this.zoomBarUp,\n+ \"mousedown\": this.zoomBarDown,\n+ \"mousemove\": this.zoomBarDrag,\n+ \"mouseup\": this.zoomBarUp\n });\n+\n+ var sz = {\n+ w: this.zoomStopWidth,\n+ h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)\n+ };\n+ var imgLocation = OpenLayers.Util.getImageLocation(\"zoombar.png\");\n+ var div = null;\n+\n+ if (OpenLayers.Util.alphaHack()) {\n+ var id = this.id + \"_\" + this.map.id;\n+ div = OpenLayers.Util.createAlphaImageDiv(id, centered, {\n+ w: sz.w,\n+ h: this.zoomStopHeight\n+ },\n+ imgLocation,\n+ \"absolute\", null, \"crop\");\n+ div.style.height = sz.h + \"px\";\n+ } else {\n+ div = OpenLayers.Util.createDiv(\n+ 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,\n+ centered,\n+ sz,\n+ imgLocation);\n+ }\n+ div.style.cursor = \"pointer\";\n+ div.className = \"olButton\";\n+ this.zoombarDiv = div;\n+\n+ this.div.appendChild(div);\n+\n+ this.startTop = parseInt(div.style.top);\n+ this.div.appendChild(slider);\n+\n+ this.map.events.register(\"zoomend\", this, this.moveZoomBar);\n+\n+ centered = centered.add(0,\n+ this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom));\n+ return centered;\n },\n \n /**\n- * Method: removeLayer\n- * Removes a layer from the control. Once removed, tiles requested for this\n- * layer will no longer be cached.\n- *\n- * Parameters:\n- * evt - {Object} Object with a layer property referencing an\n- * <OpenLayers.Layer> instance\n+ * Method: _removeZoomBar\n */\n- removeLayer: function(evt) {\n- evt.layer.events.un({\n- tileloadstart: this.makeSameOrigin,\n- tileloaded: this.onTileLoaded,\n- scope: this\n+ _removeZoomBar: function() {\n+ this.sliderEvents.un({\n+ \"touchstart\": this.zoomBarDown,\n+ \"touchmove\": this.zoomBarDrag,\n+ \"touchend\": this.zoomBarUp,\n+ \"mousedown\": this.zoomBarDown,\n+ \"mousemove\": this.zoomBarDrag,\n+ \"mouseup\": this.zoomBarUp\n });\n+ this.sliderEvents.destroy();\n+\n+ this.div.removeChild(this.zoombarDiv);\n+ this.zoombarDiv = null;\n+ this.div.removeChild(this.slider);\n+ this.slider = null;\n+\n+ this.map.events.unregister(\"zoomend\", this, this.moveZoomBar);\n },\n \n /**\n- * Method: makeSameOrigin\n- * If the tile does not have CORS image loading enabled and is from a\n- * different origin, use OpenLayers.ProxyHost to make it a same origin url.\n+ * Method: onButtonClick\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>}\n+ * evt - {Event}\n */\n- makeSameOrigin: function(evt) {\n- if (this.active) {\n- var tile = evt.tile;\n- if (tile instanceof OpenLayers.Tile.Image &&\n- !tile.crossOriginKeyword &&\n- tile.url.substr(0, 5) !== \"data:\") {\n- var sameOriginUrl = OpenLayers.Request.makeSameOrigin(\n- tile.url, OpenLayers.ProxyHost\n- );\n- OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url;\n- tile.url = sameOriginUrl;\n+ onButtonClick: function(evt) {\n+ OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments);\n+ if (evt.buttonElement === this.zoombarDiv) {\n+ var levels = evt.buttonXY.y / this.zoomStopHeight;\n+ if (this.forceFixedZoomLevel || !this.map.fractionalZoom) {\n+ levels = Math.floor(levels);\n }\n+ var zoom = (this.map.getNumZoomLevels() - 1) - levels;\n+ zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);\n+ this.map.zoomTo(zoom);\n }\n },\n \n /**\n- * Method: onTileLoaded\n- * Decides whether a tile can be cached and calls the cache method.\n+ * Method: passEventToSlider\n+ * This function is used to pass events that happen on the div, or the map,\n+ * through to the slider, which then does its moving thing.\n *\n * Parameters:\n- * evt - {Event}\n+ * evt - {<OpenLayers.Event>} \n */\n- onTileLoaded: function(evt) {\n- if (this.active && !evt.aborted &&\n- evt.tile instanceof OpenLayers.Tile.Image &&\n- evt.tile.url.substr(0, 5) !== 'data:') {\n- this.cache({\n- tile: evt.tile\n- });\n- delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url];\n+ passEventToSlider: function(evt) {\n+ this.sliderEvents.handleBrowserEvent(evt);\n+ },\n+\n+ /*\n+ * Method: zoomBarDown\n+ * event listener for clicks on the slider\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n+ */\n+ zoomBarDown: function(evt) {\n+ if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {\n+ return;\n }\n+ this.map.events.on({\n+ \"touchmove\": this.passEventToSlider,\n+ \"mousemove\": this.passEventToSlider,\n+ \"mouseup\": this.passEventToSlider,\n+ scope: this\n+ });\n+ this.mouseDragStart = evt.xy.clone();\n+ this.zoomStart = evt.xy.clone();\n+ this.div.style.cursor = \"move\";\n+ // reset the div offsets just in case the div moved\n+ this.zoombarDiv.offsets = null;\n+ OpenLayers.Event.stop(evt);\n },\n \n- /**\n- * Method: cache\n- * Adds a tile to the cache. When the cache is full, the \"cachefull\" event\n- * is triggered.\n+ /*\n+ * Method: zoomBarDrag\n+ * This is what happens when a click has occurred, and the client is\n+ * dragging. Here we must ensure that the slider doesn't go beyond the\n+ * bottom/top of the zoombar div, as well as moving the slider to its new\n+ * visual location\n *\n * Parameters:\n- * obj - {Object} Object with a tile property, tile being the\n- * <OpenLayers.Tile.Image> with the data to add to the cache\n+ * evt - {<OpenLayers.Event>} \n */\n- cache: function(obj) {\n- if (window.localStorage) {\n- var tile = obj.tile;\n- try {\n- var canvasContext = tile.getCanvasContext();\n- if (canvasContext) {\n- var urlMap = OpenLayers.Control.CacheWrite.urlMap;\n- var url = urlMap[tile.url] || tile.url;\n- window.localStorage.setItem(\n- \"olCache_\" + url,\n- canvasContext.canvas.toDataURL(this.imageFormat)\n- );\n- }\n- } catch (e) {\n- // local storage full or CORS violation\n- var reason = e.name || e.message;\n- if (reason && this.quotaRegEx.test(reason)) {\n- this.events.triggerEvent(\"cachefull\", {\n- tile: tile\n- });\n- } else {\n- OpenLayers.Console.error(e.toString());\n- }\n+ zoomBarDrag: function(evt) {\n+ if (this.mouseDragStart != null) {\n+ var deltaY = this.mouseDragStart.y - evt.xy.y;\n+ var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);\n+ if ((evt.clientY - offsets[1]) > 0 &&\n+ (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {\n+ var newTop = parseInt(this.slider.style.top) - deltaY;\n+ this.slider.style.top = newTop + \"px\";\n+ this.mouseDragStart = evt.xy.clone();\n }\n+ // set cumulative displacement\n+ this.deltaY = this.zoomStart.y - evt.xy.y;\n+ OpenLayers.Event.stop(evt);\n }\n },\n \n- /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ /*\n+ * Method: zoomBarUp\n+ * Perform cleanup when a mouseup event is received -- discover new zoom\n+ * level and switch to it.\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n */\n- destroy: function() {\n- if (this.layers || this.map) {\n- var i, layers = this.layers || this.map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.removeLayer({\n- layer: layers[i]\n- });\n- }\n+ zoomBarUp: function(evt) {\n+ if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== \"touchend\") {\n+ return;\n }\n- if (this.map) {\n+ if (this.mouseDragStart) {\n+ this.div.style.cursor = \"\";\n this.map.events.un({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n+ \"touchmove\": this.passEventToSlider,\n+ \"mouseup\": this.passEventToSlider,\n+ \"mousemove\": this.passEventToSlider,\n scope: this\n });\n+ var zoomLevel = this.map.zoom;\n+ if (!this.forceFixedZoomLevel && this.map.fractionalZoom) {\n+ zoomLevel += this.deltaY / this.zoomStopHeight;\n+ zoomLevel = Math.min(Math.max(zoomLevel, 0),\n+ this.map.getNumZoomLevels() - 1);\n+ } else {\n+ zoomLevel += this.deltaY / this.zoomStopHeight;\n+ zoomLevel = Math.max(Math.round(zoomLevel), 0);\n+ }\n+ this.map.zoomTo(zoomLevel);\n+ this.mouseDragStart = null;\n+ this.zoomStart = null;\n+ this.deltaY = 0;\n+ OpenLayers.Event.stop(evt);\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.CacheWrite\"\n-});\n-\n-/**\n- * APIFunction: OpenLayers.Control.CacheWrite.clearCache\n- * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.\n- */\n-OpenLayers.Control.CacheWrite.clearCache = function() {\n- if (!window.localStorage) {\n- return;\n- }\n- var i, key;\n- for (i = window.localStorage.length - 1; i >= 0; --i) {\n- key = window.localStorage.key(i);\n- if (key.substr(0, 8) === \"olCache_\") {\n- window.localStorage.removeItem(key);\n- }\n- }\n-};\n-\n-/**\n- * Property: OpenLayers.Control.CacheWrite.urlMap\n- * {Object} Mapping of same origin urls to cache url keys. Entries will be\n- * deleted as soon as a tile was cached.\n- */\n-OpenLayers.Control.CacheWrite.urlMap = {};\n-\n-\n-/* ======================================================================\n- OpenLayers/Control/Button.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Control.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control.Button \n- * The Button control is a very simple push-button, for use with \n- * <OpenLayers.Control.Panel>.\n- * When clicked, the function trigger() is executed.\n- * \n- * Inherits from:\n- * - <OpenLayers.Control>\n- *\n- * Use:\n- * (code)\n- * var button = new OpenLayers.Control.Button({\n- * displayClass: \"MyButton\", trigger: myFunction\n- * });\n- * panel.addControls([button]);\n- * (end)\n- * \n- * Will create a button with CSS class MyButtonItemInactive, that\n- * will call the function MyFunction() when clicked.\n- */\n-OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {\n- /**\n- * Property: type\n- * {Integer} OpenLayers.Control.TYPE_BUTTON.\n- */\n- type: OpenLayers.Control.TYPE_BUTTON,\n-\n- /**\n- * Method: trigger\n- * Called by a control panel when the button is clicked.\n+ /*\n+ * Method: moveZoomBar\n+ * Change the location of the slider to match the current zoom level.\n */\n- trigger: function() {},\n+ moveZoomBar: function() {\n+ var newTop =\n+ ((this.map.getNumZoomLevels() - 1) - this.map.getZoom()) *\n+ this.zoomStopHeight + this.startTop + 1;\n+ this.slider.style.top = newTop + \"px\";\n+ },\n \n- CLASS_NAME: \"OpenLayers.Control.Button\"\n+ CLASS_NAME: \"OpenLayers.Control.PanZoomBar\"\n });\n /* ======================================================================\n OpenLayers/Control/Pan.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -69510,175 +64904,1461 @@\n new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)\n ]);\n },\n \n CLASS_NAME: \"OpenLayers.Control.PanPanel\"\n });\n /* ======================================================================\n- OpenLayers/Control/ZoomIn.js\n+ OpenLayers/Control/CacheRead.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control/Button.js\n+ * @requires OpenLayers/Control.js\n */\n \n /**\n- * Class: OpenLayers.Control.ZoomIn\n- * The ZoomIn control is a button to increase the zoom level of a map.\n+ * Class: OpenLayers.Control.CacheRead\n+ * A control for using image tiles cached with <OpenLayers.Control.CacheWrite>\n+ * from the browser's local storage.\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {\n+OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Method: trigger\n+ * APIProperty: fetchEvent\n+ * {String} The layer event to listen to for replacing remote resource tile\n+ * URLs with cached data URIs. Supported values are \"tileerror\" (try\n+ * remote first, fall back to cached) and \"tileloadstart\" (try cache\n+ * first, fall back to remote). Default is \"tileloadstart\".\n+ *\n+ * Note that \"tileerror\" will not work for CORS enabled images (see\n+ * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers\n+ * configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in\n+ * <OpenLayers.Layer.Grid.tileOptions>.\n */\n- trigger: function() {\n+ fetchEvent: \"tileloadstart\",\n+\n+ /**\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these\n+ * layers will receive tiles from the cache.\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.CacheRead\n+ *\n+ * Parameters:\n+ * options - {Object} Object with API properties for this control\n+ */\n+\n+ /** \n+ * Method: setMap\n+ * Set the map property for the control. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ var i, layers = this.layers || map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.addLayer({\n+ layer: layers[i]\n+ });\n+ }\n+ if (!this.layers) {\n+ map.events.on({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: addLayer\n+ * Adds a layer to the control. Once added, tiles requested for this layer\n+ * will be cached.\n+ *\n+ * Parameters:\n+ * evt - {Object} Object with a layer property referencing an\n+ * <OpenLayers.Layer> instance\n+ */\n+ addLayer: function(evt) {\n+ evt.layer.events.register(this.fetchEvent, this, this.fetch);\n+ },\n+\n+ /**\n+ * Method: removeLayer\n+ * Removes a layer from the control. Once removed, tiles requested for this\n+ * layer will no longer be cached.\n+ *\n+ * Parameters:\n+ * evt - {Object} Object with a layer property referencing an\n+ * <OpenLayers.Layer> instance\n+ */\n+ removeLayer: function(evt) {\n+ evt.layer.events.unregister(this.fetchEvent, this, this.fetch);\n+ },\n+\n+ /**\n+ * Method: fetch\n+ * Listener to the <fetchEvent> event. Replaces a tile's url with a data\n+ * URI from the cache.\n+ *\n+ * Parameters:\n+ * evt - {Object} Event object with a tile property.\n+ */\n+ fetch: function(evt) {\n+ if (this.active && window.localStorage &&\n+ evt.tile instanceof OpenLayers.Tile.Image) {\n+ var tile = evt.tile,\n+ url = tile.url;\n+ // deal with modified tile urls when both CacheWrite and CacheRead\n+ // are active\n+ if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost &&\n+ url.indexOf(OpenLayers.ProxyHost) === 0) {\n+ url = OpenLayers.Control.CacheWrite.urlMap[url];\n+ }\n+ var dataURI = window.localStorage.getItem(\"olCache_\" + url);\n+ if (dataURI) {\n+ tile.url = dataURI;\n+ if (evt.type === \"tileerror\") {\n+ tile.setImgSrc(dataURI);\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n+ */\n+ destroy: function() {\n+ if (this.layers || this.map) {\n+ var i, layers = this.layers || this.map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.removeLayer({\n+ layer: layers[i]\n+ });\n+ }\n+ }\n if (this.map) {\n- this.map.zoomIn();\n+ this.map.events.un({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ });\n }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomIn\"\n+ CLASS_NAME: \"OpenLayers.Control.CacheRead\"\n });\n /* ======================================================================\n- OpenLayers/Control/ZoomOut.js\n+ OpenLayers/Control/Scale.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control/Button.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Lang.js\n */\n \n /**\n- * Class: OpenLayers.Control.ZoomOut\n- * The ZoomOut control is a button to decrease the zoom level of a map.\n+ * Class: OpenLayers.Control.Scale\n+ * The Scale control displays the current map scale as a ratio (e.g. Scale = \n+ * 1:1M). By default it is displayed in the lower right corner of the map.\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {\n+OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Method: trigger\n+ * Property: element\n+ * {DOMElement}\n */\n- trigger: function() {\n- if (this.map) {\n- this.map.zoomOut();\n+ element: null,\n+\n+ /**\n+ * APIProperty: geodesic\n+ * {Boolean} Use geodesic measurement. Default is false. The recommended\n+ * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to\n+ * true, the scale will be calculated based on the horizontal size of the\n+ * pixel in the center of the map viewport.\n+ */\n+ geodesic: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Scale\n+ * \n+ * Parameters:\n+ * element - {DOMElement} \n+ * options - {Object} \n+ */\n+ initialize: function(element, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.element = OpenLayers.Util.getElement(element);\n+ },\n+\n+ /**\n+ * Method: draw\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.element) {\n+ this.element = document.createElement(\"div\");\n+ this.div.appendChild(this.element);\n }\n+ this.map.events.register('moveend', this, this.updateScale);\n+ this.updateScale();\n+ return this.div;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomOut\"\n+ /**\n+ * Method: updateScale\n+ */\n+ updateScale: function() {\n+ var scale;\n+ if (this.geodesic === true) {\n+ var units = this.map.getUnits();\n+ if (!units) {\n+ return;\n+ }\n+ var inches = OpenLayers.INCHES_PER_UNIT;\n+ scale = (this.map.getGeodesicPixelSize().w || 0.000001) *\n+ inches[\"km\"] * OpenLayers.DOTS_PER_INCH;\n+ } else {\n+ scale = this.map.getScale();\n+ }\n+\n+ if (!scale) {\n+ return;\n+ }\n+\n+ if (scale >= 9500 && scale <= 950000) {\n+ scale = Math.round(scale / 1000) + \"K\";\n+ } else if (scale >= 950000) {\n+ scale = Math.round(scale / 1000000) + \"M\";\n+ } else {\n+ scale = Math.round(scale);\n+ }\n+\n+ this.element.innerHTML = OpenLayers.i18n(\"Scale = 1 : ${scaleDenom}\", {\n+ 'scaleDenom': scale\n+ });\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Scale\"\n });\n+\n /* ======================================================================\n- OpenLayers/Control/ZoomToMaxExtent.js\n+ OpenLayers/Control/LayerSwitcher.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control/Button.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Events/buttonclick.js\n */\n \n /**\n- * Class: OpenLayers.Control.ZoomToMaxExtent \n- * The ZoomToMaxExtent control is a button that zooms out to the maximum\n- * extent of the map. It is designed to be used with a \n- * <OpenLayers.Control.Panel>.\n- * \n+ * Class: OpenLayers.Control.LayerSwitcher\n+ * The LayerSwitcher control displays a table of contents for the map. This\n+ * allows the user interface to switch between BaseLasyers and to show or hide\n+ * Overlays. By default the switcher is shown minimized on the right edge of\n+ * the map, the user may expand it by clicking on the handle.\n+ *\n+ * To create the LayerSwitcher outside of the map, pass the Id of a html div\n+ * as the first argument to the constructor.\n+ *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {\n+OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * Property: layerStates \n+ * {Array(Object)} Basically a copy of the \"state\" of the map's layers \n+ * the last time the control was drawn. We have this in order to avoid\n+ * unnecessarily redrawing the control.\n+ */\n+ layerStates: null,\n+\n+ // DOM Elements\n \n /**\n- * Method: trigger\n+ * Property: layersDiv\n+ * {DOMElement}\n+ */\n+ layersDiv: null,\n+\n+ /**\n+ * Property: baseLayersDiv\n+ * {DOMElement}\n+ */\n+ baseLayersDiv: null,\n+\n+ /**\n+ * Property: baseLayers\n+ * {Array(Object)}\n+ */\n+ baseLayers: null,\n+\n+\n+ /**\n+ * Property: dataLbl\n+ * {DOMElement}\n+ */\n+ dataLbl: null,\n+\n+ /**\n+ * Property: dataLayersDiv\n+ * {DOMElement}\n+ */\n+ dataLayersDiv: null,\n+\n+ /**\n+ * Property: dataLayers\n+ * {Array(Object)}\n+ */\n+ dataLayers: null,\n+\n+\n+ /**\n+ * Property: minimizeDiv\n+ * {DOMElement}\n+ */\n+ minimizeDiv: null,\n+\n+ /**\n+ * Property: maximizeDiv\n+ * {DOMElement}\n+ */\n+ maximizeDiv: null,\n+\n+ /**\n+ * APIProperty: ascending\n+ * {Boolean}\n+ */\n+ ascending: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.LayerSwitcher\n+ *\n+ * Parameters:\n+ * options - {Object}\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ this.layerStates = [];\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+\n+ //clear out layers info and unregister their events\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: setMap\n+ *\n+ * Properties:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+\n+ this.map.events.on({\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ }\n+ },\n+\n+ /**\n+ * Method: draw\n+ *\n+ * Returns:\n+ * {DOMElement} A reference to the DIV DOMElement containing the\n+ * switcher tabs.\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this);\n+\n+ // create layout divs\n+ this.loadContents();\n+\n+ // set mode to minimize\n+ if (!this.outsideViewport) {\n+ this.minimizeControl();\n+ }\n+\n+ // populate div with current info\n+ this.redraw();\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: onButtonClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ onButtonClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.minimizeDiv) {\n+ this.minimizeControl();\n+ } else if (button === this.maximizeDiv) {\n+ this.maximizeControl();\n+ } else if (button._layerSwitcher === this.id) {\n+ if (button[\"for\"]) {\n+ button = document.getElementById(button[\"for\"]);\n+ }\n+ if (!button.disabled) {\n+ if (button.type == \"radio\") {\n+ button.checked = true;\n+ this.map.setBaseLayer(this.map.getLayer(button._layer));\n+ } else {\n+ button.checked = !button.checked;\n+ this.updateMap();\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: clearLayersArray\n+ * User specifies either \"base\" or \"data\". we then clear all the\n+ * corresponding listeners, the div, and reinitialize a new array.\n+ *\n+ * Parameters:\n+ * layersType - {String}\n+ */\n+ clearLayersArray: function(layersType) {\n+ this[layersType + \"LayersDiv\"].innerHTML = \"\";\n+ this[layersType + \"Layers\"] = [];\n+ },\n+\n+\n+ /**\n+ * Method: checkRedraw\n+ * Checks if the layer state has changed since the last redraw() call.\n+ *\n+ * Returns:\n+ * {Boolean} The layer state changed since the last redraw() call.\n+ */\n+ checkRedraw: function() {\n+ if (!this.layerStates.length ||\n+ (this.map.layers.length != this.layerStates.length)) {\n+ return true;\n+ }\n+\n+ for (var i = 0, len = this.layerStates.length; i < len; i++) {\n+ var layerState = this.layerStates[i];\n+ var layer = this.map.layers[i];\n+ if ((layerState.name != layer.name) ||\n+ (layerState.inRange != layer.inRange) ||\n+ (layerState.id != layer.id) ||\n+ (layerState.visibility != layer.visibility)) {\n+ return true;\n+ }\n+ }\n+\n+ return false;\n+ },\n+\n+ /**\n+ * Method: redraw\n+ * Goes through and takes the current state of the Map and rebuilds the\n+ * control to display that state. Groups base layers into a\n+ * radio-button group and lists each data layer with a checkbox.\n+ *\n+ * Returns:\n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n+ */\n+ redraw: function() {\n+ //if the state hasn't changed since last redraw, no need\n+ // to do anything. Just return the existing div.\n+ if (!this.checkRedraw()) {\n+ return this.div;\n+ }\n+\n+ //clear out previous layers\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+\n+ var containsOverlays = false;\n+ var containsBaseLayers = false;\n+\n+ // Save state -- for checking layer if the map state changed.\n+ // We save this before redrawing, because in the process of redrawing\n+ // we will trigger more visibility changes, and we want to not redraw\n+ // and enter an infinite loop.\n+ var len = this.map.layers.length;\n+ this.layerStates = new Array(len);\n+ for (var i = 0; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ this.layerStates[i] = {\n+ 'name': layer.name,\n+ 'visibility': layer.visibility,\n+ 'inRange': layer.inRange,\n+ 'id': layer.id\n+ };\n+ }\n+\n+ var layers = this.map.layers.slice();\n+ if (!this.ascending) {\n+ layers.reverse();\n+ }\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ var baseLayer = layer.isBaseLayer;\n+\n+ if (layer.displayInLayerSwitcher) {\n+\n+ if (baseLayer) {\n+ containsBaseLayers = true;\n+ } else {\n+ containsOverlays = true;\n+ }\n+\n+ // only check a baselayer if it is *the* baselayer, check data\n+ // layers if they are visible\n+ var checked = (baseLayer) ? (layer == this.map.baseLayer) :\n+ layer.getVisibility();\n+\n+ // create input element\n+ var inputElem = document.createElement(\"input\"),\n+ // The input shall have an id attribute so we can use\n+ // labels to interact with them.\n+ inputId = OpenLayers.Util.createUniqueID(\n+ this.id + \"_input_\"\n+ );\n+\n+ inputElem.id = inputId;\n+ inputElem.name = (baseLayer) ? this.id + \"_baseLayers\" : layer.name;\n+ inputElem.type = (baseLayer) ? \"radio\" : \"checkbox\";\n+ inputElem.value = layer.name;\n+ inputElem.checked = checked;\n+ inputElem.defaultChecked = checked;\n+ inputElem.className = \"olButton\";\n+ inputElem._layer = layer.id;\n+ inputElem._layerSwitcher = this.id;\n+\n+ if (!baseLayer && !layer.inRange) {\n+ inputElem.disabled = true;\n+ }\n+\n+ // create span\n+ var labelSpan = document.createElement(\"label\");\n+ // this isn't the DOM attribute 'for', but an arbitrary name we\n+ // use to find the appropriate input element in <onButtonClick>\n+ labelSpan[\"for\"] = inputElem.id;\n+ OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n+ labelSpan._layer = layer.id;\n+ labelSpan._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ labelSpan.style.color = \"gray\";\n+ }\n+ labelSpan.innerHTML = layer.name;\n+ labelSpan.style.verticalAlign = (baseLayer) ? \"bottom\" :\n+ \"baseline\";\n+ // create line break\n+ var br = document.createElement(\"br\");\n+\n+\n+ var groupArray = (baseLayer) ? this.baseLayers :\n+ this.dataLayers;\n+ groupArray.push({\n+ 'layer': layer,\n+ 'inputElem': inputElem,\n+ 'labelSpan': labelSpan\n+ });\n+\n+\n+ var groupDiv = (baseLayer) ? this.baseLayersDiv :\n+ this.dataLayersDiv;\n+ groupDiv.appendChild(inputElem);\n+ groupDiv.appendChild(labelSpan);\n+ groupDiv.appendChild(br);\n+ }\n+ }\n+\n+ // if no overlays, dont display the overlay label\n+ this.dataLbl.style.display = (containsOverlays) ? \"\" : \"none\";\n+\n+ // if no baselayers, dont display the baselayer label\n+ this.baseLbl.style.display = (containsBaseLayers) ? \"\" : \"none\";\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: updateMap\n+ * Cycles through the loaded data and base layer input arrays and makes\n+ * the necessary calls to the Map object such that that the map's\n+ * visual state corresponds to what the user has selected in\n+ * the control.\n+ */\n+ updateMap: function() {\n+\n+ // set the newly selected base layer\n+ for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n+ var layerEntry = this.baseLayers[i];\n+ if (layerEntry.inputElem.checked) {\n+ this.map.setBaseLayer(layerEntry.layer, false);\n+ }\n+ }\n+\n+ // set the correct visibilities for the overlays\n+ for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n+ var layerEntry = this.dataLayers[i];\n+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked);\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: maximizeControl\n+ * Set up the labels and divs for the control\n+ *\n+ * Parameters:\n+ * e - {Event}\n+ */\n+ maximizeControl: function(e) {\n+\n+ // set the div's width and height to empty values, so\n+ // the div dimensions can be controlled by CSS\n+ this.div.style.width = \"\";\n+ this.div.style.height = \"\";\n+\n+ this.showControls(false);\n+\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n+ }\n+ },\n+\n+ /**\n+ * Method: minimizeControl\n+ * Hide all the contents of the control, shrink the size,\n+ * add the maximize icon\n+ *\n+ * Parameters:\n+ * e - {Event}\n+ */\n+ minimizeControl: function(e) {\n+\n+ // to minimize the control we set its div's width\n+ // and height to 0px, we cannot just set \"display\"\n+ // to \"none\" because it would hide the maximize\n+ // div\n+ this.div.style.width = \"0px\";\n+ this.div.style.height = \"0px\";\n+\n+ this.showControls(true);\n+\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n+ }\n+ },\n+\n+ /**\n+ * Method: showControls\n+ * Hide/Show all LayerSwitcher controls depending on whether we are\n+ * minimized or not\n+ *\n+ * Parameters:\n+ * minimize - {Boolean}\n+ */\n+ showControls: function(minimize) {\n+\n+ this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n+ this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n+\n+ this.layersDiv.style.display = minimize ? \"none\" : \"\";\n+ },\n+\n+ /**\n+ * Method: loadContents\n+ * Set up the labels and divs for the control\n+ */\n+ loadContents: function() {\n+\n+ // layers list div\n+ this.layersDiv = document.createElement(\"div\");\n+ this.layersDiv.id = this.id + \"_layersDiv\";\n+ OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n+\n+ this.baseLbl = document.createElement(\"div\");\n+ this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n+ OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n+\n+ this.baseLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n+\n+ this.dataLbl = document.createElement(\"div\");\n+ this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n+ OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n+\n+ this.dataLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n+\n+ if (this.ascending) {\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ } else {\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ }\n+\n+ this.div.appendChild(this.layersDiv);\n+\n+ // maximize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ \"OpenLayers_Control_MaximizeDiv\",\n+ null,\n+ null,\n+ img,\n+ \"absolute\");\n+ OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n+ this.maximizeDiv.style.display = \"none\";\n+\n+ this.div.appendChild(this.maximizeDiv);\n+\n+ // minimize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ \"OpenLayers_Control_MinimizeDiv\",\n+ null,\n+ null,\n+ img,\n+ \"absolute\");\n+ OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n+ this.minimizeDiv.style.display = \"none\";\n+\n+ this.div.appendChild(this.minimizeDiv);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Zoom.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Zoom\n+ * The Zoom control is a pair of +/- links for zooming in and out.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /**\n+ * APIProperty: zoomInText\n+ * {String}\n+ * Text for zoom-in link. Default is \"+\".\n+ */\n+ zoomInText: \"+\",\n+\n+ /**\n+ * APIProperty: zoomInId\n+ * {String}\n+ * Instead of having the control create a zoom in link, you can provide \n+ * the identifier for an anchor element already added to the document.\n+ * By default, an element with id \"olZoomInLink\" will be searched for\n+ * and used if it exists.\n+ */\n+ zoomInId: \"olZoomInLink\",\n+\n+ /**\n+ * APIProperty: zoomOutText\n+ * {String}\n+ * Text for zoom-out link. Default is \"\\u2212\".\n+ */\n+ zoomOutText: \"\\u2212\",\n+\n+ /**\n+ * APIProperty: zoomOutId\n+ * {String}\n+ * Instead of having the control create a zoom out link, you can provide \n+ * the identifier for an anchor element already added to the document.\n+ * By default, an element with id \"olZoomOutLink\" will be searched for\n+ * and used if it exists.\n+ */\n+ zoomOutId: \"olZoomOutLink\",\n+\n+ /**\n+ * Method: draw\n+ *\n+ * Returns:\n+ * {DOMElement} A reference to the DOMElement containing the zoom links.\n+ */\n+ draw: function() {\n+ var div = OpenLayers.Control.prototype.draw.apply(this),\n+ links = this.getOrCreateLinks(div),\n+ zoomIn = links.zoomIn,\n+ zoomOut = links.zoomOut,\n+ eventsInstance = this.map.events;\n+\n+ if (zoomOut.parentNode !== div) {\n+ eventsInstance = this.events;\n+ eventsInstance.attachToElement(zoomOut.parentNode);\n+ }\n+ eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n+\n+ this.zoomInLink = zoomIn;\n+ this.zoomOutLink = zoomOut;\n+ return div;\n+ },\n+\n+ /**\n+ * Method: getOrCreateLinks\n * \n- * Called whenever this control is being rendered inside of a panel and a \n- * click occurs on this controls element. Actually zooms to the maximum\n- * extent of this controls map.\n+ * Parameters:\n+ * el - {DOMElement}\n+ *\n+ * Return: \n+ * {Object} Object with zoomIn and zoomOut properties referencing links.\n */\n- trigger: function() {\n+ getOrCreateLinks: function(el) {\n+ var zoomIn = document.getElementById(this.zoomInId),\n+ zoomOut = document.getElementById(this.zoomOutId);\n+ if (!zoomIn) {\n+ zoomIn = document.createElement(\"a\");\n+ zoomIn.href = \"#zoomIn\";\n+ zoomIn.appendChild(document.createTextNode(this.zoomInText));\n+ zoomIn.className = \"olControlZoomIn\";\n+ el.appendChild(zoomIn);\n+ }\n+ OpenLayers.Element.addClass(zoomIn, \"olButton\");\n+ if (!zoomOut) {\n+ zoomOut = document.createElement(\"a\");\n+ zoomOut.href = \"#zoomOut\";\n+ zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n+ zoomOut.className = \"olControlZoomOut\";\n+ el.appendChild(zoomOut);\n+ }\n+ OpenLayers.Element.addClass(zoomOut, \"olButton\");\n+ return {\n+ zoomIn: zoomIn,\n+ zoomOut: zoomOut\n+ };\n+ },\n+\n+ /**\n+ * Method: onZoomClick\n+ * Called when zoomin/out link is clicked.\n+ */\n+ onZoomClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.zoomInLink) {\n+ this.map.zoomIn();\n+ } else if (button === this.zoomOutLink) {\n+ this.map.zoomOut();\n+ }\n+ },\n+\n+ /** \n+ * Method: destroy\n+ * Clean up.\n+ */\n+ destroy: function() {\n if (this.map) {\n- this.map.zoomToMaxExtent();\n+ this.map.events.unregister(\"buttonclick\", this, this.onZoomClick);\n }\n+ delete this.zoomInLink;\n+ delete this.zoomOutLink;\n+ OpenLayers.Control.prototype.destroy.apply(this);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomToMaxExtent\"\n+ CLASS_NAME: \"OpenLayers.Control.Zoom\"\n });\n /* ======================================================================\n- OpenLayers/Control/ZoomPanel.js\n+ OpenLayers/Control/Split.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control/Panel.js\n- * @requires OpenLayers/Control/ZoomIn.js\n- * @requires OpenLayers/Control/ZoomOut.js\n- * @requires OpenLayers/Control/ZoomToMaxExtent.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Path.js\n+ * @requires OpenLayers/Layer/Vector.js\n */\n \n /**\n- * Class: OpenLayers.Control.ZoomPanel\n- * The ZoomPanel control is a compact collecton of 3 zoom controls: a \n- * <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a\n- * <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left \n- * corner of the map.\n+ * Class: OpenLayers.Control.Split\n+ * Acts as a split feature agent while editing vector features.\n *\n- * Note: \n- * If you wish to use this class with the default images and you want \n- * it to look nice in ie6, you should add the following, conditionally\n- * added css stylesheet to your HTML file:\n- * \n- * (code)\n- * <!--[if lte IE 6]>\n- * <link rel=\"stylesheet\" href=\"../theme/default/ie6-style.css\" type=\"text/css\" />\n- * <![endif]-->\n- * (end)\n- * \n * Inherits from:\n- * - <OpenLayers.Control.Panel>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n+OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforesplit - Triggered before a split occurs. Listeners receive an\n+ * event object with *source* and *target* properties.\n+ * split - Triggered when a split occurs. Listeners receive an event with\n+ * an *original* property and a *features* property. The original\n+ * is a reference to the target feature that the sketch or modified\n+ * feature intersects. The features property is a list of all features\n+ * that result from this single split. This event is triggered before\n+ * the resulting features are added to the layer (while the layer still\n+ * has a reference to the original).\n+ * aftersplit - Triggered after all splits resulting from a single sketch\n+ * or feature modification have occurred. The original features\n+ * have been destroyed and features that result from the split\n+ * have already been added to the layer. Listeners receive an event\n+ * with a *source* and *features* property. The source references the\n+ * sketch or modified feature used as a splitter. The features\n+ * property is a list of all resulting features.\n+ */\n \n /**\n- * Constructor: OpenLayers.Control.ZoomPanel \n- * Add the three zooming controls.\n+ * APIProperty: layer\n+ * {<OpenLayers.Layer.Vector>} The target layer with features to be split.\n+ * Set at construction or after construction with <setLayer>.\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: source\n+ * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created\n+ * or modified features from this layer will be used to split features\n+ * on the target layer. If not provided, a temporary sketch layer will\n+ * be created.\n+ */\n+ source: null,\n+\n+ /**\n+ * Property: sourceOptions\n+ * {Options} If a temporary sketch layer is created, these layer options\n+ * will be applied.\n+ */\n+ sourceOptions: null,\n+\n+ /**\n+ * APIProperty: tolerance\n+ * {Number} Distance between the calculated intersection and a vertex on\n+ * the source geometry below which the existing vertex will be used\n+ * for the split. Default is null.\n+ */\n+ tolerance: null,\n+\n+ /**\n+ * APIProperty: edge\n+ * {Boolean} Allow splits given intersection of edges only. Default is\n+ * true. If false, a vertex on the source must be within the\n+ * <tolerance> distance of the calculated intersection for a split\n+ * to occur.\n+ */\n+ edge: true,\n+\n+ /**\n+ * APIProperty: deferDelete\n+ * {Boolean} Instead of removing features from the layer, set feature\n+ * states of split features to DELETE. This assumes a save strategy\n+ * or other component is in charge of removing features from the\n+ * layer. Default is false. If false, split features will be\n+ * immediately deleted from the layer.\n+ */\n+ deferDelete: false,\n+\n+ /**\n+ * APIProperty: mutual\n+ * {Boolean} If source and target layers are the same, split source\n+ * features and target features where they intersect. Default is\n+ * true. If false, only target features will be split.\n+ */\n+ mutual: true,\n+\n+ /**\n+ * APIProperty: targetFilter\n+ * {<OpenLayers.Filter>} Optional filter that will be evaluated\n+ * to determine if a feature from the target layer is eligible for\n+ * splitting.\n+ */\n+ targetFilter: null,\n+\n+ /**\n+ * APIProperty: sourceFilter\n+ * {<OpenLayers.Filter>} Optional filter that will be evaluated\n+ * to determine if a feature from the source layer is eligible for\n+ * splitting.\n+ */\n+ sourceFilter: null,\n+\n+ /**\n+ * Property: handler\n+ * {<OpenLayers.Handler.Path>} The temporary sketch handler created if\n+ * no source layer is provided.\n+ */\n+ handler: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Split\n+ * Creates a new split control. A control is constructed with a target\n+ * layer and an optional source layer. While the control is active,\n+ * creating new features or modifying existing features on the source\n+ * layer will result in splitting any eligible features on the target\n+ * layer. If no source layer is provided, a temporary sketch layer will\n+ * be created to create lines for splitting features on the target.\n *\n * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n+ * options - {Object} An object containing all configuration properties for\n+ * the control.\n+ *\n+ * Valid options:\n+ * layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this\n+ * layer will be split by new or modified features on the source layer\n+ * or temporary sketch layer.\n+ * source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided\n+ * newly created features or modified features will be used to split\n+ * features on the target layer. If not provided, a temporary sketch\n+ * layer will be created for drawing lines.\n+ * tolerance - {Number} Optional value for the distance between a source\n+ * vertex and the calculated intersection below which the split will\n+ * occur at the vertex.\n+ * edge - {Boolean} Allow splits given intersection of edges only. Default\n+ * is true. If false, a vertex on the source must be within the\n+ * <tolerance> distance of the calculated intersection for a split\n+ * to occur.\n+ * mutual - {Boolean} If source and target are the same, split source\n+ * features and target features where they intersect. Default is\n+ * true. If false, only target features will be split.\n+ * targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated\n+ * to determine if a feature from the target layer is eligible for\n+ * splitting.\n+ * sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated\n+ * to determine if a feature from the target layer is eligible for\n+ * splitting.\n */\n initialize: function(options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n- this.addControls([\n- new OpenLayers.Control.ZoomIn(),\n- new OpenLayers.Control.ZoomToMaxExtent(),\n- new OpenLayers.Control.ZoomOut()\n- ]);\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.options = options || {}; // TODO: this could be done by the super\n+\n+ // set the source layer if provided\n+ if (this.options.source) {\n+ this.setSource(this.options.source);\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomPanel\"\n+ /**\n+ * APIMethod: setSource\n+ * Set the source layer for edits layer.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If\n+ * null, a temporary sketch layer will be created.\n+ */\n+ setSource: function(layer) {\n+ if (this.active) {\n+ this.deactivate();\n+ if (this.handler) {\n+ this.handler.destroy();\n+ delete this.handler;\n+ }\n+ this.source = layer;\n+ this.activate();\n+ } else {\n+ this.source = layer;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ * Activate the control. Activating the control registers listeners for\n+ * editing related events so that during feature creation and\n+ * modification, features in the target will be considered for\n+ * splitting.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ if (!this.source) {\n+ if (!this.handler) {\n+ this.handler = new OpenLayers.Handler.Path(this, {\n+ done: function(geometry) {\n+ this.onSketchComplete({\n+ feature: new OpenLayers.Feature.Vector(geometry)\n+ });\n+ }\n+ }, {\n+ layerOptions: this.sourceOptions\n+ });\n+ }\n+ this.handler.activate();\n+ } else if (this.source.events) {\n+ this.source.events.on({\n+ sketchcomplete: this.onSketchComplete,\n+ afterfeaturemodified: this.afterFeatureModified,\n+ scope: this\n+ });\n+ }\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * Deactivate the control. Deactivating the control unregisters listeners\n+ * so feature editing may proceed without engaging the split agent.\n+ */\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.source && this.source.events) {\n+ this.source.events.un({\n+ sketchcomplete: this.onSketchComplete,\n+ afterfeaturemodified: this.afterFeatureModified,\n+ scope: this\n+ });\n+ }\n+ }\n+ return deactivated;\n+ },\n+\n+ /**\n+ * Method: onSketchComplete\n+ * Registered as a listener for the sketchcomplete event on the editable\n+ * layer.\n+ *\n+ * Parameters:\n+ * event - {Object} The sketch complete event.\n+ *\n+ * Returns:\n+ * {Boolean} Stop the sketch from being added to the layer (it has been\n+ * split).\n+ */\n+ onSketchComplete: function(event) {\n+ this.feature = null;\n+ return !this.considerSplit(event.feature);\n+ },\n+\n+ /**\n+ * Method: afterFeatureModified\n+ * Registered as a listener for the afterfeaturemodified event on the\n+ * editable layer.\n+ *\n+ * Parameters:\n+ * event - {Object} The after feature modified event.\n+ */\n+ afterFeatureModified: function(event) {\n+ if (event.modified) {\n+ var feature = event.feature;\n+ if (typeof feature.geometry.split === \"function\") {\n+ this.feature = event.feature;\n+ this.considerSplit(event.feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: removeByGeometry\n+ * Remove a feature from a list based on the given geometry.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} A list of features.\n+ * geometry - {<OpenLayers.Geometry>} A geometry.\n+ */\n+ removeByGeometry: function(features, geometry) {\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ if (features[i].geometry === geometry) {\n+ features.splice(i, 1);\n+ break;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: isEligible\n+ * Test if a target feature is eligible for splitting.\n+ *\n+ * Parameters:\n+ * target - {<OpenLayers.Feature.Vector>} The target feature.\n+ *\n+ * Returns:\n+ * {Boolean} The target is eligible for splitting.\n+ */\n+ isEligible: function(target) {\n+ if (!target.geometry) {\n+ return false;\n+ } else {\n+ return (\n+ target.state !== OpenLayers.State.DELETE\n+ ) && (\n+ typeof target.geometry.split === \"function\"\n+ ) && (\n+ this.feature !== target\n+ ) && (\n+ !this.targetFilter ||\n+ this.targetFilter.evaluate(target.attributes)\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: considerSplit\n+ * Decide whether or not to split target features with the supplied\n+ * feature. If <mutual> is true, both the source and target features\n+ * will be split if eligible.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The newly created or modified\n+ * feature.\n+ *\n+ * Returns:\n+ * {Boolean} The supplied feature was split (and destroyed).\n+ */\n+ considerSplit: function(feature) {\n+ var sourceSplit = false;\n+ var targetSplit = false;\n+ if (!this.sourceFilter ||\n+ this.sourceFilter.evaluate(feature.attributes)) {\n+ var features = this.layer && this.layer.features || [];\n+ var target, results, proceed;\n+ var additions = [],\n+ removals = [];\n+ var mutual = (this.layer === this.source) && this.mutual;\n+ var options = {\n+ edge: this.edge,\n+ tolerance: this.tolerance,\n+ mutual: mutual\n+ };\n+ var sourceParts = [feature.geometry];\n+ var targetFeature, targetParts;\n+ var source, parts;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ targetFeature = features[i];\n+ if (this.isEligible(targetFeature)) {\n+ targetParts = [targetFeature.geometry];\n+ // work through source geoms - this array may change\n+ for (var j = 0; j < sourceParts.length; ++j) {\n+ source = sourceParts[j];\n+ // work through target parts - this array may change\n+ for (var k = 0; k < targetParts.length; ++k) {\n+ target = targetParts[k];\n+ if (source.getBounds().intersectsBounds(target.getBounds())) {\n+ results = source.split(target, options);\n+ if (results) {\n+ proceed = this.events.triggerEvent(\n+ \"beforesplit\", {\n+ source: feature,\n+ target: targetFeature\n+ }\n+ );\n+ if (proceed !== false) {\n+ if (mutual) {\n+ parts = results[0];\n+ // handle parts that result from source splitting\n+ if (parts.length > 1) {\n+ // splice in new source parts\n+ parts.unshift(j, 1); // add args for splice below\n+ Array.prototype.splice.apply(sourceParts, parts);\n+ j += parts.length - 3;\n+ }\n+ results = results[1];\n+ }\n+ // handle parts that result from target splitting\n+ if (results.length > 1) {\n+ // splice in new target parts\n+ results.unshift(k, 1); // add args for splice below\n+ Array.prototype.splice.apply(targetParts, results);\n+ k += results.length - 3;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ this.geomsToFeatures(targetFeature, targetParts);\n+ this.events.triggerEvent(\"split\", {\n+ original: targetFeature,\n+ features: targetParts\n+ });\n+ Array.prototype.push.apply(additions, targetParts);\n+ removals.push(targetFeature);\n+ targetSplit = true;\n+ }\n+ }\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ this.geomsToFeatures(feature, sourceParts);\n+ this.events.triggerEvent(\"split\", {\n+ original: feature,\n+ features: sourceParts\n+ });\n+ Array.prototype.push.apply(additions, sourceParts);\n+ removals.push(feature);\n+ sourceSplit = true;\n+ }\n+ if (sourceSplit || targetSplit) {\n+ // remove and add feature events are suppressed\n+ // listen for split event on this control instead\n+ if (this.deferDelete) {\n+ // Set state instead of removing. Take care to avoid\n+ // setting delete for features that have not yet been\n+ // inserted - those should be destroyed immediately.\n+ var feat, destroys = [];\n+ for (var i = 0, len = removals.length; i < len; ++i) {\n+ feat = removals[i];\n+ if (feat.state === OpenLayers.State.INSERT) {\n+ destroys.push(feat);\n+ } else {\n+ feat.state = OpenLayers.State.DELETE;\n+ this.layer.drawFeature(feat);\n+ }\n+ }\n+ this.layer.destroyFeatures(destroys, {\n+ silent: true\n+ });\n+ for (var i = 0, len = additions.length; i < len; ++i) {\n+ additions[i].state = OpenLayers.State.INSERT;\n+ }\n+ } else {\n+ this.layer.destroyFeatures(removals, {\n+ silent: true\n+ });\n+ }\n+ this.layer.addFeatures(additions, {\n+ silent: true\n+ });\n+ this.events.triggerEvent(\"aftersplit\", {\n+ source: feature,\n+ features: additions\n+ });\n+ }\n+ }\n+ return sourceSplit;\n+ },\n+\n+ /**\n+ * Method: geomsToFeatures\n+ * Create new features given a template feature and a list of geometries.\n+ * The list of geometries is modified in place. The result will be\n+ * a list of new features.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned.\n+ * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will\n+ * become a list of new features.\n+ */\n+ geomsToFeatures: function(feature, geoms) {\n+ var clone = feature.clone();\n+ delete clone.geometry;\n+ var newFeature;\n+ for (var i = 0, len = geoms.length; i < len; ++i) {\n+ // turn results list from geoms to features\n+ newFeature = clone.clone();\n+ newFeature.geometry = geoms[i];\n+ newFeature.state = OpenLayers.State.INSERT;\n+ geoms[i] = newFeature;\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ * Clean up the control.\n+ */\n+ destroy: function() {\n+ if (this.active) {\n+ this.deactivate(); // TODO: this should be handled by the super\n+ }\n+ OpenLayers.Control.prototype.destroy.call(this);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Split\"\n });\n /* ======================================================================\n OpenLayers/Control/GetFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -70293,10357 +66973,13096 @@\n var ur = this.map.getLonLatFromPixel(urPx);\n return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat);\n },\n \n CLASS_NAME: \"OpenLayers.Control.GetFeature\"\n });\n /* ======================================================================\n- OpenLayers/Control/Snapping.js\n+ OpenLayers/Control/TouchNavigation.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control/DragPan.js\n+ * @requires OpenLayers/Control/PinchZoom.js\n+ * @requires OpenLayers/Handler/Click.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.TouchNavigation\n+ * The navigation control handles map browsing with touch events (dragging,\n+ * double-tapping, tap with two fingers, and pinch zoom). Create a new \n+ * control with the <OpenLayers.Control.TouchNavigation> constructor.\n+ *\n+ * If you\u2019re only targeting touch enabled devices with your mapping application,\n+ * you can create a map with only a TouchNavigation control. The \n+ * <OpenLayers.Control.Navigation> control is mobile ready by default, but \n+ * you can generate a smaller build of the library by only including this\n+ * touch navigation control if you aren't concerned about mouse interaction.\n+ *\n+ * Inherits:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /**\n+ * Property: dragPan\n+ * {<OpenLayers.Control.DragPan>}\n+ */\n+ dragPan: null,\n+\n+ /**\n+ * APIProperty: dragPanOptions\n+ * {Object} Options passed to the DragPan control.\n+ */\n+ dragPanOptions: null,\n+\n+ /**\n+ * Property: pinchZoom\n+ * {<OpenLayers.Control.PinchZoom>}\n+ */\n+ pinchZoom: null,\n+\n+ /**\n+ * APIProperty: pinchZoomOptions\n+ * {Object} Options passed to the PinchZoom control.\n+ */\n+ pinchZoomOptions: null,\n+\n+ /**\n+ * APIProperty: clickHandlerOptions\n+ * {Object} Options passed to the Click handler.\n+ */\n+ clickHandlerOptions: null,\n+\n+ /**\n+ * APIProperty: documentDrag\n+ * {Boolean} Allow panning of the map by dragging outside map viewport.\n+ * Default is false.\n+ */\n+ documentDrag: false,\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.TouchNavigation\n+ * Create a new navigation control\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * the control\n+ */\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n+ */\n+ destroy: function() {\n+ this.deactivate();\n+ if (this.dragPan) {\n+ this.dragPan.destroy();\n+ }\n+ this.dragPan = null;\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy();\n+ delete this.pinchZoom;\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: activate\n+ */\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.dragPan.activate();\n+ this.handlers.click.activate();\n+ this.pinchZoom.activate();\n+ return true;\n+ }\n+ return false;\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ */\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.pinchZoom.deactivate();\n+ return true;\n+ }\n+ return false;\n+ },\n+\n+ /**\n+ * Method: draw\n+ */\n+ draw: function() {\n+ var clickCallbacks = {\n+ click: this.defaultClick,\n+ dblclick: this.defaultDblClick\n+ };\n+ var clickOptions = OpenLayers.Util.extend({\n+ \"double\": true,\n+ stopDouble: true,\n+ pixelTolerance: 2\n+ }, this.clickHandlerOptions);\n+ this.handlers.click = new OpenLayers.Handler.Click(\n+ this, clickCallbacks, clickOptions\n+ );\n+ this.dragPan = new OpenLayers.Control.DragPan(\n+ OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions)\n+ );\n+ this.dragPan.draw();\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(\n+ OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions)\n+ );\n+ },\n+\n+ /**\n+ * Method: defaultClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut();\n+ }\n+ },\n+\n+ /**\n+ * Method: defaultDblClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/WMS.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.WMS\n+ * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n+ * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n+ /**\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs \n+ */\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n+ },\n+\n+ /**\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Default is true for WMS layer\n+ */\n+ isBaseLayer: true,\n+\n+ /**\n+ * APIProperty: encodeBBOX\n+ * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n+ * but some services want it that way. Default false.\n+ */\n+ encodeBBOX: false,\n+\n+ /** \n+ * APIProperty: noMagic \n+ * {Boolean} If true, the image format will not be automagicaly switched \n+ * from image/jpeg to image/png or image/gif when using \n+ * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n+ * constructor. Default false. \n+ */\n+ noMagic: false,\n+\n+ /**\n+ * Property: yx\n+ * {Object} Keys in this object are EPSG codes for which the axis order\n+ * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n+ * true as value. This is only relevant for WMS versions >= 1.3.0, and\n+ * only if yx is not set in <OpenLayers.Projection.defaults> for the\n+ * used projection.\n+ */\n+ yx: {},\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.WMS\n+ * Create a new WMS layer object\n+ *\n+ * Examples:\n+ *\n+ * The code below creates a simple WMS layer using the image/jpeg format.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {layers: \"modis,global_mosaic\"});\n+ * (end)\n+ * Note the 3rd argument (params). Properties added to this object will be\n+ * added to the WMS GetMap requests used for this layer's tiles. The only\n+ * mandatory parameter is \"layers\". Other common WMS params include\n+ * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n+ * always be ignored. Instead, it will be derived from the baseLayer's or\n+ * map's projection.\n+ *\n+ * The code below creates a transparent WMS layer with additional options.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {\n+ * layers: \"modis,global_mosaic\",\n+ * transparent: true\n+ * }, {\n+ * opacity: 0.5,\n+ * singleTile: true\n+ * });\n+ * (end)\n+ * Note that by default, a WMS layer is configured as baseLayer. Setting\n+ * the \"transparent\" param to true will apply some magic (see <noMagic>).\n+ * The default image format changes from image/jpeg to image/png, and the\n+ * layer is not configured as baseLayer.\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the WMS\n+ * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n+ * params - {Object} An object with key/value pairs representing the\n+ * GetMap query string parameters and parameter values.\n+ * options - {Object} Hashtable of extra options to tag onto the layer.\n+ * These options include all properties listed above, plus the ones\n+ * inherited from superclasses.\n+ */\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ //uppercase params\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\";\n+ }\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n+ );\n+\n+\n+ //layer is transparent \n+ if (!this.noMagic && this.params.TRANSPARENT &&\n+ this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+\n+ // unless explicitly set in options, make layer an overlay\n+ if ((options == null) || (!options.isBaseLayer)) {\n+ this.isBaseLayer = false;\n+ }\n+\n+ // jpegs can never be transparent, so intelligently switch the \n+ // format, depending on the browser's capabilities\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n+ \"image/png\";\n+ }\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n+ },\n+\n+ /**\n+ * APIMethod: reverseAxisOrder\n+ * Returns true if the axis order is reversed for the WMS version and\n+ * projection of the layer.\n+ * \n+ * Returns:\n+ * {Boolean} true if the axis order is reversed, false otherwise.\n+ */\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 &&\n+ !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n+ OpenLayers.Projection.defaults[projCode].yx));\n+ },\n+\n+ /**\n+ * Method: getURL\n+ * Return a GetMap query string for this layer\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters.\n+ */\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ // WMS 1.3 introduced axis order\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ?\n+ bounds.toBBOX(null, reverseAxisOrder) :\n+ bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString;\n+ },\n+\n+ /**\n+ * APIMethod: mergeNewParams\n+ * Catch changeParams and uppercase the new params to be merged in\n+ * before calling changeParams on the super class.\n+ * \n+ * Once params have been changed, the tiles will be reloaded with\n+ * the new parameters.\n+ * \n+ * Parameters:\n+ * newParams - {Object} Hashtable of new params to use\n+ */\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n+ newArguments);\n+ },\n+\n+ /** \n+ * APIMethod: getFullRequestString\n+ * Combine the layer's url with its params and these newParams. \n+ * \n+ * Add the SRS parameter from projection -- this is probably\n+ * more eloquently done via a setProjection() method, but this \n+ * works for now and always.\n+ *\n+ * Parameters:\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n+ * \n+ * Returns:\n+ * {String} \n+ */\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n+ this.projection.getCode() :\n+ mapProjection.getCode();\n+ var value = (projectionCode == \"none\") ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value;\n+ } else {\n+ this.params.SRS = value;\n+ }\n+\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n+ }\n+\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n+ this, arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/SLDSelect.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Layer/Vector.js\n+ * @requires OpenLayers/Layer/WMS.js\n+ * @requires OpenLayers/Handler/RegularPolygon.js\n+ * @requires OpenLayers/Handler/Polygon.js\n+ * @requires OpenLayers/Handler/Path.js\n+ * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Filter/Spatial.js\n+ * @requires OpenLayers/Format/SLD/v1_0_0.js\n */\n \n /**\n- * Class: OpenLayers.Control.Snapping\n- * Acts as a snapping agent while editing vector features.\n+ * Class: OpenLayers.Control.SLDSelect\n+ * Perform selections on WMS layers using Styled Layer Descriptor (SLD)\n *\n * Inherits from:\n * - <OpenLayers.Control>\n */\n-OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n * APIProperty: events\n * {<OpenLayers.Events>} Events instance for listeners and triggering\n * control specific events.\n *\n * Register a listener for a particular event with the following syntax:\n * (code)\n * control.events.register(type, obj, listener);\n * (end)\n *\n * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforesnap - Triggered before a snap occurs. Listeners receive an\n- * event object with *point*, *x*, *y*, *distance*, *layer*, and\n- * *snapType* properties. The point property will be original point\n- * geometry considered for snapping. The x and y properties represent\n- * coordinates the point will receive. The distance is the distance\n- * of the snap. The layer is the target layer. The snapType property\n- * will be one of \"node\", \"vertex\", or \"edge\". Return false to stop\n- * snapping from occurring.\n- * snap - Triggered when a snap occurs. Listeners receive an event with\n- * *point*, *snapType*, *layer*, and *distance* properties. The point\n- * will be the location snapped to. The snapType will be one of \"node\",\n- * \"vertex\", or \"edge\". The layer will be the target layer. The\n- * distance will be the distance of the snap in map units.\n- * unsnap - Triggered when a vertex is unsnapped. Listeners receive an\n- * event with a *point* property.\n+ * selected - Triggered when a selection occurs. Listeners receive an \n+ * event with *filters* and *layer* properties. Filters will be an \n+ * array of OpenLayers.Filter objects created in order to perform \n+ * the particular selection.\n */\n \n /**\n- * CONSTANT: DEFAULTS\n- * Default target properties.\n+ * APIProperty: clearOnDeactivate\n+ * {Boolean} Should the selection be cleared when the control is \n+ * deactivated. Default value is false.\n */\n- DEFAULTS: {\n- tolerance: 10,\n- node: true,\n- edge: true,\n- vertex: true\n- },\n+ clearOnDeactivate: false,\n \n /**\n- * Property: greedy\n- * {Boolean} Snap to closest feature in first layer with an eligible\n- * feature. Default is true.\n+ * APIProperty: layers\n+ * {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work \n+ * on.\n */\n- greedy: true,\n+ layers: null,\n \n /**\n- * Property: precedence\n- * {Array} List representing precedence of different snapping types.\n- * Default is \"node\", \"vertex\", \"edge\".\n+ * Property: callbacks\n+ * {Object} The functions that are sent to the handler for callback\n */\n- precedence: [\"node\", \"vertex\", \"edge\"],\n+ callbacks: null,\n \n /**\n- * Property: resolution\n- * {Float} The map resolution for the previously considered snap.\n+ * APIProperty: selectionSymbolizer\n+ * {Object} Determines the styling of the selected objects. Default is\n+ * a selection in red.\n */\n- resolution: null,\n+ selectionSymbolizer: {\n+ 'Polygon': {\n+ fillColor: '#FF0000',\n+ stroke: false\n+ },\n+ 'Line': {\n+ strokeColor: '#FF0000',\n+ strokeWidth: 2\n+ },\n+ 'Point': {\n+ graphicName: 'square',\n+ fillColor: '#FF0000',\n+ pointRadius: 5\n+ }\n+ },\n \n /**\n- * Property: geoToleranceCache\n- * {Object} A cache of geo-tolerances. Tolerance values (in map units) are\n- * calculated when the map resolution changes.\n+ * APIProperty: layerOptions\n+ * {Object} The options to apply to the selection layer, by default the\n+ * selection layer will be kept out of the layer switcher.\n */\n- geoToleranceCache: null,\n+ layerOptions: null,\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The current editable layer. Set at\n- * construction or after construction with <setLayer>.\n+ * APIProperty: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n */\n- layer: null,\n \n /**\n- * Property: feature\n- * {<OpenLayers.Feature.Vector>} The current editable feature.\n+ * APIProperty: sketchStyle\n+ * {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch\n+ * handler. The recommended way of styling the sketch layer, however, is\n+ * to configure an <OpenLayers.StyleMap> in the layerOptions of the\n+ * <handlerOptions>:\n+ * \n+ * (code)\n+ * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, {\n+ * handlerOptions: {\n+ * layerOptions: {\n+ * styleMap: new OpenLayers.StyleMap({\n+ * \"default\": {strokeColor: \"yellow\"}\n+ * })\n+ * }\n+ * }\n+ * });\n+ * (end)\n */\n- feature: null,\n+ sketchStyle: null,\n \n /**\n- * Property: point\n- * {<OpenLayers.Geometry.Point>} The currently snapped vertex.\n+ * APIProperty: wfsCache\n+ * {Object} Cache to use for storing parsed results from\n+ * <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided,\n+ * these will be cached on the prototype.\n */\n- point: null,\n+ wfsCache: {},\n \n /**\n- * Constructor: OpenLayers.Control.Snapping\n- * Creates a new snapping control. A control is constructed with an editable\n- * layer and a set of configuration objects for target layers. While the\n- * control is active, dragging vertices while drawing new features or\n- * modifying existing features on the editable layer will engage\n- * snapping to features on the target layers. Whether a vertex snaps to\n- * a feature on a target layer depends on the target layer configuration.\n+ * APIProperty: layerCache\n+ * {Object} Cache to use for storing references to the selection layers.\n+ * Normally each source layer will have exactly 1 selection layer of\n+ * type OpenLayers.Layer.WMS. If not provided, layers will\n+ * be cached on the prototype. Note that if <clearOnDeactivate> is\n+ * true, the layer will no longer be cached after deactivating the\n+ * control.\n+ */\n+ layerCache: {},\n+\n+ /**\n+ * Constructor: OpenLayers.Control.SLDSelect\n+ * Create a new control for selecting features in WMS layers using\n+ * Styled Layer Descriptor (SLD).\n *\n * Parameters:\n+ * handler - {<OpenLayers.Class>} A sketch handler class. This determines\n+ * the type of selection, e.g. box (<OpenLayers.Handler.Box>), point\n+ * (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or\n+ * polygon (<OpenLayers.Handler.Polygon>) selection. To use circle\n+ * type selection, use <OpenLayers.Handler.RegularPolygon> and pass\n+ * the number of desired sides (e.g. 40) as \"sides\" property to the\n+ * <handlerOptions>.\n * options - {Object} An object containing all configuration properties for\n * the control.\n *\n * Valid options:\n- * layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this\n- * layer that are digitized or modified may have vertices snapped to\n- * features from any of the target layers.\n- * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for\n- * configuring target layers. See valid properties of the target\n- * objects below. If the items in the targets list are vector layers\n- * (instead of configuration objects), the defaults from the <defaults>\n- * property will apply. The editable layer itself may be a target\n- * layer, allowing newly created or edited features to be snapped to\n- * existing features from the same layer. If no targets are provided\n- * the layer given in the constructor (as <layer>) will become the\n- * initial target.\n- * defaults - {Object} An object with default properties to be applied\n- * to all target objects.\n- * greedy - {Boolean} Snap to closest feature in first target layer that\n- * applies. Default is true. If false, all features in all target\n- * layers will be checked and the closest feature in all target layers\n- * will be chosen. The greedy property determines if the order of the\n- * target layers is significant. By default, the order of the target\n- * layers is significant where layers earlier in the target layer list\n- * have precedence over layers later in the list. Within a single\n- * layer, the closest feature is always chosen for snapping. This\n- * property only determines whether the search for a closer feature\n- * continues after an eligible feature is found in a target layer.\n+ * layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the\n+ * selection on.\n+ */\n+ initialize: function(handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+\n+ this.callbacks = OpenLayers.Util.extend({\n+ done: this.select,\n+ click: this.select\n+ }, this.callbacks);\n+ this.handlerOptions = this.handlerOptions || {};\n+ this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {\n+ displayInLayerSwitcher: false,\n+ tileOptions: {\n+ maxGetUrlLength: 2048\n+ }\n+ });\n+ if (this.sketchStyle) {\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n+ this.handlerOptions.layerOptions, {\n+ styleMap: new OpenLayers.StyleMap({\n+ \"default\": this.sketchStyle\n+ })\n+ }\n+ );\n+ }\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions);\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Take care of things that are not handled in superclass.\n+ */\n+ destroy: function() {\n+ for (var key in this.layerCache) {\n+ delete this.layerCache[key];\n+ }\n+ for (var key in this.wfsCache) {\n+ delete this.wfsCache[key];\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: coupleLayerVisiblity\n+ * Couple the selection layer and the source layer with respect to\n+ * layer visibility. So if the source layer is turned off, the\n+ * selection layer is also turned off.\n *\n- * Valid target properties:\n- * layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this\n- * layer will be eligible to act as snapping target for the editable\n+ * Context: \n+ * - {<OpenLayers.Layer>}\n+ *\n+ * Parameters:\n+ * evt - {Object}\n+ */\n+ coupleLayerVisiblity: function(evt) {\n+ this.setVisibility(evt.object.getVisibility());\n+ },\n+\n+ /**\n+ * Method: createSelectionLayer\n+ * Creates a \"clone\" from the source layer in which the selection can\n+ * be drawn. This ensures both the source layer and the selection are \n+ * visible and not only the selection.\n+ *\n+ * Parameters:\n+ * source - {<OpenLayers.Layer.WMS>} The source layer on which the selection\n+ * is performed.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048\n+ * since SLD selections can easily get quite long.\n+ */\n+ createSelectionLayer: function(source) {\n+ // check if we already have a selection layer for the source layer\n+ var selectionLayer;\n+ if (!this.layerCache[source.id]) {\n+ selectionLayer = new OpenLayers.Layer.WMS(source.name,\n+ source.url, source.params,\n+ OpenLayers.Util.applyDefaults(\n+ this.layerOptions,\n+ source.getOptions())\n+ );\n+ this.layerCache[source.id] = selectionLayer;\n+ // make sure the layers are coupled wrt visibility, but only\n+ // if they are not displayed in the layer switcher, because in\n+ // that case the user cannot control visibility.\n+ if (this.layerOptions.displayInLayerSwitcher === false) {\n+ source.events.on({\n+ \"visibilitychanged\": this.coupleLayerVisiblity,\n+ scope: selectionLayer\n+ });\n+ }\n+ this.map.addLayer(selectionLayer);\n+ } else {\n+ selectionLayer = this.layerCache[source.id];\n+ }\n+ return selectionLayer;\n+ },\n+\n+ /**\n+ * Method: createSLD\n+ * Create the SLD document for the layer using the supplied filters.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.WMS>}\n+ * filters - Array({<OpenLayers.Filter>}) The filters to be applied.\n+ * geometryAttributes - Array({Object}) The geometry attributes of the \n * layer.\n- * tolerance - {Float} The distance (in pixels) at which snapping may occur.\n- * Default is 10.\n- * node - {Boolean} Snap to nodes (first or last point in a geometry) in\n- * target layer. Default is true.\n- * nodeTolerance - {Float} Optional distance at which snapping may occur\n- * for nodes specifically. If none is provided, <tolerance> will be\n- * used.\n- * vertex - {Boolean} Snap to vertices in target layer. Default is true.\n- * vertexTolerance - {Float} Optional distance at which snapping may occur\n- * for vertices specifically. If none is provided, <tolerance> will be\n- * used.\n- * edge - {Boolean} Snap to edges in target layer. Default is true.\n- * edgeTolerance - {Float} Optional distance at which snapping may occur\n- * for edges specifically. If none is provided, <tolerance> will be\n- * used.\n- * filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if\n- * feature is eligible for snapping. If filter evaluates to true for a\n- * target feature a vertex may be snapped to the feature. \n- * minResolution - {Number} If a minResolution is provided, snapping to this\n- * target will only be considered if the map resolution is greater than\n- * or equal to this value (the minResolution is inclusive). Default is\n- * no minimum resolution limit.\n- * maxResolution - {Number} If a maxResolution is provided, snapping to this\n- * target will only be considered if the map resolution is strictly\n- * less than this value (the maxResolution is exclusive). Default is\n- * no maximum resolution limit.\n+ *\n+ * Returns:\n+ * {String} The SLD document generated as a string.\n */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.options = options || {}; // TODO: this could be done by the super\n+ createSLD: function(layer, filters, geometryAttributes) {\n+ var sld = {\n+ version: \"1.0.0\",\n+ namedLayers: {}\n+ };\n+ var layerNames = [layer.params.LAYERS].join(\",\").split(\",\");\n+ for (var i = 0, len = layerNames.length; i < len; i++) {\n+ var name = layerNames[i];\n+ sld.namedLayers[name] = {\n+ name: name,\n+ userStyles: []\n+ };\n+ var symbolizer = this.selectionSymbolizer;\n+ var geometryAttribute = geometryAttributes[i];\n+ if (geometryAttribute.type.indexOf('Polygon') >= 0) {\n+ symbolizer = {\n+ Polygon: this.selectionSymbolizer['Polygon']\n+ };\n+ } else if (geometryAttribute.type.indexOf('LineString') >= 0) {\n+ symbolizer = {\n+ Line: this.selectionSymbolizer['Line']\n+ };\n+ } else if (geometryAttribute.type.indexOf('Point') >= 0) {\n+ symbolizer = {\n+ Point: this.selectionSymbolizer['Point']\n+ };\n+ }\n+ var filter = filters[i];\n+ sld.namedLayers[name].userStyles.push({\n+ name: 'default',\n+ rules: [\n+ new OpenLayers.Rule({\n+ symbolizer: symbolizer,\n+ filter: filter,\n+ maxScaleDenominator: layer.options.minScale\n+ })\n+ ]\n+ });\n+ }\n+ return new OpenLayers.Format.SLD({\n+ srsName: this.map.getProjection()\n+ }).write(sld);\n+ },\n \n- // set the editable layer if provided\n- if (this.options.layer) {\n- this.setLayer(this.options.layer);\n+ /**\n+ * Method: parseDescribeLayer\n+ * Parse the SLD WMS DescribeLayer response and issue the corresponding\n+ * WFS DescribeFeatureType request\n+ *\n+ * request - {XMLHttpRequest} The request object.\n+ */\n+ parseDescribeLayer: function(request) {\n+ var format = new OpenLayers.Format.WMSDescribeLayer();\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n }\n- // configure target layers\n- var defaults = OpenLayers.Util.extend({}, this.options.defaults);\n- this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);\n- this.setTargets(this.options.targets);\n- if (this.targets.length === 0 && this.layer) {\n- this.addTargetLayer(this.layer);\n+ var describeLayer = format.read(doc);\n+ var typeNames = [];\n+ var url = null;\n+ for (var i = 0, len = describeLayer.length; i < len; i++) {\n+ // perform a WFS DescribeFeatureType request\n+ if (describeLayer[i].owsType == \"WFS\") {\n+ typeNames.push(describeLayer[i].typeName);\n+ url = describeLayer[i].owsURL;\n+ }\n }\n+ var options = {\n+ url: url,\n+ params: {\n+ SERVICE: \"WFS\",\n+ TYPENAME: typeNames.toString(),\n+ REQUEST: \"DescribeFeatureType\",\n+ VERSION: \"1.0.0\"\n+ },\n+ callback: function(request) {\n+ var format = new OpenLayers.Format.WFSDescribeFeatureType();\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ var describeFeatureType = format.read(doc);\n+ this.control.wfsCache[this.layer.id] = describeFeatureType;\n+ this.control._queue && this.control.applySelection();\n+ },\n+ scope: this\n+ };\n+ OpenLayers.Request.GET(options);\n+ },\n \n- this.geoToleranceCache = {};\n+ /**\n+ * Method: getGeometryAttributes\n+ * Look up the geometry attributes from the WFS DescribeFeatureType response\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the \n+ * geometry attributes.\n+ *\n+ * Returns:\n+ * Array({Object}) Array of geometry attributes\n+ */\n+ getGeometryAttributes: function(layer) {\n+ var result = [];\n+ var cache = this.wfsCache[layer.id];\n+ for (var i = 0, len = cache.featureTypes.length; i < len; i++) {\n+ var typeName = cache.featureTypes[i];\n+ var properties = typeName.properties;\n+ for (var j = 0, lenj = properties.length; j < lenj; j++) {\n+ var property = properties[j];\n+ var type = property.type;\n+ if ((type.indexOf('LineString') >= 0) ||\n+ (type.indexOf('GeometryAssociationType') >= 0) ||\n+ (type.indexOf('GeometryPropertyType') >= 0) ||\n+ (type.indexOf('Point') >= 0) ||\n+ (type.indexOf('Polygon') >= 0)) {\n+ result.push(property);\n+ }\n+ }\n+ }\n+ return result;\n },\n \n /**\n- * APIMethod: setLayer\n- * Set the editable layer. Call the setLayer method if the editable layer\n- * changes and the same control should be used on a new editable layer.\n+ * APIMethod: activate\n+ * Activate the control. Activating the control will perform a SLD WMS\n+ * DescribeLayer request followed by a WFS DescribeFeatureType request\n+ * so that the proper symbolizers can be chosen based on the geometry\n+ * type.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer && !this.wfsCache[layer.id]) {\n+ var options = {\n+ url: layer.url,\n+ params: {\n+ SERVICE: \"WMS\",\n+ VERSION: layer.params.VERSION,\n+ LAYERS: layer.params.LAYERS,\n+ REQUEST: \"DescribeLayer\"\n+ },\n+ callback: this.parseDescribeLayer,\n+ scope: {\n+ layer: layer,\n+ control: this\n+ }\n+ };\n+ OpenLayers.Request.GET(options);\n+ }\n+ }\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ * Deactivate the control. If clearOnDeactivate is true, remove the\n+ * selection layer(s).\n+ */\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer && this.clearOnDeactivate === true) {\n+ var layerCache = this.layerCache;\n+ var selectionLayer = layerCache[layer.id];\n+ if (selectionLayer) {\n+ layer.events.un({\n+ \"visibilitychanged\": this.coupleLayerVisiblity,\n+ scope: selectionLayer\n+ });\n+ selectionLayer.destroy();\n+ delete layerCache[layer.id];\n+ }\n+ }\n+ }\n+ }\n+ return deactivated;\n+ },\n+\n+ /**\n+ * APIMethod: setLayers\n+ * Set the layers on which the selection should be performed. Call the \n+ * setLayers method if the layer(s) to be used change and the same \n+ * control should be used on a new set of layers.\n * If the control is already active, it will be active after the new\n- * layer is set.\n+ * set of layers is set.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} The new editable layer.\n+ * layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which \n+ * the selection should be performed.\n */\n- setLayer: function(layer) {\n+ setLayers: function(layers) {\n if (this.active) {\n this.deactivate();\n- this.layer = layer;\n+ this.layers = layers;\n this.activate();\n } else {\n- this.layer = layer;\n+ this.layers = layers;\n }\n },\n \n /**\n- * Method: setTargets\n- * Set the targets for the snapping agent.\n+ * Function: createFilter\n+ * Create the filter to be used in the SLD.\n *\n * Parameters:\n- * targets - {Array} An array of target configs or target layers.\n+ * geometryAttribute - {Object} Used to get the name of the geometry \n+ * attribute which is needed for constructing the spatial filter.\n+ * geometry - {<OpenLayers.Geometry>} The geometry to use.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Filter.Spatial>} The spatial filter created.\n */\n- setTargets: function(targets) {\n- this.targets = [];\n- if (targets && targets.length) {\n- var target;\n- for (var i = 0, len = targets.length; i < len; ++i) {\n- target = targets[i];\n- if (target instanceof OpenLayers.Layer.Vector) {\n- this.addTargetLayer(target);\n- } else {\n- this.addTarget(target);\n+ createFilter: function(geometryAttribute, geometry) {\n+ var filter = null;\n+ if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {\n+ // box\n+ if (this.handler.irregular === true) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ property: geometryAttribute.name,\n+ value: geometry.getBounds()\n+ });\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ });\n+ }\n+ } else if (this.handler instanceof OpenLayers.Handler.Polygon) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ });\n+ } else if (this.handler instanceof OpenLayers.Handler.Path) {\n+ // if source layer is point based, use DWITHIN instead\n+ if (geometryAttribute.type.indexOf('Point') >= 0) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.DWITHIN,\n+ property: geometryAttribute.name,\n+ distance: this.map.getExtent().getWidth() * 0.01,\n+ distanceUnits: this.map.getUnits(),\n+ value: geometry\n+ });\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ });\n+ }\n+ } else if (this.handler instanceof OpenLayers.Handler.Click) {\n+ if (geometryAttribute.type.indexOf('Polygon') >= 0) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ });\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.DWITHIN,\n+ property: geometryAttribute.name,\n+ distance: this.map.getExtent().getWidth() * 0.01,\n+ distanceUnits: this.map.getUnits(),\n+ value: geometry\n+ });\n+ }\n+ }\n+ return filter;\n+ },\n+\n+ /**\n+ * Method: select\n+ * When the handler is done, use SLD_BODY on the selection layer to\n+ * display the selection in the map.\n+ *\n+ * Parameters:\n+ * geometry - {Object} or {<OpenLayers.Geometry>}\n+ */\n+ select: function(geometry) {\n+ this._queue = function() {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ var geometryAttributes = this.getGeometryAttributes(layer);\n+ var filters = [];\n+ for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) {\n+ var geometryAttribute = geometryAttributes[j];\n+ if (geometryAttribute !== null) {\n+ // from the click handler we will not get an actual \n+ // geometry so transform\n+ if (!(geometry instanceof OpenLayers.Geometry)) {\n+ var point = this.map.getLonLatFromPixel(\n+ geometry.xy);\n+ geometry = new OpenLayers.Geometry.Point(\n+ point.lon, point.lat);\n+ }\n+ var filter = this.createFilter(geometryAttribute,\n+ geometry);\n+ if (filter !== null) {\n+ filters.push(filter);\n+ }\n+ }\n }\n+\n+ var selectionLayer = this.createSelectionLayer(layer);\n+\n+ this.events.triggerEvent(\"selected\", {\n+ layer: layer,\n+ filters: filters\n+ });\n+\n+ var sld = this.createSLD(layer, filters, geometryAttributes);\n+\n+ selectionLayer.mergeNewParams({\n+ SLD_BODY: sld\n+ });\n+ delete this._queue;\n+ }\n+ };\n+ this.applySelection();\n+ },\n+\n+ /**\n+ * Method: applySelection\n+ * Checks if all required wfs data is cached, and applies the selection\n+ */\n+ applySelection: function() {\n+ var canApply = true;\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ if (!this.wfsCache[this.layers[i].id]) {\n+ canApply = false;\n+ break;\n }\n }\n+ canApply && this._queue.call(this);\n },\n \n+ CLASS_NAME: \"OpenLayers.Control.SLDSelect\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Vector/RootContainer.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector.RootContainer\n+ * A special layer type to combine multiple vector layers inside a single\n+ * renderer root container. This class is not supposed to be instantiated\n+ * from user space, it is a helper class for controls that require event\n+ * processing for multiple vector layers.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Vector>\n+ */\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+\n /**\n- * Method: addTargetLayer\n- * Add a target layer with the default target config.\n+ * Property: displayInLayerSwitcher\n+ * Set to false for this layer type\n+ */\n+ displayInLayerSwitcher: false,\n+\n+ /**\n+ * APIProperty: layers\n+ * Layers that are attached to this container. Required config option.\n+ */\n+ layers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Vector.RootContainer\n+ * Create a new root container for multiple vector layer. This constructor\n+ * is not supposed to be used from user space, it is only to be used by\n+ * controls that need feature selection across multiple vector layers.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} A target layer.\n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n+ * \n+ * Required options properties:\n+ * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n+ * container\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n+ * container\n */\n- addTargetLayer: function(layer) {\n- this.addTarget({\n- layer: layer\n+\n+ /**\n+ * Method: display\n+ */\n+ display: function() {},\n+\n+ /**\n+ * Method: getFeatureFromEvent\n+ * walk through the layers to find the feature returned by the event\n+ * \n+ * Parameters:\n+ * evt - {Object} event object with a feature property\n+ * \n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: setMap\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer);\n+ },\n+\n+ /**\n+ * Method: removeMap\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ removeMap: function(map) {\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: collectRoots\n+ * Collects the root nodes of all layers this control is configured with\n+ * and moveswien the nodes to this control's layer\n+ */\n+ collectRoots: function() {\n+ var layer;\n+ // walk through all map layers, because we want to keep the order\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: resetRoots\n+ * Resets the root nodes back into the layers they belong to.\n+ */\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: handleChangeLayer\n+ * Event handler for the map's changelayer event. We need to rebuild\n+ * this container's layer dom if order of one of its layers changes.\n+ * This handler is added with the setMap method, and removed with the\n+ * removeMap method.\n+ * \n+ * Parameters:\n+ * evt - {Object}\n+ */\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" &&\n+ OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/SelectFeature.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Handler/Feature.js\n+ * @requires OpenLayers/Layer/Vector/RootContainer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.SelectFeature\n+ * The SelectFeature control selects vector features from a given layer on \n+ * click or hover. \n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforefeaturehighlighted - Triggered before a feature is highlighted\n+ * featurehighlighted - Triggered when a feature is highlighted\n+ * featureunhighlighted - Triggered when a feature is unhighlighted\n+ * boxselectionstart - Triggered before box selection starts\n+ * boxselectionend - Triggered after box selection ends\n+ */\n+\n+ /**\n+ * Property: multipleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <multiple> property to true. Default is null.\n+ */\n+ multipleKey: null,\n+\n+ /**\n+ * Property: toggleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <toggle> property to true. Default is null.\n+ */\n+ toggleKey: null,\n+\n+ /**\n+ * APIProperty: multiple\n+ * {Boolean} Allow selection of multiple geometries. Default is false.\n+ */\n+ multiple: false,\n+\n+ /**\n+ * APIProperty: clickout\n+ * {Boolean} Unselect features when clicking outside any feature.\n+ * Default is true.\n+ */\n+ clickout: true,\n+\n+ /**\n+ * APIProperty: toggle\n+ * {Boolean} Unselect a selected feature on click. Default is false. Only\n+ * has meaning if hover is false.\n+ */\n+ toggle: false,\n+\n+ /**\n+ * APIProperty: hover\n+ * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n+ * ignores clicks and only listens to mouse moves.\n+ */\n+ hover: false,\n+\n+ /**\n+ * APIProperty: highlightOnly\n+ * {Boolean} If true do not actually select features (that is place them in \n+ * the layer's selected features array), just highlight them. This property\n+ * has no effect if hover is false. Defaults to false.\n+ */\n+ highlightOnly: false,\n+\n+ /**\n+ * APIProperty: box\n+ * {Boolean} Allow feature selection by drawing a box.\n+ */\n+ box: false,\n+\n+ /**\n+ * Property: onBeforeSelect \n+ * {Function} Optional function to be called before a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onBeforeSelect: function() {},\n+\n+ /**\n+ * APIProperty: onSelect \n+ * {Function} Optional function to be called when a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onSelect: function() {},\n+\n+ /**\n+ * APIProperty: onUnselect\n+ * {Function} Optional function to be called when a feature is unselected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onUnselect: function() {},\n+\n+ /**\n+ * Property: scope\n+ * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n+ * callbacks. If null the scope will be this control.\n+ */\n+ scope: null,\n+\n+ /**\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict selecting to a limited set of geometry types,\n+ * send a list of strings corresponding to the geometry class names.\n+ */\n+ geometryTypes: null,\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n+ * root for all layers this control is configured with (if an array of\n+ * layers was passed to the constructor), or the vector layer the control\n+ * was configured with (if a single layer was passed to the constructor).\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: layers\n+ * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n+ * or null if the control was configured with a single layer\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: callbacks\n+ * {Object} The functions that are sent to the handlers.feature for callback\n+ */\n+ callbacks: null,\n+\n+ /**\n+ * APIProperty: selectStyle \n+ * {Object} Hash of styles\n+ */\n+ selectStyle: null,\n+\n+ /**\n+ * Property: renderIntent\n+ * {String} key used to retrieve the select style from the layer's\n+ * style map.\n+ */\n+ renderIntent: \"select\",\n+\n+ /**\n+ * Property: handlers\n+ * {Object} Object with references to multiple <OpenLayers.Handler>\n+ * instances.\n+ */\n+ handlers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.SelectFeature\n+ * Create a new control for selecting features.\n+ *\n+ * Parameters:\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n+ * layer(s) this control will select features from.\n+ * options - {Object} \n+ */\n+ initialize: function(layers, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+\n+ if (this.scope === null) {\n+ this.scope = this;\n+ }\n+ this.initLayer(layers);\n+ var callbacks = {\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature\n+ };\n+ if (this.hover) {\n+ callbacks.over = this.overFeature;\n+ callbacks.out = this.outFeature;\n+ }\n+\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlers = {\n+ feature: new OpenLayers.Handler.Feature(\n+ this, this.layer, this.callbacks, {\n+ geometryTypes: this.geometryTypes\n+ }\n+ )\n+ };\n+\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(\n+ this, {\n+ done: this.selectBox\n+ }, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ }\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: initLayer\n+ * Assign the layer property. If layers is an array, we need to use\n+ * a RootContainer.\n+ *\n+ * Parameters:\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n+ */\n+ initLayer: function(layers) {\n+ if (OpenLayers.Util.isArray(layers)) {\n+ this.layers = layers;\n+ this.layer = new OpenLayers.Layer.Vector.RootContainer(\n+ this.id + \"_container\", {\n+ layers: layers\n+ }\n+ );\n+ } else {\n+ this.layer = layers;\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ if (this.active && this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.layers) {\n+ this.layer.destroy();\n+ }\n+ },\n+\n+ /**\n+ * Method: activate\n+ * Activates the control.\n+ * \n+ * Returns:\n+ * {Boolean} The control was effectively activated.\n+ */\n+ activate: function() {\n+ if (!this.active) {\n+ if (this.layers) {\n+ this.map.addLayer(this.layer);\n+ }\n+ this.handlers.feature.activate();\n+ if (this.box && this.handlers.box) {\n+ this.handlers.box.activate();\n+ }\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(\n+ this, arguments\n+ );\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ * Deactivates the control.\n+ * \n+ * Returns:\n+ * {Boolean} The control was effectively deactivated.\n+ */\n+ deactivate: function() {\n+ if (this.active) {\n+ this.handlers.feature.deactivate();\n+ if (this.handlers.box) {\n+ this.handlers.box.deactivate();\n+ }\n+ if (this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ }\n+ return OpenLayers.Control.prototype.deactivate.apply(\n+ this, arguments\n+ );\n+ },\n+\n+ /**\n+ * Method: unselectAll\n+ * Unselect all selected features. To unselect all except for a single\n+ * feature, set the options.except property to the feature.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional configuration object.\n+ */\n+ unselectAll: function(options) {\n+ // we'll want an option to supress notification here\n+ var layers = this.layers || [this.layer],\n+ layer, feature, l, numExcept;\n+ for (l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ numExcept = 0;\n+ //layer.selectedFeatures is null when layer is destroyed and \n+ //one of it's preremovelayer listener calls setLayer \n+ //with another layer on this control\n+ if (layer.selectedFeatures != null) {\n+ while (layer.selectedFeatures.length > numExcept) {\n+ feature = layer.selectedFeatures[numExcept];\n+ if (!options || options.except != feature) {\n+ this.unselect(feature);\n+ } else {\n+ ++numExcept;\n+ }\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: clickFeature\n+ * Called on click in a feature\n+ * Only responds if this.hover is false.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ clickFeature: function(feature) {\n+ if (!this.hover) {\n+ var selected = (OpenLayers.Util.indexOf(\n+ feature.layer.selectedFeatures, feature) > -1);\n+ if (selected) {\n+ if (this.toggleSelect()) {\n+ this.unselect(feature);\n+ } else if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n+ } else {\n+ if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n+ this.select(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: multipleSelect\n+ * Allow for multiple selected features based on <multiple> property and\n+ * <multipleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Allow for multiple selected features.\n+ */\n+ multipleSelect: function() {\n+ return this.multiple || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.multipleKey]);\n+ },\n+\n+ /**\n+ * Method: toggleSelect\n+ * Event should toggle the selected state of a feature based on <toggle>\n+ * property and <toggleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Toggle the selected state of a feature.\n+ */\n+ toggleSelect: function() {\n+ return this.toggle || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.toggleKey]);\n+ },\n+\n+ /**\n+ * Method: clickoutFeature\n+ * Called on click outside a previously clicked (selected) feature.\n+ * Only responds if this.hover is false.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Vector.Feature>} \n+ */\n+ clickoutFeature: function(feature) {\n+ if (!this.hover && this.clickout) {\n+ this.unselectAll();\n+ }\n+ },\n+\n+ /**\n+ * Method: overFeature\n+ * Called on over a feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ overFeature: function(feature) {\n+ var layer = feature.layer;\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ this.highlight(feature);\n+ } else if (OpenLayers.Util.indexOf(\n+ layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: outFeature\n+ * Called on out of a selected feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ outFeature: function(feature) {\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ // we do nothing if we're not the last highlighter of the\n+ // feature\n+ if (feature._lastHighlighter == this.id) {\n+ // if another select control had highlighted the feature before\n+ // we did it ourself then we use that control to highlight the\n+ // feature as it was before we highlighted it, else we just\n+ // unhighlight it\n+ if (feature._prevHighlighter &&\n+ feature._prevHighlighter != this.id) {\n+ delete feature._lastHighlighter;\n+ var control = this.map.getControl(\n+ feature._prevHighlighter);\n+ if (control) {\n+ control.highlight(feature);\n+ }\n+ } else {\n+ this.unhighlight(feature);\n+ }\n+ }\n+ } else {\n+ this.unselect(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: highlight\n+ * Redraw feature with the select style.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ highlight: function(feature) {\n+ var layer = feature.layer;\n+ var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n+ feature: feature\n });\n+ if (cont !== false) {\n+ feature._prevHighlighter = feature._lastHighlighter;\n+ feature._lastHighlighter = this.id;\n+ var style = this.selectStyle || this.renderIntent;\n+ layer.drawFeature(feature, style);\n+ this.events.triggerEvent(\"featurehighlighted\", {\n+ feature: feature\n+ });\n+ }\n },\n \n /**\n- * Method: addTarget\n- * Add a configured target layer.\n+ * Method: unhighlight\n+ * Redraw feature with the \"default\" style\n *\n * Parameters:\n- * target - {Object} A target config.\n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- addTarget: function(target) {\n- target = OpenLayers.Util.applyDefaults(target, this.defaults);\n- target.nodeTolerance = target.nodeTolerance || target.tolerance;\n- target.vertexTolerance = target.vertexTolerance || target.tolerance;\n- target.edgeTolerance = target.edgeTolerance || target.tolerance;\n- this.targets.push(target);\n+ unhighlight: function(feature) {\n+ var layer = feature.layer;\n+ // three cases:\n+ // 1. there's no other highlighter, in that case _prev is undefined,\n+ // and we just need to undef _last\n+ // 2. another control highlighted the feature after we did it, in\n+ // that case _last references this other control, and we just\n+ // need to undef _prev\n+ // 3. another control highlighted the feature before we did it, in\n+ // that case _prev references this other control, and we need to\n+ // set _last to _prev and undef _prev\n+ if (feature._prevHighlighter == undefined) {\n+ delete feature._lastHighlighter;\n+ } else if (feature._prevHighlighter == this.id) {\n+ delete feature._prevHighlighter;\n+ } else {\n+ feature._lastHighlighter = feature._prevHighlighter;\n+ delete feature._prevHighlighter;\n+ }\n+ layer.drawFeature(feature, feature.style || feature.layer.style ||\n+ \"default\");\n+ this.events.triggerEvent(\"featureunhighlighted\", {\n+ feature: feature\n+ });\n+ },\n+\n+ /**\n+ * Method: select\n+ * Add feature to the layer's selectedFeature array, render the feature as\n+ * selected, and call the onSelect function.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ select: function(feature) {\n+ var cont = this.onBeforeSelect.call(this.scope, feature);\n+ var layer = feature.layer;\n+ if (cont !== false) {\n+ cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ layer.selectedFeatures.push(feature);\n+ this.highlight(feature);\n+ // if the feature handler isn't involved in the feature\n+ // selection (because the box handler is used or the\n+ // feature is selected programatically) we fake the\n+ // feature handler to allow unselecting on click\n+ if (!this.handlers.feature.lastFeature) {\n+ this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n+ }\n+ layer.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ });\n+ this.onSelect.call(this.scope, feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: unselect\n+ * Remove feature from the layer's selectedFeature array, render the feature as\n+ * normal, and call the onUnselect function.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ unselect: function(feature) {\n+ var layer = feature.layer;\n+ // Store feature style for restoration later\n+ this.unhighlight(feature);\n+ OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n+ layer.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n+ });\n+ this.onUnselect.call(this.scope, feature);\n+ },\n+\n+ /**\n+ * Method: selectBox\n+ * Callback from the handlers.box set up when <box> selection is true\n+ * on.\n+ *\n+ * Parameters:\n+ * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n+ */\n+ selectBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ var bounds = new OpenLayers.Bounds(\n+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n+ );\n+\n+ // if multiple is false, first deselect currently selected features\n+ if (!this.multipleSelect()) {\n+ this.unselectAll();\n+ }\n+\n+ // because we're using a box, we consider we want multiple selection\n+ var prevMultiple = this.multiple;\n+ this.multiple = true;\n+ var layers = this.layers || [this.layer];\n+ this.events.triggerEvent(\"boxselectionstart\", {\n+ layers: layers\n+ });\n+ var layer;\n+ for (var l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ for (var i = 0, len = layer.features.length; i < len; ++i) {\n+ var feature = layer.features[i];\n+ // check if the feature is displayed\n+ if (!feature.getVisibility()) {\n+ continue;\n+ }\n+\n+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n+ this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n+ if (bounds.toGeometry().intersects(feature.geometry)) {\n+ if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n+ }\n+ }\n+ }\n+ }\n+ }\n+ this.multiple = prevMultiple;\n+ this.events.triggerEvent(\"boxselectionend\", {\n+ layers: layers\n+ });\n+ }\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * Set the map property for the control. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ this.handlers.feature.setMap(map);\n+ if (this.box) {\n+ this.handlers.box.setMap(map);\n+ }\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ },\n+\n+ /**\n+ * APIMethod: setLayer\n+ * Attach a new layer to the control, overriding any existing layers.\n+ *\n+ * Parameters:\n+ * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n+ * {<OpenLayers.Layer.Vector>}\n+ */\n+ setLayer: function(layers) {\n+ var isActive = this.active;\n+ this.unselectAll();\n+ this.deactivate();\n+ if (this.layers) {\n+ this.layer.destroy();\n+ this.layers = null;\n+ }\n+ this.initLayer(layers);\n+ this.handlers.feature.layer = this.layer;\n+ if (isActive) {\n+ this.activate();\n+ }\n },\n \n- /**\n- * Method: removeTargetLayer\n- * Remove a target layer.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} The target layer to remove.\n+ CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Tile/UTFGrid.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Tile.js\n+ * @requires OpenLayers/Format/JSON.js\n+ * @requires OpenLayers/Request.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Tile.UTFGrid\n+ * Instances of OpenLayers.Tile.UTFGrid are used to manage \n+ * UTFGrids. This is an unusual tile type in that it doesn't have a\n+ * rendered image; only a 'hit grid' that can be used to \n+ * look up feature attributes.\n+ *\n+ * See the <OpenLayers.Tile.UTFGrid> constructor for details on constructing a\n+ * new instance.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Tile>\n+ */\n+OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n+\n+ /** \n+ * Property: url\n+ * {String}\n+ * The URL of the UTFGrid file being requested. Provided by the <getURL>\n+ * method. \n+ */\n+ url: null,\n+\n+ /**\n+ * Property: utfgridResolution\n+ * {Number}\n+ * Ratio of the pixel width to the width of a UTFGrid data point. If an \n+ * entry in the grid represents a 4x4 block of pixels, the \n+ * utfgridResolution would be 4. Default is 2.\n+ */\n+ utfgridResolution: 2,\n+\n+ /** \n+ * Property: json\n+ * {Object}\n+ * Stores the parsed JSON tile data structure. \n+ */\n+ json: null,\n+\n+ /** \n+ * Property: format\n+ * {OpenLayers.Format.JSON}\n+ * Parser instance used to parse JSON for cross browser support. The native\n+ * JSON.parse method will be used where available (all except IE<8).\n+ */\n+ format: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Tile.UTFGrid\n+ * Constructor for a new <OpenLayers.Tile.UTFGrid> instance.\n+ * \n+ * Parameters:\n+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n+ * position - {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * url - {<String>} Deprecated. Remove me in 3.0.\n+ * size - {<OpenLayers.Size>}\n+ * options - {Object}\n+ */\n+\n+ /** \n+ * APIMethod: destroy\n+ * Clean up.\n */\n- removeTargetLayer: function(layer) {\n- var target;\n- for (var i = this.targets.length - 1; i >= 0; --i) {\n- target = this.targets[i];\n- if (target.layer === layer) {\n- this.removeTarget(target);\n- }\n- }\n+ destroy: function() {\n+ this.clear();\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: removeTarget\n- * Remove a target.\n- *\n- * Parameters:\n- * target - {Object} A target config.\n- *\n+ * Method: draw\n+ * Check that a tile should be drawn, and draw it.\n+ * In the case of UTFGrids, \"drawing\" it means fetching and\n+ * parsing the json. \n+ * \n * Returns:\n- * {Array} The targets array.\n+ * {Boolean} Was a tile drawn?\n */\n- removeTarget: function(target) {\n- return OpenLayers.Util.removeItem(this.targets, target);\n- },\n+ draw: function() {\n+ var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (drawn) {\n+ if (this.isLoading) {\n+ this.abortLoading();\n+ //if we're already loading, send 'reload' instead of 'loadstart'.\n+ this.events.triggerEvent(\"reload\");\n+ } else {\n+ this.isLoading = true;\n+ this.events.triggerEvent(\"loadstart\");\n+ }\n+ this.url = this.layer.getURL(this.bounds);\n \n- /**\n- * APIMethod: activate\n- * Activate the control. Activating the control registers listeners for\n- * editing related events so that during feature creation and\n- * modification, moving vertices will trigger snapping.\n- */\n- activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- if (this.layer && this.layer.events) {\n- this.layer.events.on({\n- sketchstarted: this.onSketchModified,\n- sketchmodified: this.onSketchModified,\n- vertexmodified: this.onVertexModified,\n+ if (this.layer.useJSONP) {\n+ // Use JSONP method to avoid xbrowser policy\n+ var ols = new OpenLayers.Protocol.Script({\n+ url: this.url,\n+ callback: function(response) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ this.json = response.data;\n+ },\n scope: this\n });\n- }\n- }\n- return activated;\n- },\n-\n- /**\n- * APIMethod: deactivate\n- * Deactivate the control. Deactivating the control unregisters listeners\n- * so feature editing may proceed without engaging the snapping agent.\n- */\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.layer && this.layer.events) {\n- this.layer.events.un({\n- sketchstarted: this.onSketchModified,\n- sketchmodified: this.onSketchModified,\n- vertexmodified: this.onVertexModified,\n+ ols.read();\n+ this.request = ols;\n+ } else {\n+ // Use standard XHR\n+ this.request = OpenLayers.Request.GET({\n+ url: this.url,\n+ callback: function(response) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ if (response.status === 200) {\n+ this.parseData(response.responseText);\n+ }\n+ },\n scope: this\n });\n }\n+ } else {\n+ this.unload();\n }\n- this.feature = null;\n- this.point = null;\n- return deactivated;\n+ return drawn;\n },\n \n /**\n- * Method: onSketchModified\n- * Registered as a listener for the sketchmodified event on the editable\n- * layer.\n- *\n- * Parameters:\n- * event - {Object} The sketch modified event.\n+ * Method: abortLoading\n+ * Cancel a pending request.\n */\n- onSketchModified: function(event) {\n- this.feature = event.feature;\n- this.considerSnapping(event.vertex, event.vertex);\n+ abortLoading: function() {\n+ if (this.request) {\n+ this.request.abort();\n+ delete this.request;\n+ }\n+ this.isLoading = false;\n },\n \n /**\n- * Method: onVertexModified\n- * Registered as a listener for the vertexmodified event on the editable\n- * layer.\n+ * Method: getFeatureInfo\n+ * Get feature information associated with a pixel offset. If the pixel\n+ * offset corresponds to a feature, the returned object will have id\n+ * and data properties. Otherwise, null will be returned.\n+ * \n *\n * Parameters:\n- * event - {Object} The vertex modified event.\n+ * i - {Number} X-axis pixel offset (from top left of tile)\n+ * j - {Number} Y-axis pixel offset (from top left of tile)\n+ *\n+ * Returns:\n+ * {Object} Object with feature id and data properties corresponding to the \n+ * given pixel offset.\n */\n- onVertexModified: function(event) {\n- this.feature = event.feature;\n- var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);\n- this.considerSnapping(\n- event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)\n- );\n+ getFeatureInfo: function(i, j) {\n+ var info = null;\n+ if (this.json) {\n+ var id = this.getFeatureId(i, j);\n+ if (id !== null) {\n+ info = {\n+ id: id,\n+ data: this.json.data[id]\n+ };\n+ }\n+ }\n+ return info;\n },\n \n /**\n- * Method: considerSnapping\n+ * Method: getFeatureId\n+ * Get the identifier for the feature associated with a pixel offset.\n *\n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or\n- * unsnapped).\n- * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map\n- * coords.\n+ * i - {Number} X-axis pixel offset (from top left of tile)\n+ * j - {Number} Y-axis pixel offset (from top left of tile)\n+ *\n+ * Returns:\n+ * {Object} The feature identifier corresponding to the given pixel offset.\n+ * Returns null if pixel doesn't correspond to a feature.\n */\n- considerSnapping: function(point, loc) {\n- var best = {\n- rank: Number.POSITIVE_INFINITY,\n- dist: Number.POSITIVE_INFINITY,\n- x: null,\n- y: null\n- };\n- var snapped = false;\n- var result, target;\n- for (var i = 0, len = this.targets.length; i < len; ++i) {\n- target = this.targets[i];\n- result = this.testTarget(target, loc);\n- if (result) {\n- if (this.greedy) {\n- best = result;\n- best.target = target;\n- snapped = true;\n- break;\n- } else {\n- if ((result.rank < best.rank) ||\n- (result.rank === best.rank && result.dist < best.dist)) {\n- best = result;\n- best.target = target;\n- snapped = true;\n- }\n- }\n- }\n- }\n- if (snapped) {\n- var proceed = this.events.triggerEvent(\"beforesnap\", {\n- point: point,\n- x: best.x,\n- y: best.y,\n- distance: best.dist,\n- layer: best.target.layer,\n- snapType: this.precedence[best.rank]\n- });\n- if (proceed !== false) {\n- point.x = best.x;\n- point.y = best.y;\n- this.point = point;\n- this.events.triggerEvent(\"snap\", {\n- point: point,\n- snapType: this.precedence[best.rank],\n- layer: best.target.layer,\n- distance: best.dist\n- });\n- } else {\n- snapped = false;\n+ getFeatureId: function(i, j) {\n+ var id = null;\n+ if (this.json) {\n+ var resolution = this.utfgridResolution;\n+ var row = Math.floor(j / resolution);\n+ var col = Math.floor(i / resolution);\n+ var charCode = this.json.grid[row].charCodeAt(col);\n+ var index = this.indexFromCharCode(charCode);\n+ var keys = this.json.keys;\n+ if (!isNaN(index) && (index in keys)) {\n+ id = keys[index];\n }\n }\n- if (this.point && !snapped) {\n- point.x = loc.x;\n- point.y = loc.y;\n- this.point = null;\n- this.events.triggerEvent(\"unsnap\", {\n- point: point\n- });\n- }\n+ return id;\n },\n \n /**\n- * Method: testTarget\n+ * Method: indexFromCharCode\n+ * Given a character code for one of the UTFGrid \"grid\" characters, \n+ * resolve the integer index for the feature id in the UTFGrid \"keys\"\n+ * array.\n *\n * Parameters:\n- * target - {Object} Object with target layer configuration.\n- * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map\n- * coords.\n+ * charCode - {Integer}\n *\n * Returns:\n- * {Object} A result object with rank, dist, x, and y properties.\n- * Returns null if candidate is not eligible for snapping.\n+ * {Integer} Index for the feature id from the keys array.\n */\n- testTarget: function(target, loc) {\n- var resolution = this.layer.map.getResolution();\n- if (\"minResolution\" in target) {\n- if (resolution < target.minResolution) {\n- return null;\n- }\n- }\n- if (\"maxResolution\" in target) {\n- if (resolution >= target.maxResolution) {\n- return null;\n- }\n+ indexFromCharCode: function(charCode) {\n+ if (charCode >= 93) {\n+ charCode--;\n }\n- var tolerance = {\n- node: this.getGeoTolerance(target.nodeTolerance, resolution),\n- vertex: this.getGeoTolerance(target.vertexTolerance, resolution),\n- edge: this.getGeoTolerance(target.edgeTolerance, resolution)\n- };\n- // this could be cached if we don't support setting tolerance values directly\n- var maxTolerance = Math.max(\n- tolerance.node, tolerance.vertex, tolerance.edge\n- );\n- var result = {\n- rank: Number.POSITIVE_INFINITY,\n- dist: Number.POSITIVE_INFINITY\n- };\n- var eligible = false;\n- var features = target.layer.features;\n- var feature, type, vertices, vertex, closest, dist, found;\n- var numTypes = this.precedence.length;\n- var ll = new OpenLayers.LonLat(loc.x, loc.y);\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- if (feature !== this.feature && !feature._sketch &&\n- feature.state !== OpenLayers.State.DELETE &&\n- (!target.filter || target.filter.evaluate(feature))) {\n- if (feature.atPoint(ll, maxTolerance, maxTolerance)) {\n- for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) {\n- type = this.precedence[j];\n- if (target[type]) {\n- if (type === \"edge\") {\n- closest = feature.geometry.distanceTo(loc, {\n- details: true\n- });\n- dist = closest.distance;\n- if (dist <= tolerance[type] && dist < result.dist) {\n- result = {\n- rank: j,\n- dist: dist,\n- x: closest.x0,\n- y: closest.y0 // closest coords on feature\n- };\n- eligible = true;\n- // don't look for lower precedence types for this feature\n- break;\n- }\n- } else {\n- // look for nodes or vertices\n- vertices = feature.geometry.getVertices(type === \"node\");\n- found = false;\n- for (var k = 0, klen = vertices.length; k < klen; ++k) {\n- vertex = vertices[k];\n- dist = vertex.distanceTo(loc);\n- if (dist <= tolerance[type] &&\n- (j < result.rank || (j === result.rank && dist < result.dist))) {\n- result = {\n- rank: j,\n- dist: dist,\n- x: vertex.x,\n- y: vertex.y\n- };\n- eligible = true;\n- found = true;\n- }\n- }\n- if (found) {\n- // don't look for lower precedence types for this feature\n- break;\n- }\n- }\n- }\n- }\n- }\n- }\n+ if (charCode >= 35) {\n+ charCode--;\n }\n- return eligible ? result : null;\n+ return charCode - 32;\n },\n \n /**\n- * Method: getGeoTolerance\n- * Calculate a tolerance in map units given a tolerance in pixels. This\n- * takes advantage of the <geoToleranceCache> when the map resolution\n- * has not changed.\n- * \n- * Parameters:\n- * tolerance - {Number} A tolerance value in pixels.\n- * resolution - {Number} Map resolution.\n+ * Method: parseData\n+ * Parse the JSON from a request\n *\n+ * Parameters:\n+ * str - {String} UTFGrid as a JSON string. \n+ * \n * Returns:\n- * {Number} A tolerance value in map units.\n+ * {Object} parsed javascript data\n */\n- getGeoTolerance: function(tolerance, resolution) {\n- if (resolution !== this.resolution) {\n- this.resolution = resolution;\n- this.geoToleranceCache = {};\n- }\n- var geoTolerance = this.geoToleranceCache[tolerance];\n- if (geoTolerance === undefined) {\n- geoTolerance = tolerance * resolution;\n- this.geoToleranceCache[tolerance] = geoTolerance;\n+ parseData: function(str) {\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.JSON();\n }\n- return geoTolerance;\n+ this.json = this.format.read(str);\n },\n \n- /**\n- * Method: destroy\n- * Clean up the control.\n+ /** \n+ * Method: clear\n+ * Delete data stored with this tile.\n */\n- destroy: function() {\n- if (this.active) {\n- this.deactivate(); // TODO: this should be handled by the super\n- }\n- delete this.layer;\n- delete this.targets;\n- OpenLayers.Control.prototype.destroy.call(this);\n+ clear: function() {\n+ this.json = null;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Snapping\"\n+ CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n+\n });\n /* ======================================================================\n- OpenLayers/Control/WMTSGetFeatureInfo.js\n+ OpenLayers/Tile/Image/IFrame.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Click.js\n- * @requires OpenLayers/Handler/Hover.js\n- * @requires OpenLayers/Request.js\n- * @requires OpenLayers/Format/WMSGetFeatureInfo.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Control.WMTSGetFeatureInfo\n- * The WMTSGetFeatureInfo control uses a WMTS query to get information about a \n- * point on the map. The information may be in a display-friendly format \n- * such as HTML, or a machine-friendly format such as GML, depending on the \n- * server's capabilities and the client's configuration. This control \n- * handles click or hover events, attempts to parse the results using an \n- * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer\n- * queried.\n+ * Constant: OpenLayers.Tile.Image.IFrame\n+ * Mixin for tiles that use form-encoded POST requests to get images from\n+ * remote services. Images will be loaded using HTTP-POST into an IFrame.\n *\n- * Inherits from:\n- * - <OpenLayers.Control>\n+ * This mixin will be applied to <OpenLayers.Tile.Image> instances\n+ * configured with <OpenLayers.Tile.Image.maxGetUrlLength> set.\n */\n-OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Tile.Image.IFrame = {\n \n /**\n- * APIProperty: hover\n- * {Boolean} Send GetFeatureInfo requests when mouse stops moving.\n- * Default is false.\n+ * Property: useIFrame\n+ * {Boolean} true if we are currently using an IFrame to render POST\n+ * responses, false if we are using an img element to render GET responses.\n */\n- hover: false,\n+ useIFrame: null,\n \n /**\n- * Property: requestEncoding\n- * {String} One of \"KVP\" or \"REST\". Only KVP encoding is supported at this \n- * time.\n+ * Property: blankImageUrl\n+ * {String} Using a data scheme url is not supported by all browsers, but\n+ * we don't care because we either set it as css backgroundImage, or the\n+ * image's display style is set to \"none\" when we use it.\n */\n- requestEncoding: \"KVP\",\n+ blankImageUrl: \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7\",\n \n /**\n- * APIProperty: drillDown\n- * {Boolean} Drill down over all WMTS layers in the map. When\n- * using drillDown mode, hover is not possible. A getfeatureinfo event\n- * will be fired for each layer queried.\n+ * Method: draw\n+ * Set useIFrame in the instance, and operate the image/iframe switch.\n+ * Then call Tile.Image.draw.\n+ *\n+ * Returns:\n+ * {Boolean}\n */\n- drillDown: false,\n+ draw: function() {\n+ var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);\n+ if (draw) {\n \n- /**\n- * APIProperty: maxFeatures\n- * {Integer} Maximum number of features to return from a WMTS query. This\n- * sets the feature_count parameter on WMTS GetFeatureInfo\n- * requests.\n- */\n- maxFeatures: 10,\n+ // this.url isn't set to the currect value yet, so we call getURL\n+ // on the layer and store the result in a local variable\n+ var url = this.layer.getURL(this.bounds);\n \n- /** APIProperty: clickCallback\n- * {String} The click callback to register in the\n- * {<OpenLayers.Handler.Click>} object created when the hover\n- * option is set to false. Default is \"click\".\n- */\n- clickCallback: \"click\",\n+ var usedIFrame = this.useIFrame;\n+ this.useIFrame = this.maxGetUrlLength !== null &&\n+ !this.layer.async &&\n+ url.length > this.maxGetUrlLength;\n \n- /**\n- * Property: layers\n- * {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info.\n- * If omitted, all map WMTS layers will be considered.\n- */\n- layers: null,\n+ var fromIFrame = usedIFrame && !this.useIFrame;\n+ var toIFrame = !usedIFrame && this.useIFrame;\n \n- /**\n- * APIProperty: queryVisible\n- * {Boolean} Filter out hidden layers when searching the map for layers to \n- * query. Default is true.\n- */\n- queryVisible: true,\n+ if (fromIFrame || toIFrame) {\n \n- /**\n- * Property: infoFormat\n- * {String} The mimetype to request from the server\n- */\n- infoFormat: 'text/html',\n+ // Switching between GET (image) and POST (iframe).\n \n- /**\n- * Property: vendorParams\n- * {Object} Additional parameters that will be added to the request, for\n- * WMTS implementations that support them. This could e.g. look like\n- * (start code)\n- * {\n- * radius: 5\n- * }\n- * (end)\n- */\n- vendorParams: {},\n+ // We remove the imgDiv (really either an image or an iframe)\n+ // from the frame and set it to null to make sure initImage\n+ // will call getImage.\n \n- /**\n- * Property: format\n- * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.\n- * Default is <OpenLayers.Format.WMSGetFeatureInfo>.\n- */\n- format: null,\n+ if (this.imgDiv && this.imgDiv.parentNode === this.frame) {\n+ this.frame.removeChild(this.imgDiv);\n+ }\n+ this.imgDiv = null;\n \n- /**\n- * Property: formatOptions\n- * {Object} Optional properties to set on the format (if one is not provided\n- * in the <format> property.\n- */\n- formatOptions: null,\n+ // And if we had an iframe we also remove the event pane.\n \n- /**\n- * APIProperty: handlerOptions\n- * {Object} Additional options for the handlers used by this control, e.g.\n- * (start code)\n- * {\n- * \"click\": {delay: 100},\n- * \"hover\": {delay: 300}\n- * }\n- * (end)\n- */\n+ if (fromIFrame) {\n+ this.frame.removeChild(this.frame.firstChild);\n+ }\n+ }\n+ }\n+ return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments);\n+ },\n \n /**\n- * Property: handler\n- * {Object} Reference to the <OpenLayers.Handler> for this control\n+ * Method: getImage\n+ * Creates the content for the frame on the tile.\n */\n- handler: null,\n+ getImage: function() {\n+ if (this.useIFrame === true) {\n+ if (!this.frame.childNodes.length) {\n+ var eventPane = document.createElement(\"div\"),\n+ style = eventPane.style;\n+ style.position = \"absolute\";\n+ style.width = \"100%\";\n+ style.height = \"100%\";\n+ style.zIndex = 1;\n+ style.backgroundImage = \"url(\" + this.blankImageUrl + \")\";\n+ this.frame.appendChild(eventPane);\n+ }\n \n- /**\n- * Property: hoverRequest\n- * {<OpenLayers.Request>} contains the currently running hover request\n- * (if any).\n- */\n- hoverRequest: null,\n+ var id = this.id + '_iFrame',\n+ iframe;\n+ if (parseFloat(navigator.appVersion.split(\"MSIE\")[1]) < 9) {\n+ // Older IE versions do not set the name attribute of an iFrame \n+ // properly via DOM manipulation, so we need to do it on our own with\n+ // this hack.\n+ iframe = document.createElement('<iframe name=\"' + id + '\">');\n \n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforegetfeatureinfo - Triggered before each request is sent.\n- * The event object has an *xy* property with the position of the \n- * mouse click or hover event that triggers the request and a *layer*\n- * property referencing the layer about to be queried. If a listener\n- * returns false, the request will not be issued.\n- * getfeatureinfo - Triggered when a GetFeatureInfo response is received.\n- * The event object has a *text* property with the body of the\n- * response (String), a *features* property with an array of the\n- * parsed features, an *xy* property with the position of the mouse\n- * click or hover event that triggered the request, a *layer* property\n- * referencing the layer queried and a *request* property with the \n- * request itself. If drillDown is set to true, one event will be fired\n- * for each layer queried.\n- * exception - Triggered when a GetFeatureInfo request fails (with a \n- * status other than 200) or whenparsing fails. Listeners will receive \n- * an event with *request*, *xy*, and *layer* properties. In the case \n- * of a parsing error, the event will also contain an *error* property.\n- */\n+ // IFrames in older IE versions are not transparent, if you set\n+ // the backgroundColor transparent. This is a workaround to get \n+ // transparent iframes.\n+ iframe.style.backgroundColor = '#FFFFFF';\n+ iframe.style.filter = 'chroma(color=#FFFFFF)';\n+ } else {\n+ iframe = document.createElement('iframe');\n+ iframe.style.backgroundColor = 'transparent';\n \n- /** \n- * Property: pending\n- * {Number} The number of pending requests.\n- */\n- pending: 0,\n+ // iframe.name needs to be an unique id, otherwise it \n+ // could happen that other iframes are overwritten.\n+ iframe.name = id;\n+ }\n+\n+ // some special properties to avoid scaling the images and scrollbars \n+ // in the iframe\n+ iframe.scrolling = 'no';\n+ iframe.marginWidth = '0px';\n+ iframe.marginHeight = '0px';\n+ iframe.frameBorder = '0';\n+\n+ iframe.style.position = \"absolute\";\n+ iframe.style.width = \"100%\";\n+ iframe.style.height = \"100%\";\n+\n+ if (this.layer.opacity < 1) {\n+ OpenLayers.Util.modifyDOMElement(iframe, null, null, null,\n+ null, null, null, this.layer.opacity);\n+ }\n+ this.frame.appendChild(iframe);\n+ this.imgDiv = iframe;\n+ return iframe;\n+ } else {\n+ return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments);\n+ }\n+ },\n \n /**\n- * Constructor: <OpenLayers.Control.WMTSGetFeatureInfo>\n+ * Method: createRequestForm\n+ * Create the html <form> element with width, height, bbox and all \n+ * parameters specified in the layer params.\n *\n- * Parameters:\n- * options - {Object} \n+ * Returns: \n+ * {DOMElement} The form element which sends the HTTP-POST request to the\n+ * WMS. \n */\n- initialize: function(options) {\n- options = options || {};\n- options.handlerOptions = options.handlerOptions || {};\n+ createRequestForm: function() {\n+ // creation of the form element\n+ var form = document.createElement('form');\n+ form.method = 'POST';\n+ var cacheId = this.layer.params[\"_OLSALT\"];\n+ cacheId = (cacheId ? cacheId + \"_\" : \"\") + this.bounds.toBBOX();\n+ form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);\n+ form.target = this.id + '_iFrame';\n \n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ // adding all parameters in layer params as hidden fields to the html\n+ // form element\n+ var imageSize = this.layer.getImageSize(),\n+ params = OpenLayers.Util.getParameters(this.url),\n+ field;\n \n- if (!this.format) {\n- this.format = new OpenLayers.Format.WMSGetFeatureInfo(\n- options.formatOptions\n- );\n+ for (var par in params) {\n+ field = document.createElement('input');\n+ field.type = 'hidden';\n+ field.name = par;\n+ field.value = params[par];\n+ form.appendChild(field);\n }\n \n- if (this.drillDown === true) {\n- this.hover = false;\n- }\n+ return form;\n+ },\n \n- if (this.hover) {\n- this.handler = new OpenLayers.Handler.Hover(\n- this, {\n- move: this.cancelHover,\n- pause: this.getInfoForHover\n- },\n- OpenLayers.Util.extend(\n- this.handlerOptions.hover || {}, {\n- delay: 250\n- }\n- )\n- );\n+ /**\n+ * Method: setImgSrc\n+ * Sets the source for the tile image\n+ *\n+ * Parameters:\n+ * url - {String}\n+ */\n+ setImgSrc: function(url) {\n+ if (this.useIFrame === true) {\n+ if (url) {\n+ var form = this.createRequestForm();\n+ this.frame.appendChild(form);\n+ form.submit();\n+ this.frame.removeChild(form);\n+ } else if (this.imgDiv.parentNode === this.frame) {\n+ // we don't reuse iframes to avoid caching issues\n+ this.frame.removeChild(this.imgDiv);\n+ this.imgDiv = null;\n+ }\n } else {\n- var callbacks = {};\n- callbacks[this.clickCallback] = this.getInfoForClick;\n- this.handler = new OpenLayers.Handler.Click(\n- this, callbacks, this.handlerOptions.click || {}\n- );\n+ OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments);\n }\n },\n \n /**\n- * Method: getInfoForClick \n- * Called on click\n- *\n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n+ * Method: onImageLoad\n+ * Handler for the image onload event\n */\n- getInfoForClick: function(evt) {\n- this.request(evt.xy, {});\n+ onImageLoad: function() {\n+ //TODO de-uglify opacity handling\n+ OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments);\n+ if (this.useIFrame === true) {\n+ this.imgDiv.style.opacity = 1;\n+ this.frame.style.opacity = this.layer.opacity;\n+ }\n },\n \n /**\n- * Method: getInfoForHover\n- * Pause callback for the hover handler\n+ * Method: createBackBuffer\n+ * Override createBackBuffer to do nothing when we use an iframe. Moving an\n+ * iframe from one element to another makes it necessary to reload the iframe\n+ * because its content is lost. So we just give up.\n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.useIFrame === false) {\n+ backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this);\n+ }\n+ return backBuffer;\n+ }\n+};\n+/* ======================================================================\n+ OpenLayers/Marker/Box.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Marker.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Marker.Box\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Marker> \n+ */\n+OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {\n+\n+ /** \n+ * Property: bounds \n+ * {<OpenLayers.Bounds>} \n+ */\n+ bounds: null,\n+\n+ /** \n+ * Property: div \n+ * {DOMElement} \n+ */\n+ div: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Marker.Box\n *\n * Parameters:\n- * evt - {Object}\n+ * bounds - {<OpenLayers.Bounds>} \n+ * borderColor - {String} \n+ * borderWidth - {int} \n */\n- getInfoForHover: function(evt) {\n- this.request(evt.xy, {\n- hover: true\n- });\n+ initialize: function(bounds, borderColor, borderWidth) {\n+ this.bounds = bounds;\n+ this.div = OpenLayers.Util.createDiv();\n+ this.div.style.overflow = 'hidden';\n+ this.events = new OpenLayers.Events(this, this.div);\n+ this.setBorder(borderColor, borderWidth);\n },\n \n /**\n- * Method: cancelHover\n- * Cancel callback for the hover handler\n+ * Method: destroy \n */\n- cancelHover: function() {\n- if (this.hoverRequest) {\n- --this.pending;\n- if (this.pending <= 0) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.pending = 0;\n- }\n- this.hoverRequest.abort();\n- this.hoverRequest = null;\n- }\n+ destroy: function() {\n+\n+ this.bounds = null;\n+ this.div = null;\n+\n+ OpenLayers.Marker.prototype.destroy.apply(this, arguments);\n },\n \n- /**\n- * Method: findLayers\n- * Internal method to get the layers, independent of whether we are\n- * inspecting the map or using a client-provided array\n+ /** \n+ * Method: setBorder\n+ * Allow the user to change the box's color and border width\n+ * \n+ * Parameters:\n+ * color - {String} Default is \"red\"\n+ * width - {int} Default is 2\n */\n- findLayers: function() {\n- var candidates = this.layers || this.map.layers;\n- var layers = [];\n- var layer;\n- for (var i = candidates.length - 1; i >= 0; --i) {\n- layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.WMTS &&\n- layer.requestEncoding === this.requestEncoding &&\n- (!this.queryVisible || layer.getVisibility())) {\n- layers.push(layer);\n- if (!this.drillDown || this.hover) {\n- break;\n- }\n- }\n+ setBorder: function(color, width) {\n+ if (!color) {\n+ color = \"red\";\n }\n- return layers;\n+ if (!width) {\n+ width = 2;\n+ }\n+ this.div.style.border = width + \"px solid \" + color;\n },\n \n- /**\n- * Method: buildRequestOptions\n- * Build an object with the relevant options for the GetFeatureInfo request.\n- *\n+ /** \n+ * Method: draw\n+ * \n * Parameters:\n- * layer - {<OpenLayers.Layer.WMTS>} A WMTS layer.\n- * xy - {<OpenLayers.Pixel>} The position on the map where the \n- * mouse event occurred.\n+ * px - {<OpenLayers.Pixel>} \n+ * sz - {<OpenLayers.Size>} \n+ * \n+ * Returns: \n+ * {DOMElement} A new DOM Image with this marker's icon set at the \n+ * location passed-in\n */\n- buildRequestOptions: function(layer, xy) {\n- var loc = this.map.getLonLatFromPixel(xy);\n- var getTileUrl = layer.getURL(\n- new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat)\n- );\n- var params = OpenLayers.Util.getParameters(getTileUrl);\n- var tileInfo = layer.getTileInfo(loc);\n- OpenLayers.Util.extend(params, {\n- service: \"WMTS\",\n- version: layer.version,\n- request: \"GetFeatureInfo\",\n- infoFormat: this.infoFormat,\n- i: tileInfo.i,\n- j: tileInfo.j\n- });\n- OpenLayers.Util.applyDefaults(params, this.vendorParams);\n- return {\n- url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,\n- params: OpenLayers.Util.upperCaseObject(params),\n- callback: function(request) {\n- this.handleResponse(xy, request, layer);\n- },\n- scope: this\n- };\n+ draw: function(px, sz) {\n+ OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);\n+ return this.div;\n },\n \n /**\n- * Method: request\n- * Sends a GetFeatureInfo request to the WMTS\n- * \n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event \n- * occurred.\n- * options - {Object} additional options for this method.\n+ * Method: onScreen\n * \n- * Valid options:\n- * - *hover* {Boolean} true if we do the request for the hover handler\n+ * Rreturn:\n+ * {Boolean} Whether or not the marker is currently visible on screen.\n */\n- request: function(xy, options) {\n- options = options || {};\n- var layers = this.findLayers();\n- if (layers.length > 0) {\n- var issue, layer;\n- for (var i = 0, len = layers.length; i < len; i++) {\n- layer = layers[i];\n- issue = this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: xy,\n- layer: layer\n- });\n- if (issue !== false) {\n- ++this.pending;\n- var requestOptions = this.buildRequestOptions(layer, xy);\n- var request = OpenLayers.Request.GET(requestOptions);\n- if (options.hover === true) {\n- this.hoverRequest = request;\n- }\n- }\n- }\n- if (this.pending > 0) {\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n- }\n+ onScreen: function() {\n+ var onScreen = false;\n+ if (this.map) {\n+ var screenBounds = this.map.getExtent();\n+ onScreen = screenBounds.containsBounds(this.bounds, true, true);\n }\n+ return onScreen;\n },\n \n /**\n- * Method: handleResponse\n- * Handler for the GetFeatureInfo response.\n+ * Method: display\n+ * Hide or show the icon\n * \n * Parameters:\n- * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event \n- * occurred.\n- * request - {XMLHttpRequest} The request object.\n- * layer - {<OpenLayers.Layer.WMTS>} The queried layer.\n+ * display - {Boolean} \n */\n- handleResponse: function(xy, request, layer) {\n- --this.pending;\n- if (this.pending <= 0) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.pending = 0;\n- }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"exception\", {\n- xy: xy,\n- request: request,\n- layer: layer\n- });\n- } else {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- var features, except;\n- try {\n- features = this.format.read(doc);\n- } catch (error) {\n- except = true;\n- this.events.triggerEvent(\"exception\", {\n- xy: xy,\n- request: request,\n- error: error,\n- layer: layer\n- });\n- }\n- if (!except) {\n- this.events.triggerEvent(\"getfeatureinfo\", {\n- text: request.responseText,\n- features: features,\n- request: request,\n- xy: xy,\n- layer: layer\n- });\n- }\n- }\n+ display: function(display) {\n+ this.div.style.display = (display) ? \"\" : \"none\";\n },\n \n- CLASS_NAME: \"OpenLayers.Control.WMTSGetFeatureInfo\"\n+ CLASS_NAME: \"OpenLayers.Marker.Box\"\n });\n+\n /* ======================================================================\n- OpenLayers/Control/PinchZoom.js\n+ OpenLayers/Popup/Anchored.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Handler/Pinch.js\n+ * @requires OpenLayers/Popup.js\n */\n \n /**\n- * Class: OpenLayers.Control.PinchZoom\n- *\n- * Inherits:\n- * - <OpenLayers.Control>\n+ * Class: OpenLayers.Popup.Anchored\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Popup>\n */\n-OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Popup.Anchored =\n+ OpenLayers.Class(OpenLayers.Popup, {\n \n- /** \n- * Property: type\n- * {OpenLayers.Control.TYPES}\n- */\n- type: OpenLayers.Control.TYPE_TOOL,\n+ /** \n+ * Property: relativePosition\n+ * {String} Relative position of the popup (\"br\", \"tr\", \"tl\" or \"bl\").\n+ */\n+ relativePosition: null,\n \n- /**\n- * Property: pinchOrigin\n- * {Object} Cached object representing the pinch start (in pixels).\n- */\n- pinchOrigin: null,\n+ /**\n+ * APIProperty: keepInMap \n+ * {Boolean} If panMapIfOutOfView is false, and this property is true, \n+ * contrain the popup such that it always fits in the available map\n+ * space. By default, this is set. If you are creating popups that are\n+ * near map edges and not allowing pannning, and especially if you have\n+ * a popup which has a fixedRelativePosition, setting this to false may\n+ * be a smart thing to do.\n+ * \n+ * For anchored popups, default is true, since subclasses will\n+ * usually want this functionality.\n+ */\n+ keepInMap: true,\n \n- /**\n- * Property: currentCenter\n- * {Object} Cached object representing the latest pinch center (in pixels).\n- */\n- currentCenter: null,\n+ /**\n+ * Property: anchor\n+ * {Object} Object to which we'll anchor the popup. Must expose a \n+ * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).\n+ */\n+ anchor: null,\n \n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n- */\n- autoActivate: true,\n+ /** \n+ * Constructor: OpenLayers.Popup.Anchored\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * contentSize - {<OpenLayers.Size>}\n+ * contentHTML - {String}\n+ * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> \n+ * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).\n+ * closeBox - {Boolean}\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ */\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n+ closeBoxCallback) {\n+ var newArguments = [\n+ id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback\n+ ];\n+ OpenLayers.Popup.prototype.initialize.apply(this, newArguments);\n \n- /**\n- * APIProperty: preserveCenter\n- * {Boolean} Set this to true if you don't want the map center to change\n- * while pinching. For example you may want to set preserveCenter to\n- * true when the user location is being watched and you want to preserve\n- * the user location at the center of the map even if he zooms in or\n- * out using pinch. This property's value can be changed any time on an\n- * existing instance. Default is false.\n- */\n- preserveCenter: false,\n+ this.anchor = (anchor != null) ? anchor :\n+ {\n+ size: new OpenLayers.Size(0, 0),\n+ offset: new OpenLayers.Pixel(0, 0)\n+ };\n+ },\n \n- /**\n- * APIProperty: handlerOptions\n- * {Object} Used to set non-default properties on the pinch handler\n- */\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.anchor = null;\n+ this.relativePosition = null;\n \n- /**\n- * Constructor: OpenLayers.Control.PinchZoom\n- * Create a control for zooming with pinch gestures. This works on devices\n- * with multi-touch support.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * the control\n- */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- this.handler = new OpenLayers.Handler.Pinch(this, {\n- start: this.pinchStart,\n- move: this.pinchMove,\n- done: this.pinchDone\n- }, this.handlerOptions);\n- },\n+ OpenLayers.Popup.prototype.destroy.apply(this, arguments);\n+ },\n \n- /**\n- * Method: pinchStart\n- *\n- * Parameters:\n- * evt - {Event}\n- * pinchData - {Object} pinch data object related to the current touchmove\n- * of the pinch gesture. This give us the current scale of the pinch.\n- */\n- pinchStart: function(evt, pinchData) {\n- var xy = (this.preserveCenter) ?\n- this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;\n- this.pinchOrigin = xy;\n- this.currentCenter = xy;\n- },\n+ /**\n+ * APIMethod: show\n+ * Overridden from Popup since user might hide popup and then show() it \n+ * in a new location (meaning we might want to update the relative\n+ * position on the show)\n+ */\n+ show: function() {\n+ this.updatePosition();\n+ OpenLayers.Popup.prototype.show.apply(this, arguments);\n+ },\n \n- /**\n- * Method: pinchMove\n- *\n- * Parameters:\n- * evt - {Event}\n- * pinchData - {Object} pinch data object related to the current touchmove\n- * of the pinch gesture. This give us the current scale of the pinch.\n- */\n- pinchMove: function(evt, pinchData) {\n- var scale = pinchData.scale;\n- var containerOrigin = this.map.layerContainerOriginPx;\n- var pinchOrigin = this.pinchOrigin;\n- var current = (this.preserveCenter) ?\n- this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;\n+ /**\n+ * Method: moveTo\n+ * Since the popup is moving to a new px, it might need also to be moved\n+ * relative to where the marker is. We first calculate the new \n+ * relativePosition, and then we calculate the new px where we will \n+ * put the popup, based on the new relative position. \n+ * \n+ * If the relativePosition has changed, we must also call \n+ * updateRelativePosition() to make any visual changes to the popup \n+ * which are associated with putting it in a new relativePosition.\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ */\n+ moveTo: function(px) {\n+ var oldRelativePosition = this.relativePosition;\n+ this.relativePosition = this.calculateRelativePosition(px);\n \n- var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));\n- var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));\n+ OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));\n \n- this.map.applyTransform(dx, dy, scale);\n- this.currentCenter = current;\n- },\n+ //if this move has caused the popup to change its relative position, \n+ // we need to make the appropriate cosmetic changes.\n+ if (this.relativePosition != oldRelativePosition) {\n+ this.updateRelativePosition();\n+ }\n+ },\n \n- /**\n- * Method: pinchDone\n- *\n- * Parameters:\n- * evt - {Event}\n- * start - {Object} pinch data object related to the touchstart event that\n- * started the pinch gesture.\n- * last - {Object} pinch data object related to the last touchmove event\n- * of the pinch gesture. This give us the final scale of the pinch.\n- */\n- pinchDone: function(evt, start, last) {\n- this.map.applyTransform();\n- var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);\n- if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {\n- var resolution = this.map.getResolutionForZoom(zoom);\n+ /**\n+ * APIMethod: setSize\n+ * \n+ * Parameters:\n+ * contentSize - {<OpenLayers.Size>} the new size for the popup's \n+ * contents div (in pixels).\n+ */\n+ setSize: function(contentSize) {\n+ OpenLayers.Popup.prototype.setSize.apply(this, arguments);\n \n- var location = this.map.getLonLatFromPixel(this.pinchOrigin);\n- var zoomPixel = this.currentCenter;\n- var size = this.map.getSize();\n+ if ((this.lonlat) && (this.map)) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ this.moveTo(px);\n+ }\n+ },\n \n- location.lon += resolution * ((size.w / 2) - zoomPixel.x);\n- location.lat -= resolution * ((size.h / 2) - zoomPixel.y);\n+ /** \n+ * Method: calculateRelativePosition\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {String} The relative position (\"br\" \"tr\" \"tl\" \"bl\") at which the popup\n+ * should be placed.\n+ */\n+ calculateRelativePosition: function(px) {\n+ var lonlat = this.map.getLonLatFromLayerPx(px);\n \n- // Force a reflow before calling setCenter. This is to work\n- // around an issue occuring in iOS.\n- //\n- // See https://github.com/openlayers/openlayers/pull/351.\n- //\n- // Without a reflow setting the layer container div's top left\n- // style properties to \"0px\" - as done in Map.moveTo when zoom\n- // is changed - won't actually correctly reposition the layer\n- // container div.\n- //\n- // Also, we need to use a statement that the Google Closure\n- // compiler won't optimize away.\n- this.map.div.clientWidth = this.map.div.clientWidth;\n+ var extent = this.map.getExtent();\n+ var quadrant = extent.determineQuadrant(lonlat);\n \n- this.map.setCenter(location, zoom);\n- }\n- },\n+ return OpenLayers.Bounds.oppositeQuadrant(quadrant);\n+ },\n \n- CLASS_NAME: \"OpenLayers.Control.PinchZoom\"\n+ /**\n+ * Method: updateRelativePosition\n+ * The popup has been moved to a new relative location, so we may want to \n+ * make some cosmetic adjustments to it. \n+ * \n+ * Note that in the classic Anchored popup, there is nothing to do \n+ * here, since the popup looks exactly the same in all four positions.\n+ * Subclasses such as Framed, however, will want to do something\n+ * special here.\n+ */\n+ updateRelativePosition: function() {\n+ //to be overridden by subclasses\n+ },\n \n-});\n+ /** \n+ * Method: calculateNewPx\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen\n+ * relative to the passed-in px.\n+ */\n+ calculateNewPx: function(px) {\n+ var newPx = px.offset(this.anchor.offset);\n+\n+ //use contentSize if size is not already set\n+ var size = this.size || this.contentSize;\n+\n+ var top = (this.relativePosition.charAt(0) == 't');\n+ newPx.y += (top) ? -size.h : this.anchor.size.h;\n+\n+ var left = (this.relativePosition.charAt(1) == 'l');\n+ newPx.x += (left) ? -size.w : this.anchor.size.w;\n+\n+ return newPx;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup.Anchored\"\n+ });\n /* ======================================================================\n- OpenLayers/Control/OverviewMap.js\n+ OpenLayers/Popup/Framed.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/** \n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/BaseTypes.js\n- * @requires OpenLayers/Events/buttonclick.js\n- * @requires OpenLayers/Map.js\n- * @requires OpenLayers/Handler/Click.js\n- * @requires OpenLayers/Handler/Drag.js\n+/**\n+ * @requires OpenLayers/Popup/Anchored.js\n */\n \n /**\n- * Class: OpenLayers.Control.OverviewMap\n- * The OverMap control creates a small overview map, useful to display the \n- * extent of a zoomed map and your main map and provide additional \n- * navigation options to the User. By default the overview map is drawn in\n- * the lower right corner of the main map. Create a new overview map with the\n- * <OpenLayers.Control.OverviewMap> constructor.\n- *\n+ * Class: OpenLayers.Popup.Framed\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Popup.Anchored>\n */\n-OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Popup.Framed =\n+ OpenLayers.Class(OpenLayers.Popup.Anchored, {\n \n- /**\n- * Property: element\n- * {DOMElement} The DOM element that contains the overview map\n- */\n- element: null,\n+ /**\n+ * Property: imageSrc\n+ * {String} location of the image to be used as the popup frame\n+ */\n+ imageSrc: null,\n \n- /**\n- * APIProperty: ovmap\n- * {<OpenLayers.Map>} A reference to the overview map itself.\n- */\n- ovmap: null,\n+ /**\n+ * Property: imageSize\n+ * {<OpenLayers.Size>} Size (measured in pixels) of the image located\n+ * by the 'imageSrc' property.\n+ */\n+ imageSize: null,\n \n- /**\n- * APIProperty: size\n- * {<OpenLayers.Size>} The overvew map size in pixels. Note that this is\n- * the size of the map itself - the element that contains the map (default\n- * class name olControlOverviewMapElement) may have padding or other style\n- * attributes added via CSS.\n- */\n- size: {\n- w: 180,\n- h: 90\n- },\n+ /**\n+ * APIProperty: isAlphaImage\n+ * {Boolean} The image has some alpha and thus needs to use the alpha \n+ * image hack. Note that setting this to true will have no noticeable\n+ * effect in FF or IE7 browsers, but will all but crush the ie6 \n+ * browser. \n+ * Default is false.\n+ */\n+ isAlphaImage: false,\n \n- /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map.\n- * If none are sent at construction, the base layer for the main map is used.\n- */\n- layers: null,\n+ /**\n+ * Property: positionBlocks\n+ * {Object} Hash of different position blocks (Object/Hashs). Each block \n+ * will be keyed by a two-character 'relativePosition' \n+ * code string (ie \"tl\", \"tr\", \"bl\", \"br\"). Block properties are \n+ * 'offset', 'padding' (self-explanatory), and finally the 'blocks'\n+ * parameter, which is an array of the block objects. \n+ * \n+ * Each block object must have 'size', 'anchor', and 'position' \n+ * properties.\n+ * \n+ * Note that positionBlocks should never be modified at runtime.\n+ */\n+ positionBlocks: null,\n \n- /**\n- * APIProperty: minRectSize\n- * {Integer} The minimum width or height (in pixels) of the extent\n- * rectangle on the overview map. When the extent rectangle reaches\n- * this size, it will be replaced depending on the value of the\n- * <minRectDisplayClass> property. Default is 15 pixels.\n- */\n- minRectSize: 15,\n+ /**\n+ * Property: blocks\n+ * {Array[Object]} Array of objects, each of which is one \"block\" of the \n+ * popup. Each block has a 'div' and an 'image' property, both of \n+ * which are DOMElements, and the latter of which is appended to the \n+ * former. These are reused as the popup goes changing positions for\n+ * great economy and elegance.\n+ */\n+ blocks: null,\n \n- /**\n- * APIProperty: minRectDisplayClass\n- * {String} Replacement style class name for the extent rectangle when\n- * <minRectSize> is reached. This string will be suffixed on to the\n- * displayClass. Default is \"RectReplacement\".\n- *\n- * Example CSS declaration:\n- * (code)\n- * .olControlOverviewMapRectReplacement {\n- * overflow: hidden;\n- * cursor: move;\n- * background-image: url(\"img/overview_replacement.gif\");\n- * background-repeat: no-repeat;\n- * background-position: center;\n- * }\n- * (end)\n- */\n- minRectDisplayClass: \"RectReplacement\",\n+ /** \n+ * APIProperty: fixedRelativePosition\n+ * {Boolean} We want the framed popup to work dynamically placed relative\n+ * to its anchor but also in just one fixed position. A well designed\n+ * framed popup will have the pixels and logic to display itself in \n+ * any of the four relative positions, but (understandably), this will\n+ * not be the case for all of them. By setting this property to 'true', \n+ * framed popup will not recalculate for the best placement each time\n+ * it's open, but will always open the same way. \n+ * Note that if this is set to true, it is generally advisable to also\n+ * set the 'panIntoView' property to true so that the popup can be \n+ * scrolled into view (since it will often be offscreen on open)\n+ * Default is false.\n+ */\n+ fixedRelativePosition: false,\n \n- /**\n- * APIProperty: minRatio\n- * {Float} The ratio of the overview map resolution to the main map\n- * resolution at which to zoom farther out on the overview map.\n- */\n- minRatio: 8,\n+ /** \n+ * Constructor: OpenLayers.Popup.Framed\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * contentSize - {<OpenLayers.Size>}\n+ * contentHTML - {String}\n+ * anchor - {Object} Object to which we'll anchor the popup. Must expose \n+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n+ * (Note that this is generally an <OpenLayers.Icon>).\n+ * closeBox - {Boolean}\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ */\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n+ closeBoxCallback) {\n \n- /**\n- * APIProperty: maxRatio\n- * {Float} The ratio of the overview map resolution to the main map\n- * resolution at which to zoom farther in on the overview map.\n- */\n- maxRatio: 32,\n+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);\n \n- /**\n- * APIProperty: mapOptions\n- * {Object} An object containing any non-default properties to be sent to\n- * the overview map's map constructor. These should include any\n- * non-default options that the main map was constructed with.\n- */\n- mapOptions: null,\n+ if (this.fixedRelativePosition) {\n+ //based on our decided relativePostion, set the current padding\n+ // this keeps us from getting into trouble \n+ this.updateRelativePosition();\n \n- /**\n- * APIProperty: autoPan\n- * {Boolean} Always pan the overview map, so the extent marker remains in\n- * the center. Default is false. If true, when you drag the extent\n- * marker, the overview map will update itself so the marker returns\n- * to the center.\n- */\n- autoPan: false,\n+ //make calculateRelativePosition always return the specified\n+ // fixed position.\n+ this.calculateRelativePosition = function(px) {\n+ return this.relativePosition;\n+ };\n+ }\n \n- /**\n- * Property: handlers\n- * {Object}\n- */\n- handlers: null,\n+ this.contentDiv.style.position = \"absolute\";\n+ this.contentDiv.style.zIndex = 1;\n \n- /**\n- * Property: resolutionFactor\n- * {Object}\n- */\n- resolutionFactor: 1,\n+ if (closeBox) {\n+ this.closeDiv.style.zIndex = 1;\n+ }\n \n- /**\n- * APIProperty: maximized\n- * {Boolean} Start as maximized (visible). Defaults to false.\n- */\n- maximized: false,\n+ this.groupDiv.style.position = \"absolute\";\n+ this.groupDiv.style.top = \"0px\";\n+ this.groupDiv.style.left = \"0px\";\n+ this.groupDiv.style.height = \"100%\";\n+ this.groupDiv.style.width = \"100%\";\n+ },\n \n- /**\n- * APIProperty: maximizeTitle\n- * {String} This property is used for showing a tooltip over the \n- * maximize div. Defaults to \"\" (no title).\n- */\n- maximizeTitle: \"\",\n+ /** \n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.imageSrc = null;\n+ this.imageSize = null;\n+ this.isAlphaImage = null;\n \n- /**\n- * APIProperty: minimizeTitle\n- * {String} This property is used for showing a tooltip over the \n- * minimize div. Defaults to \"\" (no title).\n- */\n- minimizeTitle: \"\",\n+ this.fixedRelativePosition = false;\n+ this.positionBlocks = null;\n \n- /**\n- * Constructor: OpenLayers.Control.OverviewMap\n- * Create a new overview map\n- *\n- * Parameters:\n- * options - {Object} Properties of this object will be set on the overview\n- * map object. Note, to set options on the map object contained in this\n- * control, set <mapOptions> as one of the options properties.\n- */\n- initialize: function(options) {\n- this.layers = [];\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- },\n+ //remove our blocks\n+ for (var i = 0; i < this.blocks.length; i++) {\n+ var block = this.blocks[i];\n \n- /**\n- * APIMethod: destroy\n- * Deconstruct the control\n- */\n- destroy: function() {\n- if (!this.mapDiv) { // we've already been destroyed\n- return;\n- }\n- if (this.handlers.click) {\n- this.handlers.click.destroy();\n- }\n- if (this.handlers.drag) {\n- this.handlers.drag.destroy();\n- }\n+ if (block.image) {\n+ block.div.removeChild(block.image);\n+ }\n+ block.image = null;\n \n- this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle);\n- this.extentRectangle = null;\n+ if (block.div) {\n+ this.groupDiv.removeChild(block.div);\n+ }\n+ block.div = null;\n+ }\n+ this.blocks = null;\n \n- if (this.rectEvents) {\n- this.rectEvents.destroy();\n- this.rectEvents = null;\n- }\n+ OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);\n+ },\n \n- if (this.ovmap) {\n- this.ovmap.destroy();\n- this.ovmap = null;\n- }\n+ /**\n+ * APIMethod: setBackgroundColor\n+ */\n+ setBackgroundColor: function(color) {\n+ //does nothing since the framed popup's entire scheme is based on a \n+ // an image -- changing the background color makes no sense. \n+ },\n \n- this.element.removeChild(this.mapDiv);\n- this.mapDiv = null;\n+ /**\n+ * APIMethod: setBorder\n+ */\n+ setBorder: function() {\n+ //does nothing since the framed popup's entire scheme is based on a \n+ // an image -- changing the popup's border makes no sense. \n+ },\n \n- this.div.removeChild(this.element);\n- this.element = null;\n+ /**\n+ * Method: setOpacity\n+ * Sets the opacity of the popup.\n+ * \n+ * Parameters:\n+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ */\n+ setOpacity: function(opacity) {\n+ //does nothing since we suppose that we'll never apply an opacity\n+ // to a framed popup\n+ },\n \n- if (this.maximizeDiv) {\n- this.div.removeChild(this.maximizeDiv);\n- this.maximizeDiv = null;\n- }\n+ /**\n+ * APIMethod: setSize\n+ * Overridden here, because we need to update the blocks whenever the size\n+ * of the popup has changed.\n+ * \n+ * Parameters:\n+ * contentSize - {<OpenLayers.Size>} the new size for the popup's \n+ * contents div (in pixels).\n+ */\n+ setSize: function(contentSize) {\n+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);\n \n- if (this.minimizeDiv) {\n- this.div.removeChild(this.minimizeDiv);\n- this.minimizeDiv = null;\n- }\n+ this.updateBlocks();\n+ },\n \n- this.map.events.un({\n- buttonclick: this.onButtonClick,\n- moveend: this.update,\n- changebaselayer: this.baseLayerDraw,\n- scope: this\n- });\n+ /**\n+ * Method: updateRelativePosition\n+ * When the relative position changes, we need to set the new padding \n+ * BBOX on the popup, reposition the close div, and update the blocks.\n+ */\n+ updateRelativePosition: function() {\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- },\n+ //update the padding\n+ this.padding = this.positionBlocks[this.relativePosition].padding;\n \n- /**\n- * Method: draw\n- * Render the control in the browser.\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.layers.length === 0) {\n- if (this.map.baseLayer) {\n- var layer = this.map.baseLayer.clone();\n- this.layers = [layer];\n- } else {\n- this.map.events.register(\"changebaselayer\", this, this.baseLayerDraw);\n- return this.div;\n+ //update the position of our close box to new padding\n+ if (this.closeDiv) {\n+ // use the content div's css padding to determine if we should\n+ // padd the close div\n+ var contentDivPadding = this.getContentDivPadding();\n+\n+ this.closeDiv.style.right = contentDivPadding.right +\n+ this.padding.right + \"px\";\n+ this.closeDiv.style.top = contentDivPadding.top +\n+ this.padding.top + \"px\";\n }\n- }\n \n- // create overview map DOM elements\n- this.element = document.createElement('div');\n- this.element.className = this.displayClass + 'Element';\n- this.element.style.display = 'none';\n+ this.updateBlocks();\n+ },\n \n- this.mapDiv = document.createElement('div');\n- this.mapDiv.style.width = this.size.w + 'px';\n- this.mapDiv.style.height = this.size.h + 'px';\n- this.mapDiv.style.position = 'relative';\n- this.mapDiv.style.overflow = 'hidden';\n- this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');\n+ /** \n+ * Method: calculateNewPx\n+ * Besides the standard offset as determined by the Anchored class, our \n+ * Framed popups have a special 'offset' property for each of their \n+ * positions, which is used to offset the popup relative to its anchor.\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} The the new px position of the popup on the screen\n+ * relative to the passed-in px.\n+ */\n+ calculateNewPx: function(px) {\n+ var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(\n+ this, arguments\n+ );\n \n- this.extentRectangle = document.createElement('div');\n- this.extentRectangle.style.position = 'absolute';\n- this.extentRectangle.style.zIndex = 1000; //HACK\n- this.extentRectangle.className = this.displayClass + 'ExtentRectangle';\n+ newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);\n \n- this.element.appendChild(this.mapDiv);\n+ return newPx;\n+ },\n \n- this.div.appendChild(this.element);\n+ /**\n+ * Method: createBlocks\n+ */\n+ createBlocks: function() {\n+ this.blocks = [];\n \n- // Optionally add min/max buttons if the control will go in the\n- // map viewport.\n- if (!this.outsideViewport) {\n- this.div.className += \" \" + this.displayClass + 'Container';\n- // maximize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n- this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- this.displayClass + 'MaximizeButton',\n- null,\n- null,\n- img,\n- 'absolute');\n- this.maximizeDiv.style.display = 'none';\n- this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton';\n- if (this.maximizeTitle) {\n- this.maximizeDiv.title = this.maximizeTitle;\n+ //since all positions contain the same number of blocks, we can \n+ // just pick the first position and use its blocks array to create\n+ // our blocks array\n+ var firstPosition = null;\n+ for (var key in this.positionBlocks) {\n+ firstPosition = key;\n+ break;\n }\n- this.div.appendChild(this.maximizeDiv);\n \n- // minimize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n- this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- 'OpenLayers_Control_minimizeDiv',\n- null,\n- null,\n- img,\n- 'absolute');\n- this.minimizeDiv.style.display = 'none';\n- this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton';\n- if (this.minimizeTitle) {\n- this.minimizeDiv.title = this.minimizeTitle;\n+ var position = this.positionBlocks[firstPosition];\n+ for (var i = 0; i < position.blocks.length; i++) {\n+\n+ var block = {};\n+ this.blocks.push(block);\n+\n+ var divId = this.id + '_FrameDecorationDiv_' + i;\n+ block.div = OpenLayers.Util.createDiv(divId,\n+ null, null, null, \"absolute\", null, \"hidden\", null\n+ );\n+\n+ var imgId = this.id + '_FrameDecorationImg_' + i;\n+ var imageCreator =\n+ (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv :\n+ OpenLayers.Util.createImage;\n+\n+ block.image = imageCreator(imgId,\n+ null, this.imageSize, this.imageSrc,\n+ \"absolute\", null, null, null\n+ );\n+\n+ block.div.appendChild(block.image);\n+ this.groupDiv.appendChild(block.div);\n }\n- this.div.appendChild(this.minimizeDiv);\n- this.minimizeControl();\n- } else {\n- // show the overview map\n- this.element.style.display = '';\n- }\n- if (this.map.getExtent()) {\n- this.update();\n- }\n+ },\n \n- this.map.events.on({\n- buttonclick: this.onButtonClick,\n- moveend: this.update,\n- scope: this\n- });\n+ /**\n+ * Method: updateBlocks\n+ * Internal method, called on initialize and when the popup's relative\n+ * position has changed. This function takes care of re-positioning\n+ * the popup's blocks in their appropropriate places.\n+ */\n+ updateBlocks: function() {\n+ if (!this.blocks) {\n+ this.createBlocks();\n+ }\n \n- if (this.maximized) {\n- this.maximizeControl();\n- }\n- return this.div;\n- },\n+ if (this.size && this.relativePosition) {\n+ var position = this.positionBlocks[this.relativePosition];\n+ for (var i = 0; i < position.blocks.length; i++) {\n+\n+ var positionBlock = position.blocks[i];\n+ var block = this.blocks[i];\n+\n+ // adjust sizes\n+ var l = positionBlock.anchor.left;\n+ var b = positionBlock.anchor.bottom;\n+ var r = positionBlock.anchor.right;\n+ var t = positionBlock.anchor.top;\n+\n+ //note that we use the isNaN() test here because if the \n+ // size object is initialized with a \"auto\" parameter, the \n+ // size constructor calls parseFloat() on the string, \n+ // which will turn it into NaN\n+ //\n+ var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) :\n+ positionBlock.size.w;\n+\n+ var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) :\n+ positionBlock.size.h;\n+\n+ block.div.style.width = (w < 0 ? 0 : w) + 'px';\n+ block.div.style.height = (h < 0 ? 0 : h) + 'px';\n+\n+ block.div.style.left = (l != null) ? l + 'px' : '';\n+ block.div.style.bottom = (b != null) ? b + 'px' : '';\n+ block.div.style.right = (r != null) ? r + 'px' : '';\n+ block.div.style.top = (t != null) ? t + 'px' : '';\n+\n+ block.image.style.left = positionBlock.position.x + 'px';\n+ block.image.style.top = positionBlock.position.y + 'px';\n+ }\n+\n+ this.contentDiv.style.left = this.padding.left + \"px\";\n+ this.contentDiv.style.top = this.padding.top + \"px\";\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup.Framed\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Popup/FramedCloud.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Popup/Framed.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes/Bounds.js\n+ * @requires OpenLayers/BaseTypes/Pixel.js\n+ * @requires OpenLayers/BaseTypes/Size.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Popup.FramedCloud\n+ * \n+ * Inherits from: \n+ * - <OpenLayers.Popup.Framed>\n+ */\n+OpenLayers.Popup.FramedCloud =\n+ OpenLayers.Class(OpenLayers.Popup.Framed, {\n+\n+ /** \n+ * Property: contentDisplayClass\n+ * {String} The CSS class of the popup content div.\n+ */\n+ contentDisplayClass: \"olFramedCloudPopupContent\",\n+\n+ /**\n+ * APIProperty: autoSize\n+ * {Boolean} Framed Cloud is autosizing by default.\n+ */\n+ autoSize: true,\n+\n+ /**\n+ * APIProperty: panMapIfOutOfView\n+ * {Boolean} Framed Cloud does pan into view by default.\n+ */\n+ panMapIfOutOfView: true,\n+\n+ /**\n+ * APIProperty: imageSize\n+ * {<OpenLayers.Size>}\n+ */\n+ imageSize: new OpenLayers.Size(1276, 736),\n+\n+ /**\n+ * APIProperty: isAlphaImage\n+ * {Boolean} The FramedCloud does not use an alpha image (in honor of the \n+ * good ie6 folk out there)\n+ */\n+ isAlphaImage: false,\n+\n+ /** \n+ * APIProperty: fixedRelativePosition\n+ * {Boolean} The Framed Cloud popup works in just one fixed position.\n+ */\n+ fixedRelativePosition: false,\n+\n+ /**\n+ * Property: positionBlocks\n+ * {Object} Hash of differen position blocks, keyed by relativePosition\n+ * two-character code string (ie \"tl\", \"tr\", \"bl\", \"br\")\n+ */\n+ positionBlocks: {\n+ \"tl\": {\n+ 'offset': new OpenLayers.Pixel(44, 0),\n+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n+ 'blocks': [{ // top-left\n+ size: new OpenLayers.Size('auto', 'auto'),\n+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, { //top-right\n+ size: new OpenLayers.Size(22, 'auto'),\n+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, { //bottom-left\n+ size: new OpenLayers.Size('auto', 19),\n+ anchor: new OpenLayers.Bounds(0, 32, 22, null),\n+ position: new OpenLayers.Pixel(0, -631)\n+ }, { //bottom-right\n+ size: new OpenLayers.Size(22, 18),\n+ anchor: new OpenLayers.Bounds(null, 32, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -632)\n+ }, { // stem\n+ size: new OpenLayers.Size(81, 35),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(0, -688)\n+ }]\n+ },\n+ \"tr\": {\n+ 'offset': new OpenLayers.Pixel(-45, 0),\n+ 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n+ 'blocks': [{ // top-left\n+ size: new OpenLayers.Size('auto', 'auto'),\n+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, { //top-right\n+ size: new OpenLayers.Size(22, 'auto'),\n+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, { //bottom-left\n+ size: new OpenLayers.Size('auto', 19),\n+ anchor: new OpenLayers.Bounds(0, 32, 22, null),\n+ position: new OpenLayers.Pixel(0, -631)\n+ }, { //bottom-right\n+ size: new OpenLayers.Size(22, 19),\n+ anchor: new OpenLayers.Bounds(null, 32, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -631)\n+ }, { // stem\n+ size: new OpenLayers.Size(81, 35),\n+ anchor: new OpenLayers.Bounds(0, 0, null, null),\n+ position: new OpenLayers.Pixel(-215, -687)\n+ }]\n+ },\n+ \"bl\": {\n+ 'offset': new OpenLayers.Pixel(45, 0),\n+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n+ 'blocks': [{ // top-left\n+ size: new OpenLayers.Size('auto', 'auto'),\n+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, { //top-right\n+ size: new OpenLayers.Size(22, 'auto'),\n+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, { //bottom-left\n+ size: new OpenLayers.Size('auto', 21),\n+ anchor: new OpenLayers.Bounds(0, 0, 22, null),\n+ position: new OpenLayers.Pixel(0, -629)\n+ }, { //bottom-right\n+ size: new OpenLayers.Size(22, 21),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -629)\n+ }, { // stem\n+ size: new OpenLayers.Size(81, 33),\n+ anchor: new OpenLayers.Bounds(null, null, 0, 0),\n+ position: new OpenLayers.Pixel(-101, -674)\n+ }]\n+ },\n+ \"br\": {\n+ 'offset': new OpenLayers.Pixel(-44, 0),\n+ 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n+ 'blocks': [{ // top-left\n+ size: new OpenLayers.Size('auto', 'auto'),\n+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, { //top-right\n+ size: new OpenLayers.Size(22, 'auto'),\n+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, { //bottom-left\n+ size: new OpenLayers.Size('auto', 21),\n+ anchor: new OpenLayers.Bounds(0, 0, 22, null),\n+ position: new OpenLayers.Pixel(0, -629)\n+ }, { //bottom-right\n+ size: new OpenLayers.Size(22, 21),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -629)\n+ }, { // stem\n+ size: new OpenLayers.Size(81, 33),\n+ anchor: new OpenLayers.Bounds(0, null, null, 0),\n+ position: new OpenLayers.Pixel(-311, -674)\n+ }]\n+ }\n+ },\n+\n+ /**\n+ * APIProperty: minSize\n+ * {<OpenLayers.Size>}\n+ */\n+ minSize: new OpenLayers.Size(105, 10),\n+\n+ /**\n+ * APIProperty: maxSize\n+ * {<OpenLayers.Size>}\n+ */\n+ maxSize: new OpenLayers.Size(1200, 660),\n+\n+ /** \n+ * Constructor: OpenLayers.Popup.FramedCloud\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * contentSize - {<OpenLayers.Size>}\n+ * contentHTML - {String}\n+ * anchor - {Object} Object to which we'll anchor the popup. Must expose \n+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n+ * (Note that this is generally an <OpenLayers.Icon>).\n+ * closeBox - {Boolean}\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ */\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n+ closeBoxCallback) {\n+\n+ this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');\n+ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n+ this.contentDiv.className = this.contentDisplayClass;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Layer/XYZ.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/** \n+ * Class: OpenLayers.Layer.XYZ\n+ * The XYZ class is designed to make it easier for people who have tiles\n+ * arranged by a standard XYZ grid. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * Method: baseLayerDraw\n- * Draw the base layer - called if unable to complete in the initial draw\n+ * APIProperty: isBaseLayer\n+ * Default is true, as this is designed to be a base tile source. \n */\n- baseLayerDraw: function() {\n- this.draw();\n- this.map.events.unregister(\"changebaselayer\", this, this.baseLayerDraw);\n- },\n+ isBaseLayer: true,\n \n /**\n- * Method: rectDrag\n- * Handle extent rectangle drag\n+ * APIProperty: sphericalMercator\n+ * Whether the tile extents should be set to the defaults for \n+ * spherical mercator. Useful for things like OpenStreetMap.\n+ * Default is false, except for the OSM subclass.\n+ */\n+ sphericalMercator: false,\n+\n+ /**\n+ * APIProperty: zoomOffset\n+ * {Number} If your cache has more zoom levels than you want to provide\n+ * access to with this layer, supply a zoomOffset. This zoom offset\n+ * is added to the current map zoom level to determine the level\n+ * for a requested tile. For example, if you supply a zoomOffset\n+ * of 3, when the map is at the zoom 0, tiles will be requested from\n+ * level 3 of your cache. Default is 0 (assumes cache level and map\n+ * zoom are equivalent). Using <zoomOffset> is an alternative to\n+ * setting <serverResolutions> if you only want to expose a subset\n+ * of the server resolutions.\n+ */\n+ zoomOffset: 0,\n+\n+ /**\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer; you can also look at <zoomOffset>, which is\n+ * an alternative to <serverResolutions> for that specific purpose.\n+ * (b) The map can work with resolutions that aren't supported by\n+ * the server, i.e. that aren't in <serverResolutions>. When the\n+ * map is displayed in such a resolution data for the closest\n+ * server-supported resolution is loaded and the layer div is\n+ * stretched as necessary.\n+ */\n+ serverResolutions: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.XYZ\n *\n * Parameters:\n- * px - {<OpenLayers.Pixel>} The pixel location of the drag.\n+ * name - {String}\n+ * url - {String}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- rectDrag: function(px) {\n- var deltaX = this.handlers.drag.last.x - px.x;\n- var deltaY = this.handlers.drag.last.y - px.y;\n- if (deltaX != 0 || deltaY != 0) {\n- var rectTop = this.rectPxBounds.top;\n- var rectLeft = this.rectPxBounds.left;\n- var rectHeight = Math.abs(this.rectPxBounds.getHeight());\n- var rectWidth = this.rectPxBounds.getWidth();\n- // don't allow dragging off of parent element\n- var newTop = Math.max(0, (rectTop - deltaY));\n- newTop = Math.min(newTop,\n- this.ovmap.size.h - this.hComp - rectHeight);\n- var newLeft = Math.max(0, (rectLeft - deltaX));\n- newLeft = Math.min(newLeft,\n- this.ovmap.size.w - this.wComp - rectWidth);\n- this.setRectPxBounds(new OpenLayers.Bounds(newLeft,\n- newTop + rectHeight,\n- newLeft + rectWidth,\n- newTop));\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options);\n }\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n+ name || this.name, url || this.url, {},\n+ options\n+ ]);\n },\n \n /**\n- * Method: mapDivClick\n- * Handle browser events\n+ * APIMethod: clone\n+ * Create a clone of this layer\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>} evt\n+ * obj - {Object} Is this ever used?\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n */\n- mapDivClick: function(evt) {\n- var pxCenter = this.rectPxBounds.getCenterPixel();\n- var deltaX = evt.xy.x - pxCenter.x;\n- var deltaY = evt.xy.y - pxCenter.y;\n- var top = this.rectPxBounds.top;\n- var left = this.rectPxBounds.left;\n- var height = Math.abs(this.rectPxBounds.getHeight());\n- var width = this.rectPxBounds.getWidth();\n- var newTop = Math.max(0, (top + deltaY));\n- newTop = Math.min(newTop, this.ovmap.size.h - height);\n- var newLeft = Math.max(0, (left + deltaX));\n- newLeft = Math.min(newLeft, this.ovmap.size.w - width);\n- this.setRectPxBounds(new OpenLayers.Bounds(newLeft,\n- newTop + height,\n- newLeft + width,\n- newTop));\n- this.updateMapToRect();\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name,\n+ this.url,\n+ this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ return obj;\n },\n \n /**\n- * Method: onButtonClick\n+ * Method: getURL\n *\n * Parameters:\n- * evt - {Event}\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as\n+ * parameters\n */\n- onButtonClick: function(evt) {\n- if (evt.buttonElement === this.minimizeDiv) {\n- this.minimizeControl();\n- } else if (evt.buttonElement === this.maximizeDiv) {\n- this.maximizeControl();\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = '' + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url);\n }\n+\n+ return OpenLayers.String.format(url, xyz);\n },\n \n /**\n- * Method: maximizeControl\n- * Unhide the control. Called when the control is in the map viewport.\n+ * Method: getXYZ\n+ * Calculates x, y and z for the given bounds.\n *\n * Parameters:\n- * e - {<OpenLayers.Event>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {Object} - an object with x, y and z properties.\n */\n- maximizeControl: function(e) {\n- this.element.style.display = '';\n- this.showToggle(false);\n- if (e != null) {\n- OpenLayers.Event.stop(e);\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) /\n+ (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) /\n+ (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = ((x % limit) + limit) % limit;\n }\n+\n+ return {\n+ 'x': x,\n+ 'y': y,\n+ 'z': z\n+ };\n },\n \n- /**\n- * Method: minimizeControl\n- * Hide all the contents of the control, shrink the size, \n- * add the maximize icon\n+ /* APIMethod: setMap\n+ * When the layer is added to a map, then we can fetch our origin \n+ * (if we don't have one.) \n * \n * Parameters:\n- * e - {<OpenLayers.Event>}\n+ * map - {<OpenLayers.Map>}\n */\n- minimizeControl: function(e) {\n- this.element.style.display = 'none';\n- this.showToggle(true);\n- if (e != null) {\n- OpenLayers.Event.stop(e);\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n+ this.maxExtent.bottom);\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/ArcGISCache.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/** \n+ * @requires OpenLayers/Layer/XYZ.js\n+ */\n+\n+/** \n+ * Class: OpenLayers.Layer.ArcGISCache \n+ * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. \n+ * Tile must already be cached for this layer to access it. This does not require \n+ * ArcGIS Server itself.\n+ * \n+ * A few attempts have been made at this kind of layer before. See \n+ * http://trac.osgeo.org/openlayers/ticket/1967 \n+ * and \n+ * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js\n+ *\n+ * Typically the problem encountered is that the tiles seem to \"jump around\".\n+ * This is due to the fact that the actual max extent for the tiles on AGS layers\n+ * changes at each zoom level due to the way these caches are constructed.\n+ * We have attempted to use the resolutions, tile size, and tile origin\n+ * from the cache meta data to make the appropriate changes to the max extent\n+ * of the tile to compensate for this behavior. This must be done as zoom levels change\n+ * and before tiles are requested, which is why methods from base classes are overridden.\n+ *\n+ * For reference, you can access mapcache meta data in two ways. For accessing a \n+ * mapcache through ArcGIS Server, you can simply go to the landing page for the\n+ * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer)\n+ * For accessing it directly through HTTP, there should always be a conf.xml file\n+ * in the root directory. \n+ * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml)\n+ * \n+ *Inherits from: \n+ * - <OpenLayers.Layer.XYZ> \n+ */\n+OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+\n /**\n- * Method: showToggle\n- * Hide/Show the toggle depending on whether the control is minimized\n- *\n- * Parameters:\n- * minimize - {Boolean} \n+ * APIProperty: url\n+ * {String | Array} The base URL for the layer cache. You can also\n+ * provide a list of URL strings for the layer if your cache is\n+ * available from multiple origins. This must be set before the layer\n+ * is drawn.\n */\n- showToggle: function(minimize) {\n- if (this.maximizeDiv) {\n- this.maximizeDiv.style.display = minimize ? '' : 'none';\n- }\n- if (this.minimizeDiv) {\n- this.minimizeDiv.style.display = minimize ? 'none' : '';\n- }\n- },\n+ url: null,\n \n /**\n- * Method: update\n- * Update the overview map after layers move.\n+ * APIProperty: tileOrigin\n+ * {<OpenLayers.LonLat>} The location of the tile origin for the cache.\n+ * An ArcGIS cache has it's origin at the upper-left (lowest x value\n+ * and highest y value of the coordinate system). The units for the\n+ * tile origin should be the same as the units for the cached data.\n */\n- update: function() {\n- if (this.ovmap == null) {\n- this.createMap();\n- }\n+ tileOrigin: null,\n \n- if (this.autoPan || !this.isSuitableOverview()) {\n- this.updateOverview();\n- }\n+ /**\n+ * APIProperty: tileSize\n+ * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels.\n+ */\n+ tileSize: new OpenLayers.Size(256, 256),\n \n- // update extent rectangle\n- this.updateRectToMap();\n- },\n+ /**\n+ * APIProperty: useAGS\n+ * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS)\n+ * cache via an AGS MapServer or directly through HTTP. When accessing via\n+ * AGS the path structure uses a standard z/y/x structure. But AGS actually\n+ * stores the tile images on disk using a hex based folder structure that looks\n+ * like \"http://example.com/mylayer/L00/R00000000/C00000000.png\". Learn more\n+ * about this here:\n+ * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx\n+ * Defaults to true;\n+ */\n+ useArcGISServer: true,\n \n /**\n- * Method: isSuitableOverview\n- * Determines if the overview map is suitable given the extent and\n- * resolution of the main map.\n+ * APIProperty: type\n+ * {String} Image type for the layer. This becomes the filename extension\n+ * in tile requests. Default is \"png\" (generating a url like\n+ * \"http://example.com/mylayer/L00/R00000000/C00000000.png\").\n */\n- isSuitableOverview: function() {\n- var mapExtent = this.map.getExtent();\n- var maxExtent = this.map.getMaxExtent();\n- var testExtent = new OpenLayers.Bounds(\n- Math.max(mapExtent.left, maxExtent.left),\n- Math.max(mapExtent.bottom, maxExtent.bottom),\n- Math.min(mapExtent.right, maxExtent.right),\n- Math.min(mapExtent.top, maxExtent.top));\n+ type: 'png',\n \n- if (this.ovmap.getProjection() != this.map.getProjection()) {\n- testExtent = testExtent.transform(\n- this.map.getProjectionObject(),\n- this.ovmap.getProjectionObject());\n- }\n+ /**\n+ * APIProperty: useScales\n+ * {Boolean} Optional override to indicate that the layer should use 'scale' information\n+ * returned from the server capabilities object instead of 'resolution' information.\n+ * This can be important if your tile server uses an unusual DPI for the tiles.\n+ */\n+ useScales: false,\n \n- var resRatio = this.ovmap.getResolution() / this.map.getResolution();\n- return ((resRatio > this.minRatio) &&\n- (resRatio <= this.maxRatio) &&\n- (this.ovmap.getExtent().containsBounds(testExtent)));\n- },\n+ /**\n+ * APIProperty: overrideDPI\n+ * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based \n+ * on the tile information in the server capabilities object. This can be useful \n+ * if your server has a non-standard DPI setting on its tiles, and you're only using \n+ * tiles with that DPI. This value is used while OpenLayers is calculating resolution\n+ * using scales, and is not necessary if you have resolution information. (This is\n+ * typically the case) Regardless, this setting can be useful, but is dangerous\n+ * because it will impact other layers while calculating resolution. Only use this\n+ * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale)\n+ */\n+ overrideDPI: false,\n \n /**\n- * Method updateOverview\n- * Called by <update> if <isSuitableOverview> returns true\n+ * Constructor: OpenLayers.Layer.ArcGISCache \n+ * Creates a new instance of this class \n+ * \n+ * Parameters: \n+ * name - {String} \n+ * url - {String} \n+ * options - {Object} extra layer options\n */\n- updateOverview: function() {\n- var mapRes = this.map.getResolution();\n- var targetRes = this.ovmap.getResolution();\n- var resRatio = targetRes / mapRes;\n- if (resRatio > this.maxRatio) {\n- // zoom in overview map\n- targetRes = this.minRatio * mapRes;\n- } else if (resRatio <= this.minRatio) {\n- // zoom out overview map\n- targetRes = this.maxRatio * mapRes;\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+\n+ if (this.resolutions) {\n+ this.serverResolutions = this.resolutions;\n+ this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);\n }\n- var center;\n- if (this.ovmap.getProjection() != this.map.getProjection()) {\n- center = this.map.center.clone();\n- center.transform(this.map.getProjectionObject(),\n- this.ovmap.getProjectionObject());\n- } else {\n- center = this.map.center;\n+\n+ // this block steps through translating the values from the server layer JSON \n+ // capabilities object into values that we can use. This is also a helpful\n+ // reference when configuring this layer directly.\n+ if (this.layerInfo) {\n+ // alias the object\n+ var info = this.layerInfo;\n+\n+ // build our extents\n+ var startingTileExtent = new OpenLayers.Bounds(\n+ info.fullExtent.xmin,\n+ info.fullExtent.ymin,\n+ info.fullExtent.xmax,\n+ info.fullExtent.ymax\n+ );\n+\n+ // set our projection based on the given spatial reference.\n+ // esri uses slightly different IDs, so this may not be comprehensive\n+ this.projection = 'EPSG:' + info.spatialReference.wkid;\n+ this.sphericalMercator = (info.spatialReference.wkid == 102100);\n+\n+ // convert esri units into openlayers units (basic feet or meters only)\n+ this.units = (info.units == \"esriFeet\") ? 'ft' : 'm';\n+\n+ // optional extended section based on whether or not the server returned\n+ // specific tile information\n+ if (!!info.tileInfo) {\n+ // either set the tiles based on rows/columns, or specific width/height\n+ this.tileSize = new OpenLayers.Size(\n+ info.tileInfo.width || info.tileInfo.cols,\n+ info.tileInfo.height || info.tileInfo.rows\n+ );\n+\n+ // this must be set when manually configuring this layer\n+ this.tileOrigin = new OpenLayers.LonLat(\n+ info.tileInfo.origin.x,\n+ info.tileInfo.origin.y\n+ );\n+\n+ var upperLeft = new OpenLayers.Geometry.Point(\n+ startingTileExtent.left,\n+ startingTileExtent.top\n+ );\n+\n+ var bottomRight = new OpenLayers.Geometry.Point(\n+ startingTileExtent.right,\n+ startingTileExtent.bottom\n+ );\n+\n+ if (this.useScales) {\n+ this.scales = [];\n+ } else {\n+ this.resolutions = [];\n+ }\n+\n+ this.lods = [];\n+ for (var key in info.tileInfo.lods) {\n+ if (info.tileInfo.lods.hasOwnProperty(key)) {\n+ var lod = info.tileInfo.lods[key];\n+ if (this.useScales) {\n+ this.scales.push(lod.scale);\n+ } else {\n+ this.resolutions.push(lod.resolution);\n+ }\n+\n+ var start = this.getContainingTileCoords(upperLeft, lod.resolution);\n+ lod.startTileCol = start.x;\n+ lod.startTileRow = start.y;\n+\n+ var end = this.getContainingTileCoords(bottomRight, lod.resolution);\n+ lod.endTileCol = end.x;\n+ lod.endTileRow = end.y;\n+ this.lods.push(lod);\n+ }\n+ }\n+\n+ this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);\n+ this.serverResolutions = this.resolutions;\n+ if (this.overrideDPI && info.tileInfo.dpi) {\n+ // see comment above for 'overrideDPI'\n+ OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;\n+ }\n+ }\n }\n- this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(\n- targetRes * this.resolutionFactor));\n- this.updateRectToMap();\n },\n \n- /**\n- * Method: createMap\n- * Construct the map that this control contains\n+ /** \n+ * Method: getContainingTileCoords\n+ * Calculates the x/y pixel corresponding to the position of the tile\n+ * that contains the given point and for the for the given resolution.\n+ * \n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} \n+ * res - {Float} The resolution for which to compute the extent.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n+ * of the upper left tile for the given resolution.\n */\n- createMap: function() {\n- // create the overview map\n- var options = OpenLayers.Util.extend({\n- controls: [],\n- maxResolution: 'auto',\n- fallThrough: false\n- }, this.mapOptions);\n- this.ovmap = new OpenLayers.Map(this.mapDiv, options);\n- this.ovmap.viewPortDiv.appendChild(this.extentRectangle);\n+ getContainingTileCoords: function(point, res) {\n+ return new OpenLayers.Pixel(\n+ Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0),\n+ Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0)\n+ );\n+ },\n \n- // prevent ovmap from being destroyed when the page unloads, because\n- // the OverviewMap control has to do this (and does it).\n- OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);\n+ /** \n+ * Method: calculateMaxExtentWithLOD\n+ * Given a Level of Detail object from the server, this function\n+ * calculates the actual max extent\n+ * \n+ * Parameters: \n+ * lod - {Object} a Level of Detail Object from the server capabilities object \n+ representing a particular zoom level\n+ * \n+ * Returns: \n+ * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n+ */\n+ calculateMaxExtentWithLOD: function(lod) {\n+ // the max extent we're provided with just overlaps some tiles\n+ // our real extent is the bounds of all the tiles we touch\n \n- this.ovmap.addLayers(this.layers);\n- this.ovmap.zoomToMaxExtent();\n- // check extent rectangle border width\n- this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n- 'border-left-width')) +\n- parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n- 'border-right-width'));\n- this.wComp = (this.wComp) ? this.wComp : 2;\n- this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n- 'border-top-width')) +\n- parseInt(OpenLayers.Element.getStyle(this.extentRectangle,\n- 'border-bottom-width'));\n- this.hComp = (this.hComp) ? this.hComp : 2;\n+ var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;\n+ var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;\n \n- this.handlers.drag = new OpenLayers.Handler.Drag(\n- this, {\n- move: this.rectDrag,\n- done: this.updateMapToRect\n- }, {\n- map: this.ovmap\n- }\n- );\n- this.handlers.click = new OpenLayers.Handler.Click(\n- this, {\n- \"click\": this.mapDivClick\n- }, {\n- \"single\": true,\n- \"double\": false,\n- \"stopSingle\": true,\n- \"stopDouble\": true,\n- \"pixelTolerance\": 1,\n- map: this.ovmap\n- }\n- );\n- this.handlers.click.activate();\n+ var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);\n+ var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);\n \n- this.rectEvents = new OpenLayers.Events(this, this.extentRectangle,\n- null, true);\n- this.rectEvents.register(\"mouseover\", this, function(e) {\n- if (!this.handlers.drag.active && !this.map.dragging) {\n- this.handlers.drag.activate();\n- }\n- });\n- this.rectEvents.register(\"mouseout\", this, function(e) {\n- if (!this.handlers.drag.dragging) {\n- this.handlers.drag.deactivate();\n- }\n- });\n+ var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);\n+ var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);\n+ return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n+ },\n \n- if (this.ovmap.getProjection() != this.map.getProjection()) {\n- var sourceUnits = this.map.getProjectionObject().getUnits() ||\n- this.map.units || this.map.baseLayer.units;\n- var targetUnits = this.ovmap.getProjectionObject().getUnits() ||\n- this.ovmap.units || this.ovmap.baseLayer.units;\n- this.resolutionFactor = sourceUnits && targetUnits ?\n- OpenLayers.INCHES_PER_UNIT[sourceUnits] /\n- OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n- }\n+ /** \n+ * Method: calculateMaxExtentWithExtent\n+ * Given a 'suggested' max extent from the server, this function uses\n+ * information about the actual tile sizes to determine the actual\n+ * extent of the layer.\n+ * \n+ * Parameters: \n+ * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer\n+ * res - {Float} The resolution for which to compute the extent.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n+ */\n+ calculateMaxExtentWithExtent: function(extent, res) {\n+ var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);\n+ var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);\n+ var start = this.getContainingTileCoords(upperLeft, res);\n+ var end = this.getContainingTileCoords(bottomRight, res);\n+ var lod = {\n+ resolution: res,\n+ startTileCol: start.x,\n+ startTileRow: start.y,\n+ endTileCol: end.x,\n+ endTileRow: end.y\n+ };\n+ return this.calculateMaxExtentWithLOD(lod);\n },\n \n- /**\n- * Method: updateRectToMap\n- * Updates the extent rectangle position and size to match the map extent\n+ /** \n+ * Method: getUpperLeftTileCoord\n+ * Calculates the x/y pixel corresponding to the position \n+ * of the upper left tile for the given resolution.\n+ * \n+ * Parameters: \n+ * res - {Float} The resolution for which to compute the extent.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n+ * of the upper left tile for the given resolution.\n */\n- updateRectToMap: function() {\n- // If the projections differ we need to reproject\n- var bounds;\n- if (this.ovmap.getProjection() != this.map.getProjection()) {\n- bounds = this.map.getExtent().transform(\n- this.map.getProjectionObject(),\n- this.ovmap.getProjectionObject());\n- } else {\n- bounds = this.map.getExtent();\n- }\n- var pxBounds = this.getRectBoundsFromMapBounds(bounds);\n- if (pxBounds) {\n- this.setRectPxBounds(pxBounds);\n- }\n+ getUpperLeftTileCoord: function(res) {\n+ var upperLeft = new OpenLayers.Geometry.Point(\n+ this.maxExtent.left,\n+ this.maxExtent.top);\n+ return this.getContainingTileCoords(upperLeft, res);\n },\n \n- /**\n- * Method: updateMapToRect\n- * Updates the map extent to match the extent rectangle position and size\n+ /** \n+ * Method: getLowerRightTileCoord\n+ * Calculates the x/y pixel corresponding to the position \n+ * of the lower right tile for the given resolution.\n+ * \n+ * Parameters: \n+ * res - {Float} The resolution for which to compute the extent.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position\n+ * of the lower right tile for the given resolution.\n */\n- updateMapToRect: function() {\n- var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);\n- if (this.ovmap.getProjection() != this.map.getProjection()) {\n- lonLatBounds = lonLatBounds.transform(\n- this.ovmap.getProjectionObject(),\n- this.map.getProjectionObject());\n- }\n- this.map.panTo(lonLatBounds.getCenterLonLat());\n+ getLowerRightTileCoord: function(res) {\n+ var bottomRight = new OpenLayers.Geometry.Point(\n+ this.maxExtent.right,\n+ this.maxExtent.bottom);\n+ return this.getContainingTileCoords(bottomRight, res);\n },\n \n- /**\n- * Method: setRectPxBounds\n- * Set extent rectangle pixel bounds.\n+ /** \n+ * Method: getMaxExtentForResolution\n+ * Since the max extent of a set of tiles can change from zoom level\n+ * to zoom level, we need to be able to calculate that max extent \n+ * for a given resolution.\n *\n- * Parameters:\n- * pxBounds - {<OpenLayers.Bounds>}\n+ * Parameters: \n+ * res - {Float} The resolution for which to compute the extent.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Bounds>} The extent for this resolution\n */\n- setRectPxBounds: function(pxBounds) {\n- var top = Math.max(pxBounds.top, 0);\n- var left = Math.max(pxBounds.left, 0);\n- var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()),\n- this.ovmap.size.h - this.hComp);\n- var right = Math.min(pxBounds.left + pxBounds.getWidth(),\n- this.ovmap.size.w - this.wComp);\n- var width = Math.max(right - left, 0);\n- var height = Math.max(bottom - top, 0);\n- if (width < this.minRectSize || height < this.minRectSize) {\n- this.extentRectangle.className = this.displayClass +\n- this.minRectDisplayClass;\n- var rLeft = left + (width / 2) - (this.minRectSize / 2);\n- var rTop = top + (height / 2) - (this.minRectSize / 2);\n- this.extentRectangle.style.top = Math.round(rTop) + 'px';\n- this.extentRectangle.style.left = Math.round(rLeft) + 'px';\n- this.extentRectangle.style.height = this.minRectSize + 'px';\n- this.extentRectangle.style.width = this.minRectSize + 'px';\n- } else {\n- this.extentRectangle.className = this.displayClass +\n- 'ExtentRectangle';\n- this.extentRectangle.style.top = Math.round(top) + 'px';\n- this.extentRectangle.style.left = Math.round(left) + 'px';\n- this.extentRectangle.style.height = Math.round(height) + 'px';\n- this.extentRectangle.style.width = Math.round(width) + 'px';\n+ getMaxExtentForResolution: function(res) {\n+ var start = this.getUpperLeftTileCoord(res);\n+ var end = this.getLowerRightTileCoord(res);\n+\n+ var numTileCols = (end.x - start.x) + 1;\n+ var numTileRows = (end.y - start.y) + 1;\n+\n+ var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);\n+ var maxX = minX + (numTileCols * this.tileSize.w * res);\n+\n+ var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);\n+ var minY = maxY - (numTileRows * this.tileSize.h * res);\n+ return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n+ },\n+\n+ /** \n+ * APIMethod: clone \n+ * Returns an exact clone of this OpenLayers.Layer.ArcGISCache\n+ * \n+ * Parameters: \n+ * [obj] - {Object} optional object to assign the cloned instance to.\n+ * \n+ * Returns: \n+ * {<OpenLayers.Layer.ArcGISCache>} clone of this instance \n+ */\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);\n }\n- this.rectPxBounds = new OpenLayers.Bounds(\n- Math.round(left), Math.round(bottom),\n- Math.round(right), Math.round(top)\n- );\n+ return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n },\n \n /**\n- * Method: getRectBoundsFromMapBounds\n- * Get the rect bounds from the map bounds.\n- *\n+ * Method: initGriddedTiles\n+ * \n * Parameters:\n- * lonLatBounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent\n- * translated into pixel bounds for the overview map\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- getRectBoundsFromMapBounds: function(lonLatBounds) {\n- var leftBottomPx = this.getOverviewPxFromLonLat({\n- lon: lonLatBounds.left,\n- lat: lonLatBounds.bottom\n- });\n- var rightTopPx = this.getOverviewPxFromLonLat({\n- lon: lonLatBounds.right,\n- lat: lonLatBounds.top\n- });\n- var bounds = null;\n- if (leftBottomPx && rightTopPx) {\n- bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y,\n- rightTopPx.x, rightTopPx.y);\n- }\n- return bounds;\n+ initGriddedTiles: function(bounds) {\n+ delete this._tileOrigin;\n+ OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments);\n },\n \n /**\n- * Method: getMapBoundsFromRectBounds\n- * Get the map bounds from the rect bounds.\n- *\n- * Parameters:\n- * pxBounds - {<OpenLayers.Bounds>}\n+ * Method: getMaxExtent\n+ * Get this layer's maximum extent.\n *\n * Returns:\n- * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds\n- * translated into lon/lat bounds for the overview map\n+ * {<OpenLayers.Bounds>}\n */\n- getMapBoundsFromRectBounds: function(pxBounds) {\n- var leftBottomLonLat = this.getLonLatFromOverviewPx({\n- x: pxBounds.left,\n- y: pxBounds.bottom\n- });\n- var rightTopLonLat = this.getLonLatFromOverviewPx({\n- x: pxBounds.right,\n- y: pxBounds.top\n- });\n- return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat,\n- rightTopLonLat.lon, rightTopLonLat.lat);\n+ getMaxExtent: function() {\n+ var resolution = this.map.getResolution();\n+ return this.maxExtent = this.getMaxExtentForResolution(resolution);\n },\n \n /**\n- * Method: getLonLatFromOverviewPx\n- * Get a map location from a pixel location\n- *\n- * Parameters:\n- * overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or\n- * an object with a\n- * 'x' and 'y' properties.\n+ * Method: getTileOrigin\n+ * Determine the origin for aligning the grid of tiles. \n+ * The origin will be derived from the layer's <maxExtent> property. \n *\n * Returns:\n- * {Object} Location which is the passed-in overview map\n- * OpenLayers.Pixel, translated into lon/lat by the overview\n- * map. An object with a 'lon' and 'lat' properties.\n+ * {<OpenLayers.LonLat>} The tile origin.\n */\n- getLonLatFromOverviewPx: function(overviewMapPx) {\n- var size = this.ovmap.size;\n- var res = this.ovmap.getResolution();\n- var center = this.ovmap.getExtent().getCenterLonLat();\n-\n- var deltaX = overviewMapPx.x - (size.w / 2);\n- var deltaY = overviewMapPx.y - (size.h / 2);\n-\n- return {\n- lon: center.lon + deltaX * res,\n- lat: center.lat - deltaY * res\n- };\n+ getTileOrigin: function() {\n+ if (!this._tileOrigin) {\n+ var extent = this.getMaxExtent();\n+ this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom);\n+ }\n+ return this._tileOrigin;\n },\n \n /**\n- * Method: getOverviewPxFromLonLat\n- * Get a pixel location from a map location\n+ * Method: getURL\n+ * Determine the URL for a tile given the tile bounds. This is should support\n+ * urls that access tiles through an ArcGIS Server MapServer or directly through\n+ * the hex folder structure using HTTP. Just be sure to set the useArcGISServer\n+ * property appropriately! This is basically the same as \n+ * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing,\n+ * and tile rounding.\n *\n * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n+ * bounds - {<OpenLayers.Bounds>}\n *\n * Returns:\n- * {Object} Location which is the passed-in OpenLayers.LonLat, \n- * translated into overview map pixels\n+ * {String} The URL for a tile based on given bounds.\n */\n- getOverviewPxFromLonLat: function(lonlat) {\n- var res = this.ovmap.getResolution();\n- var extent = this.ovmap.getExtent();\n- if (extent) {\n- return {\n- x: Math.round(1 / res * (lonlat.lon - extent.left)),\n- y: Math.round(1 / res * (extent.top - lonlat.lat))\n- };\n+ getURL: function(bounds) {\n+ var res = this.getResolution();\n+\n+ // tile center\n+ var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2));\n+ var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2));\n+\n+ var center = bounds.getCenterLonLat();\n+ var point = {\n+ x: center.lon,\n+ y: center.lat\n+ };\n+ var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))));\n+ var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))));\n+ var z = this.map.getZoom();\n+\n+ // this prevents us from getting pink tiles (non-existant tiles)\n+ if (this.lods) {\n+ var lod = this.lods[this.map.getZoom()];\n+ if ((x < lod.startTileCol || x > lod.endTileCol) ||\n+ (y < lod.startTileRow || y > lod.endTileRow)) {\n+ return null;\n+ }\n+ } else {\n+ var start = this.getUpperLeftTileCoord(res);\n+ var end = this.getLowerRightTileCoord(res);\n+ if ((x < start.x || x >= end.x) ||\n+ (y < start.y || y >= end.y)) {\n+ return null;\n+ }\n+ }\n+\n+ // Construct the url string\n+ var url = this.url;\n+ var s = '' + x + y + z;\n+\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(s, url);\n+ }\n+\n+ // Accessing tiles through ArcGIS Server uses a different path\n+ // structure than direct access via the folder structure.\n+ if (this.useArcGISServer) {\n+ // AGS MapServers have pretty url access to tiles\n+ url = url + '/tile/${z}/${y}/${x}';\n+ } else {\n+ // The tile images are stored using hex values on disk.\n+ x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16);\n+ y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16);\n+ z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10);\n+ url = url + '/${z}/${y}/${x}.' + this.type;\n }\n+\n+ // Write the values into our formatted url\n+ url = OpenLayers.String.format(url, {\n+ 'x': x,\n+ 'y': y,\n+ 'z': z\n+ });\n+\n+ return OpenLayers.Util.urlAppend(\n+ url, OpenLayers.Util.getParameterString(this.params)\n+ );\n },\n \n- CLASS_NAME: 'OpenLayers.Control.OverviewMap'\n+ CLASS_NAME: 'OpenLayers.Layer.ArcGISCache'\n });\n /* ======================================================================\n- OpenLayers/Control/WMSGetFeatureInfo.js\n+ OpenLayers/Layer/UTFGrid.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Click.js\n- * @requires OpenLayers/Handler/Hover.js\n- * @requires OpenLayers/Request.js\n- * @requires OpenLayers/Format/WMSGetFeatureInfo.js\n+ * @requires OpenLayers/Layer/XYZ.js\n+ * @requires OpenLayers/Tile/UTFGrid.js\n */\n \n-/**\n- * Class: OpenLayers.Control.WMSGetFeatureInfo\n- * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The\n- * information may be in a display-friendly format such as HTML, or a machine-friendly format such\n- * as GML, depending on the server's capabilities and the client's configuration. This control\n- * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and\n- * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an\n- * array of features if it successfully read the response.\n+/** \n+ * Class: OpenLayers.Layer.UTFGrid\n+ * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are \n+ * essentially JSON-based ASCII art with attached attributes, they are not \n+ * visibly rendered. In order to use them in the map, you must add a \n+ * <OpenLayers.Control.UTFGrid> control as well.\n+ *\n+ * Example:\n+ *\n+ * (start code)\n+ * var world_utfgrid = new OpenLayers.Layer.UTFGrid({\n+ * url: \"/tiles/world_utfgrid/${z}/${x}/${y}.json\",\n+ * utfgridResolution: 4,\n+ * displayInLayerSwitcher: false\n+ * );\n+ * map.addLayer(world_utfgrid);\n+ * \n+ * var control = new OpenLayers.Control.UTFGrid({\n+ * layers: [world_utfgrid],\n+ * handlerMode: 'move',\n+ * callback: function(dataLookup) {\n+ * // do something with returned data\n+ * }\n+ * })\n+ * (end code)\n *\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.XYZ>\n */\n-OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n \n /**\n- * APIProperty: hover\n- * {Boolean} Send GetFeatureInfo requests when mouse stops moving.\n- * Default is false.\n+ * APIProperty: isBaseLayer\n+ * Default is false, as UTFGrids are designed to be a transparent overlay layer. \n */\n- hover: false,\n+ isBaseLayer: false,\n \n /**\n- * APIProperty: drillDown\n- * {Boolean} Drill down over all WMS layers in the map. When\n- * using drillDown mode, hover is not possible, and an infoFormat that\n- * returns parseable features is required. Default is false.\n+ * APIProperty: projection\n+ * {<OpenLayers.Projection>}\n+ * Source projection for the UTFGrids. Default is \"EPSG:900913\".\n */\n- drillDown: false,\n+ projection: new OpenLayers.Projection(\"EPSG:900913\"),\n \n /**\n- * APIProperty: maxFeatures\n- * {Integer} Maximum number of features to return from a WMS query. This\n- * sets the feature_count parameter on WMS GetFeatureInfo\n- * requests.\n+ * Property: useJSONP\n+ * {Boolean}\n+ * Should we use a JSONP script approach instead of a standard AJAX call?\n+ *\n+ * Set to true for using utfgrids from another server. \n+ * Avoids same-domain policy restrictions. \n+ * Note that this only works if the server accepts \n+ * the callback GET parameter and dynamically \n+ * wraps the returned json in a function call.\n+ * \n+ * Default is false\n */\n- maxFeatures: 10,\n+ useJSONP: false,\n \n /**\n- * APIProperty: clickCallback\n- * {String} The click callback to register in the\n- * {<OpenLayers.Handler.Click>} object created when the hover\n- * option is set to false. Default is \"click\".\n+ * APIProperty: url\n+ * {String}\n+ * URL tempate for UTFGrid tiles. Include x, y, and z parameters.\n+ * E.g. \"/tiles/${z}/${x}/${y}.json\"\n */\n- clickCallback: \"click\",\n \n /**\n- * APIProperty: output\n- * {String} Either \"features\" or \"object\". When triggering a getfeatureinfo\n- * request should we pass on an array of features or an object with with\n- * a \"features\" property and other properties (such as the url of the\n- * WMS). Default is \"features\".\n+ * APIProperty: utfgridResolution\n+ * {Number}\n+ * Ratio of the pixel width to the width of a UTFGrid data point. If an \n+ * entry in the grid represents a 4x4 block of pixels, the \n+ * utfgridResolution would be 4. Default is 2 (specified in \n+ * <OpenLayers.Tile.UTFGrid>).\n */\n- output: \"features\",\n \n /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info.\n- * If omitted, all map WMS layers with a url that matches this <url> or\n- * <layerUrls> will be considered.\n+ * Property: tileClass\n+ * {<OpenLayers.Tile>} The tile class to use for this layer.\n+ * Defaults is <OpenLayers.Tile.UTFGrid>.\n */\n- layers: null,\n+ tileClass: OpenLayers.Tile.UTFGrid,\n \n /**\n- * APIProperty: queryVisible\n- * {Boolean} If true, filter out hidden layers when searching the map for\n- * layers to query. Default is false.\n+ * Constructor: OpenLayers.Layer.UTFGrid\n+ * Create a new UTFGrid layer.\n+ *\n+ * Parameters:\n+ * config - {Object} Configuration properties for the layer.\n+ *\n+ * Required configuration properties:\n+ * url - {String} The url template for UTFGrid tiles. See the <url> property.\n */\n- queryVisible: false,\n+ initialize: function(options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(\n+ this, [options.name, options.url, {}, options]\n+ );\n+ this.tileOptions = OpenLayers.Util.extend({\n+ utfgridResolution: this.utfgridResolution\n+ }, this.tileOptions);\n+ },\n \n /**\n- * APIProperty: url\n- * {String} The URL of the WMS service to use. If not provided, the url\n- * of the first eligible layer will be used.\n+ * Method: createBackBuffer\n+ * The UTFGrid cannot create a back buffer, so this method is overriden.\n */\n- url: null,\n+ createBackBuffer: function() {},\n \n /**\n- * APIProperty: layerUrls\n- * {Array(String)} Optional list of urls for layers that should be queried.\n- * This can be used when the layer url differs from the url used for\n- * making GetFeatureInfo requests (in the case of a layer using cached\n- * tiles).\n+ * APIMethod: clone\n+ * Create a clone of this layer\n+ *\n+ * Parameters:\n+ * obj - {Object} Only used by a subclass of this layer.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid\n */\n- layerUrls: null,\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.UTFGrid(this.getOptions());\n+ }\n \n- /**\n- * APIProperty: infoFormat\n- * {String} The mimetype to request from the server. If you are using\n- * drillDown mode and have multiple servers that do not share a common\n- * infoFormat, you can override the control's infoFormat by providing an\n- * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).\n- */\n- infoFormat: 'text/html',\n+ // get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n- /**\n- * APIProperty: vendorParams\n- * {Object} Additional parameters that will be added to the request, for\n- * WMS implementations that support them. This could e.g. look like\n- * (start code)\n- * {\n- * radius: 5\n- * }\n- * (end)\n- */\n- vendorParams: {},\n+ return obj;\n+ },\n \n /**\n- * APIProperty: format\n- * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses.\n- * Default is <OpenLayers.Format.WMSGetFeatureInfo>.\n+ * APIProperty: getFeatureInfo\n+ * Get details about a feature associated with a map location. The object\n+ * returned will have id and data properties. If the given location\n+ * doesn't correspond to a feature, null will be returned.\n+ *\n+ * Parameters:\n+ * location - {<OpenLayers.LonLat>} map location\n+ *\n+ * Returns:\n+ * {Object} Object representing the feature id and UTFGrid data \n+ * corresponding to the given map location. Returns null if the given\n+ * location doesn't hit a feature.\n */\n- format: null,\n+ getFeatureInfo: function(location) {\n+ var info = null;\n+ var tileInfo = this.getTileData(location);\n+ if (tileInfo && tileInfo.tile) {\n+ info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j);\n+ }\n+ return info;\n+ },\n \n /**\n- * APIProperty: formatOptions\n- * {Object} Optional properties to set on the format (if one is not provided\n- * in the <format> property.\n+ * APIMethod: getFeatureId\n+ * Get the identifier for the feature associated with a map location.\n+ *\n+ * Parameters:\n+ * location - {<OpenLayers.LonLat>} map location\n+ *\n+ * Returns:\n+ * {String} The feature identifier corresponding to the given map location.\n+ * Returns null if the location doesn't hit a feature.\n */\n- formatOptions: null,\n+ getFeatureId: function(location) {\n+ var id = null;\n+ var info = this.getTileData(location);\n+ if (info.tile) {\n+ id = info.tile.getFeatureId(info.i, info.j);\n+ }\n+ return id;\n+ },\n \n- /**\n- * APIProperty: handlerOptions\n- * {Object} Additional options for the handlers used by this control, e.g.\n- * (start code)\n- * {\n- * \"click\": {delay: 100},\n- * \"hover\": {delay: 300}\n- * }\n- * (end)\n+ CLASS_NAME: \"OpenLayers.Layer.UTFGrid\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/KaMap.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.KaMap\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n+ /** \n+ * APIProperty: isBaseLayer\n+ * {Boolean} KaMap Layer is always a base layer \n */\n+ isBaseLayer: true,\n \n /**\n- * Property: handler\n- * {Object} Reference to the <OpenLayers.Handler> for this control\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} parameters set by default. The default parameters set \n+ * the format via the 'i' parameter to 'jpeg'. \n */\n- handler: null,\n+ DEFAULT_PARAMS: {\n+ i: 'jpeg',\n+ map: ''\n+ },\n \n /**\n- * Property: hoverRequest\n- * {<OpenLayers.Request>} contains the currently running hover request\n- * (if any).\n+ * Constructor: OpenLayers.Layer.KaMap\n+ * \n+ * Parameters:\n+ * name - {String}\n+ * url - {String}\n+ * params - {Object} Parameters to be sent to the HTTP server in the\n+ * query string for the tile. The format can be set via the 'i'\n+ * parameter (defaults to jpg) , and the map should be set via \n+ * the 'map' parameter. It has been reported that ka-Map may behave\n+ * inconsistently if your format parameter does not match the format\n+ * parameter configured in your config.php. (See ticket #327 for more\n+ * information.)\n+ * options - {Object} Additional options for the layer. Any of the \n+ * APIProperties listed on this layer, and any layer types it\n+ * extends, can be overridden through the options parameter. \n */\n- hoverRequest: null,\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n+ this.params = OpenLayers.Util.applyDefaults(\n+ this.params, this.DEFAULT_PARAMS\n+ );\n+ },\n \n /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforegetfeatureinfo - Triggered before the request is sent.\n- * The event object has an *xy* property with the position of the\n- * mouse click or hover event that triggers the request.\n- * nogetfeatureinfo - no queryable layers were found.\n- * getfeatureinfo - Triggered when a GetFeatureInfo response is received.\n- * The event object has a *text* property with the body of the\n- * response (String), a *features* property with an array of the\n- * parsed features, an *xy* property with the position of the mouse\n- * click or hover event that triggered the request, and a *request*\n- * property with the request itself. If drillDown is set to true and\n- * multiple requests were issued to collect feature info from all\n- * layers, *text* and *request* will only contain the response body\n- * and request object of the last request.\n+ * Method: getURL\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} \n+ * \n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the \n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters\n */\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var mapRes = this.map.getResolution();\n+ var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n+ var pX = Math.round(bounds.left / mapRes);\n+ var pY = -Math.round(bounds.top / mapRes);\n+ return this.getFullRequestString({\n+ t: pY,\n+ l: pX,\n+ s: scale\n+ });\n+ },\n \n- /**\n- * Constructor: <OpenLayers.Control.WMSGetFeatureInfo>\n+ /** \n+ * Method: calculateGridLayout\n+ * ka-Map uses the center point of the map as an origin for \n+ * its tiles. Override calculateGridLayout to center tiles \n+ * correctly for this case.\n *\n * Parameters:\n- * options - {Object}\n+ * bounds - {<OpenLayers.Bound>}\n+ * origin - {<OpenLayers.LonLat>}\n+ * resolution - {Number}\n+ *\n+ * Returns:\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n */\n- initialize: function(options) {\n- options = options || {};\n- options.handlerOptions = options.handlerOptions || {};\n-\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ calculateGridLayout: function(bounds, origin, resolution) {\n+ var tilelon = resolution * this.tileSize.w;\n+ var tilelat = resolution * this.tileSize.h;\n \n- if (!this.format) {\n- this.format = new OpenLayers.Format.WMSGetFeatureInfo(\n- options.formatOptions\n- );\n- }\n+ var offsetlon = bounds.left;\n+ var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n \n- if (this.drillDown === true) {\n- this.hover = false;\n- }\n+ var offsetlat = bounds.top;\n+ var tilerow = Math.floor(offsetlat / tilelat) + this.buffer;\n \n- if (this.hover) {\n- this.handler = new OpenLayers.Handler.Hover(\n- this, {\n- 'move': this.cancelHover,\n- 'pause': this.getInfoForHover\n- },\n- OpenLayers.Util.extend(this.handlerOptions.hover || {}, {\n- 'delay': 250\n- }));\n- } else {\n- var callbacks = {};\n- callbacks[this.clickCallback] = this.getInfoForClick;\n- this.handler = new OpenLayers.Handler.Click(\n- this, callbacks, this.handlerOptions.click || {});\n- }\n+ return {\n+ tilelon: tilelon,\n+ tilelat: tilelat,\n+ startcol: tilecol,\n+ startrow: tilerow\n+ };\n },\n \n /**\n- * Method: getInfoForClick\n- * Called on click\n+ * Method: getTileBoundsForGridIndex\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>}\n- */\n- getInfoForClick: function(evt) {\n- this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: evt.xy\n- });\n- // Set the cursor to \"wait\" to tell the user we're working on their\n- // click.\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.request(evt.xy, {});\n- },\n-\n- /**\n- * Method: getInfoForHover\n- * Pause callback for the hover handler\n+ * row - {Number} The row of the grid\n+ * col - {Number} The column of the grid\n *\n- * Parameters:\n- * evt - {Object}\n+ * Returns:\n+ * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n */\n- getInfoForHover: function(evt) {\n- this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: evt.xy\n- });\n- this.request(evt.xy, {\n- hover: true\n- });\n+ getTileBoundsForGridIndex: function(row, col) {\n+ var origin = this.getTileOrigin();\n+ var tileLayout = this.gridLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var minX = (tileLayout.startcol + col) * tilelon;\n+ var minY = (tileLayout.startrow - row) * tilelat;\n+ return new OpenLayers.Bounds(\n+ minX, minY,\n+ minX + tilelon, minY + tilelat\n+ );\n },\n \n /**\n- * Method: cancelHover\n- * Cancel callback for the hover handler\n+ * APIMethod: clone\n+ * \n+ * Parameters: \n+ * obj - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap\n */\n- cancelHover: function() {\n- if (this.hoverRequest) {\n- this.hoverRequest.abort();\n- this.hoverRequest = null;\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.KaMap(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n }\n- },\n \n- /**\n- * Method: findLayers\n- * Internal method to get the layers, independent of whether we are\n- * inspecting the map or using a client-provided array\n- */\n- findLayers: function() {\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n- var candidates = this.layers || this.map.layers;\n- var layers = [];\n- var layer, url;\n- for (var i = candidates.length - 1; i >= 0; --i) {\n- layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.WMS &&\n- (!this.queryVisible || layer.getVisibility())) {\n- url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n- // if the control was not configured with a url, set it\n- // to the first layer url\n- if (this.drillDown === false && !this.url) {\n- this.url = url;\n- }\n- if (this.drillDown === true || this.urlMatches(url)) {\n- layers.push(layer);\n- }\n- }\n+ // copy/set any non-init, non-simple values here\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone();\n }\n- return layers;\n+\n+ // we do not want to copy reference to grid, so we make a new array\n+ obj.grid = [];\n+\n+ return obj;\n },\n \n /**\n- * Method: urlMatches\n- * Test to see if the provided url matches either the control <url> or one\n- * of the <layerUrls>.\n+ * APIMethod: getTileBounds\n+ * Returns The tile bounds for a layer given a pixel location.\n *\n * Parameters:\n- * url - {String} The url to test.\n+ * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n *\n * Returns:\n- * {Boolean} The provided url matches the control <url> or one of the\n- * <layerUrls>.\n+ * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n */\n- urlMatches: function(url) {\n- var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);\n- if (!matches && this.layerUrls) {\n- for (var i = 0, len = this.layerUrls.length; i < len; ++i) {\n- if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {\n- matches = true;\n- break;\n- }\n- }\n- }\n- return matches;\n+ getTileBounds: function(viewPortPx) {\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);\n+ var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);\n+ return new OpenLayers.Bounds(tileLeft, tileBottom,\n+ tileLeft + tileMapWidth,\n+ tileBottom + tileMapHeight);\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.KaMap\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/SphericalMercator.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Projection.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.SphericalMercator\n+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate\n+ * conversion for working with commercial APIs which use a spherical\n+ * mercator projection. Using this layer as a base layer, additional\n+ * layers can be used as overlays if they are in the same projection.\n+ *\n+ * A layer is given properties of this object by setting the sphericalMercator\n+ * property to true.\n+ *\n+ * More projection information:\n+ * - http://spatialreference.org/ref/user/google-projection/\n+ *\n+ * Proj4 Text:\n+ * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0\n+ * +k=1.0 +units=m +nadgrids=@null +no_defs\n+ *\n+ * WKT:\n+ * 900913=PROJCS[\"WGS84 / Simple Mercator\", GEOGCS[\"WGS 84\",\n+ * DATUM[\"WGS_1984\", SPHEROID[\"WGS_1984\", 6378137.0, 298.257223563]], \n+ * PRIMEM[\"Greenwich\", 0.0], UNIT[\"degree\", 0.017453292519943295], \n+ * AXIS[\"Longitude\", EAST], AXIS[\"Latitude\", NORTH]],\n+ * PROJECTION[\"Mercator_1SP_Google\"], \n+ * PARAMETER[\"latitude_of_origin\", 0.0], PARAMETER[\"central_meridian\", 0.0], \n+ * PARAMETER[\"scale_factor\", 1.0], PARAMETER[\"false_easting\", 0.0], \n+ * PARAMETER[\"false_northing\", 0.0], UNIT[\"m\", 1.0], AXIS[\"x\", EAST],\n+ * AXIS[\"y\", NORTH], AUTHORITY[\"EPSG\",\"900913\"]]\n+ */\n+OpenLayers.Layer.SphericalMercator = {\n+\n /**\n- * Method: buildWMSOptions\n- * Build an object with the relevant WMS options for the GetFeatureInfo request\n+ * Method: getExtent\n+ * Get the map's extent.\n *\n- * Parameters:\n- * url - {String} The url to be used for sending the request\n- * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers\n- * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse\n- * event occurred.\n- * format - {String} The format from the corresponding GetMap request\n+ * Returns:\n+ * {<OpenLayers.Bounds>} The map extent.\n */\n- buildWMSOptions: function(url, layers, clickPosition, format) {\n- var layerNames = [],\n- styleNames = [];\n- for (var i = 0, len = layers.length; i < len; i++) {\n- if (layers[i].params.LAYERS != null) {\n- layerNames = layerNames.concat(layers[i].params.LAYERS);\n- styleNames = styleNames.concat(this.getStyleNames(layers[i]));\n- }\n- }\n- var firstLayer = layers[0];\n- // use the firstLayer's projection if it matches the map projection -\n- // this assumes that all layers will be available in this projection\n- var projection = this.map.getProjection();\n- var layerProj = firstLayer.projection;\n- if (layerProj && layerProj.equals(this.map.getProjectionObject())) {\n- projection = layerProj.getCode();\n- }\n- var params = OpenLayers.Util.extend({\n- service: \"WMS\",\n- version: firstLayer.params.VERSION,\n- request: \"GetFeatureInfo\",\n- exceptions: firstLayer.params.EXCEPTIONS,\n- bbox: this.map.getExtent().toBBOX(null,\n- firstLayer.reverseAxisOrder()),\n- feature_count: this.maxFeatures,\n- height: this.map.getSize().h,\n- width: this.map.getSize().w,\n- format: format,\n- info_format: firstLayer.params.INFO_FORMAT || this.infoFormat\n- }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? {\n- crs: projection,\n- i: parseInt(clickPosition.x),\n- j: parseInt(clickPosition.y)\n- } : {\n- srs: projection,\n- x: parseInt(clickPosition.x),\n- y: parseInt(clickPosition.y)\n- });\n- if (layerNames.length != 0) {\n- params = OpenLayers.Util.extend({\n- layers: layerNames,\n- query_layers: layerNames,\n- styles: styleNames\n- }, params);\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.sphericalMercator) {\n+ extent = this.map.calculateBounds();\n+ } else {\n+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);\n }\n- OpenLayers.Util.applyDefaults(params, this.vendorParams);\n- return {\n- url: url,\n- params: OpenLayers.Util.upperCaseObject(params),\n- callback: function(request) {\n- this.handleResponse(clickPosition, request, url);\n- },\n- scope: this\n- };\n+ return extent;\n },\n \n /**\n- * Method: getStyleNames\n- * Gets the STYLES parameter for the layer. Make sure the STYLES parameter\n- * matches the LAYERS parameter\n- *\n+ * Method: getLonLatFromViewPortPx\n+ * Get a map location from a pixel location\n+ * \n * Parameters:\n- * layer - {<OpenLayers.Layer.WMS>}\n+ * viewPortPx - {<OpenLayers.Pixel>}\n *\n * Returns:\n- * {Array(String)} The STYLES parameter\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n+ * port OpenLayers.Pixel, translated into lon/lat by map lib\n+ * If the map lib is not loaded or not centered, returns null\n */\n- getStyleNames: function(layer) {\n- // in the event of a WMS layer bundling multiple layers but not\n- // specifying styles,we need the same number of commas to specify\n- // the default style for each of the layers. We can't just leave it\n- // blank as we may be including other layers that do specify styles.\n- var styleNames;\n- if (layer.params.STYLES) {\n- styleNames = layer.params.STYLES;\n- } else {\n- if (OpenLayers.Util.isArray(layer.params.LAYERS)) {\n- styleNames = new Array(layer.params.LAYERS.length);\n- } else { // Assume it's a String\n- styleNames = layer.params.LAYERS.replace(/[^,]/g, \"\");\n- }\n- }\n- return styleNames;\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);\n },\n \n /**\n- * Method: request\n- * Sends a GetFeatureInfo request to the WMS\n+ * Method: getViewPortPxFromLonLat\n+ * Get a pixel location from a map location\n *\n * Parameters:\n- * clickPosition - {<OpenLayers.Pixel>} The position on the map where the\n- * mouse event occurred.\n- * options - {Object} additional options for this method.\n+ * lonlat - {<OpenLayers.LonLat>}\n *\n- * Valid options:\n- * - *hover* {Boolean} true if we do the request for the hover handler\n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n+ * OpenLayers.LonLat, translated into view port pixels by map lib\n+ * If map lib is not loaded or not centered, returns null\n */\n- request: function(clickPosition, options) {\n- var layers = this.findLayers();\n- if (layers.length == 0) {\n- this.events.triggerEvent(\"nogetfeatureinfo\");\n- // Reset the cursor.\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- return;\n- }\n-\n- options = options || {};\n- if (this.drillDown === false) {\n- var wmsOptions = this.buildWMSOptions(this.url, layers,\n- clickPosition, layers[0].params.FORMAT);\n- var request = OpenLayers.Request.GET(wmsOptions);\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);\n+ },\n \n- if (options.hover === true) {\n- this.hoverRequest = request;\n- }\n- } else {\n- this._requestCount = 0;\n- this._numRequests = 0;\n- this.features = [];\n- // group according to service url to combine requests\n- var services = {},\n- url;\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var service, found = false;\n- url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n- if (url in services) {\n- services[url].push(layer);\n- } else {\n- this._numRequests++;\n- services[url] = [layer];\n- }\n- }\n- var layers;\n- for (var url in services) {\n- layers = services[url];\n- var wmsOptions = this.buildWMSOptions(url, layers,\n- clickPosition, layers[0].params.FORMAT);\n- OpenLayers.Request.GET(wmsOptions);\n- }\n+ /** \n+ * Method: initMercatorParameters \n+ * Set up the mercator parameters on the layer: resolutions,\n+ * projection, units.\n+ */\n+ initMercatorParameters: function() {\n+ // set up properties for Mercator - assume EPSG:900913\n+ this.RESOLUTIONS = [];\n+ var maxResolution = 156543.03390625;\n+ for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);\n }\n+ this.units = \"m\";\n+ this.projection = this.projection || \"EPSG:900913\";\n },\n \n /**\n- * Method: triggerGetFeatureInfo\n- * Trigger the getfeatureinfo event when all is done\n+ * APIMethod: forwardMercator\n+ * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.\n *\n * Parameters:\n- * request - {XMLHttpRequest} The request object\n- * xy - {<OpenLayers.Pixel>} The position on the map where the\n- * mouse event occurred.\n- * features - {Array(<OpenLayers.Feature.Vector>)} or\n- * {Array({Object}) when output is \"object\". The object has a url and a\n- * features property which contains an array of features.\n+ * lon - {float} \n+ * lat - {float}\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.\n */\n- triggerGetFeatureInfo: function(request, xy, features) {\n- this.events.triggerEvent(\"getfeatureinfo\", {\n- text: request.responseText,\n- features: features,\n- request: request,\n- xy: xy\n- });\n-\n- // Reset the cursor.\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- },\n+ forwardMercator: (function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(lon, lat) {\n+ var point = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ }, gg, sm);\n+ return new OpenLayers.LonLat(point.x, point.y);\n+ };\n+ })(),\n \n /**\n- * Method: handleResponse\n- * Handler for the GetFeatureInfo response.\n+ * APIMethod: inverseMercator\n+ * Given a x,y in Spherical Mercator, return a point in EPSG:4326.\n *\n * Parameters:\n- * xy - {<OpenLayers.Pixel>} The position on the map where the\n- * mouse event occurred.\n- * request - {XMLHttpRequest} The request object.\n- * url - {String} The url which was used for this request.\n+ * x - {float} A map x in Spherical Mercator.\n+ * y - {float} A map y in Spherical Mercator.\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.\n */\n- handleResponse: function(xy, request, url) {\n-\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- var features = this.format.read(doc);\n- if (this.drillDown === false) {\n- this.triggerGetFeatureInfo(request, xy, features);\n- } else {\n- this._requestCount++;\n- if (this.output === \"object\") {\n- this._features = (this._features || []).concat({\n- url: url,\n- features: features\n- });\n- } else {\n- this._features = (this._features || []).concat(features);\n- }\n- if (this._requestCount === this._numRequests) {\n- this.triggerGetFeatureInfo(request, xy, this._features.concat());\n- delete this._features;\n- delete this._requestCount;\n- delete this._numRequests;\n- }\n- }\n- },\n+ inverseMercator: (function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(x, y) {\n+ var point = OpenLayers.Projection.transform({\n+ x: x,\n+ y: y\n+ }, sm, gg);\n+ return new OpenLayers.LonLat(point.x, point.y);\n+ };\n+ })()\n \n- CLASS_NAME: \"OpenLayers.Control.WMSGetFeatureInfo\"\n-});\n+};\n /* ======================================================================\n- OpenLayers/Control/EditingToolbar.js\n+ OpenLayers/Layer/MapServer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control/Panel.js\n- * @requires OpenLayers/Control/Navigation.js\n- * @requires OpenLayers/Control/DrawFeature.js\n- * @requires OpenLayers/Handler/Point.js\n- * @requires OpenLayers/Handler/Path.js\n- * @requires OpenLayers/Handler/Polygon.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Control.EditingToolbar \n- * The EditingToolbar is a panel of 4 controls to draw polygons, lines, \n- * points, or to navigate the map by panning. By default it appears in the \n- * upper right corner of the map.\n- * \n+ * Class: OpenLayers.Layer.MapServer\n+ * Instances of OpenLayers.Layer.MapServer are used to display\n+ * data from a MapServer CGI instance.\n+ *\n * Inherits from:\n- * - <OpenLayers.Control.Panel>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.EditingToolbar = OpenLayers.Class(\n- OpenLayers.Control.Panel, {\n+OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n- /**\n- * APIProperty: citeCompliant\n- * {Boolean} If set to true, coordinates of features drawn in a map extent\n- * crossing the date line won't exceed the world bounds. Default is false.\n- */\n- citeCompliant: false,\n+ /**\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs \n+ */\n+ DEFAULT_PARAMS: {\n+ mode: \"map\",\n+ map_imagetype: \"png\"\n+ },\n \n- /**\n- * Constructor: OpenLayers.Control.EditingToolbar\n- * Create an editing toolbar for a given layer. \n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} \n- * options - {Object} \n- */\n- initialize: function(layer, options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ /**\n+ * Constructor: OpenLayers.Layer.MapServer\n+ * Create a new MapServer layer object\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the MapServer CGI\n+ * (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv)\n+ * params - {Object} An object with key/value pairs representing the\n+ * GetMap query string parameters and parameter values.\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n+ */\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n \n- this.addControls(\n- [new OpenLayers.Control.Navigation()]\n- );\n- var controls = [\n- new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {\n- displayClass: 'olControlDrawFeaturePoint',\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n- }\n- }),\n- new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {\n- displayClass: 'olControlDrawFeaturePath',\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n- }\n- }),\n- new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {\n- displayClass: 'olControlDrawFeaturePolygon',\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n- }\n- })\n- ];\n- this.addControls(controls);\n- },\n+ this.params = OpenLayers.Util.applyDefaults(\n+ this.params, this.DEFAULT_PARAMS\n+ );\n \n- /**\n- * Method: draw\n- * calls the default draw, and then activates mouse defaults.\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- draw: function() {\n- var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n- if (this.defaultControl === null) {\n- this.defaultControl = this.controls[0];\n- }\n- return div;\n- },\n+ // unless explicitly set in options, if the layer is transparent, \n+ // it will be an overlay\n+ if (options == null || options.isBaseLayer == null) {\n+ this.isBaseLayer = ((this.params.transparent != \"true\") &&\n+ (this.params.transparent != true));\n+ }\n+ },\n \n- CLASS_NAME: \"OpenLayers.Control.EditingToolbar\"\n- });\n-/* ======================================================================\n- OpenLayers/Control/Attribution.js\n- ====================================================================== */\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.MapServer>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.MapServer(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // copy/set any non-init, non-simple values here\n \n-/**\n- * @requires OpenLayers/Control.js\n- */\n+ return obj;\n+ },\n \n-/**\n- * Class: OpenLayers.Control.Attribution\n- * The attribution control adds attribution from layers to the map display. \n- * It uses 'attribution' property of each layer.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Attribution =\n- OpenLayers.Class(OpenLayers.Control, {\n+ /**\n+ * Method: getURL\n+ * Return a query string for this layer\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox \n+ * for the request\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also \n+ * the passed-in bounds and appropriate tile size specified \n+ * as parameters.\n+ */\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ // Make a list, so that getFullRequestString uses literal \",\" \n+ var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top];\n \n- /**\n- * APIProperty: separator\n- * {String} String used to separate layers.\n- */\n- separator: \", \",\n+ var imageSize = this.getImageSize();\n \n- /**\n- * APIProperty: template\n- * {String} Template for the attribution. This has to include the substring\n- * \"${layers}\", which will be replaced by the layer specific\n- * attributions, separated by <separator>. The default is \"${layers}\".\n- */\n- template: \"${layers}\",\n+ // make lists, so that literal ','s are used \n+ var url = this.getFullRequestString({\n+ mapext: extent,\n+ imgext: extent,\n+ map_size: [imageSize.w, imageSize.h],\n+ imgx: imageSize.w / 2,\n+ imgy: imageSize.h / 2,\n+ imgxy: [imageSize.w, imageSize.h]\n+ });\n \n- /**\n- * Constructor: OpenLayers.Control.Attribution \n- * \n- * Parameters:\n- * options - {Object} Options for control.\n- */\n+ return url;\n+ },\n \n- /** \n- * Method: destroy\n- * Destroy control.\n- */\n- destroy: function() {\n- this.map.events.un({\n- \"removelayer\": this.updateAttribution,\n- \"addlayer\": this.updateAttribution,\n- \"changelayer\": this.updateAttribution,\n- \"changebaselayer\": this.updateAttribution,\n- scope: this\n- });\n+ /** \n+ * Method: getFullRequestString\n+ * combine the layer's url with its params and these newParams. \n+ * \n+ * Parameters:\n+ * newParams - {Object} New parameters that should be added to the \n+ * request string.\n+ * altUrl - {String} (optional) Replace the URL in the full request \n+ * string with the provided URL.\n+ * \n+ * Returns: \n+ * {String} A string with the layer's url and parameters embedded in it.\n+ */\n+ getFullRequestString: function(newParams, altUrl) {\n+ // use layer's url unless altUrl passed in\n+ var url = (altUrl == null) ? this.url : altUrl;\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- },\n+ // create a new params hashtable with all the layer params and the \n+ // new params together. then convert to string\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- /**\n- * Method: draw\n- * Initialize control.\n- * \n- * Returns: \n- * {DOMElement} A reference to the DIV DOMElement containing the control\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will deterministically select one of them in \n+ // order to evenly distribute requests to different urls.\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url);\n+ }\n \n- this.map.events.on({\n- 'changebaselayer': this.updateAttribution,\n- 'changelayer': this.updateAttribution,\n- 'addlayer': this.updateAttribution,\n- 'removelayer': this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n+ // ignore parameters that are already in the url search string\n+ var urlParams = OpenLayers.Util.upperCaseObject(\n+ OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key];\n+ }\n+ }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- return this.div;\n- },\n+ // requestString always starts with url\n+ var requestString = url;\n \n- /**\n- * Method: updateAttribution\n- * Update attribution string.\n- */\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- // add attribution only if attribution text is unique\n- if (OpenLayers.Util.indexOf(\n- attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution);\n- }\n- }\n+ // MapServer needs '+' seperating things like bounds/height/width.\n+ // Since typically this is URL encoded, we use a slight hack: we\n+ // depend on the list-like functionality of getParameterString to\n+ // leave ',' only in the case of list items (since otherwise it is\n+ // encoded) then do a regular expression replace on the , characters\n+ // to '+'\n+ //\n+ paramsString = paramsString.replace(/,/g, \"+\");\n+\n+ if (paramsString != \"\") {\n+ var lastServerChar = url.charAt(url.length - 1);\n+ if ((lastServerChar == \"&\") || (lastServerChar == \"?\")) {\n+ requestString += paramsString;\n+ } else {\n+ if (url.indexOf('?') == -1) {\n+ //serverPath has no ? -- add one\n+ requestString += '?' + paramsString;\n+ } else {\n+ //serverPath contains ?, so must already have paramsString at the end\n+ requestString += '&' + paramsString;\n }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- });\n }\n- },\n+ }\n+ return requestString;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n- });\n+ CLASS_NAME: \"OpenLayers.Layer.MapServer\"\n+});\n /* ======================================================================\n- OpenLayers/Control/PanZoom.js\n+ OpenLayers/Layer/Markers.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Layer.js\n */\n \n /**\n- * Class: OpenLayers.Control.PanZoom\n- * The PanZoom is a visible control, composed of a\n- * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By\n- * default it is drawn in the upper left corner of the map.\n- *\n+ * Class: OpenLayers.Layer.Markers\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer> \n */\n-OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {\n \n /** \n- * APIProperty: slideFactor\n- * {Integer} Number of pixels by which we'll pan the map in any direction \n- * on clicking the arrow buttons. If you want to pan by some ratio\n- * of the map dimensions, use <slideRatio> instead.\n+ * APIProperty: isBaseLayer \n+ * {Boolean} Markers layer is never a base layer. \n */\n- slideFactor: 50,\n+ isBaseLayer: false,\n \n /** \n- * APIProperty: slideRatio\n- * {Number} The fraction of map width/height by which we'll pan the map \n- * on clicking the arrow buttons. Default is null. If set, will\n- * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up\n- * button will pan up half the map height. \n+ * APIProperty: markers \n+ * {Array(<OpenLayers.Marker>)} internal marker list \n */\n- slideRatio: null,\n+ markers: null,\n \n- /** \n- * Property: buttons\n- * {Array(DOMElement)} Array of Button Divs \n- */\n- buttons: null,\n \n /** \n- * Property: position\n- * {<OpenLayers.Pixel>} \n+ * Property: drawn \n+ * {Boolean} internal state of drawing. This is a workaround for the fact\n+ * that the map does not call moveTo with a zoomChanged when the map is\n+ * first starting up. This lets us catch the case where we have *never*\n+ * drawn the layer, and draw it even if the zoom hasn't changed.\n */\n- position: null,\n+ drawn: false,\n \n /**\n- * Constructor: OpenLayers.Control.PanZoom\n- * \n+ * Constructor: OpenLayers.Layer.Markers \n+ * Create a Markers layer.\n+ *\n * Parameters:\n- * options - {Object}\n+ * name - {String} \n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- initialize: function(options) {\n- this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,\n- OpenLayers.Control.PanZoom.Y);\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ this.markers = [];\n },\n \n /**\n- * APIMethod: destroy\n+ * APIMethod: destroy \n */\n destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- }\n- this.removeButtons();\n- this.buttons = null;\n- this.position = null;\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ this.clearMarkers();\n+ this.markers = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n \n- /** \n- * Method: setMap\n- *\n- * Properties:\n- * map - {<OpenLayers.Map>} \n+ /**\n+ * APIMethod: setOpacity\n+ * Sets the opacity for all the markers.\n+ * \n+ * Parameters:\n+ * opacity - {Float}\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ setOpacity: function(opacity) {\n+ if (opacity != this.opacity) {\n+ this.opacity = opacity;\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ this.markers[i].setOpacity(this.opacity);\n+ }\n+ }\n },\n \n- /**\n- * Method: draw\n+ /** \n+ * Method: moveTo\n *\n * Parameters:\n- * px - {<OpenLayers.Pixel>} \n- * \n- * Returns:\n- * {DOMElement} A reference to the container div for the PanZoom control.\n+ * bounds - {<OpenLayers.Bounds>} \n+ * zoomChanged - {Boolean} \n+ * dragging - {Boolean} \n */\n- draw: function(px) {\n- // initialize our internal div\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- px = this.position;\n-\n- // place the controls\n- this.buttons = [];\n-\n- var sz = {\n- w: 18,\n- h: 18\n- };\n- var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n \n- this._addButton(\"panup\", \"north-mini.png\", centered, sz);\n- px.y = centered.y + sz.h;\n- this._addButton(\"panleft\", \"west-mini.png\", px, sz);\n- this._addButton(\"panright\", \"east-mini.png\", px.add(sz.w, 0), sz);\n- this._addButton(\"pandown\", \"south-mini.png\",\n- centered.add(0, sz.h * 2), sz);\n- this._addButton(\"zoomin\", \"zoom-plus-mini.png\",\n- centered.add(0, sz.h * 3 + 5), sz);\n- this._addButton(\"zoomworld\", \"zoom-world-mini.png\",\n- centered.add(0, sz.h * 4 + 5), sz);\n- this._addButton(\"zoomout\", \"zoom-minus-mini.png\",\n- centered.add(0, sz.h * 5 + 5), sz);\n- return this.div;\n+ if (zoomChanged || !this.drawn) {\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ this.drawMarker(this.markers[i]);\n+ }\n+ this.drawn = true;\n+ }\n },\n \n /**\n- * Method: _addButton\n- * \n+ * APIMethod: addMarker\n+ *\n * Parameters:\n- * id - {String} \n- * img - {String} \n- * xy - {<OpenLayers.Pixel>} \n- * sz - {<OpenLayers.Size>} \n- * \n- * Returns:\n- * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the\n- * image of the button, and has all the proper event handlers set.\n+ * marker - {<OpenLayers.Marker>} \n */\n- _addButton: function(id, img, xy, sz) {\n- var imgLocation = OpenLayers.Util.getImageLocation(img);\n- var btn = OpenLayers.Util.createAlphaImageDiv(\n- this.id + \"_\" + id,\n- xy, sz, imgLocation, \"absolute\");\n- btn.style.cursor = \"pointer\";\n- //we want to add the outer div\n- this.div.appendChild(btn);\n- btn.action = id;\n- btn.className = \"olButton\";\n+ addMarker: function(marker) {\n+ this.markers.push(marker);\n \n- //we want to remember/reference the outer div\n- this.buttons.push(btn);\n- return btn;\n+ if (this.opacity < 1) {\n+ marker.setOpacity(this.opacity);\n+ }\n+\n+ if (this.map && this.map.getExtent()) {\n+ marker.map = this.map;\n+ this.drawMarker(marker);\n+ }\n },\n \n /**\n- * Method: _removeButton\n- * \n+ * APIMethod: removeMarker\n+ *\n * Parameters:\n- * btn - {Object}\n+ * marker - {<OpenLayers.Marker>} \n */\n- _removeButton: function(btn) {\n- this.div.removeChild(btn);\n- OpenLayers.Util.removeItem(this.buttons, btn);\n+ removeMarker: function(marker) {\n+ if (this.markers && this.markers.length) {\n+ OpenLayers.Util.removeItem(this.markers, marker);\n+ marker.erase();\n+ }\n },\n \n /**\n- * Method: removeButtons\n+ * Method: clearMarkers\n+ * This method removes all markers from a layer. The markers are not\n+ * destroyed by this function, but are removed from the list of markers.\n */\n- removeButtons: function() {\n- for (var i = this.buttons.length - 1; i >= 0; --i) {\n- this._removeButton(this.buttons[i]);\n+ clearMarkers: function() {\n+ if (this.markers != null) {\n+ while (this.markers.length > 0) {\n+ this.removeMarker(this.markers[0]);\n+ }\n }\n },\n \n- /**\n- * Method: onButtonClick\n+ /** \n+ * Method: drawMarker\n+ * Calculate the pixel location for the marker, create it, and \n+ * add it to the layer's div\n *\n * Parameters:\n- * evt - {Event}\n+ * marker - {<OpenLayers.Marker>} \n */\n- onButtonClick: function(evt) {\n- var btn = evt.buttonElement;\n- switch (btn.action) {\n- case \"panup\":\n- this.map.pan(0, -this.getSlideFactor(\"h\"));\n- break;\n- case \"pandown\":\n- this.map.pan(0, this.getSlideFactor(\"h\"));\n- break;\n- case \"panleft\":\n- this.map.pan(-this.getSlideFactor(\"w\"), 0);\n- break;\n- case \"panright\":\n- this.map.pan(this.getSlideFactor(\"w\"), 0);\n- break;\n- case \"zoomin\":\n- this.map.zoomIn();\n- break;\n- case \"zoomout\":\n- this.map.zoomOut();\n- break;\n- case \"zoomworld\":\n- this.map.zoomToMaxExtent();\n- break;\n+ drawMarker: function(marker) {\n+ var px = this.map.getLayerPxFromLonLat(marker.lonlat);\n+ if (px == null) {\n+ marker.display(false);\n+ } else {\n+ if (!marker.isDrawn()) {\n+ var markerImg = marker.draw(px);\n+ this.div.appendChild(markerImg);\n+ } else if (marker.icon) {\n+ marker.icon.moveTo(px);\n+ }\n }\n },\n \n- /**\n- * Method: getSlideFactor\n- *\n- * Parameters:\n- * dim - {String} \"w\" or \"h\" (for width or height).\n- *\n+ /** \n+ * APIMethod: getDataExtent\n+ * Calculates the max extent which includes all of the markers.\n+ * \n * Returns:\n- * {Number} The slide factor for panning in the requested direction.\n+ * {<OpenLayers.Bounds>}\n */\n- getSlideFactor: function(dim) {\n- return this.slideRatio ?\n- this.map.getSize()[dim] * this.slideRatio :\n- this.slideFactor;\n- },\n+ getDataExtent: function() {\n+ var maxExtent = null;\n \n- CLASS_NAME: \"OpenLayers.Control.PanZoom\"\n-});\n+ if (this.markers && (this.markers.length > 0)) {\n+ var maxExtent = new OpenLayers.Bounds();\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ var marker = this.markers[i];\n+ maxExtent.extend(marker.lonlat);\n+ }\n+ }\n \n-/**\n- * Constant: X\n- * {Integer}\n- */\n-OpenLayers.Control.PanZoom.X = 4;\n+ return maxExtent;\n+ },\n \n-/**\n- * Constant: Y\n- * {Integer}\n- */\n-OpenLayers.Control.PanZoom.Y = 4;\n+ CLASS_NAME: \"OpenLayers.Layer.Markers\"\n+});\n /* ======================================================================\n- OpenLayers/Control/Permalink.js\n+ OpenLayers/Layer/Text.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Control/ArgParser.js\n- * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Layer/Markers.js\n+ * @requires OpenLayers/Format/Text.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n */\n \n /**\n- * Class: OpenLayers.Control.Permalink\n- * The Permalink control is hyperlink that will return the user to the \n- * current map view. By default it is drawn in the lower right corner of the\n- * map. The href is updated as the map is zoomed, panned and whilst layers\n- * are switched.\n- * \n+ * Class: OpenLayers.Layer.Text\n+ * This layer creates markers given data in a text file. The <location>\n+ * property of the layer (specified as a property of the options argument\n+ * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited\n+ * file with data used to create markers.\n+ *\n+ * The first row of the data file should be a header line with the column names\n+ * of the data. Each column should be delimited by a tab space. The\n+ * possible columns are:\n+ * - *point* lat,lon of the point where a marker is to be placed\n+ * - *lat* Latitude of the point where a marker is to be placed\n+ * - *lon* Longitude of the point where a marker is to be placed\n+ * - *icon* or *image* URL of marker icon to use.\n+ * - *iconSize* Size of Icon to use.\n+ * - *iconOffset* Where the top-left corner of the icon is to be placed\n+ * relative to the latitude and longitude of the point.\n+ * - *title* The text of the 'title' is placed inside an 'h2' marker\n+ * inside a popup, which opens when the marker is clicked.\n+ * - *description* The text of the 'description' is placed below the h2\n+ * in the popup. this can be plain text or HTML.\n+ *\n+ * Example text file:\n+ * (code)\n+ * lat\tlon\ttitle\tdescription\ticonSize\ticonOffset\ticon\n+ * 10\t20\ttitle\tdescription\t21,25\t\t-10,-25\t\thttp://www.openlayers.org/dev/img/marker.png\n+ * (end)\n+ *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Markers>\n */\n-OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {\n \n /**\n- * APIProperty: argParserClass\n- * {Class} The ArgParser control class (not instance) to use with this\n- * control.\n- */\n- argParserClass: OpenLayers.Control.ArgParser,\n-\n- /** \n- * Property: element \n- * {DOMElement}\n+ * APIProperty: location \n+ * {String} URL of text file. Must be specified in the \"options\" argument\n+ * of the constructor. Can not be changed once passed in. \n */\n- element: null,\n+ location: null,\n \n /** \n- * APIProperty: anchor\n- * {Boolean} This option changes 3 things:\n- * the character '#' is used in place of the character '?',\n- * the window.href is updated if no element is provided.\n- * When this option is set to true it's not recommend to provide\n- * a base without provide an element.\n+ * Property: features\n+ * {Array(<OpenLayers.Feature>)} \n */\n- anchor: false,\n+ features: null,\n \n- /** \n- * APIProperty: base\n- * {String}\n+ /**\n+ * APIProperty: formatOptions\n+ * {Object} Hash of options which should be passed to the format when it is\n+ * created. Must be passed in the constructor.\n */\n- base: '',\n+ formatOptions: null,\n \n /** \n- * APIProperty: displayProjection\n- * {<OpenLayers.Projection>} Requires proj4js support. Projection used\n- * when creating the coordinates in the link. This will reproject the\n- * map coordinates into display coordinates. If you are using this\n- * functionality, the permalink which is last added to the map will\n- * determine the coordinate type which is read from the URL, which\n- * means you should not add permalinks with different\n- * displayProjections to the same map. \n+ * Property: selectedFeature\n+ * {<OpenLayers.Feature>}\n */\n- displayProjection: null,\n+ selectedFeature: null,\n \n /**\n- * Constructor: OpenLayers.Control.Permalink\n- *\n- * Parameters: \n- * element - {DOMElement} \n- * base - {String} \n- * options - {Object} options to the control.\n- *\n- * Or for anchor:\n- * options - {Object} options to the control.\n+ * Constructor: OpenLayers.Layer.Text\n+ * Create a text layer.\n+ * \n+ * Parameters:\n+ * name - {String} \n+ * options - {Object} Object with properties to be set on the layer.\n+ * Must include <location> property.\n */\n- initialize: function(element, base, options) {\n- if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) {\n- options = element;\n- this.base = document.location.href;\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (this.element != null) {\n- this.element = OpenLayers.Util.getElement(this.element);\n- }\n- } else {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.element = OpenLayers.Util.getElement(element);\n- this.base = base || document.location.href;\n- }\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);\n+ this.features = [];\n },\n \n /**\n- * APIMethod: destroy\n+ * APIMethod: destroy \n */\n destroy: function() {\n- if (this.element && this.element.parentNode == this.div) {\n- this.div.removeChild(this.element);\n- this.element = null;\n- }\n- if (this.map) {\n- this.map.events.unregister('moveend', this, this.updateLink);\n- }\n-\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ // Warning: Layer.Markers.destroy() must be called prior to calling\n+ // clearFeatures() here, otherwise we leak memory. Indeed, if\n+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n+ // able to remove the marker image elements from the layer's div since\n+ // the markers will have been destroyed by clearFeatures().\n+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n+ this.clearFeatures();\n+ this.features = null;\n },\n \n /**\n- * Method: setMap\n- * Set the map property for the control. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * Method: loadText\n+ * Start the load of the Text data. Don't do this when we first add the layer,\n+ * since we may not be visible at any point, and it would therefore be a waste.\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n-\n- //make sure we have an arg parser attached\n- for (var i = 0, len = this.map.controls.length; i < len; i++) {\n- var control = this.map.controls[i];\n- if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {\n+ loadText: function() {\n+ if (!this.loaded) {\n+ if (this.location != null) {\n \n- // If a permalink is added to the map, and an ArgParser already\n- // exists, we override the displayProjection to be the one\n- // on the permalink. \n- if (control.displayProjection != this.displayProjection) {\n- this.displayProjection = control.displayProjection;\n- }\n+ var onFail = function(e) {\n+ this.events.triggerEvent(\"loadend\");\n+ };\n \n- break;\n+ this.events.triggerEvent(\"loadstart\");\n+ OpenLayers.Request.GET({\n+ url: this.location,\n+ success: this.parseData,\n+ failure: onFail,\n+ scope: this\n+ });\n+ this.loaded = true;\n }\n }\n- if (i == this.map.controls.length) {\n- this.map.addControl(new this.argParserClass({\n- 'displayProjection': this.displayProjection\n- }));\n- }\n-\n- },\n-\n- /**\n- * Method: draw\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n-\n- if (!this.element && !this.anchor) {\n- this.element = document.createElement(\"a\");\n- this.element.innerHTML = OpenLayers.i18n(\"Permalink\");\n- this.element.href = \"\";\n- this.div.appendChild(this.element);\n- }\n- this.map.events.on({\n- 'moveend': this.updateLink,\n- 'changelayer': this.updateLink,\n- 'changebaselayer': this.updateLink,\n- scope: this\n- });\n-\n- // Make it so there is at least a link even though the map may not have\n- // moved yet.\n- this.updateLink();\n-\n- return this.div;\n },\n \n /**\n- * Method: updateLink \n+ * Method: moveTo\n+ * If layer is visible and Text has not been loaded, load Text. \n+ * \n+ * Parameters:\n+ * bounds - {Object} \n+ * zoomChanged - {Object} \n+ * minor - {Object} \n */\n- updateLink: function() {\n- var separator = this.anchor ? '#' : '?';\n- var href = this.base;\n- var anchor = null;\n- if (href.indexOf(\"#\") != -1 && this.anchor == false) {\n- anchor = href.substring(href.indexOf(\"#\"), href.length);\n- }\n- if (href.indexOf(separator) != -1) {\n- href = href.substring(0, href.indexOf(separator));\n- }\n- var splits = href.split(\"#\");\n- href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams());\n- if (anchor) {\n- href += anchor;\n- }\n- if (this.anchor && !this.element) {\n- window.location.href = href;\n- } else {\n- this.element.href = href;\n+ moveTo: function(bounds, zoomChanged, minor) {\n+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n+ if (this.visibility && !this.loaded) {\n+ this.loadText();\n }\n },\n \n /**\n- * APIMethod: createParams\n- * Creates the parameters that need to be encoded into the permalink url.\n- * \n+ * Method: parseData\n+ *\n * Parameters:\n- * center - {<OpenLayers.LonLat>} center to encode in the permalink.\n- * Defaults to the current map center.\n- * zoom - {Integer} zoom level to encode in the permalink. Defaults to the\n- * current map zoom level.\n- * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink.\n- * Defaults to the current map layers.\n- * \n- * Returns:\n- * {Object} Hash of parameters that will be url-encoded into the\n- * permalink.\n+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n */\n- createParams: function(center, zoom, layers) {\n- center = center || this.map.getCenter();\n+ parseData: function(ajaxRequest) {\n+ var text = ajaxRequest.responseText;\n \n- var params = OpenLayers.Util.getParameters(this.base);\n+ var options = {};\n \n- // If there's still no center, map is not initialized yet. \n- // Break out of this function, and simply return the params from the\n- // base link.\n- if (center) {\n+ OpenLayers.Util.extend(options, this.formatOptions);\n \n- //zoom\n- params.zoom = zoom || this.map.getZoom();\n+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n+ options.externalProjection = this.projection;\n+ options.internalProjection = this.map.getProjectionObject();\n+ }\n \n- //lon,lat\n- var lat = center.lat;\n- var lon = center.lon;\n+ var parser = new OpenLayers.Format.Text(options);\n+ var features = parser.read(text);\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ var data = {};\n+ var feature = features[i];\n+ var location;\n+ var iconSize, iconOffset;\n \n- if (this.displayProjection) {\n- var mapPosition = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- },\n- this.map.getProjectionObject(),\n- this.displayProjection);\n- lon = mapPosition.x;\n- lat = mapPosition.y;\n+ location = new OpenLayers.LonLat(feature.geometry.x,\n+ feature.geometry.y);\n+\n+ if (feature.style.graphicWidth &&\n+ feature.style.graphicHeight) {\n+ iconSize = new OpenLayers.Size(\n+ feature.style.graphicWidth,\n+ feature.style.graphicHeight);\n }\n- params.lat = Math.round(lat * 100000) / 100000;\n- params.lon = Math.round(lon * 100000) / 100000;\n \n- //layers \n- layers = layers || this.map.layers;\n- params.layers = '';\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n+ // FIXME: At the moment, we only use this if we have an \n+ // externalGraphic, because icon has no setOffset API Method.\n+ /**\n+ * FIXME FIRST!!\n+ * The Text format does all sorts of parseFloating\n+ * The result of a parseFloat for a bogus string is NaN. That\n+ * means the three possible values here are undefined, NaN, or a\n+ * number. The previous check was an identity check for null. This\n+ * means it was failing for all undefined or NaN. A slightly better\n+ * check is for undefined. An even better check is to see if the\n+ * value is a number (see #1441).\n+ */\n+ if (feature.style.graphicXOffset !== undefined &&\n+ feature.style.graphicYOffset !== undefined) {\n+ iconOffset = new OpenLayers.Pixel(\n+ feature.style.graphicXOffset,\n+ feature.style.graphicYOffset);\n+ }\n \n- if (layer.isBaseLayer) {\n- params.layers += (layer == this.map.baseLayer) ? \"B\" : \"0\";\n- } else {\n- params.layers += (layer.getVisibility()) ? \"T\" : \"F\";\n+ if (feature.style.externalGraphic != null) {\n+ data.icon = new OpenLayers.Icon(feature.style.externalGraphic,\n+ iconSize,\n+ iconOffset);\n+ } else {\n+ data.icon = OpenLayers.Marker.defaultIcon();\n+\n+ //allows for the case where the image url is not \n+ // specified but the size is. use a default icon\n+ // but change the size\n+ if (iconSize != null) {\n+ data.icon.setSize(iconSize);\n }\n }\n+\n+ if ((feature.attributes.title != null) &&\n+ (feature.attributes.description != null)) {\n+ data['popupContentHTML'] =\n+ '<h2>' + feature.attributes.title + '</h2>' +\n+ '<p>' + feature.attributes.description + '</p>';\n+ }\n+\n+ data['overflow'] = feature.attributes.overflow || \"auto\";\n+\n+ var markerFeature = new OpenLayers.Feature(this, location, data);\n+ this.features.push(markerFeature);\n+ var marker = markerFeature.createMarker();\n+ if ((feature.attributes.title != null) &&\n+ (feature.attributes.description != null)) {\n+ marker.events.register('click', markerFeature, this.markerClick);\n+ }\n+ this.addMarker(marker);\n }\n+ this.events.triggerEvent(\"loadend\");\n+ },\n \n- return params;\n+ /**\n+ * Property: markerClick\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ *\n+ * Context:\n+ * - {<OpenLayers.Feature>}\n+ */\n+ markerClick: function(evt) {\n+ var sameMarkerClicked = (this == this.layer.selectedFeature);\n+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i]);\n+ }\n+ if (!sameMarkerClicked) {\n+ this.layer.map.addPopup(this.createPopup());\n+ }\n+ OpenLayers.Event.stop(evt);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Permalink\"\n+ /**\n+ * Method: clearFeatures\n+ */\n+ clearFeatures: function() {\n+ if (this.features != null) {\n+ while (this.features.length > 0) {\n+ var feature = this.features[0];\n+ OpenLayers.Util.removeItem(this.features, feature);\n+ feature.destroy();\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Text\"\n });\n /* ======================================================================\n- OpenLayers/Control/Split.js\n+ OpenLayers/Layer/TMS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Path.js\n- * @requires OpenLayers/Layer/Vector.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Control.Split\n- * Acts as a split feature agent while editing vector features.\n+ * Class: OpenLayers.Layer.TMS\n+ * Create a layer for accessing tiles from services that conform with the \n+ * Tile Map Service Specification \n+ * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification).\n *\n+ * Example:\n+ * (code)\n+ * var layer = new OpenLayers.Layer.TMS(\n+ * \"My Layer\", // name for display in LayerSwitcher\n+ * \"http://tilecache.osgeo.org/wms-c/Basic.py/\", // service endpoint\n+ * {layername: \"basic\", type: \"png\"} // required properties\n+ * );\n+ * (end)\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforesplit - Triggered before a split occurs. Listeners receive an\n- * event object with *source* and *target* properties.\n- * split - Triggered when a split occurs. Listeners receive an event with\n- * an *original* property and a *features* property. The original\n- * is a reference to the target feature that the sketch or modified\n- * feature intersects. The features property is a list of all features\n- * that result from this single split. This event is triggered before\n- * the resulting features are added to the layer (while the layer still\n- * has a reference to the original).\n- * aftersplit - Triggered after all splits resulting from a single sketch\n- * or feature modification have occurred. The original features\n- * have been destroyed and features that result from the split\n- * have already been added to the layer. Listeners receive an event\n- * with a *source* and *features* property. The source references the\n- * sketch or modified feature used as a splitter. The features\n- * property is a list of all resulting features.\n- */\n-\n- /**\n- * APIProperty: layer\n- * {<OpenLayers.Layer.Vector>} The target layer with features to be split.\n- * Set at construction or after construction with <setLayer>.\n- */\n- layer: null,\n-\n- /**\n- * Property: source\n- * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created\n- * or modified features from this layer will be used to split features\n- * on the target layer. If not provided, a temporary sketch layer will\n- * be created.\n- */\n- source: null,\n-\n- /**\n- * Property: sourceOptions\n- * {Options} If a temporary sketch layer is created, these layer options\n- * will be applied.\n- */\n- sourceOptions: null,\n+OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * APIProperty: tolerance\n- * {Number} Distance between the calculated intersection and a vertex on\n- * the source geometry below which the existing vertex will be used\n- * for the split. Default is null.\n+ * APIProperty: serviceVersion\n+ * {String} Service version for tile requests. Default is \"1.0.0\".\n */\n- tolerance: null,\n+ serviceVersion: \"1.0.0\",\n \n /**\n- * APIProperty: edge\n- * {Boolean} Allow splits given intersection of edges only. Default is\n- * true. If false, a vertex on the source must be within the\n- * <tolerance> distance of the calculated intersection for a split\n- * to occur.\n+ * APIProperty: layername\n+ * {String} The identifier for the <TileMap> as advertised by the service. \n+ * For example, if the service advertises a <TileMap> with \n+ * 'href=\"http://tms.osgeo.org/1.0.0/vmap0\"', the <layername> property \n+ * would be set to \"vmap0\".\n */\n- edge: true,\n+ layername: null,\n \n /**\n- * APIProperty: deferDelete\n- * {Boolean} Instead of removing features from the layer, set feature\n- * states of split features to DELETE. This assumes a save strategy\n- * or other component is in charge of removing features from the\n- * layer. Default is false. If false, split features will be\n- * immediately deleted from the layer.\n+ * APIProperty: type\n+ * {String} The format extension corresponding to the requested tile image\n+ * type. This is advertised in a <TileFormat> element as the \n+ * \"extension\" attribute. For example, if the service advertises a \n+ * <TileMap> with <TileFormat width=\"256\" height=\"256\" mime-type=\"image/jpeg\" extension=\"jpg\" />,\n+ * the <type> property would be set to \"jpg\".\n */\n- deferDelete: false,\n+ type: null,\n \n /**\n- * APIProperty: mutual\n- * {Boolean} If source and target layers are the same, split source\n- * features and target features where they intersect. Default is\n- * true. If false, only target features will be split.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Make this layer a base layer. Default is true. Set false to\n+ * use the layer as an overlay.\n */\n- mutual: true,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: targetFilter\n- * {<OpenLayers.Filter>} Optional filter that will be evaluated\n- * to determine if a feature from the target layer is eligible for\n- * splitting.\n+ * APIProperty: tileOrigin\n+ * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n+ * If provided, requests for tiles at all resolutions will be aligned\n+ * with this location (no tiles shall overlap this location). If\n+ * not provided, the grid of tiles will be aligned with the bottom-left\n+ * corner of the map's <maxExtent>. Default is ``null``.\n+ *\n+ * Example:\n+ * (code)\n+ * var layer = new OpenLayers.Layer.TMS(\n+ * \"My Layer\",\n+ * \"http://tilecache.osgeo.org/wms-c/Basic.py/\",\n+ * {\n+ * layername: \"basic\", \n+ * type: \"png\",\n+ * // set if different than the bottom left of map.maxExtent\n+ * tileOrigin: new OpenLayers.LonLat(-180, -90)\n+ * }\n+ * );\n+ * (end)\n */\n- targetFilter: null,\n+ tileOrigin: null,\n \n /**\n- * APIProperty: sourceFilter\n- * {<OpenLayers.Filter>} Optional filter that will be evaluated\n- * to determine if a feature from the source layer is eligible for\n- * splitting.\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer; you can also look at <zoomOffset>, which is\n+ * an alternative to <serverResolutions> for that specific purpose.\n+ * (b) The map can work with resolutions that aren't supported by\n+ * the server, i.e. that aren't in <serverResolutions>. When the\n+ * map is displayed in such a resolution data for the closest\n+ * server-supported resolution is loaded and the layer div is\n+ * stretched as necessary.\n */\n- sourceFilter: null,\n+ serverResolutions: null,\n \n /**\n- * Property: handler\n- * {<OpenLayers.Handler.Path>} The temporary sketch handler created if\n- * no source layer is provided.\n+ * APIProperty: zoomOffset\n+ * {Number} If your cache has more zoom levels than you want to provide\n+ * access to with this layer, supply a zoomOffset. This zoom offset\n+ * is added to the current map zoom level to determine the level\n+ * for a requested tile. For example, if you supply a zoomOffset\n+ * of 3, when the map is at the zoom 0, tiles will be requested from\n+ * level 3 of your cache. Default is 0 (assumes cache level and map\n+ * zoom are equivalent). Using <zoomOffset> is an alternative to\n+ * setting <serverResolutions> if you only want to expose a subset\n+ * of the server resolutions.\n */\n- handler: null,\n+ zoomOffset: 0,\n \n /**\n- * Constructor: OpenLayers.Control.Split\n- * Creates a new split control. A control is constructed with a target\n- * layer and an optional source layer. While the control is active,\n- * creating new features or modifying existing features on the source\n- * layer will result in splitting any eligible features on the target\n- * layer. If no source layer is provided, a temporary sketch layer will\n- * be created to create lines for splitting features on the target.\n- *\n+ * Constructor: OpenLayers.Layer.TMS\n+ * \n * Parameters:\n- * options - {Object} An object containing all configuration properties for\n- * the control.\n- *\n- * Valid options:\n- * layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this\n- * layer will be split by new or modified features on the source layer\n- * or temporary sketch layer.\n- * source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided\n- * newly created features or modified features will be used to split\n- * features on the target layer. If not provided, a temporary sketch\n- * layer will be created for drawing lines.\n- * tolerance - {Number} Optional value for the distance between a source\n- * vertex and the calculated intersection below which the split will\n- * occur at the vertex.\n- * edge - {Boolean} Allow splits given intersection of edges only. Default\n- * is true. If false, a vertex on the source must be within the\n- * <tolerance> distance of the calculated intersection for a split\n- * to occur.\n- * mutual - {Boolean} If source and target are the same, split source\n- * features and target features where they intersect. Default is\n- * true. If false, only target features will be split.\n- * targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated\n- * to determine if a feature from the target layer is eligible for\n- * splitting.\n- * sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated\n- * to determine if a feature from the target layer is eligible for\n- * splitting.\n+ * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher>\n+ * url - {String} Service endpoint (without the version number). E.g.\n+ * \"http://tms.osgeo.org/\".\n+ * options - {Object} Additional properties to be set on the layer. The\n+ * <layername> and <type> properties must be set here.\n */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.options = options || {}; // TODO: this could be done by the super\n-\n- // set the source layer if provided\n- if (this.options.source) {\n- this.setSource(this.options.source);\n- }\n+ initialize: function(name, url, options) {\n+ var newArguments = [];\n+ newArguments.push(name, url, {}, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n },\n \n /**\n- * APIMethod: setSource\n- * Set the source layer for edits layer.\n+ * APIMethod: clone\n+ * Create a complete copy of this layer.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If\n- * null, a temporary sketch layer will be created.\n+ * obj - {Object} Should only be provided by subclasses that call this\n+ * method.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>\n */\n- setSource: function(layer) {\n- if (this.active) {\n- this.deactivate();\n- if (this.handler) {\n- this.handler.destroy();\n- delete this.handler;\n- }\n- this.source = layer;\n- this.activate();\n- } else {\n- this.source = layer;\n- }\n- },\n+ clone: function(obj) {\n \n- /**\n- * APIMethod: activate\n- * Activate the control. Activating the control registers listeners for\n- * editing related events so that during feature creation and\n- * modification, features in the target will be considered for\n- * splitting.\n- */\n- activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- if (!this.source) {\n- if (!this.handler) {\n- this.handler = new OpenLayers.Handler.Path(this, {\n- done: function(geometry) {\n- this.onSketchComplete({\n- feature: new OpenLayers.Feature.Vector(geometry)\n- });\n- }\n- }, {\n- layerOptions: this.sourceOptions\n- });\n- }\n- this.handler.activate();\n- } else if (this.source.events) {\n- this.source.events.on({\n- sketchcomplete: this.onSketchComplete,\n- afterfeaturemodified: this.afterFeatureModified,\n- scope: this\n- });\n- }\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.TMS(this.name,\n+ this.url,\n+ this.getOptions());\n }\n- return activated;\n- },\n \n- /**\n- * APIMethod: deactivate\n- * Deactivate the control. Deactivating the control unregisters listeners\n- * so feature editing may proceed without engaging the split agent.\n- */\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.source && this.source.events) {\n- this.source.events.un({\n- sketchcomplete: this.onSketchComplete,\n- afterfeaturemodified: this.afterFeatureModified,\n- scope: this\n- });\n- }\n- }\n- return deactivated;\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n },\n \n /**\n- * Method: onSketchComplete\n- * Registered as a listener for the sketchcomplete event on the editable\n- * layer.\n- *\n+ * Method: getURL\n+ * \n * Parameters:\n- * event - {Object} The sketch complete event.\n- *\n+ * bounds - {<OpenLayers.Bounds>}\n+ * \n * Returns:\n- * {Boolean} Stop the sketch from being added to the layer (it has been\n- * split).\n+ * {String} A string with the layer's url and parameters and also the \n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters\n */\n- onSketchComplete: function(event) {\n- this.feature = null;\n- return !this.considerSplit(event.feature);\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+ var path = this.serviceVersion + \"/\" + this.layername + \"/\" + z + \"/\" + x + \"/\" + y + \".\" + this.type;\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url);\n+ }\n+ return url + path;\n },\n \n- /**\n- * Method: afterFeatureModified\n- * Registered as a listener for the afterfeaturemodified event on the\n- * editable layer.\n- *\n+ /** \n+ * Method: setMap\n+ * When the layer is added to a map, then we can fetch our origin \n+ * (if we don't have one.) \n+ * \n * Parameters:\n- * event - {Object} The after feature modified event.\n+ * map - {<OpenLayers.Map>}\n */\n- afterFeatureModified: function(event) {\n- if (event.modified) {\n- var feature = event.feature;\n- if (typeof feature.geometry.split === \"function\") {\n- this.feature = event.feature;\n- this.considerSplit(event.feature);\n- }\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n+ this.map.maxExtent.bottom);\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.TMS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/KaMapCache.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Layer/KaMap.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.KaMapCache\n+ * \n+ * This class is designed to talk directly to a web-accessible ka-Map\n+ * cache generated by the precache2.php script.\n+ * \n+ * To create a a new KaMapCache layer, you must indicate also the \"i\" parameter\n+ * (that will be used to calculate the file extension), and another special\n+ * parameter, object names \"metaTileSize\", with \"h\" (height) and \"w\" (width)\n+ * properties.\n+ * \n+ * // Create a new kaMapCache layer. \n+ * var kamap_base = new OpenLayers.Layer.KaMapCache(\n+ * \"Satellite\",\n+ * \"http://www.example.org/web/acessible/cache\",\n+ * {g: \"satellite\", map: \"world\", i: 'png24', metaTileSize: {w: 5, h: 5} }\n+ * );\n+ * \n+ * // Create an kaMapCache overlay layer (using \"isBaseLayer: false\"). \n+ * // Forces the output to be a \"gif\", using the \"i\" parameter.\n+ * var kamap_overlay = new OpenLayers.Layer.KaMapCache(\n+ * \"Streets\",\n+ * \"http://www.example.org/web/acessible/cache\",\n+ * {g: \"streets\", map: \"world\", i: \"gif\", metaTileSize: {w: 5, h: 5} },\n+ * {isBaseLayer: false}\n+ * );\n+ *\n+ * The cache URLs must look like: \n+ * var/cache/World/50000/Group_Name/def/t-440320/l20480\n+ * \n+ * This means that the cache generated via tile.php will *not* work with\n+ * this class, and should instead use the KaMap layer.\n+ *\n+ * More information is available in Ticket #1518.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.KaMap>\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {\n+\n /**\n- * Method: removeByGeometry\n- * Remove a feature from a list based on the given geometry.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} A list of features.\n- * geometry - {<OpenLayers.Geometry>} A geometry.\n+ * Constant: IMAGE_EXTENSIONS\n+ * {Object} Simple hash map to convert format to extension.\n */\n- removeByGeometry: function(features, geometry) {\n- for (var i = 0, len = features.length; i < len; ++i) {\n- if (features[i].geometry === geometry) {\n- features.splice(i, 1);\n- break;\n- }\n- }\n+ IMAGE_EXTENSIONS: {\n+ 'jpeg': 'jpg',\n+ 'gif': 'gif',\n+ 'png': 'png',\n+ 'png8': 'png',\n+ 'png24': 'png',\n+ 'dithered': 'png'\n },\n \n /**\n- * Method: isEligible\n- * Test if a target feature is eligible for splitting.\n- *\n- * Parameters:\n- * target - {<OpenLayers.Feature.Vector>} The target feature.\n- *\n- * Returns:\n- * {Boolean} The target is eligible for splitting.\n+ * Constant: DEFAULT_FORMAT\n+ * {Object} Simple hash map to convert format to extension.\n */\n- isEligible: function(target) {\n- if (!target.geometry) {\n- return false;\n- } else {\n- return (\n- target.state !== OpenLayers.State.DELETE\n- ) && (\n- typeof target.geometry.split === \"function\"\n- ) && (\n- this.feature !== target\n- ) && (\n- !this.targetFilter ||\n- this.targetFilter.evaluate(target.attributes)\n- );\n- }\n- },\n+ DEFAULT_FORMAT: 'jpeg',\n \n /**\n- * Method: considerSplit\n- * Decide whether or not to split target features with the supplied\n- * feature. If <mutual> is true, both the source and target features\n- * will be split if eligible.\n- *\n+ * Constructor: OpenLayers.Layer.KaMapCache\n+ * \n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The newly created or modified\n- * feature.\n- *\n- * Returns:\n- * {Boolean} The supplied feature was split (and destroyed).\n+ * name - {String}\n+ * url - {String}\n+ * params - {Object} Parameters to be sent to the HTTP server in the\n+ * query string for the tile. The format can be set via the 'i'\n+ * parameter (defaults to jpg) , and the map should be set via \n+ * the 'map' parameter. It has been reported that ka-Map may behave\n+ * inconsistently if your format parameter does not match the format\n+ * parameter configured in your config.php. (See ticket #327 for more\n+ * information.)\n+ * options - {Object} Additional options for the layer. Any of the \n+ * APIProperties listed on this layer, and any layer types it\n+ * extends, can be overridden through the options parameter. \n */\n- considerSplit: function(feature) {\n- var sourceSplit = false;\n- var targetSplit = false;\n- if (!this.sourceFilter ||\n- this.sourceFilter.evaluate(feature.attributes)) {\n- var features = this.layer && this.layer.features || [];\n- var target, results, proceed;\n- var additions = [],\n- removals = [];\n- var mutual = (this.layer === this.source) && this.mutual;\n- var options = {\n- edge: this.edge,\n- tolerance: this.tolerance,\n- mutual: mutual\n- };\n- var sourceParts = [feature.geometry];\n- var targetFeature, targetParts;\n- var source, parts;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- targetFeature = features[i];\n- if (this.isEligible(targetFeature)) {\n- targetParts = [targetFeature.geometry];\n- // work through source geoms - this array may change\n- for (var j = 0; j < sourceParts.length; ++j) {\n- source = sourceParts[j];\n- // work through target parts - this array may change\n- for (var k = 0; k < targetParts.length; ++k) {\n- target = targetParts[k];\n- if (source.getBounds().intersectsBounds(target.getBounds())) {\n- results = source.split(target, options);\n- if (results) {\n- proceed = this.events.triggerEvent(\n- \"beforesplit\", {\n- source: feature,\n- target: targetFeature\n- }\n- );\n- if (proceed !== false) {\n- if (mutual) {\n- parts = results[0];\n- // handle parts that result from source splitting\n- if (parts.length > 1) {\n- // splice in new source parts\n- parts.unshift(j, 1); // add args for splice below\n- Array.prototype.splice.apply(sourceParts, parts);\n- j += parts.length - 3;\n- }\n- results = results[1];\n- }\n- // handle parts that result from target splitting\n- if (results.length > 1) {\n- // splice in new target parts\n- results.unshift(k, 1); // add args for splice below\n- Array.prototype.splice.apply(targetParts, results);\n- k += results.length - 3;\n- }\n- }\n- }\n- }\n- }\n- }\n- if (targetParts && targetParts.length > 1) {\n- this.geomsToFeatures(targetFeature, targetParts);\n- this.events.triggerEvent(\"split\", {\n- original: targetFeature,\n- features: targetParts\n- });\n- Array.prototype.push.apply(additions, targetParts);\n- removals.push(targetFeature);\n- targetSplit = true;\n- }\n- }\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- this.geomsToFeatures(feature, sourceParts);\n- this.events.triggerEvent(\"split\", {\n- original: feature,\n- features: sourceParts\n- });\n- Array.prototype.push.apply(additions, sourceParts);\n- removals.push(feature);\n- sourceSplit = true;\n- }\n- if (sourceSplit || targetSplit) {\n- // remove and add feature events are suppressed\n- // listen for split event on this control instead\n- if (this.deferDelete) {\n- // Set state instead of removing. Take care to avoid\n- // setting delete for features that have not yet been\n- // inserted - those should be destroyed immediately.\n- var feat, destroys = [];\n- for (var i = 0, len = removals.length; i < len; ++i) {\n- feat = removals[i];\n- if (feat.state === OpenLayers.State.INSERT) {\n- destroys.push(feat);\n- } else {\n- feat.state = OpenLayers.State.DELETE;\n- this.layer.drawFeature(feat);\n- }\n- }\n- this.layer.destroyFeatures(destroys, {\n- silent: true\n- });\n- for (var i = 0, len = additions.length; i < len; ++i) {\n- additions[i].state = OpenLayers.State.INSERT;\n- }\n- } else {\n- this.layer.destroyFeatures(removals, {\n- silent: true\n- });\n- }\n- this.layer.addFeatures(additions, {\n- silent: true\n- });\n- this.events.triggerEvent(\"aftersplit\", {\n- source: feature,\n- features: additions\n- });\n- }\n- }\n- return sourceSplit;\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);\n+ this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT];\n },\n \n /**\n- * Method: geomsToFeatures\n- * Create new features given a template feature and a list of geometries.\n- * The list of geometries is modified in place. The result will be\n- * a list of new features.\n- *\n+ * Method: getURL\n+ * \n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned.\n- * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will\n- * become a list of new features.\n+ * bounds - {<OpenLayers.Bounds>} \n+ * \n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the \n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters\n */\n- geomsToFeatures: function(feature, geoms) {\n- var clone = feature.clone();\n- delete clone.geometry;\n- var newFeature;\n- for (var i = 0, len = geoms.length; i < len; ++i) {\n- // turn results list from geoms to features\n- newFeature = clone.clone();\n- newFeature.geometry = geoms[i];\n- newFeature.state = OpenLayers.State.INSERT;\n- geoms[i] = newFeature;\n- }\n- },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var mapRes = this.map.getResolution();\n+ var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n+ var pX = Math.round(bounds.left / mapRes);\n+ var pY = -Math.round(bounds.top / mapRes);\n \n- /**\n- * Method: destroy\n- * Clean up the control.\n- */\n- destroy: function() {\n- if (this.active) {\n- this.deactivate(); // TODO: this should be handled by the super\n+ var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;\n+ var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;\n+\n+ var components = [\n+ \"/\",\n+ this.params.map,\n+ \"/\",\n+ scale,\n+ \"/\",\n+ this.params.g.replace(/\\s/g, '_'),\n+ \"/def/t\",\n+ metaY,\n+ \"/l\",\n+ metaX,\n+ \"/t\",\n+ pY,\n+ \"l\",\n+ pX,\n+ \".\",\n+ this.extension\n+ ];\n+\n+ var url = this.url;\n+\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(components.join(''), url);\n }\n- OpenLayers.Control.prototype.destroy.call(this);\n+ return url + components.join(\"\");\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Split\"\n+ CLASS_NAME: \"OpenLayers.Layer.KaMapCache\"\n });\n /* ======================================================================\n- OpenLayers/Control/PanZoomBar.js\n+ OpenLayers/Layer/FixedZoomLevels.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control/PanZoom.js\n+ * @requires OpenLayers/Layer.js\n */\n \n /**\n- * Class: OpenLayers.Control.PanZoomBar\n- * The PanZoomBar is a visible control composed of a\n- * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>. \n- * By default it is displayed in the upper left corner of the map as 4\n- * directional arrows above a vertical slider.\n+ * Class: OpenLayers.Layer.FixedZoomLevels\n+ * Some Layers will already have established zoom levels (like google \n+ * or ve). Instead of trying to determine them and populate a resolutions[]\n+ * Array with those values, we will hijack the resolution functionality\n+ * here.\n+ * \n+ * When you subclass FixedZoomLevels: \n+ * \n+ * The initResolutions() call gets nullified, meaning no resolutions[] array \n+ * is set up. Which would be a big problem getResolution() in Layer, since \n+ * it merely takes map.zoom and indexes into resolutions[]... but....\n+ * \n+ * The getResolution() call is also overridden. Instead of using the \n+ * resolutions[] array, we simply calculate the current resolution based\n+ * on the current extent and the current map size. But how will we be able\n+ * to calculate the current extent without knowing the resolution...?\n+ * \n+ * The getExtent() function is also overridden. Instead of calculating extent\n+ * based on the center point and the current resolution, we instead \n+ * calculate the extent by getting the lonlats at the top-left and \n+ * bottom-right by using the getLonLatFromViewPortPx() translation function,\n+ * taken from the pixel locations (0,0) and the size of the map. But how \n+ * will we be able to do lonlat-px translation without resolution....?\n+ * \n+ * The getZoomForResolution() method is overridden. Instead of indexing into\n+ * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in\n+ * the desired resolution. With this extent, we then call getZoomForExtent() \n+ * \n+ * \n+ * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, \n+ * it is your responsibility to provide the following three functions:\n+ * \n+ * - getLonLatFromViewPortPx\n+ * - getViewPortPxFromLonLat\n+ * - getZoomForExtent\n+ * \n+ * ...those three functions should generally be provided by any reasonable \n+ * API that you might be working from.\n *\n- * Inherits from:\n- * - <OpenLayers.Control.PanZoom>\n */\n-OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {\n-\n- /** \n- * APIProperty: zoomStopWidth\n- */\n- zoomStopWidth: 18,\n-\n- /** \n- * APIProperty: zoomStopHeight\n- */\n- zoomStopHeight: 11,\n-\n- /** \n- * Property: slider\n- */\n- slider: null,\n-\n- /** \n- * Property: sliderEvents\n- * {<OpenLayers.Events>}\n- */\n- sliderEvents: null,\n-\n- /** \n- * Property: zoombarDiv\n- * {DOMElement}\n- */\n- zoombarDiv: null,\n+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n \n- /** \n- * APIProperty: zoomWorldIcon\n- * {Boolean}\n- */\n- zoomWorldIcon: false,\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /* The following functions must all be implemented */\n+ /* by all base layers */\n+ /* */\n+ /********************************************************/\n \n /**\n- * APIProperty: panIcons\n- * {Boolean} Set this property to false not to display the pan icons. If\n- * false the zoom world icon is placed under the zoom bar. Defaults to\n- * true.\n+ * Constructor: OpenLayers.Layer.FixedZoomLevels\n+ * Create a new fixed zoom levels layer.\n */\n- panIcons: true,\n+ initialize: function() {\n+ //this class is only just to add the following functions... \n+ // nothing to actually do here... but it is probably a good\n+ // idea to have layers that use these functions call this \n+ // inititalize() anyways, in case at some point we decide we \n+ // do want to put some functionality or state in here. \n+ },\n \n /**\n- * APIProperty: forceFixedZoomLevel\n- * {Boolean} Force a fixed zoom level even though the map has \n- * fractionalZoom\n+ * Method: initResolutions\n+ * Populate the resolutions array\n */\n- forceFixedZoomLevel: false,\n+ initResolutions: function() {\n \n- /**\n- * Property: mouseDragStart\n- * {<OpenLayers.Pixel>}\n- */\n- mouseDragStart: null,\n+ var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];\n \n- /**\n- * Property: deltaY\n- * {Number} The cumulative vertical pixel offset during a zoom bar drag.\n- */\n- deltaY: null,\n+ for (var i = 0, len = props.length; i < len; i++) {\n+ var property = props[i];\n+ this[property] = (this.options[property] != null) ?\n+ this.options[property] :\n+ this.map[property];\n+ }\n \n- /**\n- * Property: zoomStart\n- * {<OpenLayers.Pixel>}\n- */\n- zoomStart: null,\n+ if ((this.minZoomLevel == null) ||\n+ (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) {\n+ this.minZoomLevel = this.MIN_ZOOM_LEVEL;\n+ }\n \n- /**\n- * Constructor: OpenLayers.Control.PanZoomBar\n- */\n+ //\n+ // At this point, we know what the minimum desired zoom level is, and\n+ // we must calculate the total number of zoom levels. \n+ // \n+ // Because we allow for the setting of either the 'numZoomLevels'\n+ // or the 'maxZoomLevel' properties... on either the layer or the \n+ // map, we have to define some rules to see which we take into\n+ // account first in this calculation. \n+ //\n+ // The following is the precedence list for these properties:\n+ // \n+ // (1) numZoomLevels set on layer\n+ // (2) maxZoomLevel set on layer\n+ // (3) numZoomLevels set on map\n+ // (4) maxZoomLevel set on map*\n+ // (5) none of the above*\n+ //\n+ // *Note that options (4) and (5) are only possible if the user \n+ // _explicitly_ sets the 'numZoomLevels' property on the map to \n+ // null, since it is set by default to 16. \n+ //\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n+ //\n+ // Note to future: In 3.0, I think we should remove the default \n+ // value of 16 for map.numZoomLevels. Rather, I think that value \n+ // should be set as a default on the Layer.WMS class. If someone\n+ // creates a 3rd party layer and does not specify any 'minZoomLevel', \n+ // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly \n+ // specified any of those on the map object either.. then I think\n+ // it is fair to say that s/he wants all the zoom levels available.\n+ // \n+ // By making map.numZoomLevels *null* by default, that will be the \n+ // case. As it is, I don't feel comfortable changing that right now\n+ // as it would be a glaring API change and actually would probably\n+ // break many peoples' codes. \n+ //\n \n- this._removeZoomBar();\n+ //the number of zoom levels we'd like to have.\n+ var desiredZoomLevels;\n \n- this.map.events.un({\n- \"changebaselayer\": this.redraw,\n- \"updatesize\": this.redraw,\n- scope: this\n- });\n+ //this is the maximum number of zoom levels the layer will allow, \n+ // given the specified starting minimum zoom level.\n+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n \n- OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);\n+ if (((this.options.numZoomLevels == null) &&\n+ (this.options.maxZoomLevel != null)) // (2)\n+ ||\n+ ((this.numZoomLevels == null) &&\n+ (this.maxZoomLevel != null)) // (4)\n+ ) {\n+ //calculate based on specified maxZoomLevel (on layer or map)\n+ desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;\n+ } else {\n+ //calculate based on specified numZoomLevels (on layer or map)\n+ // this covers cases (1) and (3)\n+ desiredZoomLevels = this.numZoomLevels;\n+ }\n \n- delete this.mouseDragStart;\n- delete this.zoomStart;\n- },\n+ if (desiredZoomLevels != null) {\n+ //Now that we know what we would *like* the number of zoom levels\n+ // to be, based on layer or map options, we have to make sure that\n+ // it does not conflict with the actual limit, as specified by \n+ // the constants on the layer itself (and calculated into the\n+ // 'limitZoomLevels' variable). \n+ this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);\n+ } else {\n+ // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was \n+ // set on either the layer or the map. So we just use the \n+ // maximum limit as calculated by the layer's constants.\n+ this.numZoomLevels = limitZoomLevels;\n+ }\n \n- /**\n- * Method: setMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);\n- this.map.events.on({\n- \"changebaselayer\": this.redraw,\n- \"updatesize\": this.redraw,\n- scope: this\n- });\n- },\n+ //now that the 'numZoomLevels' is appropriately, safely set, \n+ // we go back and re-calculate the 'maxZoomLevel'.\n+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n \n- /** \n- * Method: redraw\n- * clear the div and start over.\n- */\n- redraw: function() {\n- if (this.div != null) {\n- this.removeButtons();\n- this._removeZoomBar();\n+ if (this.RESOLUTIONS != null) {\n+ var resolutionsIndex = 0;\n+ this.resolutions = [];\n+ for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];\n+ }\n+ this.maxResolution = this.resolutions[0];\n+ this.minResolution = this.resolutions[this.resolutions.length - 1];\n }\n- this.draw();\n },\n \n /**\n- * Method: draw \n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>} \n+ * APIMethod: getResolution\n+ * Get the current map resolution\n+ * \n+ * Returns:\n+ * {Float} Map units per Pixel\n */\n- draw: function(px) {\n- // initialize our internal div\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- px = this.position.clone();\n-\n- // place the controls\n- this.buttons = [];\n-\n- var sz = {\n- w: 18,\n- h: 18\n- };\n- if (this.panIcons) {\n- var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);\n- var wposition = sz.w;\n+ getResolution: function() {\n \n- if (this.zoomWorldIcon) {\n- centered = new OpenLayers.Pixel(px.x + sz.w, px.y);\n- }\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);\n+ } else {\n+ var resolution = null;\n \n- this._addButton(\"panup\", \"north-mini.png\", centered, sz);\n- px.y = centered.y + sz.h;\n- this._addButton(\"panleft\", \"west-mini.png\", px, sz);\n- if (this.zoomWorldIcon) {\n- this._addButton(\"zoomworld\", \"zoom-world-mini.png\", px.add(sz.w, 0), sz);\n+ var viewSize = this.map.getSize();\n+ var extent = this.getExtent();\n \n- wposition *= 2;\n- }\n- this._addButton(\"panright\", \"east-mini.png\", px.add(wposition, 0), sz);\n- this._addButton(\"pandown\", \"south-mini.png\", centered.add(0, sz.h * 2), sz);\n- this._addButton(\"zoomin\", \"zoom-plus-mini.png\", centered.add(0, sz.h * 3 + 5), sz);\n- centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5));\n- this._addButton(\"zoomout\", \"zoom-minus-mini.png\", centered, sz);\n- } else {\n- this._addButton(\"zoomin\", \"zoom-plus-mini.png\", px, sz);\n- centered = this._addZoomBar(px.add(0, sz.h));\n- this._addButton(\"zoomout\", \"zoom-minus-mini.png\", centered, sz);\n- if (this.zoomWorldIcon) {\n- centered = centered.add(0, sz.h + 3);\n- this._addButton(\"zoomworld\", \"zoom-world-mini.png\", centered, sz);\n+ if ((viewSize != null) && (extent != null)) {\n+ resolution = Math.max(extent.getWidth() / viewSize.w,\n+ extent.getHeight() / viewSize.h);\n }\n+ return resolution;\n }\n- return this.div;\n },\n \n- /** \n- * Method: _addZoomBar\n+ /**\n+ * APIMethod: getExtent\n+ * Calculates using px-> lonlat translation functions on tl and br \n+ * corners of viewport\n * \n- * Parameters:\n- * centered - {<OpenLayers.Pixel>} where zoombar drawing is to start.\n+ * Returns:\n+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort.\n */\n- _addZoomBar: function(centered) {\n- var imgLocation = OpenLayers.Util.getImageLocation(\"slider.png\");\n- var id = this.id + \"_\" + this.map.id;\n- var minZoom = this.map.getMinZoom();\n- var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();\n- var slider = OpenLayers.Util.createAlphaImageDiv(id,\n- centered.add(-1, zoomsToEnd * this.zoomStopHeight), {\n- w: 20,\n- h: 9\n- },\n- imgLocation,\n- \"absolute\");\n- slider.style.cursor = \"move\";\n- this.slider = slider;\n-\n- this.sliderEvents = new OpenLayers.Events(this, slider, null, true, {\n- includeXY: true\n+ getExtent: function() {\n+ var size = this.map.getSize();\n+ var tl = this.getLonLatFromViewPortPx({\n+ x: 0,\n+ y: 0\n });\n- this.sliderEvents.on({\n- \"touchstart\": this.zoomBarDown,\n- \"touchmove\": this.zoomBarDrag,\n- \"touchend\": this.zoomBarUp,\n- \"mousedown\": this.zoomBarDown,\n- \"mousemove\": this.zoomBarDrag,\n- \"mouseup\": this.zoomBarUp\n+ var br = this.getLonLatFromViewPortPx({\n+ x: size.w,\n+ y: size.h\n });\n \n- var sz = {\n- w: this.zoomStopWidth,\n- h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)\n- };\n- var imgLocation = OpenLayers.Util.getImageLocation(\"zoombar.png\");\n- var div = null;\n-\n- if (OpenLayers.Util.alphaHack()) {\n- var id = this.id + \"_\" + this.map.id;\n- div = OpenLayers.Util.createAlphaImageDiv(id, centered, {\n- w: sz.w,\n- h: this.zoomStopHeight\n- },\n- imgLocation,\n- \"absolute\", null, \"crop\");\n- div.style.height = sz.h + \"px\";\n+ if ((tl != null) && (br != null)) {\n+ return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);\n } else {\n- div = OpenLayers.Util.createDiv(\n- 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,\n- centered,\n- sz,\n- imgLocation);\n+ return null;\n }\n- div.style.cursor = \"pointer\";\n- div.className = \"olButton\";\n- this.zoombarDiv = div;\n-\n- this.div.appendChild(div);\n-\n- this.startTop = parseInt(div.style.top);\n- this.div.appendChild(slider);\n-\n- this.map.events.register(\"zoomend\", this, this.moveZoomBar);\n-\n- centered = centered.add(0,\n- this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom));\n- return centered;\n- },\n-\n- /**\n- * Method: _removeZoomBar\n- */\n- _removeZoomBar: function() {\n- this.sliderEvents.un({\n- \"touchstart\": this.zoomBarDown,\n- \"touchmove\": this.zoomBarDrag,\n- \"touchend\": this.zoomBarUp,\n- \"mousedown\": this.zoomBarDown,\n- \"mousemove\": this.zoomBarDrag,\n- \"mouseup\": this.zoomBarUp\n- });\n- this.sliderEvents.destroy();\n-\n- this.div.removeChild(this.zoombarDiv);\n- this.zoombarDiv = null;\n- this.div.removeChild(this.slider);\n- this.slider = null;\n-\n- this.map.events.unregister(\"zoomend\", this, this.moveZoomBar);\n },\n \n /**\n- * Method: onButtonClick\n+ * Method: getZoomForResolution\n+ * Get the zoom level for a given resolution\n *\n * Parameters:\n- * evt - {Event}\n- */\n- onButtonClick: function(evt) {\n- OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments);\n- if (evt.buttonElement === this.zoombarDiv) {\n- var levels = evt.buttonXY.y / this.zoomStopHeight;\n- if (this.forceFixedZoomLevel || !this.map.fractionalZoom) {\n- levels = Math.floor(levels);\n- }\n- var zoom = (this.map.getNumZoomLevels() - 1) - levels;\n- zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);\n- this.map.zoomTo(zoom);\n- }\n- },\n-\n- /**\n- * Method: passEventToSlider\n- * This function is used to pass events that happen on the div, or the map,\n- * through to the slider, which then does its moving thing.\n+ * resolution - {Float}\n *\n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n+ * Returns:\n+ * {Integer} A suitable zoom level for the specified resolution.\n+ * If no baselayer is set, returns null.\n */\n- passEventToSlider: function(evt) {\n- this.sliderEvents.handleBrowserEvent(evt);\n- },\n+ getZoomForResolution: function(resolution) {\n \n- /*\n- * Method: zoomBarDown\n- * event listener for clicks on the slider\n- *\n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n- */\n- zoomBarDown: function(evt) {\n- if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {\n- return;\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);\n+ } else {\n+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n+ return this.getZoomForExtent(extent);\n }\n- this.map.events.on({\n- \"touchmove\": this.passEventToSlider,\n- \"mousemove\": this.passEventToSlider,\n- \"mouseup\": this.passEventToSlider,\n- scope: this\n- });\n- this.mouseDragStart = evt.xy.clone();\n- this.zoomStart = evt.xy.clone();\n- this.div.style.cursor = \"move\";\n- // reset the div offsets just in case the div moved\n- this.zoombarDiv.offsets = null;\n- OpenLayers.Event.stop(evt);\n },\n \n- /*\n- * Method: zoomBarDrag\n- * This is what happens when a click has occurred, and the client is\n- * dragging. Here we must ensure that the slider doesn't go beyond the\n- * bottom/top of the zoombar div, as well as moving the slider to its new\n- * visual location\n+\n+\n+\n+ /********************************************************/\n+ /* */\n+ /* Translation Functions */\n+ /* */\n+ /* The following functions translate GMaps and OL */\n+ /* formats for Pixel, LonLat, Bounds, and Zoom */\n+ /* */\n+ /********************************************************/\n+\n+\n+ //\n+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom\n+ //\n+\n+ /**\n+ * Method: getOLZoomFromMapObjectZoom\n+ * Get the OL zoom index from the map object zoom level\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>} \n+ * moZoom - {Integer}\n+ * \n+ * Returns:\n+ * {Integer} An OpenLayers Zoom level, translated from the passed in zoom\n+ * Returns null if null value is passed in\n */\n- zoomBarDrag: function(evt) {\n- if (this.mouseDragStart != null) {\n- var deltaY = this.mouseDragStart.y - evt.xy.y;\n- var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);\n- if ((evt.clientY - offsets[1]) > 0 &&\n- (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {\n- var newTop = parseInt(this.slider.style.top) - deltaY;\n- this.slider.style.top = newTop + \"px\";\n- this.mouseDragStart = evt.xy.clone();\n+ getOLZoomFromMapObjectZoom: function(moZoom) {\n+ var zoom = null;\n+ if (moZoom != null) {\n+ zoom = moZoom - this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.map.baseLayer.getZoomForResolution(\n+ this.getResolutionForZoom(zoom)\n+ );\n }\n- // set cumulative displacement\n- this.deltaY = this.zoomStart.y - evt.xy.y;\n- OpenLayers.Event.stop(evt);\n }\n+ return zoom;\n },\n \n- /*\n- * Method: zoomBarUp\n- * Perform cleanup when a mouseup event is received -- discover new zoom\n- * level and switch to it.\n+ /**\n+ * Method: getMapObjectZoomFromOLZoom\n+ * Get the map object zoom level from the OL zoom level\n *\n * Parameters:\n- * evt - {<OpenLayers.Event>} \n+ * olZoom - {Integer}\n+ * \n+ * Returns:\n+ * {Integer} A MapObject level, translated from the passed in olZoom\n+ * Returns null if null value is passed in\n */\n- zoomBarUp: function(evt) {\n- if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== \"touchend\") {\n- return;\n- }\n- if (this.mouseDragStart) {\n- this.div.style.cursor = \"\";\n- this.map.events.un({\n- \"touchmove\": this.passEventToSlider,\n- \"mouseup\": this.passEventToSlider,\n- \"mousemove\": this.passEventToSlider,\n- scope: this\n- });\n- var zoomLevel = this.map.zoom;\n- if (!this.forceFixedZoomLevel && this.map.fractionalZoom) {\n- zoomLevel += this.deltaY / this.zoomStopHeight;\n- zoomLevel = Math.min(Math.max(zoomLevel, 0),\n- this.map.getNumZoomLevels() - 1);\n- } else {\n- zoomLevel += this.deltaY / this.zoomStopHeight;\n- zoomLevel = Math.max(Math.round(zoomLevel), 0);\n+ getMapObjectZoomFromOLZoom: function(olZoom) {\n+ var zoom = null;\n+ if (olZoom != null) {\n+ zoom = olZoom + this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.getZoomForResolution(\n+ this.map.baseLayer.getResolutionForZoom(zoom)\n+ );\n }\n- this.map.zoomTo(zoomLevel);\n- this.mouseDragStart = null;\n- this.zoomStart = null;\n- this.deltaY = 0;\n- OpenLayers.Event.stop(evt);\n }\n+ return zoom;\n },\n \n- /*\n- * Method: moveZoomBar\n- * Change the location of the slider to match the current zoom level.\n- */\n- moveZoomBar: function() {\n- var newTop =\n- ((this.map.getNumZoomLevels() - 1) - this.map.getZoom()) *\n- this.zoomStopHeight + this.startTop + 1;\n- this.slider.style.top = newTop + \"px\";\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control.PanZoomBar\"\n+ CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n });\n+\n /* ======================================================================\n- OpenLayers/Control/DragFeature.js\n+ OpenLayers/Layer/OSM.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Drag.js\n- * @requires OpenLayers/Handler/Feature.js\n+ * @requires OpenLayers/Layer/XYZ.js\n */\n \n /**\n- * Class: OpenLayers.Control.DragFeature\n- * The DragFeature control moves a feature with a drag of the mouse. Create a\n- * new control with the <OpenLayers.Control.DragFeature> constructor.\n+ * Class: OpenLayers.Layer.OSM\n+ * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n+ * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n+ * a different layer instead, you need to provide a different\n+ * URL to the constructor. Here's an example for using OpenCycleMap:\n+ * \n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n *\n- * Inherits From:\n- * - <OpenLayers.Control>\n+ * Inherits from:\n+ * - <OpenLayers.Layer.XYZ>\n */\n-OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n \n /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict dragging to a limited set of geometry types,\n- * send a list of strings corresponding to the geometry class names.\n+ * APIProperty: name\n+ * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n+ * argument to the constructor is null or undefined.\n */\n- geometryTypes: null,\n+ name: \"OpenStreetMap\",\n \n /**\n- * APIProperty: onStart\n- * {Function} Define this function if you want to know when a drag starts.\n- * The function should expect to receive two arguments: the feature\n- * that is about to be dragged and the pixel location of the mouse.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be\n- * dragged.\n- * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n+ * APIProperty: url\n+ * {String} The tileset URL scheme. Defaults to\n+ * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n+ * (the official OSM tileset) if the second argument to the constructor\n+ * is null or undefined. To use another tileset you can have something\n+ * like this:\n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n */\n- onStart: function(feature, pixel) {},\n+ url: [\n+ 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n+ ],\n \n /**\n- * APIProperty: onDrag\n- * {Function} Define this function if you want to know about each move of a\n- * feature. The function should expect to receive two arguments: the\n- * feature that is being dragged and the pixel location of the mouse.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n- * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n+ * Property: attribution\n+ * {String} The layer attribution.\n */\n- onDrag: function(feature, pixel) {},\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n \n /**\n- * APIProperty: onComplete\n- * {Function} Define this function if you want to know when a feature is\n- * done dragging. The function should expect to receive two arguments:\n- * the feature that is being dragged and the pixel location of the\n- * mouse.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n- * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse.\n+ * Property: sphericalMercator\n+ * {Boolean}\n */\n- onComplete: function(feature, pixel) {},\n+ sphericalMercator: true,\n \n /**\n- * APIProperty: onEnter\n- * {Function} Define this function if you want to know when the mouse\n- * goes over a feature and thereby makes this feature a candidate\n- * for dragging.\n+ * Property: wrapDateLine\n+ * {Boolean}\n+ */\n+ wrapDateLine: true,\n+\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that is ready\n- * to be dragged.\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n+ *\n+ * When using OSM tilesets other than the default ones, it may be\n+ * necessary to set this to\n+ *\n+ * (code)\n+ * {crossOriginKeyword: null}\n+ * (end)\n+ *\n+ * if the server does not send Access-Control-Allow-Origin headers.\n */\n- onEnter: function(feature) {},\n+ tileOptions: null,\n \n /**\n- * APIProperty: onLeave\n- * {Function} Define this function if you want to know when the mouse\n- * goes out of the feature that was dragged.\n+ * Constructor: OpenLayers.Layer.OSM\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged.\n+ * name - {String} The layer name.\n+ * url - {String} The tileset URL scheme.\n+ * options - {Object} Configuration options for the layer. Any inherited\n+ * layer option can be set in this object (e.g.\n+ * <OpenLayers.Layer.Grid.buffer>).\n */\n- onLeave: function(feature) {},\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options && this.options.tileOptions);\n+ },\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, mouse dragging will continue even if the\n- * mouse cursor leaves the map viewport. Default is false.\n+ * Method: clone\n */\n- documentDrag: false,\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(\n+ this.name, this.url, this.getOptions());\n+ }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj;\n+ },\n \n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>}\n- */\n- layer: null,\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/MapGuide.js\n+ ====================================================================== */\n \n- /**\n- * Property: feature\n- * {<OpenLayers.Feature.Vector>}\n- */\n- feature: null,\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.MapGuide\n+ * Instances of OpenLayers.Layer.MapGuide are used to display\n+ * data from a MapGuide OS instance.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n+ /** \n+ * APIProperty: isBaseLayer\n+ * {Boolean} Treat this layer as a base layer. Default is true.\n+ **/\n+ isBaseLayer: true,\n \n /**\n- * Property: dragCallbacks\n- * {Object} The functions that are sent to the drag handler for callback.\n- */\n- dragCallbacks: {},\n+ * APIProperty: useHttpTile\n+ * {Boolean} use a tile cache exposed directly via a webserver rather than the \n+ * via mapguide server. This does require extra configuration on the Mapguide Server,\n+ * and will only work when singleTile is false. The url for the layer must be set to the\n+ * webserver path rather than the Mapguide mapagent.\n+ * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp\n+ **/\n+ useHttpTile: false,\n+\n+ /** \n+ * APIProperty: singleTile\n+ * {Boolean} use tile server or request single tile image. \n+ **/\n+ singleTile: false,\n+\n+ /** \n+ * APIProperty: useOverlay\n+ * {Boolean} flag to indicate if the layer should be retrieved using\n+ * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.\n+ **/\n+ useOverlay: false,\n+\n+ /** \n+ * APIProperty: useAsyncOverlay\n+ * {Boolean} indicates if the MapGuide site supports the asynchronous \n+ * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010\n+ * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG \n+ * is called asynchronously, allows selections to be drawn separately from \n+ * the map and offers styling options.\n+ * \n+ * With older versions of MapGuide, set useAsyncOverlay=false. Note that in\n+ * this case a synchronous AJAX call is issued and the mapname and session\n+ * parameters must be used to initialize the layer, not the mapdefinition\n+ * parameter. Also note that this will issue a synchronous AJAX request \n+ * before the image request can be issued so the users browser may lock\n+ * up if the MG Web tier does not respond in a timely fashion.\n+ **/\n+ useAsyncOverlay: true,\n \n /**\n- * Property: featureCallbacks\n- * {Object} The functions that are sent to the feature handler for callback.\n+ * Constant: TILE_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs for tiled layer\n */\n- featureCallbacks: {},\n+ TILE_PARAMS: {\n+ operation: 'GETTILEIMAGE',\n+ version: '1.2.0'\n+ },\n \n /**\n- * Property: lastPixel\n- * {<OpenLayers.Pixel>}\n+ * Constant: SINGLE_TILE_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs for untiled layer\n */\n- lastPixel: null,\n+ SINGLE_TILE_PARAMS: {\n+ operation: 'GETMAPIMAGE',\n+ format: 'PNG',\n+ locale: 'en',\n+ clip: '1',\n+ version: '1.0.0'\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.DragFeature\n- * Create a new control to drag features.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be\n- * dragged.\n- * options - {Object} Optional object whose properties will be set on the\n- * control.\n+ * Constant: OVERLAY_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs for untiled layer\n */\n- initialize: function(layer, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.layer = layer;\n- this.handlers = {\n- drag: new OpenLayers.Handler.Drag(\n- this, OpenLayers.Util.extend({\n- down: this.downFeature,\n- move: this.moveFeature,\n- up: this.upFeature,\n- out: this.cancel,\n- done: this.doneDragging\n- }, this.dragCallbacks), {\n- documentDrag: this.documentDrag\n- }\n- ),\n- feature: new OpenLayers.Handler.Feature(\n- this, this.layer, OpenLayers.Util.extend({\n- // 'click' and 'clickout' callback are for the mobile\n- // support: no 'over' or 'out' in touch based browsers.\n- click: this.clickFeature,\n- clickout: this.clickoutFeature,\n- over: this.overFeature,\n- out: this.outFeature\n- }, this.featureCallbacks), {\n- geometryTypes: this.geometryTypes\n- }\n- )\n- };\n+ OVERLAY_PARAMS: {\n+ operation: 'GETDYNAMICMAPOVERLAYIMAGE',\n+ format: 'PNG',\n+ locale: 'en',\n+ clip: '1',\n+ version: '2.0.0'\n },\n \n- /**\n- * Method: clickFeature\n- * Called when the feature handler detects a click-in on a feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n+ /** \n+ * Constant: FOLDER_PARAMS\n+ * {Object} Hashtable of parameter key/value pairs which describe \n+ * the folder structure for tiles as configured in the mapguide \n+ * serverconfig.ini section [TileServiceProperties]\n */\n- clickFeature: function(feature) {\n- if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {\n- this.handlers.drag.dragstart(this.handlers.feature.evt);\n- // to let the events propagate to the feature handler (click callback)\n- this.handlers.drag.stopDown = false;\n- }\n+ FOLDER_PARAMS: {\n+ tileColumnsPerFolder: 30,\n+ tileRowsPerFolder: 30,\n+ format: 'png',\n+ querystring: null\n },\n \n+ /** \n+ * Property: defaultSize\n+ * {<OpenLayers.Size>} Tile size as produced by MapGuide server\n+ **/\n+ defaultSize: new OpenLayers.Size(300, 300),\n+\n+ /** \n+ * Property: tileOriginCorner\n+ * {String} MapGuide tile server uses top-left as tile origin\n+ **/\n+ tileOriginCorner: \"tl\",\n+\n /**\n- * Method: clickoutFeature\n- * Called when the feature handler detects a click-out on a feature.\n+ * Constructor: OpenLayers.Layer.MapGuide\n+ * Create a new Mapguide layer, either tiled or untiled. \n+ *\n+ * For tiled layers, the 'groupName' and 'mapDefinition' values \n+ * must be specified as parameters in the constructor.\n+ *\n+ * For untiled base layers, specify either combination of 'mapName' and\n+ * 'session', or 'mapDefinition' and 'locale'. \n+ *\n+ * For older versions of MapGuide and overlay layers, set useAsyncOverlay \n+ * to false and in this case mapName and session are required parameters \n+ * for the constructor.\n+ *\n+ * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion \n+ * factor that are different than the defaults used in OpenLayers, \n+ * so these must be adjusted accordingly in your application. \n+ * See the MapGuide example for how to set these values for MGOS.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n+ * name - {String} Name of the layer displayed in the interface\n+ * url - {String} Location of the MapGuide mapagent executable\n+ * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)\n+ * params - {Object} hashtable of additional parameters to use. Some\n+ * parameters may require additional code on the server. The ones that\n+ * you may want to use are: \n+ * - mapDefinition - {String} The MapGuide resource definition\n+ * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)\n+ * - locale - Locale setting \n+ * (for untiled overlays layers only)\n+ * - mapName - {String} Name of the map as stored in the MapGuide session.\n+ * (for untiled layers with a session parameter only)\n+ * - session - { String} MapGuide session ID \n+ * (for untiled overlays layers only)\n+ * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only\n+ * - format - Image format to be returned (for untiled overlay layers only)\n+ * - showLayers - {String} A comma separated list of GUID's for the\n+ * layers to display eg: 'cvc-xcv34,453-345-345sdf'.\n+ * - hideLayers - {String} A comma separated list of GUID's for the\n+ * layers to hide eg: 'cvc-xcv34,453-345-345sdf'.\n+ * - showGroups - {String} A comma separated list of GUID's for the\n+ * groups to display eg: 'cvc-xcv34,453-345-345sdf'.\n+ * - hideGroups - {String} A comma separated list of GUID's for the\n+ * groups to hide eg: 'cvc-xcv34,453-345-345sdf'\n+ * - selectionXml - {String} A selection xml string Some server plumbing\n+ * is required to read such a value.\n+ * options - {Object} Hashtable of extra options to tag onto the layer; \n+ * will vary depending if tiled or untiled maps are being requested\n */\n- clickoutFeature: function(feature) {\n- if (this.handlers.feature.touch && this.over) {\n- this.outFeature(feature);\n- this.handlers.drag.stopDown = true;\n+ initialize: function(name, url, params, options) {\n+\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n+\n+ // unless explicitly set in options, if the layer is transparent, \n+ // it will be an overlay\n+ if (options == null || options.isBaseLayer == null) {\n+ this.isBaseLayer = ((this.transparent != \"true\") &&\n+ (this.transparent != true));\n }\n- },\n \n- /**\n- * APIMethod: destroy\n- * Take care of things that are not handled in superclass\n- */\n- destroy: function() {\n- this.layer = null;\n- OpenLayers.Control.prototype.destroy.apply(this, []);\n- },\n+ if (options && options.useOverlay != null) {\n+ this.useOverlay = options.useOverlay;\n+ }\n \n- /**\n- * APIMethod: activate\n- * Activate the control and the feature handler.\n- * \n- * Returns:\n- * {Boolean} Successfully activated the control and feature handler.\n- */\n- activate: function() {\n- return (this.handlers.feature.activate() &&\n- OpenLayers.Control.prototype.activate.apply(this, arguments));\n+ //initialize for untiled layers\n+ if (this.singleTile) {\n+ if (this.useOverlay) {\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ this.OVERLAY_PARAMS\n+ );\n+ if (!this.useAsyncOverlay) {\n+ this.params.version = \"1.0.0\";\n+ }\n+ } else {\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ this.SINGLE_TILE_PARAMS\n+ );\n+ }\n+ } else {\n+ //initialize for tiled layers\n+ if (this.useHttpTile) {\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ this.FOLDER_PARAMS\n+ );\n+ } else {\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ this.TILE_PARAMS\n+ );\n+ }\n+ this.setTileSize(this.defaultSize);\n+ }\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the control and all handlers.\n- * \n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n * Returns:\n- * {Boolean} Successfully deactivated the control.\n+ * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer\n */\n- deactivate: function() {\n- // the return from the handlers is unimportant in this case\n- this.handlers.drag.deactivate();\n- this.handlers.feature.deactivate();\n- this.feature = null;\n- this.dragging = false;\n- this.lastPixel = null;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, this.displayClass + \"Over\"\n- );\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.MapGuide(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ return obj;\n },\n \n /**\n- * Method: overFeature\n- * Called when the feature handler detects a mouse-over on a feature.\n- * This activates the drag handler.\n+ * Method: getURL\n+ * Return a query string for this layer\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The selected feature.\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox \n+ * for the request\n *\n * Returns:\n- * {Boolean} Successfully activated the drag handler.\n+ * {String} A string with the layer's url and parameters and also \n+ * the passed-in bounds and appropriate tile size specified \n+ * as parameters.\n */\n- overFeature: function(feature) {\n- var activated = false;\n- if (!this.handlers.drag.dragging) {\n- this.feature = feature;\n- this.handlers.drag.activate();\n- activated = true;\n- this.over = true;\n- OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n- this.onEnter(feature);\n+ getURL: function(bounds) {\n+ var url;\n+ var center = bounds.getCenterLonLat();\n+ var mapSize = this.map.getSize();\n+\n+ if (this.singleTile) {\n+ //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with\n+ //dynamic map parameters\n+ var params = {\n+ setdisplaydpi: OpenLayers.DOTS_PER_INCH,\n+ setdisplayheight: mapSize.h * this.ratio,\n+ setdisplaywidth: mapSize.w * this.ratio,\n+ setviewcenterx: center.lon,\n+ setviewcentery: center.lat,\n+ setviewscale: this.map.getScale()\n+ };\n+\n+ if (this.useOverlay && !this.useAsyncOverlay) {\n+ //first we need to call GETVISIBLEMAPEXTENT to set the extent\n+ var getVisParams = {};\n+ getVisParams = OpenLayers.Util.extend(getVisParams, params);\n+ getVisParams.operation = \"GETVISIBLEMAPEXTENT\";\n+ getVisParams.version = \"1.0.0\";\n+ getVisParams.session = this.params.session;\n+ getVisParams.mapName = this.params.mapName;\n+ getVisParams.format = 'text/xml';\n+ url = this.getFullRequestString(getVisParams);\n+\n+ OpenLayers.Request.GET({\n+ url: url,\n+ async: false\n+ });\n+ }\n+ //construct the full URL\n+ url = this.getFullRequestString(params);\n } else {\n- if (this.feature.id == feature.id) {\n- this.over = true;\n+\n+ //tiled version\n+ var currentRes = this.map.getResolution();\n+ var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);\n+ colidx = Math.round(colidx / this.tileSize.w);\n+ var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);\n+ rowidx = Math.round(rowidx / this.tileSize.h);\n+\n+ if (this.useHttpTile) {\n+ url = this.getImageFilePath({\n+ tilecol: colidx,\n+ tilerow: rowidx,\n+ scaleindex: this.resolutions.length - this.map.zoom - 1\n+ });\n+\n } else {\n- this.over = false;\n+ url = this.getFullRequestString({\n+ tilecol: colidx,\n+ tilerow: rowidx,\n+ scaleindex: this.resolutions.length - this.map.zoom - 1\n+ });\n }\n }\n- return activated;\n+ return url;\n },\n \n /**\n- * Method: downFeature\n- * Called when the drag handler detects a mouse-down.\n+ * Method: getFullRequestString\n+ * getFullRequestString on MapGuide layers is special, because we \n+ * do a regular expression replace on ',' in parameters to '+'.\n+ * This is why it is subclassed here.\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n+ * altUrl - {String} Alternative base URL to use.\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url appropriately encoded for MapGuide\n */\n- downFeature: function(pixel) {\n- this.lastPixel = pixel;\n- this.onStart(this.feature, pixel);\n- },\n+ getFullRequestString: function(newParams, altUrl) {\n+ // use layer's url unless altUrl passed in\n+ var url = (altUrl == null) ? this.url : altUrl;\n \n- /**\n- * Method: moveFeature\n- * Called when the drag handler detects a mouse-move. Also calls the\n- * optional onDrag method.\n- * \n- * Parameters:\n- * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n- */\n- moveFeature: function(pixel) {\n- var res = this.map.getResolution();\n- this.feature.geometry.move(res * (pixel.x - this.lastPixel.x),\n- res * (this.lastPixel.y - pixel.y));\n- this.layer.drawFeature(this.feature);\n- this.lastPixel = pixel;\n- this.onDrag(this.feature, pixel);\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will randomly select one of them in order\n+ // to evenly distribute requests to different urls.\n+ if (typeof url == \"object\") {\n+ url = url[Math.floor(Math.random() * url.length)];\n+ }\n+ // requestString always starts with url\n+ var requestString = url;\n+\n+ // create a new params hashtable with all the layer params and the \n+ // new params together. then convert to string\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ // ignore parameters that are already in the url search string\n+ var urlParams = OpenLayers.Util.upperCaseObject(\n+ OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key];\n+ }\n+ }\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+\n+ /* MapGuide needs '+' seperating things like bounds/height/width.\n+ Since typically this is URL encoded, we use a slight hack: we\n+ depend on the list-like functionality of getParameterString to\n+ leave ',' only in the case of list items (since otherwise it is\n+ encoded) then do a regular expression replace on the , characters\n+ to '+' */\n+ paramsString = paramsString.replace(/,/g, \"+\");\n+\n+ if (paramsString != \"\") {\n+ var lastServerChar = url.charAt(url.length - 1);\n+ if ((lastServerChar == \"&\") || (lastServerChar == \"?\")) {\n+ requestString += paramsString;\n+ } else {\n+ if (url.indexOf('?') == -1) {\n+ //serverPath has no ? -- add one\n+ requestString += '?' + paramsString;\n+ } else {\n+ //serverPath contains ?, so must already have paramsString at the end\n+ requestString += '&' + paramsString;\n+ }\n+ }\n+ }\n+ return requestString;\n },\n \n- /**\n- * Method: upFeature\n- * Called when the drag handler detects a mouse-up.\n- * \n+ /** \n+ * Method: getImageFilePath\n+ * special handler to request mapguide tiles from an http exposed tilecache \n+ *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} Location of the mouse event.\n+ * altUrl - {String} Alternative base URL to use.\n+ *\n+ * Returns:\n+ * {String} A string with the url for the tile image\n */\n- upFeature: function(pixel) {\n- if (!this.over) {\n- this.handlers.drag.deactivate();\n+ getImageFilePath: function(newParams, altUrl) {\n+ // use layer's url unless altUrl passed in\n+ var url = (altUrl == null) ? this.url : altUrl;\n+\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will randomly select one of them in order\n+ // to evenly distribute requests to different urls.\n+ if (typeof url == \"object\") {\n+ url = url[Math.floor(Math.random() * url.length)];\n+ }\n+ // requestString always starts with url\n+ var requestString = url;\n+\n+ var tileRowGroup = \"\";\n+ var tileColGroup = \"\";\n+\n+ if (newParams.tilerow < 0) {\n+ tileRowGroup = '-';\n+ }\n+\n+ if (newParams.tilerow == 0) {\n+ tileRowGroup += '0';\n+ } else {\n+ tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;\n+ }\n+\n+ if (newParams.tilecol < 0) {\n+ tileColGroup = '-';\n+ }\n+\n+ if (newParams.tilecol == 0) {\n+ tileColGroup += '0';\n+ } else {\n+ tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;\n+ }\n+\n+ var tilePath = '/S' + Math.floor(newParams.scaleindex) +\n+ '/' + this.params.basemaplayergroupname +\n+ '/R' + tileRowGroup +\n+ '/C' + tileColGroup +\n+ '/' + (newParams.tilerow % this.params.tileRowsPerFolder) +\n+ '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) +\n+ '.' + this.params.format;\n+\n+ if (this.params.querystring) {\n+ tilePath += \"?\" + this.params.querystring;\n }\n+\n+ requestString += tilePath;\n+ return requestString;\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.MapGuide\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Boxes.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Layer/Markers.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Boxes\n+ * Draw divs as 'boxes' on the layer. \n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Markers>\n+ */\n+OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {\n+\n /**\n- * Method: doneDragging\n- * Called when the drag handler is done dragging.\n+ * Constructor: OpenLayers.Layer.Boxes\n *\n * Parameters:\n- * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event\n- * came from a mouseout, this may not be in the map viewport.\n+ * name - {String} \n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- doneDragging: function(pixel) {\n- this.onComplete(this.feature, pixel);\n- },\n \n /**\n- * Method: outFeature\n- * Called when the feature handler detects a mouse-out on a feature.\n+ * Method: drawMarker \n+ * Calculate the pixel location for the marker, create it, and\n+ * add it to the layer's div\n *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left.\n+ * Parameters: \n+ * marker - {<OpenLayers.Marker.Box>} \n */\n- outFeature: function(feature) {\n- if (!this.handlers.drag.dragging) {\n- this.over = false;\n- this.handlers.drag.deactivate();\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, this.displayClass + \"Over\"\n- );\n- this.onLeave(feature);\n- this.feature = null;\n+ drawMarker: function(marker) {\n+ var topleft = this.map.getLayerPxFromLonLat({\n+ lon: marker.bounds.left,\n+ lat: marker.bounds.top\n+ });\n+ var botright = this.map.getLayerPxFromLonLat({\n+ lon: marker.bounds.right,\n+ lat: marker.bounds.bottom\n+ });\n+ if (botright == null || topleft == null) {\n+ marker.display(false);\n } else {\n- if (this.feature.id == feature.id) {\n- this.over = false;\n+ var markerDiv = marker.draw(topleft, {\n+ w: Math.max(1, botright.x - topleft.x),\n+ h: Math.max(1, botright.y - topleft.y)\n+ });\n+ if (!marker.drawn) {\n+ this.div.appendChild(markerDiv);\n+ marker.drawn = true;\n }\n }\n },\n \n- /**\n- * Method: cancel\n- * Called when the drag handler detects a mouse-out (from the map viewport).\n- */\n- cancel: function() {\n- this.handlers.drag.deactivate();\n- this.over = false;\n- },\n \n /**\n- * Method: setMap\n- * Set the map property for the control and all handlers.\n- *\n- * Parameters: \n- * map - {<OpenLayers.Map>} The control's map.\n+ * APIMethod: removeMarker \n+ * \n+ * Parameters:\n+ * marker - {<OpenLayers.Marker.Box>} \n */\n- setMap: function(map) {\n- this.handlers.drag.setMap(map);\n- this.handlers.feature.setMap(map);\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ removeMarker: function(marker) {\n+ OpenLayers.Util.removeItem(this.markers, marker);\n+ if ((marker.div != null) &&\n+ (marker.div.parentNode == this.div)) {\n+ this.div.removeChild(marker.div);\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.DragFeature\"\n+ CLASS_NAME: \"OpenLayers.Layer.Boxes\"\n });\n /* ======================================================================\n- OpenLayers/Control/ScaleLine.js\n+ OpenLayers/Layer/GeoRSS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Layer/Markers.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n */\n \n /**\n- * Class: OpenLayers.Control.ScaleLine\n- * The ScaleLine displays a small line indicator representing the current \n- * map scale on the map. By default it is drawn in the lower left corner of\n- * the map.\n+ * Class: OpenLayers.Layer.GeoRSS\n+ * Add GeoRSS Point features to your map. \n * \n * Inherits from:\n- * - <OpenLayers.Control>\n- * \n- * Is a very close copy of:\n- * - <OpenLayers.Control.Scale>\n+ * - <OpenLayers.Layer.Markers>\n */\n-OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {\n \n- /**\n- * Property: maxWidth\n- * {Integer} Maximum width of the scale line in pixels. Default is 100.\n+ /** \n+ * Property: location \n+ * {String} store url of text file \n */\n- maxWidth: 100,\n+ location: null,\n \n- /**\n- * Property: topOutUnits\n- * {String} Units for zoomed out on top bar. Default is km.\n+ /** \n+ * Property: features \n+ * {Array(<OpenLayers.Feature>)} \n */\n- topOutUnits: \"km\",\n+ features: null,\n \n /**\n- * Property: topInUnits\n- * {String} Units for zoomed in on top bar. Default is m.\n+ * APIProperty: formatOptions\n+ * {Object} Hash of options which should be passed to the format when it is\n+ * created. Must be passed in the constructor.\n */\n- topInUnits: \"m\",\n+ formatOptions: null,\n \n- /**\n- * Property: bottomOutUnits\n- * {String} Units for zoomed out on bottom bar. Default is mi.\n+ /** \n+ * Property: selectedFeature \n+ * {<OpenLayers.Feature>} \n */\n- bottomOutUnits: \"mi\",\n+ selectedFeature: null,\n \n- /**\n- * Property: bottomInUnits\n- * {String} Units for zoomed in on bottom bar. Default is ft.\n+ /** \n+ * APIProperty: icon \n+ * {<OpenLayers.Icon>}. This determines the Icon to be used on the map\n+ * for this GeoRSS layer.\n */\n- bottomInUnits: \"ft\",\n+ icon: null,\n \n /**\n- * Property: eTop\n- * {DOMElement}\n+ * APIProperty: popupSize\n+ * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If \n+ * not provided, defaults to 250px by 120px. \n */\n- eTop: null,\n+ popupSize: null,\n \n- /**\n- * Property: eBottom\n- * {DOMElement}\n+ /** \n+ * APIProperty: useFeedTitle \n+ * {Boolean} Set layer.name to the first <title> element in the feed. Default is true. \n */\n- eBottom: null,\n+ useFeedTitle: true,\n \n /**\n- * APIProperty: geodesic\n- * {Boolean} Use geodesic measurement. Default is false. The recommended\n- * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to\n- * true, the scale will be calculated based on the horizontal size of the\n- * pixel in the center of the map viewport.\n+ * Constructor: OpenLayers.Layer.GeoRSS\n+ * Create a GeoRSS Layer.\n+ *\n+ * Parameters:\n+ * name - {String} \n+ * location - {String} \n+ * options - {Object}\n */\n- geodesic: false,\n+ initialize: function(name, location, options) {\n+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);\n+ this.location = location;\n+ this.features = [];\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.ScaleLine\n- * Create a new scale line control.\n- * \n- * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n+ * Method: destroy \n */\n+ destroy: function() {\n+ // Warning: Layer.Markers.destroy() must be called prior to calling\n+ // clearFeatures() here, otherwise we leak memory. Indeed, if\n+ // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n+ // able to remove the marker image elements from the layer's div since\n+ // the markers will have been destroyed by clearFeatures().\n+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n+ this.clearFeatures();\n+ this.features = null;\n+ },\n \n /**\n- * Method: draw\n- * \n- * Returns:\n- * {DOMElement}\n+ * Method: loadRSS\n+ * Start the load of the RSS data. Don't do this when we first add the layer,\n+ * since we may not be visible at any point, and it would therefore be a waste.\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.eTop) {\n- // stick in the top bar\n- this.eTop = document.createElement(\"div\");\n- this.eTop.className = this.displayClass + \"Top\";\n- var theLen = this.topInUnits.length;\n- this.div.appendChild(this.eTop);\n- if ((this.topOutUnits == \"\") || (this.topInUnits == \"\")) {\n- this.eTop.style.visibility = \"hidden\";\n- } else {\n- this.eTop.style.visibility = \"visible\";\n- }\n-\n- // and the bottom bar\n- this.eBottom = document.createElement(\"div\");\n- this.eBottom.className = this.displayClass + \"Bottom\";\n- this.div.appendChild(this.eBottom);\n- if ((this.bottomOutUnits == \"\") || (this.bottomInUnits == \"\")) {\n- this.eBottom.style.visibility = \"hidden\";\n- } else {\n- this.eBottom.style.visibility = \"visible\";\n- }\n+ loadRSS: function() {\n+ if (!this.loaded) {\n+ this.events.triggerEvent(\"loadstart\");\n+ OpenLayers.Request.GET({\n+ url: this.location,\n+ success: this.parseData,\n+ scope: this\n+ });\n+ this.loaded = true;\n }\n- this.map.events.register('moveend', this, this.update);\n- this.update();\n- return this.div;\n },\n \n- /** \n- * Method: getBarLen\n- * Given a number, round it down to the nearest 1,2,5 times a power of 10.\n- * That seems a fairly useful set of number groups to use.\n+ /**\n+ * Method: moveTo\n+ * If layer is visible and RSS has not been loaded, load RSS. \n * \n * Parameters:\n- * maxLen - {float} the number we're rounding down from\n- * \n- * Returns:\n- * {Float} the rounded number (less than or equal to maxLen)\n+ * bounds - {Object} \n+ * zoomChanged - {Object} \n+ * minor - {Object} \n */\n- getBarLen: function(maxLen) {\n- // nearest power of 10 lower than maxLen\n- var digits = parseInt(Math.log(maxLen) / Math.log(10));\n- var pow10 = Math.pow(10, digits);\n-\n- // ok, find first character\n- var firstChar = parseInt(maxLen / pow10);\n-\n- // right, put it into the correct bracket\n- var barLen;\n- if (firstChar > 5) {\n- barLen = 5;\n- } else if (firstChar > 2) {\n- barLen = 2;\n- } else {\n- barLen = 1;\n+ moveTo: function(bounds, zoomChanged, minor) {\n+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n+ if (this.visibility && !this.loaded) {\n+ this.loadRSS();\n }\n-\n- // scale it up the correct power of 10\n- return barLen * pow10;\n },\n \n /**\n- * Method: update\n- * Update the size of the bars, and the labels they contain.\n+ * Method: parseData\n+ * Parse the data returned from the Events call.\n+ *\n+ * Parameters:\n+ * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n */\n- update: function() {\n- var res = this.map.getResolution();\n- if (!res) {\n- return;\n+ parseData: function(ajaxRequest) {\n+ var doc = ajaxRequest.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);\n }\n \n- var curMapUnits = this.map.getUnits();\n- var inches = OpenLayers.INCHES_PER_UNIT;\n-\n- // convert maxWidth to map units\n- var maxSizeData = this.maxWidth * res * inches[curMapUnits];\n- var geodesicRatio = 1;\n- if (this.geodesic === true) {\n- var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w ||\n- 0.000001) * this.maxWidth;\n- var maxSizeKilometers = maxSizeData / inches[\"km\"];\n- geodesicRatio = maxSizeGeodesic / maxSizeKilometers;\n- maxSizeData *= geodesicRatio;\n+ if (this.useFeedTitle) {\n+ var name = null;\n+ try {\n+ name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;\n+ } catch (e) {\n+ try {\n+ name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;\n+ } catch (e) {}\n+ }\n+ if (name) {\n+ this.setName(name);\n+ }\n }\n \n- // decide whether to use large or small scale units \n- var topUnits;\n- var bottomUnits;\n- if (maxSizeData > 100000) {\n- topUnits = this.topOutUnits;\n- bottomUnits = this.bottomOutUnits;\n- } else {\n- topUnits = this.topInUnits;\n- bottomUnits = this.bottomInUnits;\n+ var options = {};\n+\n+ OpenLayers.Util.extend(options, this.formatOptions);\n+\n+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n+ options.externalProjection = this.projection;\n+ options.internalProjection = this.map.getProjectionObject();\n }\n \n- // and to map units units\n- var topMax = maxSizeData / inches[topUnits];\n- var bottomMax = maxSizeData / inches[bottomUnits];\n+ var format = new OpenLayers.Format.GeoRSS(options);\n+ var features = format.read(doc);\n \n- // now trim this down to useful block length\n- var topRounded = this.getBarLen(topMax);\n- var bottomRounded = this.getBarLen(bottomMax);\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ var data = {};\n+ var feature = features[i];\n \n- // and back to display units\n- topMax = topRounded / inches[curMapUnits] * inches[topUnits];\n- bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];\n+ // we don't support features with no geometry in the GeoRSS\n+ // layer at this time. \n+ if (!feature.geometry) {\n+ continue;\n+ }\n \n- // and to pixel units\n- var topPx = topMax / res / geodesicRatio;\n- var bottomPx = bottomMax / res / geodesicRatio;\n+ var title = feature.attributes.title ?\n+ feature.attributes.title : \"Untitled\";\n \n- // now set the pixel widths\n- // and the values inside them\n+ var description = feature.attributes.description ?\n+ feature.attributes.description : \"No description.\";\n \n- if (this.eBottom.style.visibility == \"visible\") {\n- this.eBottom.style.width = Math.round(bottomPx) + \"px\";\n- this.eBottom.innerHTML = bottomRounded + \" \" + bottomUnits;\n+ var link = feature.attributes.link ? feature.attributes.link : \"\";\n+\n+ var location = feature.geometry.getBounds().getCenterLonLat();\n+\n+\n+ data.icon = this.icon == null ?\n+ OpenLayers.Marker.defaultIcon() :\n+ this.icon.clone();\n+\n+ data.popupSize = this.popupSize ?\n+ this.popupSize.clone() :\n+ new OpenLayers.Size(250, 120);\n+\n+ if (title || description) {\n+ // we have supplemental data, store them.\n+ data.title = title;\n+ data.description = description;\n+\n+ var contentHTML = '<div class=\"olLayerGeoRSSClose\">[x]</div>';\n+ contentHTML += '<div class=\"olLayerGeoRSSTitle\">';\n+ if (link) {\n+ contentHTML += '<a class=\"link\" href=\"' + link + '\" target=\"_blank\">';\n+ }\n+ contentHTML += title;\n+ if (link) {\n+ contentHTML += '</a>';\n+ }\n+ contentHTML += '</div>';\n+ contentHTML += '<div style=\"\" class=\"olLayerGeoRSSDescription\">';\n+ contentHTML += description;\n+ contentHTML += '</div>';\n+ data['popupContentHTML'] = contentHTML;\n+ }\n+ var feature = new OpenLayers.Feature(this, location, data);\n+ this.features.push(feature);\n+ var marker = feature.createMarker();\n+ marker.events.register('click', feature, this.markerClick);\n+ this.addMarker(marker);\n }\n+ this.events.triggerEvent(\"loadend\");\n+ },\n \n- if (this.eTop.style.visibility == \"visible\") {\n- this.eTop.style.width = Math.round(topPx) + \"px\";\n- this.eTop.innerHTML = topRounded + \" \" + topUnits;\n+ /**\n+ * Method: markerClick\n+ *\n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ markerClick: function(evt) {\n+ var sameMarkerClicked = (this == this.layer.selectedFeature);\n+ this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i]);\n+ }\n+ if (!sameMarkerClicked) {\n+ var popup = this.createPopup();\n+ OpenLayers.Event.observe(popup.div, \"click\",\n+ OpenLayers.Function.bind(function() {\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i]);\n+ }\n+ }, this)\n+ );\n+ this.layer.map.addPopup(popup);\n }\n+ OpenLayers.Event.stop(evt);\n+ },\n \n+ /**\n+ * Method: clearFeatures\n+ * Destroy all features in this layer.\n+ */\n+ clearFeatures: function() {\n+ if (this.features != null) {\n+ while (this.features.length > 0) {\n+ var feature = this.features[0];\n+ OpenLayers.Util.removeItem(this.features, feature);\n+ feature.destroy();\n+ }\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ScaleLine\"\n+ CLASS_NAME: \"OpenLayers.Layer.GeoRSS\"\n });\n-\n /* ======================================================================\n- OpenLayers/Control/TransformFeature.js\n+ OpenLayers/Layer/ArcIMS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Control/DragFeature.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Format/ArcXML.js\n+ * @requires OpenLayers/Request.js\n */\n \n /**\n- * Class: OpenLayers.Control.TransformFeature\n- * Control to transform features with a standard transformation box.\n- *\n- * Inherits From:\n- * - <OpenLayers.Control>\n+ * Class: OpenLayers.Layer.ArcIMS\n+ * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS\n+ * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS>\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforesetfeature - Triggered before a feature is set for\n- * tranformation. The feature will not be set if a listener returns\n- * false. Listeners receive a *feature* property, with the feature\n- * that will be set for transformation. Listeners are allowed to\n- * set the control's *scale*, *ratio* and *rotation* properties,\n- * which will set the initial scale, ratio and rotation of the\n- * feature, like the <setFeature> method's initialParams argument.\n- * setfeature - Triggered when a feature is set for tranformation.\n- * Listeners receive a *feature* property, with the feature that\n- * is now set for transformation.\n- * beforetransform - Triggered while dragging, before a feature is\n- * transformed. The feature will not be transformed if a listener\n- * returns false (but the box still will). Listeners receive one or\n- * more of *center*, *scale*, *ratio* and *rotation*. The *center*\n- * property is an <OpenLayers.Geometry.Point> object with the new\n- * center of the transformed feature, the others are Floats with the\n- * scale, ratio or rotation change since the last transformation.\n- * transform - Triggered while dragging, when a feature is transformed.\n- * Listeners receive an event object with one or more of *center*,\n- * scale*, *ratio* and *rotation*. The *center* property is an\n- * <OpenLayers.Geometry.Point> object with the new center of the\n- * transformed feature, the others are Floats with the scale, ratio\n- * or rotation change of the feature since the last transformation.\n- * transformcomplete - Triggered after dragging. Listeners receive\n- * an event object with the transformed *feature*.\n- */\n+OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict transformation to a limited set of geometry\n- * types, send a list of strings corresponding to the geometry class\n- * names.\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Default query string parameters.\n */\n- geometryTypes: null,\n+ DEFAULT_PARAMS: {\n+ ClientVersion: \"9.2\",\n+ ServiceName: ''\n+ },\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>}\n+ * APIProperty: featureCoordSys\n+ * {String} Code for feature coordinate system. Default is \"4326\".\n */\n- layer: null,\n+ featureCoordSys: \"4326\",\n \n /**\n- * APIProperty: preserveAspectRatio\n- * {Boolean} set to true to not change the feature's aspect ratio.\n+ * APIProperty: filterCoordSys\n+ * {String} Code for filter coordinate system. Default is \"4326\".\n */\n- preserveAspectRatio: false,\n+ filterCoordSys: \"4326\",\n \n /**\n- * APIProperty: rotate\n- * {Boolean} set to false if rotation should be disabled. Default is true.\n- * To be passed with the constructor or set when the control is not\n- * active.\n+ * APIProperty: layers\n+ * {Array} An array of objects with layer properties.\n */\n- rotate: true,\n+ layers: null,\n \n /**\n- * APIProperty: feature\n- * {<OpenLayers.Feature.Vector>} Feature currently available for\n- * transformation. Read-only, use <setFeature> to set it manually.\n+ * APIProperty: async\n+ * {Boolean} Request images asynchronously. Default is true.\n */\n- feature: null,\n+ async: true,\n \n /**\n- * APIProperty: renderIntent\n- * {String|Object} Render intent for the transformation box and\n- * handles. A symbolizer object can also be provided here.\n+ * APIProperty: name\n+ * {String} Layer name. Default is \"ArcIMS\".\n */\n- renderIntent: \"temporary\",\n+ name: \"ArcIMS\",\n \n /**\n- * APIProperty: rotationHandleSymbolizer\n- * {Object|String} Optional. A custom symbolizer for the rotation handles.\n- * A render intent can also be provided here. Defaults to\n- * (code)\n- * {\n- * stroke: false,\n- * pointRadius: 10,\n- * fillOpacity: 0,\n- * cursor: \"pointer\"\n- * }\n- * (end)\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is true.\n */\n- rotationHandleSymbolizer: null,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: box\n- * {<OpenLayers.Feature.Vector>} The transformation box rectangle.\n- * Read-only.\n+ * Constant: DEFAULT_OPTIONS\n+ * {Object} Default layers properties.\n */\n- box: null,\n+ DEFAULT_OPTIONS: {\n+ tileSize: new OpenLayers.Size(512, 512),\n+ featureCoordSys: \"4326\",\n+ filterCoordSys: \"4326\",\n+ layers: null,\n+ isBaseLayer: true,\n+ async: true,\n+ name: \"ArcIMS\"\n+ },\n \n /**\n- * APIProperty: center\n- * {<OpenLayers.Geometry.Point>} The center of the feature bounds.\n- * Read-only.\n+ * Constructor: OpenLayers.Layer.ArcIMS\n+ * Create a new ArcIMS layer object.\n+ *\n+ * Example:\n+ * (code)\n+ * var arcims = new OpenLayers.Layer.ArcIMS(\n+ * \"Global Sample\",\n+ * \"http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap\", \n+ * {\n+ * service: \"OpenLayers_Sample\", \n+ * layers: [\n+ * // layers to manipulate\n+ * {id: \"1\", visible: true}\n+ * ]\n+ * }\n+ * );\n+ * (end)\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the ArcIMS server\n+ * options - {Object} Optional object with properties to be set on the\n+ * layer.\n */\n- center: null,\n+ initialize: function(name, url, options) {\n \n- /**\n- * APIProperty: scale\n- * {Float} The scale of the feature, relative to the scale the time the\n- * feature was set. Read-only, except for *beforesetfeature*\n- * listeners.\n- */\n- scale: 1,\n+ this.tileSize = new OpenLayers.Size(512, 512);\n \n- /**\n- * APIProperty: ratio\n- * {Float} The ratio of the feature relative to the ratio the time the\n- * feature was set. Read-only, except for *beforesetfeature*\n- * listeners.\n- */\n- ratio: 1,\n+ // parameters\n+ this.params = OpenLayers.Util.applyDefaults({\n+ ServiceName: options.serviceName\n+ },\n+ this.DEFAULT_PARAMS\n+ );\n+ this.options = OpenLayers.Util.applyDefaults(\n+ options, this.DEFAULT_OPTIONS\n+ );\n \n- /**\n- * Property: rotation\n- * {Integer} the current rotation angle of the box. Read-only, except for\n- * *beforesetfeature* listeners.\n- */\n- rotation: 0,\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(\n+ this, [name, url, this.params, options]\n+ );\n \n- /**\n- * APIProperty: handles\n- * {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available\n- * for scaling/resizing. Numbered counterclockwise, starting from the\n- * southwest corner. Read-only.\n- */\n- handles: null,\n+ //layer is transparent \n+ if (this.transparent) {\n \n- /**\n- * APIProperty: rotationHandles\n- * {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently\n- * available for rotating. Numbered counterclockwise, starting from\n- * the southwest corner. Read-only.\n- */\n- rotationHandles: null,\n+ // unless explicitly set in options, make layer an overlay\n+ if (!this.isBaseLayer) {\n+ this.isBaseLayer = false;\n+ }\n \n- /**\n- * Property: dragControl\n- * {<OpenLayers.Control.DragFeature>}\n- */\n- dragControl: null,\n+ // jpegs can never be transparent, so intelligently switch the \n+ // format, depending on the browser's capabilities\n+ if (this.format == \"image/jpeg\") {\n+ this.format = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\";\n+ }\n+ }\n \n- /**\n- * APIProperty: irregular\n- * {Boolean} Make scaling/resizing work irregularly. If true then\n- * dragging a handle causes the feature to resize in the direction\n- * of movement. If false then the feature resizes symetrically\n- * about it's center.\n- */\n- irregular: false,\n+ // create an empty layer list if no layers specified in the options\n+ if (this.options.layers === null) {\n+ this.options.layers = [];\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.TransformFeature\n- * Create a new transform feature control.\n+ * Method: getURL\n+ * Return an image url this layer.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that\n- * will be transformed.\n- * options - {Object} Optional object whose properties will be set on the\n- * control.\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n+ *\n+ * Returns:\n+ * {String} A string with the map image's url.\n */\n- initialize: function(layer, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ getURL: function(bounds) {\n+ var url = \"\";\n+ bounds = this.adjustBounds(bounds);\n \n- this.layer = layer;\n+ // create an arcxml request to generate the image\n+ var axlReq = new OpenLayers.Format.ArcXML(\n+ OpenLayers.Util.extend(this.options, {\n+ requesttype: \"image\",\n+ envelope: bounds.toArray(),\n+ tileSize: this.tileSize\n+ })\n+ );\n \n- if (!this.rotationHandleSymbolizer) {\n- this.rotationHandleSymbolizer = {\n- stroke: false,\n- pointRadius: 10,\n- fillOpacity: 0,\n- cursor: \"pointer\"\n- };\n+ // create a synchronous ajax request to get an arcims image\n+ var req = new OpenLayers.Request.POST({\n+ url: this.getFullRequestString(),\n+ data: axlReq.write(),\n+ async: false\n+ });\n+\n+ // if the response exists\n+ if (req != null) {\n+ var doc = req.responseXML;\n+\n+ if (!doc || !doc.documentElement) {\n+ doc = req.responseText;\n+ }\n+\n+ // create a new arcxml format to read the response\n+ var axlResp = new OpenLayers.Format.ArcXML();\n+ var arcxml = axlResp.read(doc);\n+ url = this.getUrlOrImage(arcxml.image.output);\n }\n \n- this.createBox();\n- this.createControl();\n+ return url;\n },\n \n+\n /**\n- * APIMethod: activate\n- * Activates the control.\n+ * Method: getURLasync\n+ * Get an image url this layer asynchronously, and execute a callback\n+ * when the image url is generated.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n+ * callback - {Function} Function to call when image url is retrieved.\n+ * scope - {Object} The scope of the callback method.\n */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.dragControl.activate();\n- this.layer.addFeatures([this.box]);\n- this.rotate && this.layer.addFeatures(this.rotationHandles);\n- this.layer.addFeatures(this.handles);\n- activated = true;\n- }\n- return activated;\n+ getURLasync: function(bounds, callback, scope) {\n+ bounds = this.adjustBounds(bounds);\n+\n+ // create an arcxml request to generate the image\n+ var axlReq = new OpenLayers.Format.ArcXML(\n+ OpenLayers.Util.extend(this.options, {\n+ requesttype: \"image\",\n+ envelope: bounds.toArray(),\n+ tileSize: this.tileSize\n+ })\n+ );\n+\n+ // create an asynchronous ajax request to get an arcims image\n+ OpenLayers.Request.POST({\n+ url: this.getFullRequestString(),\n+ async: true,\n+ data: axlReq.write(),\n+ callback: function(req) {\n+ // process the response from ArcIMS, and call the callback function\n+ // to set the image URL\n+ var doc = req.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = req.responseText;\n+ }\n+\n+ // create a new arcxml format to read the response\n+ var axlResp = new OpenLayers.Format.ArcXML();\n+ var arcxml = axlResp.read(doc);\n+\n+ callback.call(scope, this.getUrlOrImage(arcxml.image.output));\n+ },\n+ scope: this\n+ });\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivates the control.\n+ * Method: getUrlOrImage\n+ * Extract a url or image from the ArcXML image output.\n+ *\n+ * Parameters:\n+ * output - {Object} The image.output property of the object returned from\n+ * the ArcXML format read method.\n+ *\n+ * Returns:\n+ * {String} A URL for an image (potentially with the data protocol).\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.layer.removeFeatures(this.handles);\n- this.rotate && this.layer.removeFeatures(this.rotationHandles);\n- this.layer.removeFeatures([this.box]);\n- this.dragControl.deactivate();\n- deactivated = true;\n+ getUrlOrImage: function(output) {\n+ var ret = \"\";\n+ if (output.url) {\n+ // If the image response output url is a string, then the image\n+ // data is not inline.\n+ ret = output.url;\n+ } else if (output.data) {\n+ // The image data is inline and base64 encoded, create a data\n+ // url for the image. This will only work for small images,\n+ // due to browser url length limits.\n+ ret = \"data:image/\" + output.type +\n+ \";base64,\" + output.data;\n }\n- return deactivated;\n+ return ret;\n },\n \n /**\n- * Method: setMap\n- * \n+ * Method: setLayerQuery\n+ * Set the query definition on this layer. Query definitions are used to\n+ * render parts of the spatial data in an image, and can be used to\n+ * filter features or layers in the ArcIMS service.\n+ *\n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * id - {String} The ArcIMS layer ID.\n+ * querydef - {Object} The query definition to apply to this layer.\n */\n- setMap: function(map) {\n- this.dragControl.setMap(map);\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ setLayerQuery: function(id, querydef) {\n+ // find the matching layer, if it exists\n+ for (var lyr = 0; lyr < this.options.layers.length; lyr++) {\n+ if (id == this.options.layers[lyr].id) {\n+ // replace this layer definition\n+ this.options.layers[lyr].query = querydef;\n+ return;\n+ }\n+ }\n+\n+ // no layer found, create a new definition\n+ this.options.layers.push({\n+ id: id,\n+ visible: true,\n+ query: querydef\n+ });\n },\n \n /**\n- * APIMethod: setFeature\n- * Place the transformation box on a feature and start transforming it.\n- * If the control is not active, it will be activated.\n- * \n+ * Method: getFeatureInfo\n+ * Get feature information from ArcIMS. Using the applied geometry, apply\n+ * the options to the query (buffer, area/envelope intersection), and\n+ * query the ArcIMS service.\n+ *\n+ * A note about accuracy:\n+ * ArcIMS interprets the accuracy attribute in feature requests to be\n+ * something like the 'modulus' operator on feature coordinates,\n+ * applied to the database geometry of the feature. It doesn't round,\n+ * so your feature coordinates may be up to (1 x accuracy) offset from\n+ * the actual feature coordinates. If the accuracy of the layer is not\n+ * specified, the accuracy will be computed to be approximately 1\n+ * feature coordinate per screen pixel.\n+ *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * initialParams - {Object} Initial values for rotation, scale or ratio.\n- * Setting a rotation value here will cause the transformation box to\n- * start rotated. Setting a scale or ratio will not affect the\n- * transormation box, but applications may use this to keep track of\n- * scale and ratio of a feature across multiple transforms.\n+ * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The\n+ * geometry to use when making the query. This should be a closed\n+ * polygon for behavior approximating a free selection.\n+ * layer - {Object} The ArcIMS layer definition. This is an anonymous object\n+ * that looks like:\n+ * (code)\n+ * {\n+ * id: \"ArcXML layer ID\", // the ArcXML layer ID\n+ * query: {\n+ * where: \"STATE = 'PA'\", // the where clause of the query\n+ * accuracy: 100 // the accuracy of the returned feature\n+ * }\n+ * }\n+ * (end)\n+ * options - {Object} Object with non-default properties to set on the layer.\n+ * Supported properties are buffer, callback, scope, and any other\n+ * properties applicable to the ArcXML format. Set the 'callback' and\n+ * 'scope' for an object and function to recieve the parsed features\n+ * from ArcIMS.\n */\n- setFeature: function(feature, initialParams) {\n- initialParams = OpenLayers.Util.applyDefaults(initialParams, {\n- rotation: 0,\n- scale: 1,\n- ratio: 1\n- });\n+ getFeatureInfo: function(geometry, layer, options) {\n+ // set the buffer to 1 unit (dd/m/ft?) by default\n+ var buffer = options.buffer || 1;\n+ // empty callback by default\n+ var callback = options.callback || function() {};\n+ // default scope is window (global)\n+ var scope = options.scope || window;\n \n- var oldRotation = this.rotation;\n- var oldCenter = this.center;\n- OpenLayers.Util.extend(this, initialParams);\n+ // apply these option to the request options\n+ var requestOptions = {};\n+ OpenLayers.Util.extend(requestOptions, this.options);\n \n- var cont = this.events.triggerEvent(\"beforesetfeature\", {\n- feature: feature\n- });\n- if (cont === false) {\n- return;\n- }\n+ // this is a feature request\n+ requestOptions.requesttype = \"feature\";\n \n- this.feature = feature;\n- this.activate();\n+ if (geometry instanceof OpenLayers.LonLat) {\n+ // create an envelope if the geometry is really a lon/lat\n+ requestOptions.polygon = null;\n+ requestOptions.envelope = [\n+ geometry.lon - buffer,\n+ geometry.lat - buffer,\n+ geometry.lon + buffer,\n+ geometry.lat + buffer\n+ ];\n+ } else if (geometry instanceof OpenLayers.Geometry.Polygon) {\n+ // use the polygon assigned, and empty the envelope\n+ requestOptions.envelope = null;\n+ requestOptions.polygon = geometry;\n+ }\n \n- this._setfeature = true;\n+ // create an arcxml request to get feature requests\n+ var arcxml = new OpenLayers.Format.ArcXML(requestOptions);\n \n- var featureBounds = this.feature.geometry.getBounds();\n- this.box.move(featureBounds.getCenterLonLat());\n- this.box.geometry.rotate(-oldRotation, oldCenter);\n- this._angle = 0;\n+ // apply any get feature options to the arcxml request\n+ OpenLayers.Util.extend(arcxml.request.get_feature, options);\n \n- var ll;\n- if (this.rotation) {\n- var geom = feature.geometry.clone();\n- geom.rotate(-this.rotation, this.center);\n- var box = new OpenLayers.Feature.Vector(\n- geom.getBounds().toGeometry());\n- box.geometry.rotate(this.rotation, this.center);\n- this.box.geometry.rotate(this.rotation, this.center);\n- this.box.move(box.geometry.getBounds().getCenterLonLat());\n- var llGeom = box.geometry.components[0].components[0];\n- ll = llGeom.getBounds().getCenterLonLat();\n+ arcxml.request.get_feature.layer = layer.id;\n+ if (typeof layer.query.accuracy == \"number\") {\n+ // set the accuracy if it was specified\n+ arcxml.request.get_feature.query.accuracy = layer.query.accuracy;\n } else {\n- ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);\n+ // guess that the accuracy is 1 per screen pixel\n+ var mapCenter = this.map.getCenter();\n+ var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);\n+ viewPx.x++;\n+ var mapOffCenter = this.map.getLonLatFromPixel(viewPx);\n+ arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;\n }\n- this.handles[0].move(ll);\n \n- delete this._setfeature;\n+ // set the get_feature query to be the same as the layer passed in\n+ arcxml.request.get_feature.query.where = layer.query.where;\n \n- this.events.triggerEvent(\"setfeature\", {\n- feature: feature\n+ // use area_intersection\n+ arcxml.request.get_feature.query.spatialfilter.relation = \"area_intersection\";\n+\n+ // create a new asynchronous request to get the feature info\n+ OpenLayers.Request.POST({\n+ url: this.getFullRequestString({\n+ 'CustomService': 'Query'\n+ }),\n+ data: arcxml.write(),\n+ callback: function(request) {\n+ // parse the arcxml response\n+ var response = arcxml.parseResponse(request.responseText);\n+\n+ if (!arcxml.iserror()) {\n+ // if the arcxml is not an error, call the callback with the features parsed\n+ callback.call(scope, response.features);\n+ } else {\n+ // if the arcxml is an error, return null features selected\n+ callback.call(scope, null);\n+ }\n+ }\n });\n },\n \n /**\n- * APIMethod: unsetFeature\n- * Remove the transformation box off any feature.\n- * If the control is active, it will be deactivated first.\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer\n */\n- unsetFeature: function() {\n- if (this.active) {\n- this.deactivate();\n- } else {\n- this.feature = null;\n- this.rotation = 0;\n- this.scale = 1;\n- this.ratio = 1;\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcIMS(this.name,\n+ this.url,\n+ this.getOptions());\n }\n- },\n \n- /**\n- * Method: createBox\n- * Creates the box with all handles and transformation handles.\n- */\n- createBox: function() {\n- var control = this;\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n- this.center = new OpenLayers.Geometry.Point(0, 0);\n- this.box = new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.LineString([\n- new OpenLayers.Geometry.Point(-1, -1),\n- new OpenLayers.Geometry.Point(0, -1),\n- new OpenLayers.Geometry.Point(1, -1),\n- new OpenLayers.Geometry.Point(1, 0),\n- new OpenLayers.Geometry.Point(1, 1),\n- new OpenLayers.Geometry.Point(0, 1),\n- new OpenLayers.Geometry.Point(-1, 1),\n- new OpenLayers.Geometry.Point(-1, 0),\n- new OpenLayers.Geometry.Point(-1, -1)\n- ]), null,\n- typeof this.renderIntent == \"string\" ? null : this.renderIntent\n- );\n+ // copy/set any non-init, non-simple values here\n \n- // Override for box move - make sure that the center gets updated\n- this.box.geometry.move = function(x, y) {\n- control._moving = true;\n- OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);\n- control.center.move(x, y);\n- delete control._moving;\n- };\n+ return obj;\n+ },\n \n- // Overrides for vertex move, resize and rotate - make sure that\n- // handle and rotationHandle geometries are also moved, resized and\n- // rotated.\n- var vertexMoveFn = function(x, y) {\n- OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.move(x, y);\n- this._handle.geometry.move(x, y);\n- };\n- var vertexResizeFn = function(scale, center, ratio) {\n- OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.resize(\n- scale, center, ratio);\n- this._handle.geometry.resize(scale, center, ratio);\n- };\n- var vertexRotateFn = function(angle, center) {\n- OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.rotate(\n- angle, center);\n- this._handle.geometry.rotate(angle, center);\n- };\n+ CLASS_NAME: \"OpenLayers.Layer.ArcIMS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/TileCache.js\n+ ====================================================================== */\n \n- // Override for handle move - make sure that the box and other handles\n- // are updated, and finally transform the feature.\n- var handleMoveFn = function(x, y) {\n- var oldX = this.x,\n- oldY = this.y;\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- if (control._moving) {\n- return;\n- }\n- var evt = control.dragControl.handlers.drag.evt;\n- var preserveAspectRatio = !control._setfeature &&\n- control.preserveAspectRatio;\n- var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);\n- var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);\n- var centerGeometry = control.center;\n- this.rotate(-control.rotation, centerGeometry);\n- oldGeom.rotate(-control.rotation, centerGeometry);\n- var dx1 = this.x - centerGeometry.x;\n- var dy1 = this.y - centerGeometry.y;\n- var dx0 = dx1 - (this.x - oldGeom.x);\n- var dy0 = dy1 - (this.y - oldGeom.y);\n- if (control.irregular && !control._setfeature) {\n- dx1 -= (this.x - oldGeom.x) / 2;\n- dy1 -= (this.y - oldGeom.y) / 2;\n- }\n- this.x = oldX;\n- this.y = oldY;\n- var scale, ratio = 1;\n- if (reshape) {\n- scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;\n- ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;\n- } else {\n- var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));\n- var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));\n- scale = l1 / l0;\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- // rotate the box to 0 before resizing - saves us some\n- // calculations and is inexpensive because we don't drawFeature.\n- control._moving = true;\n- control.box.geometry.rotate(-control.rotation, centerGeometry);\n- delete control._moving;\n \n- control.box.geometry.resize(scale, centerGeometry, ratio);\n- control.box.geometry.rotate(control.rotation, centerGeometry);\n- control.transformFeature({\n- scale: scale,\n- ratio: ratio\n- });\n- if (control.irregular && !control._setfeature) {\n- var newCenter = centerGeometry.clone();\n- newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX);\n- newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY);\n- control.box.geometry.move(this.x - oldX, this.y - oldY);\n- control.transformFeature({\n- center: newCenter\n- });\n- }\n- };\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n \n- // Override for rotation handle move - make sure that the box and\n- // other handles are updated, and finally transform the feature.\n- var rotationHandleMoveFn = function(x, y) {\n- var oldX = this.x,\n- oldY = this.y;\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- if (control._moving) {\n- return;\n- }\n- var evt = control.dragControl.handlers.drag.evt;\n- var constrain = (evt && evt.shiftKey) ? 45 : 1;\n- var centerGeometry = control.center;\n- var dx1 = this.x - centerGeometry.x;\n- var dy1 = this.y - centerGeometry.y;\n- var dx0 = dx1 - x;\n- var dy0 = dy1 - y;\n- this.x = oldX;\n- this.y = oldY;\n- var a0 = Math.atan2(dy0, dx0);\n- var a1 = Math.atan2(dy1, dx1);\n- var angle = a1 - a0;\n- angle *= 180 / Math.PI;\n- control._angle = (control._angle + angle) % 360;\n- var diff = control.rotation % constrain;\n- if (Math.abs(control._angle) >= constrain || diff !== 0) {\n- angle = Math.round(control._angle / constrain) * constrain -\n- diff;\n- control._angle = 0;\n- control.box.geometry.rotate(angle, centerGeometry);\n- control.transformFeature({\n- rotation: angle\n- });\n- }\n- };\n+/**\n+ * Class: OpenLayers.Layer.TileCache\n+ * A read only TileCache layer. Used to requests tiles cached by TileCache in\n+ * a web accessible cache. This means that you have to pre-populate your\n+ * cache before this layer can be used. It is meant only to read tiles\n+ * created by TileCache, and not to make calls to TileCache for tile\n+ * creation. Create a new instance with the\n+ * <OpenLayers.Layer.TileCache> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n- var handles = new Array(8);\n- var rotationHandles = new Array(4);\n- var geom, handle, rotationHandle;\n- var positions = [\"sw\", \"s\", \"se\", \"e\", \"ne\", \"n\", \"nw\", \"w\"];\n- for (var i = 0; i < 8; ++i) {\n- geom = this.box.geometry.components[i];\n- handle = new OpenLayers.Feature.Vector(geom.clone(), {\n- role: positions[i] + \"-resize\"\n- }, typeof this.renderIntent == \"string\" ? null :\n- this.renderIntent);\n- if (i % 2 == 0) {\n- rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {\n- role: positions[i] + \"-rotate\"\n- }, typeof this.rotationHandleSymbolizer == \"string\" ?\n- null : this.rotationHandleSymbolizer);\n- rotationHandle.geometry.move = rotationHandleMoveFn;\n- geom._rotationHandle = rotationHandle;\n- rotationHandles[i / 2] = rotationHandle;\n- }\n- geom.move = vertexMoveFn;\n- geom.resize = vertexResizeFn;\n- geom.rotate = vertexRotateFn;\n- handle.geometry.move = handleMoveFn;\n- geom._handle = handle;\n- handles[i] = handle;\n- }\n+ /** \n+ * APIProperty: isBaseLayer\n+ * {Boolean} Treat this layer as a base layer. Default is true.\n+ */\n+ isBaseLayer: true,\n \n- this.rotationHandles = rotationHandles;\n- this.handles = handles;\n- },\n+ /** \n+ * APIProperty: format\n+ * {String} Mime type of the images returned. Default is image/png.\n+ */\n+ format: 'image/png',\n \n /**\n- * Method: createControl\n- * Creates a DragFeature control for this control.\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer. (b) The map can work with resolutions\n+ * that aren't supported by the server, i.e. that aren't in\n+ * <serverResolutions>. When the map is displayed in such a resolution\n+ * data for the closest server-supported resolution is loaded and the\n+ * layer div is stretched as necessary.\n */\n- createControl: function() {\n- var control = this;\n- this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {\n- documentDrag: true,\n- // avoid moving the feature itself - move the box instead\n- moveFeature: function(pixel) {\n- if (this.feature === control.feature) {\n- this.feature = control.box;\n- }\n- OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this,\n- arguments);\n- },\n- // transform while dragging\n- onDrag: function(feature, pixel) {\n- if (feature === control.box) {\n- control.transformFeature({\n- center: control.center\n- });\n- }\n- },\n- // set a new feature\n- onStart: function(feature, pixel) {\n- var eligible = !control.geometryTypes ||\n- OpenLayers.Util.indexOf(control.geometryTypes,\n- feature.geometry.CLASS_NAME) !== -1;\n- var i = OpenLayers.Util.indexOf(control.handles, feature);\n- i += OpenLayers.Util.indexOf(control.rotationHandles,\n- feature);\n- if (feature !== control.feature && feature !== control.box &&\n- i == -2 && eligible) {\n- control.setFeature(feature);\n- }\n- },\n- onComplete: function(feature, pixel) {\n- control.events.triggerEvent(\"transformcomplete\", {\n- feature: control.feature\n- });\n- }\n- });\n- },\n+ serverResolutions: null,\n \n /**\n- * Method: drawHandles\n- * Draws the handles to match the box.\n+ * Constructor: OpenLayers.Layer.TileCache\n+ * Create a new read only TileCache layer.\n+ *\n+ * Parameters:\n+ * name - {String} Name of the layer displayed in the interface\n+ * url - {String} Location of the web accessible cache (not the location of\n+ * your tilecache script!)\n+ * layername - {String} Layer name as defined in the TileCache \n+ * configuration\n+ * options - {Object} Optional object with properties to be set on the\n+ * layer. Note that you should speficy your resolutions to match\n+ * your TileCache configuration. This can be done by setting\n+ * the resolutions array directly (here or on the map), by setting\n+ * maxResolution and numZoomLevels, or by using scale based properties.\n */\n- drawHandles: function() {\n- var layer = this.layer;\n- for (var i = 0; i < 8; ++i) {\n- if (this.rotate && i % 2 === 0) {\n- layer.drawFeature(this.rotationHandles[i / 2],\n- this.rotationHandleSymbolizer);\n- }\n- layer.drawFeature(this.handles[i], this.renderIntent);\n- }\n+ initialize: function(name, url, layername, options) {\n+ this.layername = layername;\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this,\n+ [name, url, {}, options]);\n+ this.extension = this.format.split('/')[1].toLowerCase();\n+ this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;\n },\n \n /**\n- * Method: transformFeature\n- * Transforms the feature.\n+ * APIMethod: clone\n+ * obj - {Object} \n * \n- * Parameters:\n- * mods - {Object} An object with optional scale, ratio, rotation and\n- * center properties.\n+ * Returns:\n+ * {<OpenLayers.Layer.TileCache>} An exact clone of this \n+ * <OpenLayers.Layer.TileCache>\n */\n- transformFeature: function(mods) {\n- if (!this._setfeature) {\n- this.scale *= (mods.scale || 1);\n- this.ratio *= (mods.ratio || 1);\n- var oldRotation = this.rotation;\n- this.rotation = (this.rotation + (mods.rotation || 0)) % 360;\n+ clone: function(obj) {\n \n- if (this.events.triggerEvent(\"beforetransform\", mods) !== false) {\n- var feature = this.feature;\n- var geom = feature.geometry;\n- var center = this.center;\n- geom.rotate(-oldRotation, center);\n- if (mods.scale || mods.ratio) {\n- geom.resize(mods.scale, center, mods.ratio);\n- } else if (mods.center) {\n- feature.move(mods.center.getBounds().getCenterLonLat());\n- }\n- geom.rotate(this.rotation, center);\n- this.layer.drawFeature(feature);\n- feature.toState(OpenLayers.State.UPDATE);\n- this.events.triggerEvent(\"transform\", mods);\n- }\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.TileCache(this.name,\n+ this.url,\n+ this.layername,\n+ this.getOptions());\n }\n- this.layer.drawFeature(this.box, this.renderIntent);\n- this.drawHandles();\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n },\n \n /**\n- * APIMethod: destroy\n- * Take care of things that are not handled in superclass.\n+ * Method: getURL\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} \n+ * \n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the \n+ * passed-in bounds and appropriate tile size specified as parameters.\n */\n- destroy: function() {\n- var geom;\n- for (var i = 0; i < 8; ++i) {\n- geom = this.box.geometry.components[i];\n- geom._handle.destroy();\n- geom._handle = null;\n- geom._rotationHandle && geom._rotationHandle.destroy();\n- geom._rotationHandle = null;\n+ getURL: function(bounds) {\n+ var res = this.getServerResolution();\n+ var bbox = this.maxExtent;\n+ var size = this.tileSize;\n+ var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));\n+ var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));\n+ var tileZ = this.serverResolutions != null ?\n+ OpenLayers.Util.indexOf(this.serverResolutions, res) :\n+ this.map.getZoom();\n+\n+ var components = [\n+ this.layername,\n+ OpenLayers.Number.zeroPad(tileZ, 2),\n+ OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3),\n+ OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3),\n+ OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3),\n+ OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3),\n+ OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3),\n+ OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension\n+ ];\n+ var path = components.join('/');\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url);\n }\n- this.center = null;\n- this.feature = null;\n- this.handles = null;\n- this.rotationHandleSymbolizer = null;\n- this.rotationHandles = null;\n- this.box.destroy();\n- this.box = null;\n- this.layer = null;\n- this.dragControl.destroy();\n- this.dragControl = null;\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ url = (url.charAt(url.length - 1) == '/') ? url : url + '/';\n+ return url + path;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.TransformFeature\"\n+ CLASS_NAME: \"OpenLayers.Layer.TileCache\"\n });\n /* ======================================================================\n- OpenLayers/Control/KeyboardDefaults.js\n+ OpenLayers/Layer/Zoomify.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+/*\n+ * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online\n+ * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic.\n+ */\n+\n+\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Keyboard.js\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Control.KeyboardDefaults\n- * The KeyboardDefaults control adds panning and zooming functions, controlled\n- * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page\n- * Down/Home/End scroll by three quarters of a page.\n- * \n- * This control has no visible appearance.\n+ * Class: OpenLayers.Layer.Zoomify\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * Property: size\n+ * {<OpenLayers.Size>} The Zoomify image size in pixels.\n */\n- autoActivate: true,\n+ size: null,\n \n /**\n- * APIProperty: slideFactor\n- * Pixels to slide by.\n+ * APIProperty: isBaseLayer\n+ * {Boolean}\n */\n- slideFactor: 75,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: observeElement\n- * {DOMelement|String} The DOM element to handle keys for. You\n- * can use the map div here, to have the navigation keys\n- * work when the map div has the focus. If undefined the\n- * document is used.\n+ * Property: standardTileSize\n+ * {Integer} The size of a standard (non-border) square tile in pixels.\n */\n- observeElement: null,\n+ standardTileSize: 256,\n+\n+ /** \n+ * Property: tileOriginCorner\n+ * {String} This layer uses top-left as tile origin\n+ **/\n+ tileOriginCorner: \"tl\",\n \n /**\n- * Constructor: OpenLayers.Control.KeyboardDefaults\n+ * Property: numberOfTiers\n+ * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels)\n+ * - filled during Zoomify pyramid initialization.\n */\n+ numberOfTiers: 0,\n \n /**\n- * Method: draw\n- * Create handler.\n+ * Property: tileCountUpToTier\n+ * {Array(Integer)} Number of tiles up to the given tier of pyramid.\n+ * - filled during Zoomify pyramid initialization.\n */\n- draw: function() {\n- var observeElement = this.observeElement || document;\n- this.handler = new OpenLayers.Handler.Keyboard(this, {\n- \"keydown\": this.defaultKeyPress\n- }, {\n- observeElement: observeElement\n- });\n+ tileCountUpToTier: null,\n+\n+ /**\n+ * Property: tierSizeInTiles\n+ * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid.\n+ * - filled during Zoomify pyramid initialization.\n+ */\n+ tierSizeInTiles: null,\n+\n+ /**\n+ * Property: tierImageSize\n+ * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier.\n+ * - filled during Zoomify pyramid initialization.\n+ */\n+ tierImageSize: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Zoomify\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer.\n+ * url - {String} - Relative or absolute path to the image or more\n+ * precisly to the TileGroup[X] directories root.\n+ * Flash plugin use the variable name \"zoomifyImagePath\" for this.\n+ * size - {<OpenLayers.Size>} The size (in pixels) of the image.\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n+ */\n+ initialize: function(name, url, size, options) {\n+\n+ // initilize the Zoomify pyramid for given size\n+ this.initializeZoomify(size);\n+\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n+ name, url, size, {},\n+ options\n+ ]);\n },\n \n /**\n- * Method: defaultKeyPress\n- * When handling the key event, we only use evt.keyCode. This holds \n- * some drawbacks, though we get around them below. When interpretting\n- * the keycodes below (including the comments associated with them),\n- * consult the URL below. For instance, the Safari browser returns\n- * \"IE keycodes\", and so is supported by any keycode labeled \"IE\".\n- * \n- * Very informative URL:\n- * http://unixpapa.com/js/key.html\n+ * Method: initializeZoomify\n+ * It generates constants for all tiers of the Zoomify pyramid\n *\n * Parameters:\n- * evt - {Event} \n+ * size - {<OpenLayers.Size>} The size of the image in pixels\n+ *\n */\n- defaultKeyPress: function(evt) {\n- var size, handled = true;\n+ initializeZoomify: function(size) {\n \n- var target = OpenLayers.Event.element(evt);\n- if (target &&\n- (target.tagName == 'INPUT' ||\n- target.tagName == 'TEXTAREA' ||\n- target.tagName == 'SELECT')) {\n- return;\n+ var imageSize = size.clone();\n+ this.size = size.clone();\n+ var tiles = new OpenLayers.Size(\n+ Math.ceil(imageSize.w / this.standardTileSize),\n+ Math.ceil(imageSize.h / this.standardTileSize)\n+ );\n+\n+ this.tierSizeInTiles = [tiles];\n+ this.tierImageSize = [imageSize];\n+\n+ while (imageSize.w > this.standardTileSize ||\n+ imageSize.h > this.standardTileSize) {\n+\n+ imageSize = new OpenLayers.Size(\n+ Math.floor(imageSize.w / 2),\n+ Math.floor(imageSize.h / 2)\n+ );\n+ tiles = new OpenLayers.Size(\n+ Math.ceil(imageSize.w / this.standardTileSize),\n+ Math.ceil(imageSize.h / this.standardTileSize)\n+ );\n+ this.tierSizeInTiles.push(tiles);\n+ this.tierImageSize.push(imageSize);\n }\n \n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_LEFT:\n- this.map.pan(-this.slideFactor, 0);\n- break;\n- case OpenLayers.Event.KEY_RIGHT:\n- this.map.pan(this.slideFactor, 0);\n- break;\n- case OpenLayers.Event.KEY_UP:\n- this.map.pan(0, -this.slideFactor);\n- break;\n- case OpenLayers.Event.KEY_DOWN:\n- this.map.pan(0, this.slideFactor);\n- break;\n+ this.tierSizeInTiles.reverse();\n+ this.tierImageSize.reverse();\n \n- case 33: // Page Up. Same in all browsers.\n- size = this.map.getSize();\n- this.map.pan(0, -0.75 * size.h);\n- break;\n- case 34: // Page Down. Same in all browsers.\n- size = this.map.getSize();\n- this.map.pan(0, 0.75 * size.h);\n- break;\n- case 35: // End. Same in all browsers.\n- size = this.map.getSize();\n- this.map.pan(0.75 * size.w, 0);\n- break;\n- case 36: // Home. Same in all browsers.\n- size = this.map.getSize();\n- this.map.pan(-0.75 * size.w, 0);\n- break;\n+ this.numberOfTiers = this.tierSizeInTiles.length;\n+ var resolutions = [1];\n+ this.tileCountUpToTier = [0];\n+ for (var i = 1; i < this.numberOfTiers; i++) {\n+ resolutions.unshift(Math.pow(2, i));\n+ this.tileCountUpToTier.push(\n+ this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h +\n+ this.tileCountUpToTier[i - 1]\n+ );\n+ }\n+ if (!this.serverResolutions) {\n+ this.serverResolutions = resolutions;\n+ }\n+ },\n \n- case 43: // +/= (ASCII), keypad + (ASCII, Opera)\n- case 61: // +/= (Mozilla, Opera, some ASCII)\n- case 187: // +/= (IE)\n- case 107: // keypad + (IE, Mozilla)\n- this.map.zoomIn();\n- break;\n- case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera)\n- case 109: // -/_ (Mozilla), keypad - (Mozilla, IE)\n- case 189: // -/_ (IE)\n- case 95: // -/_ (some ASCII)\n- this.map.zoomOut();\n- break;\n- default:\n- handled = false;\n+ /**\n+ * APIMethod:destroy\n+ */\n+ destroy: function() {\n+ // for now, nothing special to do here.\n+ OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);\n+\n+ // Remove from memory the Zoomify pyramid - is that enough?\n+ this.tileCountUpToTier.length = 0;\n+ this.tierSizeInTiles.length = 0;\n+ this.tierImageSize.length = 0;\n+\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ *\n+ * Parameters:\n+ * obj - {Object}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify>\n+ */\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Zoomify(this.name,\n+ this.url,\n+ this.size,\n+ this.options);\n }\n- if (handled) {\n- // prevent browser default not to move the page\n- // when moving the page with the keyboard\n- OpenLayers.Event.stop(evt);\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n+ },\n+\n+ /**\n+ * Method: getURL\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as\n+ * parameters\n+ */\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getZoomForResolution(res);\n+\n+ var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];\n+ var path = \"TileGroup\" + Math.floor((tileIndex) / 256) +\n+ \"/\" + z + \"-\" + x + \"-\" + y + \".jpg\";\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url);\n }\n+ return url + path;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.KeyboardDefaults\"\n+ /**\n+ * Method: getImageSize\n+ * getImageSize returns size for a particular tile. If bounds are given as\n+ * first argument, size is calculated (bottom-right tiles are non square).\n+ *\n+ */\n+ getImageSize: function() {\n+ if (arguments.length > 0) {\n+ var bounds = this.adjustBounds(arguments[0]);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getZoomForResolution(res);\n+ var w = this.standardTileSize;\n+ var h = this.standardTileSize;\n+ if (x == this.tierSizeInTiles[z].w - 1) {\n+ var w = this.tierImageSize[z].w % this.standardTileSize;\n+ }\n+ if (y == this.tierSizeInTiles[z].h - 1) {\n+ var h = this.tierImageSize[z].h % this.standardTileSize;\n+ }\n+ return (new OpenLayers.Size(w, h));\n+ } else {\n+ return this.tileSize;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: setMap\n+ * When the layer is added to a map, then we can fetch our origin\n+ * (if we don't have one.)\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n+ this.map.maxExtent.top);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Zoomify\"\n });\n /* ======================================================================\n- OpenLayers/Control/UTFGrid.js\n+ OpenLayers/Layer/Bing.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Hover.js\n- * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Layer/XYZ.js\n */\n \n-/**\n- * Class: OpenLayers.Control.UTFGrid\n- *\n- * This Control provides behavior associated with UTFGrid Layers.\n- * These 'hit grids' provide underlying feature attributes without\n- * calling the server (again). This control allows Mousemove, Hovering \n- * and Click events to trigger callbacks that use the attributes in \n- * whatever way you need. \n- *\n- * The most common example may be a UTFGrid layer containing feature\n- * attributes that are displayed in a div as you mouseover.\n- *\n- * Example Code:\n- *\n- * (start code)\n- * var world_utfgrid = new OpenLayers.Layer.UTFGrid( \n- * 'UTFGrid Layer', \n- * \"http://tiles/world_utfgrid/${z}/${x}/${y}.json\"\n- * );\n- * map.addLayer(world_utfgrid);\n+/** \n+ * Class: OpenLayers.Layer.Bing\n+ * Bing layer using direct tile access as provided by Bing Maps REST Services.\n+ * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n+ * information. Note: Terms of Service compliant use requires the map to be\n+ * configured with an <OpenLayers.Control.Attribution> control and the\n+ * attribution placed on or near the map.\n * \n- * var control = new OpenLayers.Control.UTFGrid({\n- * layers: [world_utfgrid],\n- * handlerMode: 'move',\n- * callback: function(infoLookup) {\n- * // do something with returned data\n- *\n- * }\n- * })\n- * (end code)\n- *\n- *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.XYZ>\n */\n-OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * Property: key\n+ * {String} API key for Bing maps, get your own key \n+ * at http://bingmapsportal.com/ .\n */\n- autoActivate: true,\n+ key: null,\n \n- /** \n- * APIProperty: Layers\n- * List of layers to consider. Must be Layer.UTFGrids\n- * `null` is the default indicating all UTFGrid Layers are queried.\n- * {Array} <OpenLayers.Layer.UTFGrid> \n+ /**\n+ * Property: serverResolutions\n+ * {Array} the resolutions provided by the Bing servers.\n */\n- layers: null,\n+ serverResolutions: [\n+ 156543.03390625, 78271.516953125, 39135.7584765625,\n+ 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n+ 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n+ 305.74811309814453, 152.87405654907226, 76.43702827453613,\n+ 38.218514137268066, 19.109257068634033, 9.554628534317017,\n+ 4.777314267158508, 2.388657133579254, 1.194328566789627,\n+ 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n+ 0.07464553542435169\n+ ],\n \n- /* Property: defaultHandlerOptions\n- * The default opts passed to the handler constructors\n+ /**\n+ * Property: attributionTemplate\n+ * {String}\n */\n- defaultHandlerOptions: {\n- 'delay': 300,\n- 'pixelTolerance': 4,\n- 'stopMove': false,\n- 'single': true,\n- 'double': false,\n- 'stopSingle': false,\n- 'stopDouble': false\n- },\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n+ '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n+ '<img src=\"${logo}\" /></a></div>${copyrights}' +\n+ '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n+ 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n+ 'Terms of Use</a></span>',\n \n- /* APIProperty: handlerMode\n- * Defaults to 'click'. Can be 'hover' or 'move'.\n+ /**\n+ * Property: metadata\n+ * {Object} Metadata for this layer, as returned by the callback script\n */\n- handlerMode: 'click',\n+ metadata: null,\n \n /**\n- * APIMethod: setHandler\n- * sets this.handlerMode and calls resetHandler()\n- *\n- * Parameters:\n- * hm - {String} Handler Mode string; 'click', 'hover' or 'move'.\n+ * Property: protocolRegex\n+ * {RegExp} Regular expression to match and replace http: in bing urls\n */\n- setHandler: function(hm) {\n- this.handlerMode = hm;\n- this.resetHandler();\n- },\n+ protocolRegex: /^http:/i,\n \n /**\n- * Method: resetHandler\n- * Deactivates the old hanlder and creates a new\n- * <OpenLayers.Handler> based on the mode specified in\n- * this.handlerMode\n+ * APIProperty: type\n+ * {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used. Default is \"Road\".\n+ */\n+ type: \"Road\",\n+\n+ /**\n+ * APIProperty: culture\n+ * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n+ * for the definition and the possible values. Default is \"en-US\".\n+ */\n+ culture: \"en-US\",\n+\n+ /**\n+ * APIProperty: metadataParams\n+ * {Object} Optional url parameters for the Get Imagery Metadata request\n+ * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n+ */\n+ metadataParams: null,\n+\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n *\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n */\n- resetHandler: function() {\n- if (this.handler) {\n- this.handler.deactivate();\n- this.handler.destroy();\n- this.handler = null;\n- }\n+ tileOptions: null,\n \n- if (this.handlerMode == 'hover') {\n- // Handle this event on hover\n- this.handler = new OpenLayers.Handler.Hover(\n- this, {\n- 'pause': this.handleEvent,\n- 'move': this.reset\n- },\n- this.handlerOptions\n- );\n- } else if (this.handlerMode == 'click') {\n- // Handle this event on click\n- this.handler = new OpenLayers.Handler.Click(\n- this, {\n- 'click': this.handleEvent\n- }, this.handlerOptions\n- );\n- } else if (this.handlerMode == 'move') {\n- this.handler = new OpenLayers.Handler.Hover(\n- this,\n- // Handle this event while hovering OR moving\n- {\n- 'pause': this.handleEvent,\n- 'move': this.handleEvent\n- },\n- this.handlerOptions\n- );\n- }\n- if (this.handler) {\n- return true;\n- } else {\n- return false;\n- }\n- },\n+ /** APIProperty: protocol\n+ * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n+ * Can be 'http:' 'https:' or ''\n+ *\n+ * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n+ * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n+ * this is undocumented and the Imagery Metadata API always returns HTTP\n+ * urls.\n+ *\n+ * Default is '', unless when executed from a file:/// uri, in which case\n+ * it is 'http:'.\n+ */\n+ protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n \n /**\n- * Constructor: <OpenLayers.Control.UTFGrid>\n+ * Constructor: OpenLayers.Layer.Bing\n+ * Create a new Bing layer.\n+ *\n+ * Example:\n+ * (code)\n+ * var road = new OpenLayers.Layer.Bing({\n+ * name: \"My Bing Aerial Layer\",\n+ * type: \"Aerial\",\n+ * key: \"my-api-key-here\",\n+ * });\n+ * (end)\n *\n * Parameters:\n- * options - {Object} \n+ * options - {Object} Configuration properties for the layer.\n+ *\n+ * Required configuration properties:\n+ * key - {String} Bing Maps API key for your application. Get one at\n+ * http://bingmapsportal.com/.\n+ * type - {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used.\n+ *\n+ * Any other documented layer properties can be provided in the config object.\n */\n initialize: function(options) {\n- options = options || {};\n- options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.resetHandler();\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options.tileOptions);\n+ this.loadMetadata();\n },\n \n /**\n- * Method: handleEvent\n- * Internal method called when specified event is triggered.\n- * \n- * This method does several things:\n- *\n- * Gets the lonLat of the event.\n- *\n- * Loops through the appropriate hit grid layers and gathers the attributes.\n+ * Method: loadMetadata\n+ */\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ // link the processMetadata method to the global scope and bind it\n+ // to this instance\n+ window[this._callbackId] = OpenLayers.Function.bind(\n+ OpenLayers.Layer.Bing.processMetadata, this\n+ );\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n+ this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script);\n+ },\n+\n+ /**\n+ * Method: initLayer\n *\n- * Passes the attributes to the callback\n+ * Sets layer properties according to the metadata provided by the API\n+ */\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n+ }\n+ this.addOptions({\n+ maxResolution: Math.min(\n+ this.serverResolutions[res.zoomMin],\n+ this.maxResolution || Number.POSITIVE_INFINITY\n+ ),\n+ numZoomLevels: Math.min(\n+ res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n+ )\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw();\n+ }\n+ this.updateAttribution();\n+ },\n+\n+ /**\n+ * Method: getURL\n *\n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n+ * Paramters:\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- handleEvent: function(evt) {\n- if (evt == null) {\n- this.reset();\n+ getURL: function(bounds) {\n+ if (!this.url) {\n return;\n }\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = '0';\n+ var mask = 1 << (i - 1);\n+ if ((x & mask) != 0) {\n+ digit++;\n+ }\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++;\n+ }\n+ quadDigits.push(digit);\n+ }\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl('' + x + y + z, this.url);\n \n- var lonLat = this.map.getLonLatFromPixel(evt.xy);\n- if (!lonLat) {\n+ return OpenLayers.String.format(url, {\n+ 'quadkey': quadKey\n+ });\n+ },\n+\n+ /**\n+ * Method: updateAttribution\n+ * Updates the attribution according to the requirements outlined in\n+ * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n+ */\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n return;\n }\n-\n- var layers = this.findLayers();\n- if (layers.length > 0) {\n- var infoLookup = {};\n- var layer, idx;\n- for (var i = 0, len = layers.length; i < len; i++) {\n- layer = layers[i];\n- idx = OpenLayers.Util.indexOf(this.map.layers, layer);\n- infoLookup[idx] = layer.getFeatureInfo(lonLat);\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(\n+ this.map.getProjectionObject(),\n+ new OpenLayers.Projection(\"EPSG:4326\")\n+ );\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n+ this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ // axis order provided is Y,X\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) &&\n+ zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \";\n+ }\n }\n- this.callback(infoLookup, lonLat, evt.xy);\n }\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ });\n },\n \n /**\n- * APIMethod: callback\n- * Function to be called when a mouse event corresponds with a location that\n- * includes data in one of the configured UTFGrid layers.\n- *\n+ * Method: setMap\n+ */\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution);\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * \n * Parameters:\n- * infoLookup - {Object} Keys of this object are layer indexes and can be\n- * used to resolve a layer in the map.layers array. The structure of\n- * the property values depend on the data included in the underlying\n- * UTFGrid and may be any valid JSON type. \n+ * obj - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n */\n- callback: function(infoLookup) {\n- // to be provided in the constructor\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options);\n+ }\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ // copy/set any non-init, non-simple values here\n+ return obj;\n },\n \n /**\n- * Method: reset\n- * Calls the callback with null.\n+ * Method: destroy\n */\n- reset: function(evt) {\n- this.callback(null);\n+ destroy: function() {\n+ this.map &&\n+ this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+});\n+\n+/**\n+ * Function: OpenLayers.Layer.Bing.processMetadata\n+ * This function will be bound to an instance, linked to the global scope with\n+ * an id, and called by the JSONP script returned by the API.\n+ *\n+ * Parameters:\n+ * metadata - {Object} metadata as returned by the API\n+ */\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined; // cannot delete from window in IE\n+ delete this._callbackId;\n+};\n+/* ======================================================================\n+ OpenLayers/Layer/WorldWind.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.WorldWind\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n+ DEFAULT_PARAMS: {},\n+\n /**\n- * Method: findLayers\n- * Internal method to get the layers, independent of whether we are\n- * inspecting the map or using a client-provided array\n- *\n- * The default value of this.layers is null; this causes the \n- * findLayers method to return ALL UTFGrid layers encountered.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} WorldWind layer is a base layer by default.\n+ */\n+ isBaseLayer: true,\n+\n+ /** \n+ * APIProperty: lzd\n+ * {Float} LevelZeroTileSizeDegrees\n+ */\n+ lzd: null,\n+\n+ /**\n+ * APIProperty: zoomLevels\n+ * {Integer} Number of zoom levels.\n+ */\n+ zoomLevels: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.WorldWind\n+ * \n+ * Parameters:\n+ * name - {String} Name of Layer\n+ * url - {String} Base URL \n+ * lzd - {Float} Level zero tile size degrees \n+ * zoomLevels - {Integer} number of zoom levels\n+ * params - {Object} additional parameters\n+ * options - {Object} additional options\n+ */\n+ initialize: function(name, url, lzd, zoomLevels, params, options) {\n+ this.lzd = lzd;\n+ this.zoomLevels = zoomLevels;\n+ var newArguments = [];\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ this.params = OpenLayers.Util.applyDefaults(\n+ this.params, this.DEFAULT_PARAMS\n+ );\n+ },\n+\n+ /**\n+ * Method: getZoom\n+ * Convert map zoom to WW zoom.\n+ */\n+ getZoom: function() {\n+ var zoom = this.map.getZoom();\n+ var extent = this.map.getMaxExtent();\n+ zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);\n+ return zoom;\n+ },\n+\n+ /**\n+ * Method: getURL\n *\n * Parameters:\n- * None\n+ * bounds - {<OpenLayers.Bounds>} \n *\n * Returns:\n- * {Array} Layers to handle on each event\n+ * {String} A string with the layer's url and parameters and also the \n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters\n */\n- findLayers: function() {\n- var candidates = this.layers || this.map.layers;\n- var layers = [];\n- var layer;\n- for (var i = candidates.length - 1; i >= 0; --i) {\n- layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.UTFGrid) {\n- layers.push(layer);\n- }\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var zoom = this.getZoom();\n+ var extent = this.map.getMaxExtent();\n+ var deg = this.lzd / Math.pow(2, this.getZoom());\n+ var x = Math.floor((bounds.left - extent.left) / deg);\n+ var y = Math.floor((bounds.bottom - extent.bottom) / deg);\n+ if (this.map.getResolution() <= (this.lzd / 512) &&\n+ this.getZoom() <= this.zoomLevels) {\n+ return this.getFullRequestString({\n+ L: zoom,\n+ X: x,\n+ Y: y\n+ });\n+ } else {\n+ return OpenLayers.Util.getImageLocation(\"blank.gif\");\n }\n- return layers;\n+\n },\n \n- CLASS_NAME: \"OpenLayers.Control.UTFGrid\"\n+ CLASS_NAME: \"OpenLayers.Layer.WorldWind\"\n });\n /* ======================================================================\n- OpenLayers/Control/MousePosition.js\n+ OpenLayers/Layer/EventPane.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Control.MousePosition\n- * The MousePosition control displays geographic coordinates of the mouse\n- * pointer, as it is moved about the map.\n- *\n- * You can use the <prefix>- or <suffix>-properties to provide more information\n- * about the displayed coordinates to the user:\n+ * Class: OpenLayers.Layer.EventPane\n+ * Base class for 3rd party layers, providing a DOM element which isolates\n+ * the 3rd-party layer from mouse events.\n+ * Only used by Google layers.\n *\n- * (code)\n- * var mousePositionCtrl = new OpenLayers.Control.MousePosition({\n- * prefix: '<a target=\"_blank\" ' +\n- * 'href=\"http://spatialreference.org/ref/epsg/4326/\">' +\n- * 'EPSG:4326</a> coordinates: '\n- * }\n- * );\n- * (end code)\n+ * Automatically instantiated by the Google constructor, and not usually instantiated directly.\n *\n+ * Create a new event pane layer with the\n+ * <OpenLayers.Layer.EventPane> constructor.\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer>\n */\n-OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * APIProperty: smoothDragPan\n+ * {Boolean} smoothDragPan determines whether non-public/internal API\n+ * methods are used for better performance while dragging EventPane \n+ * layers. When not in sphericalMercator mode, the smoother dragging \n+ * doesn't actually move north/south directly with the number of \n+ * pixels moved, resulting in a slight offset when you drag your mouse \n+ * north south with this option on. If this visual disparity bothers \n+ * you, you should turn this option off, or use spherical mercator. \n+ * Default is on.\n */\n- autoActivate: true,\n+ smoothDragPan: true,\n \n /**\n- * Property: element\n- * {DOMElement}\n+ * Property: isBaseLayer\n+ * {Boolean} EventPaned layers are always base layers, by necessity.\n */\n- element: null,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: prefix\n- * {String} A string to be prepended to the current pointers coordinates\n- * when it is rendered. Defaults to the empty string ''.\n+ * APIProperty: isFixed\n+ * {Boolean} EventPaned layers are fixed by default.\n */\n- prefix: '',\n+ isFixed: true,\n \n /**\n- * APIProperty: separator\n- * {String} A string to be used to seperate the two coordinates from each\n- * other. Defaults to the string ', ', which will result in a\n- * rendered coordinate of e.g. '42.12, 21.22'.\n+ * Property: pane\n+ * {DOMElement} A reference to the element that controls the events.\n */\n- separator: ', ',\n+ pane: null,\n+\n \n /**\n- * APIProperty: suffix\n- * {String} A string to be appended to the current pointers coordinates\n- * when it is rendered. Defaults to the empty string ''.\n+ * Property: mapObject\n+ * {Object} This is the object which will be used to load the 3rd party library\n+ * in the case of the google layer, this will be of type GMap, \n+ * in the case of the ve layer, this will be of type VEMap\n */\n- suffix: '',\n+ mapObject: null,\n+\n \n /**\n- * APIProperty: numDigits\n- * {Integer} The number of digits each coordinate shall have when being\n- * rendered, Defaults to 5.\n+ * Constructor: OpenLayers.Layer.EventPane\n+ * Create a new event pane layer\n+ *\n+ * Parameters:\n+ * name - {String}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- numDigits: 5,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (this.pane == null) {\n+ this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\");\n+ }\n+ },\n \n /**\n- * APIProperty: granularity\n- * {Integer}\n+ * APIMethod: destroy\n+ * Deconstruct this layer.\n */\n- granularity: 10,\n+ destroy: function() {\n+ this.mapObject = null;\n+ this.pane = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ },\n+\n \n /**\n- * APIProperty: emptyString\n- * {String} Set this to some value to set when the mouse is outside the\n- * map.\n+ * Method: setMap\n+ * Set the map property for the layer. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- emptyString: null,\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n+ this.pane.style.display = this.div.style.display;\n+ this.pane.style.width = \"100%\";\n+ this.pane.style.height = \"100%\";\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.pane.style.background =\n+ \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\";\n+ }\n+\n+ if (this.isFixed) {\n+ this.map.viewPortDiv.appendChild(this.pane);\n+ } else {\n+ this.map.layerContainerDiv.appendChild(this.pane);\n+ }\n+\n+ // once our layer has been added to the map, we can load it\n+ this.loadMapObject();\n+\n+ // if map didn't load, display warning\n+ if (this.mapObject == null) {\n+ this.loadWarningMessage();\n+ }\n+ },\n \n /**\n- * Property: lastXy\n- * {<OpenLayers.Pixel>}\n+ * APIMethod: removeMap\n+ * On being removed from the map, we'll like to remove the invisible 'pane'\n+ * div that we added to it on creation. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- lastXy: null,\n+ removeMap: function(map) {\n+ if (this.pane && this.pane.parentNode) {\n+ this.pane.parentNode.removeChild(this.pane);\n+ }\n+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments);\n+ },\n \n /**\n- * APIProperty: displayProjection\n- * {<OpenLayers.Projection>} The projection in which the mouse position is\n- * displayed.\n+ * Method: loadWarningMessage\n+ * If we can't load the map lib, then display an error message to the \n+ * user and tell them where to go for help.\n+ * \n+ * This function sets up the layout for the warning message. Each 3rd\n+ * party layer must implement its own getWarningHTML() function to \n+ * provide the actual warning message.\n */\n- displayProjection: null,\n+ loadWarningMessage: function() {\n+\n+ this.div.style.backgroundColor = \"darkblue\";\n+\n+ var viewSize = this.map.getSize();\n+\n+ var msgW = Math.min(viewSize.w, 300);\n+ var msgH = Math.min(viewSize.h, 200);\n+ var size = new OpenLayers.Size(msgW, msgH);\n+\n+ var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n+\n+ var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n+\n+ var div = OpenLayers.Util.createDiv(this.name + \"_warning\",\n+ topLeft,\n+ size,\n+ null,\n+ null,\n+ null,\n+ \"auto\");\n+\n+ div.style.padding = \"7px\";\n+ div.style.backgroundColor = \"yellow\";\n+\n+ div.innerHTML = this.getWarningHTML();\n+ this.div.appendChild(div);\n+ },\n+\n+ /** \n+ * Method: getWarningHTML\n+ * To be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {String} String with information on why layer is broken, how to get\n+ * it working.\n+ */\n+ getWarningHTML: function() {\n+ //should be implemented by subclasses\n+ return \"\";\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.MousePosition\n+ * Method: display\n+ * Set the display on the pane\n *\n * Parameters:\n- * options - {Object} Options for control.\n+ * display - {Boolean}\n */\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ this.pane.style.display = this.div.style.display;\n+ },\n \n /**\n- * Method: destroy\n+ * Method: setZIndex\n+ * Set the z-index order for the pane.\n+ * \n+ * Parameters:\n+ * zIndex - {int}\n */\n- destroy: function() {\n- this.deactivate();\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ setZIndex: function(zIndex) {\n+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n },\n \n /**\n- * APIMethod: activate\n+ * Method: moveByPx\n+ * Move the layer based on pixel vector. To be implemented by subclasses.\n+ *\n+ * Parameters:\n+ * dx - {Number} The x coord of the displacement vector.\n+ * dy - {Number} The y coord of the displacement vector.\n */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.map.events.register('mousemove', this, this.redraw);\n- this.map.events.register('mouseout', this, this.reset);\n- this.redraw();\n- return true;\n+ moveByPx: function(dx, dy) {\n+ OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n+\n+ if (this.dragPanMapObject) {\n+ this.dragPanMapObject(dx, -dy);\n } else {\n- return false;\n+ this.moveTo(this.map.getCachedCenter());\n }\n },\n \n /**\n- * APIMethod: deactivate\n+ * Method: moveTo\n+ * Handle calls to move the layer.\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.map.events.unregister('mousemove', this, this.redraw);\n- this.map.events.unregister('mouseout', this, this.reset);\n- this.element.innerHTML = \"\";\n- return true;\n- } else {\n- return false;\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+\n+ if (this.mapObject != null) {\n+\n+ var newCenter = this.map.getCenter();\n+ var newZoom = this.map.getZoom();\n+\n+ if (newCenter != null) {\n+\n+ var moOldCenter = this.getMapObjectCenter();\n+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n+\n+ var moOldZoom = this.getMapObjectZoom();\n+ var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n+\n+ if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {\n+\n+ if (!zoomChanged && oldCenter && this.dragPanMapObject &&\n+ this.smoothDragPan) {\n+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n+ this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y);\n+ } else {\n+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n+ this.setMapObjectCenter(center, zoom, dragging);\n+ }\n+ }\n+ }\n }\n },\n \n+\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /********************************************************/\n+\n /**\n- * Method: draw\n- * {DOMElement}\n+ * Method: getLonLatFromViewPortPx\n+ * Get a map location from a pixel location\n+ * \n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n+ * port OpenLayers.Pixel, translated into lon/lat by map lib\n+ * If the map lib is not loaded or not centered, returns null\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n-\n- if (!this.element) {\n- this.div.left = \"\";\n- this.div.top = \"\";\n- this.element = this.div;\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if ((this.mapObject != null) &&\n+ (this.getMapObjectCenter() != null)) {\n+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);\n }\n-\n- return this.div;\n+ return lonlat;\n },\n \n+\n /**\n- * Method: redraw\n+ * Method: getViewPortPxFromLonLat\n+ * Get a pixel location from a map location\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n+ * OpenLayers.LonLat, translated into view port pixels by map lib\n+ * If map lib is not loaded or not centered, returns null\n */\n- redraw: function(evt) {\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var viewPortPx = null;\n+ if ((this.mapObject != null) &&\n+ (this.getMapObjectCenter() != null)) {\n \n- var lonLat;\n+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n \n- if (evt == null) {\n- this.reset();\n- return;\n- } else {\n- if (this.lastXy == null ||\n- Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||\n- Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) {\n- this.lastXy = evt.xy;\n- return;\n- }\n+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);\n+ }\n+ return viewPortPx;\n+ },\n \n- lonLat = this.map.getLonLatFromPixel(evt.xy);\n- if (!lonLat) {\n- // map has not yet been properly initialized\n- return;\n- }\n- if (this.displayProjection) {\n- lonLat.transform(this.map.getProjectionObject(),\n- this.displayProjection);\n- }\n- this.lastXy = evt.xy;\n+ /********************************************************/\n+ /* */\n+ /* Translation Functions */\n+ /* */\n+ /* The following functions translate Map Object and */\n+ /* OL formats for Pixel, LonLat */\n+ /* */\n+ /********************************************************/\n \n- }\n+ //\n+ // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat\n+ //\n \n- var newHtml = this.formatOutput(lonLat);\n+ /**\n+ * Method: getOLLonLatFromMapObjectLonLat\n+ * Get an OL style map location from a 3rd party style map location\n+ *\n+ * Parameters\n+ * moLonLat - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in \n+ * MapObject LonLat\n+ * Returns null if null value is passed in\n+ */\n+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n+ var olLonLat = null;\n+ if (moLonLat != null) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ olLonLat = new OpenLayers.LonLat(lon, lat);\n+ }\n+ return olLonLat;\n+ },\n \n- if (newHtml != this.element.innerHTML) {\n- this.element.innerHTML = newHtml;\n+ /**\n+ * Method: getMapObjectLonLatFromOLLonLat\n+ * Get a 3rd party map location from an OL map location.\n+ *\n+ * Parameters:\n+ * olLonLat - {<OpenLayers.LonLat>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject LonLat, translated from the passed in \n+ * OpenLayers.LonLat\n+ * Returns null if null value is passed in\n+ */\n+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n+ var moLatLng = null;\n+ if (olLonLat != null) {\n+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,\n+ olLonLat.lat);\n }\n+ return moLatLng;\n },\n \n+\n+ //\n+ // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel\n+ //\n+\n /**\n- * Method: reset\n+ * Method: getOLPixelFromMapObjectPixel\n+ * Get an OL pixel location from a 3rd party pixel location.\n+ *\n+ * Parameters:\n+ * moPixel - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in \n+ * MapObject Pixel\n+ * Returns null if null value is passed in\n */\n- reset: function(evt) {\n- if (this.emptyString != null) {\n- this.element.innerHTML = this.emptyString;\n+ getOLPixelFromMapObjectPixel: function(moPixel) {\n+ var olPixel = null;\n+ if (moPixel != null) {\n+ var x = this.getXFromMapObjectPixel(moPixel);\n+ var y = this.getYFromMapObjectPixel(moPixel);\n+ olPixel = new OpenLayers.Pixel(x, y);\n }\n+ return olPixel;\n },\n \n /**\n- * Method: formatOutput\n- * Override to provide custom display output\n+ * Method: getMapObjectPixelFromOLPixel\n+ * Get a 3rd party pixel location from an OL pixel location\n *\n * Parameters:\n- * lonLat - {<OpenLayers.LonLat>} Location to display\n+ * olPixel - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Pixel, translated from the passed in \n+ * OpenLayers.Pixel\n+ * Returns null if null value is passed in\n */\n- formatOutput: function(lonLat) {\n- var digits = parseInt(this.numDigits);\n- var newHtml =\n- this.prefix +\n- lonLat.lon.toFixed(digits) +\n- this.separator +\n- lonLat.lat.toFixed(digits) +\n- this.suffix;\n- return newHtml;\n+ getMapObjectPixelFromOLPixel: function(olPixel) {\n+ var moPixel = null;\n+ if (olPixel != null) {\n+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);\n+ }\n+ return moPixel;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.MousePosition\"\n+ CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n });\n /* ======================================================================\n- OpenLayers/Control/Zoom.js\n+ OpenLayers/Layer/ArcGIS93Rest.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Control.Zoom\n- * The Zoom control is a pair of +/- links for zooming in and out.\n- *\n+ * Class: OpenLayers.Layer.ArcGIS93Rest\n+ * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from\n+ * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API.\n+ * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest>\n+ * constructor. More detail on the REST API is available at\n+ * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ;\n+ * specifically, the URL provided to this layer should be an export service\n+ * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html \n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * APIProperty: zoomInText\n- * {String}\n- * Text for zoom-in link. Default is \"+\".\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs \n */\n- zoomInText: \"+\",\n+ DEFAULT_PARAMS: {\n+ format: \"png\"\n+ },\n \n /**\n- * APIProperty: zoomInId\n- * {String}\n- * Instead of having the control create a zoom in link, you can provide \n- * the identifier for an anchor element already added to the document.\n- * By default, an element with id \"olZoomInLink\" will be searched for\n- * and used if it exists.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Default is true for ArcGIS93Rest layer\n */\n- zoomInId: \"olZoomInLink\",\n+ isBaseLayer: true,\n \n- /**\n- * APIProperty: zoomOutText\n- * {String}\n- * Text for zoom-out link. Default is \"\\u2212\".\n- */\n- zoomOutText: \"\\u2212\",\n \n /**\n- * APIProperty: zoomOutId\n- * {String}\n- * Instead of having the control create a zoom out link, you can provide \n- * the identifier for an anchor element already added to the document.\n- * By default, an element with id \"olZoomOutLink\" will be searched for\n- * and used if it exists.\n+ * Constructor: OpenLayers.Layer.ArcGIS93Rest\n+ * Create a new ArcGIS93Rest layer object.\n+ *\n+ * Example:\n+ * (code)\n+ * var arcims = new OpenLayers.Layer.ArcGIS93Rest(\"MyName\",\n+ * \"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export\", \n+ * {\n+ * layers: \"0,1,2\"\n+ * });\n+ * (end)\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the ArcGIS server REST service\n+ * options - {Object} An object with key/value pairs representing the\n+ * options and option values.\n+ *\n+ * Valid Options:\n+ * format - {String} MIME type of desired image type.\n+ * layers - {String} Comma-separated list of layers to display.\n+ * srs - {String} Projection ID.\n */\n- zoomOutId: \"olZoomOutLink\",\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ //uppercase params\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n+ );\n+\n+ //layer is transparent \n+ if (this.params.TRANSPARENT &&\n+ this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+\n+ // unless explicitly set in options, make layer an overlay\n+ if ((options == null) || (!options.isBaseLayer)) {\n+ this.isBaseLayer = false;\n+ }\n+\n+ // jpegs can never be transparent, so intelligently switch the \n+ // format, depending on the browser's capabilities\n+ if (this.params.FORMAT == \"jpg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"gif\" :\n+ \"png\";\n+ }\n+ }\n+ },\n \n /**\n- * Method: draw\n+ * Method: clone\n+ * Create a clone of this layer\n *\n * Returns:\n- * {DOMElement} A reference to the DOMElement containing the zoom links.\n+ * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer\n */\n- draw: function() {\n- var div = OpenLayers.Control.prototype.draw.apply(this),\n- links = this.getOrCreateLinks(div),\n- zoomIn = links.zoomIn,\n- zoomOut = links.zoomOut,\n- eventsInstance = this.map.events;\n+ clone: function(obj) {\n \n- if (zoomOut.parentNode !== div) {\n- eventsInstance = this.events;\n- eventsInstance.attachToElement(zoomOut.parentNode);\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcGIS93Rest(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n }\n- eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n \n- this.zoomInLink = zoomIn;\n- this.zoomOutLink = zoomOut;\n- return div;\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n },\n \n+\n /**\n- * Method: getOrCreateLinks\n- * \n+ * Method: getURL\n+ * Return an image url this layer.\n+ *\n * Parameters:\n- * el - {DOMElement}\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n *\n- * Return: \n- * {Object} Object with zoomIn and zoomOut properties referencing links.\n+ * Returns:\n+ * {String} A string with the map image's url.\n */\n- getOrCreateLinks: function(el) {\n- var zoomIn = document.getElementById(this.zoomInId),\n- zoomOut = document.getElementById(this.zoomOutId);\n- if (!zoomIn) {\n- zoomIn = document.createElement(\"a\");\n- zoomIn.href = \"#zoomIn\";\n- zoomIn.appendChild(document.createTextNode(this.zoomInText));\n- zoomIn.className = \"olControlZoomIn\";\n- el.appendChild(zoomIn);\n- }\n- OpenLayers.Element.addClass(zoomIn, \"olButton\");\n- if (!zoomOut) {\n- zoomOut = document.createElement(\"a\");\n- zoomOut.href = \"#zoomOut\";\n- zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n- zoomOut.className = \"olControlZoomOut\";\n- el.appendChild(zoomOut);\n- }\n- OpenLayers.Element.addClass(zoomOut, \"olButton\");\n- return {\n- zoomIn: zoomIn,\n- zoomOut: zoomOut\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+\n+ // ArcGIS Server only wants the numeric portion of the projection ID.\n+ var projWords = this.projection.getCode().split(\":\");\n+ var srid = projWords[projWords.length - 1];\n+\n+ var imageSize = this.getImageSize();\n+ var newParams = {\n+ 'BBOX': bounds.toBBOX(),\n+ 'SIZE': imageSize.w + \",\" + imageSize.h,\n+ // We always want image, the other options were json, image with a whole lotta html around it, etc.\n+ 'F': \"image\",\n+ 'BBOXSR': srid,\n+ 'IMAGESR': srid\n };\n+\n+ // Now add the filter parameters.\n+ if (this.layerDefs) {\n+ var layerDefStrList = [];\n+ var layerID;\n+ for (layerID in this.layerDefs) {\n+ if (this.layerDefs.hasOwnProperty(layerID)) {\n+ if (this.layerDefs[layerID]) {\n+ layerDefStrList.push(layerID);\n+ layerDefStrList.push(\":\");\n+ layerDefStrList.push(this.layerDefs[layerID]);\n+ layerDefStrList.push(\";\");\n+ }\n+ }\n+ }\n+ if (layerDefStrList.length > 0) {\n+ newParams['LAYERDEFS'] = layerDefStrList.join(\"\");\n+ }\n+ }\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString;\n },\n \n /**\n- * Method: onZoomClick\n- * Called when zoomin/out link is clicked.\n+ * Method: setLayerFilter\n+ * addTile creates a tile, initializes it, and adds it to the layer div. \n+ *\n+ * Parameters:\n+ * id - {String} The id of the layer to which the filter applies.\n+ * queryDef - {String} A sql-ish query filter, for more detail see the ESRI\n+ * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html\n */\n- onZoomClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.zoomInLink) {\n- this.map.zoomIn();\n- } else if (button === this.zoomOutLink) {\n- this.map.zoomOut();\n+ setLayerFilter: function(id, queryDef) {\n+ if (!this.layerDefs) {\n+ this.layerDefs = {};\n+ }\n+ if (queryDef) {\n+ this.layerDefs[id] = queryDef;\n+ } else {\n+ delete this.layerDefs[id];\n }\n },\n \n- /** \n- * Method: destroy\n- * Clean up.\n+ /**\n+ * Method: clearLayerFilter\n+ * Clears layer filters, either from a specific layer,\n+ * or all of them.\n+ *\n+ * Parameters:\n+ * id - {String} The id of the layer from which to remove any\n+ * filter. If unspecified/blank, all filters\n+ * will be removed.\n */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onZoomClick);\n+ clearLayerFilter: function(id) {\n+ if (id) {\n+ delete this.layerDefs[id];\n+ } else {\n+ delete this.layerDefs;\n }\n- delete this.zoomInLink;\n- delete this.zoomOutLink;\n- OpenLayers.Control.prototype.destroy.apply(this);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Zoom\"\n+ /**\n+ * APIMethod: mergeNewParams\n+ * Catch changeParams and uppercase the new params to be merged in\n+ * before calling changeParams on the super class.\n+ * \n+ * Once params have been changed, the tiles will be reloaded with\n+ * the new parameters.\n+ * \n+ * Parameters:\n+ * newParams - {Object} Hashtable of new params to use\n+ */\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n+ newArguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.ArcGIS93Rest\"\n });\n /* ======================================================================\n- OpenLayers/Control/SelectFeature.js\n+ OpenLayers/Layer/WMTS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Handler/Feature.js\n- * @requires OpenLayers/Layer/Vector/RootContainer.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Control.SelectFeature\n- * The SelectFeature control selects vector features from a given layer on \n- * click or hover. \n- *\n+ * Class: OpenLayers.Layer.WMTS\n+ * Instances of the WMTS class allow viewing of tiles from a service that \n+ * implements the OGC WMTS specification version 1.0.0.\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforefeaturehighlighted - Triggered before a feature is highlighted\n- * featurehighlighted - Triggered when a feature is highlighted\n- * featureunhighlighted - Triggered when a feature is unhighlighted\n- * boxselectionstart - Triggered before box selection starts\n- * boxselectionend - Triggered after box selection ends\n- */\n-\n- /**\n- * Property: multipleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <multiple> property to true. Default is null.\n- */\n- multipleKey: null,\n+OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * Property: toggleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <toggle> property to true. Default is null.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer will be considered a base layer. Default is true.\n */\n- toggleKey: null,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: multiple\n- * {Boolean} Allow selection of multiple geometries. Default is false.\n+ * Property: version\n+ * {String} WMTS version. Default is \"1.0.0\".\n */\n- multiple: false,\n+ version: \"1.0.0\",\n \n /**\n- * APIProperty: clickout\n- * {Boolean} Unselect features when clicking outside any feature.\n- * Default is true.\n+ * APIProperty: requestEncoding\n+ * {String} Request encoding. Can be \"REST\" or \"KVP\". Default is \"KVP\".\n */\n- clickout: true,\n+ requestEncoding: \"KVP\",\n \n /**\n- * APIProperty: toggle\n- * {Boolean} Unselect a selected feature on click. Default is false. Only\n- * has meaning if hover is false.\n+ * APIProperty: url\n+ * {String|Array(String)} The base URL or request URL template for the WMTS\n+ * service. Must be provided. Array is only supported for base URLs, not\n+ * for request URL templates. URL templates are only supported for\n+ * REST <requestEncoding>.\n */\n- toggle: false,\n+ url: null,\n \n /**\n- * APIProperty: hover\n- * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n- * ignores clicks and only listens to mouse moves.\n+ * APIProperty: layer\n+ * {String} The layer identifier advertised by the WMTS service. Must be \n+ * provided.\n */\n- hover: false,\n+ layer: null,\n \n- /**\n- * APIProperty: highlightOnly\n- * {Boolean} If true do not actually select features (that is place them in \n- * the layer's selected features array), just highlight them. This property\n- * has no effect if hover is false. Defaults to false.\n+ /** \n+ * APIProperty: matrixSet\n+ * {String} One of the advertised matrix set identifiers. Must be provided.\n */\n- highlightOnly: false,\n+ matrixSet: null,\n \n- /**\n- * APIProperty: box\n- * {Boolean} Allow feature selection by drawing a box.\n+ /** \n+ * APIProperty: style\n+ * {String} One of the advertised layer styles. Must be provided.\n */\n- box: false,\n+ style: null,\n \n- /**\n- * Property: onBeforeSelect \n- * {Function} Optional function to be called before a feature is selected.\n- * The function should expect to be called with a feature.\n+ /** \n+ * APIProperty: format\n+ * {String} The image MIME type. Default is \"image/jpeg\".\n */\n- onBeforeSelect: function() {},\n+ format: \"image/jpeg\",\n \n /**\n- * APIProperty: onSelect \n- * {Function} Optional function to be called when a feature is selected.\n- * The function should expect to be called with a feature.\n+ * APIProperty: tileOrigin\n+ * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map \n+ * units. If the tile origin for each matrix in a set is different,\n+ * the <matrixIds> should include a topLeftCorner property. If\n+ * not provided, the tile origin will default to the top left corner\n+ * of the layer <maxExtent>.\n */\n- onSelect: function() {},\n+ tileOrigin: null,\n \n /**\n- * APIProperty: onUnselect\n- * {Function} Optional function to be called when a feature is unselected.\n- * The function should expect to be called with a feature.\n+ * APIProperty: tileFullExtent\n+ * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,\n+ * the layer's <maxExtent> property will be used.\n */\n- onUnselect: function() {},\n+ tileFullExtent: null,\n \n /**\n- * Property: scope\n- * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n- * callbacks. If null the scope will be this control.\n+ * APIProperty: formatSuffix\n+ * {String} For REST request encoding, an image format suffix must be \n+ * included in the request. If not provided, the suffix will be derived\n+ * from the <format> property.\n */\n- scope: null,\n+ formatSuffix: null,\n \n /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict selecting to a limited set of geometry types,\n- * send a list of strings corresponding to the geometry class names.\n+ * APIProperty: matrixIds\n+ * {Array} A list of tile matrix identifiers. If not provided, the matrix\n+ * identifiers will be assumed to be integers corresponding to the \n+ * map zoom level. If a list of strings is provided, each item should\n+ * be the matrix identifier that corresponds to the map zoom level.\n+ * Additionally, a list of objects can be provided. Each object should\n+ * describe the matrix as presented in the WMTS capabilities. These\n+ * objects should have the propertes shown below.\n+ * \n+ * Matrix properties:\n+ * identifier - {String} The matrix identifier (required).\n+ * scaleDenominator - {Number} The matrix scale denominator.\n+ * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the \n+ * matrix. Must be provided if different than the layer <tileOrigin>.\n+ * tileWidth - {Number} The tile width for the matrix. Must be provided \n+ * if different than the width given in the layer <tileSize>.\n+ * tileHeight - {Number} The tile height for the matrix. Must be provided \n+ * if different than the height given in the layer <tileSize>.\n */\n- geometryTypes: null,\n+ matrixIds: null,\n \n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n- * root for all layers this control is configured with (if an array of\n- * layers was passed to the constructor), or the vector layer the control\n- * was configured with (if a single layer was passed to the constructor).\n+ * APIProperty: dimensions\n+ * {Array} For RESTful request encoding, extra dimensions may be specified.\n+ * Items in this list should be property names in the <params> object.\n+ * Values of extra dimensions will be determined from the corresponding\n+ * values in the <params> object.\n */\n- layer: null,\n+ dimensions: null,\n \n /**\n- * Property: layers\n- * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n- * or null if the control was configured with a single layer\n+ * APIProperty: params\n+ * {Object} Extra parameters to include in tile requests. For KVP \n+ * <requestEncoding>, these properties will be encoded in the request \n+ * query string. For REST <requestEncoding>, these properties will\n+ * become part of the request path, with order determined by the \n+ * <dimensions> list.\n */\n- layers: null,\n+ params: null,\n \n /**\n- * APIProperty: callbacks\n- * {Object} The functions that are sent to the handlers.feature for callback\n+ * APIProperty: zoomOffset\n+ * {Number} If your cache has more levels than you want to provide\n+ * access to with this layer, supply a zoomOffset. This zoom offset\n+ * is added to the current map zoom level to determine the level\n+ * for a requested tile. For example, if you supply a zoomOffset\n+ * of 3, when the map is at the zoom 0, tiles will be requested from\n+ * level 3 of your cache. Default is 0 (assumes cache level and map\n+ * zoom are equivalent). Additionally, if this layer is to be used\n+ * as an overlay and the cache has fewer zoom levels than the base\n+ * layer, you can supply a negative zoomOffset. For example, if a\n+ * map zoom level of 1 corresponds to your cache level zero, you would\n+ * supply a -1 zoomOffset (and set the maxResolution of the layer\n+ * appropriately). The zoomOffset value has no effect if complete\n+ * matrix definitions (including scaleDenominator) are supplied in\n+ * the <matrixIds> property. Defaults to 0 (no zoom offset).\n */\n- callbacks: null,\n+ zoomOffset: 0,\n \n /**\n- * APIProperty: selectStyle \n- * {Object} Hash of styles\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer; you can also look at <zoomOffset>, which is\n+ * an alternative to <serverResolutions> for that specific purpose.\n+ * (b) The map can work with resolutions that aren't supported by\n+ * the server, i.e. that aren't in <serverResolutions>. When the\n+ * map is displayed in such a resolution data for the closest\n+ * server-supported resolution is loaded and the layer div is\n+ * stretched as necessary.\n */\n- selectStyle: null,\n+ serverResolutions: null,\n \n /**\n- * Property: renderIntent\n- * {String} key used to retrieve the select style from the layer's\n- * style map.\n+ * Property: formatSuffixMap\n+ * {Object} a map between WMTS 'format' request parameter and tile image file suffix\n */\n- renderIntent: \"select\",\n+ formatSuffixMap: {\n+ \"image/png\": \"png\",\n+ \"image/png8\": \"png\",\n+ \"image/png24\": \"png\",\n+ \"image/png32\": \"png\",\n+ \"png\": \"png\",\n+ \"image/jpeg\": \"jpg\",\n+ \"image/jpg\": \"jpg\",\n+ \"jpeg\": \"jpg\",\n+ \"jpg\": \"jpg\"\n+ },\n \n /**\n- * Property: handlers\n- * {Object} Object with references to multiple <OpenLayers.Handler>\n- * instances.\n+ * Property: matrix\n+ * {Object} Matrix definition for the current map resolution. Updated by\n+ * the <updateMatrixProperties> method.\n */\n- handlers: null,\n+ matrix: null,\n \n /**\n- * Constructor: OpenLayers.Control.SelectFeature\n- * Create a new control for selecting features.\n+ * Constructor: OpenLayers.Layer.WMTS\n+ * Create a new WMTS layer.\n+ *\n+ * Example:\n+ * (code)\n+ * var wmts = new OpenLayers.Layer.WMTS({\n+ * name: \"My WMTS Layer\",\n+ * url: \"http://example.com/wmts\", \n+ * layer: \"layer_id\",\n+ * style: \"default\",\n+ * matrixSet: \"matrix_id\"\n+ * });\n+ * (end)\n *\n * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n- * layer(s) this control will select features from.\n- * options - {Object} \n+ * config - {Object} Configuration properties for the layer.\n+ *\n+ * Required configuration properties:\n+ * url - {String} The base url for the service. See the <url> property.\n+ * layer - {String} The layer identifier. See the <layer> property.\n+ * style - {String} The layer style identifier. See the <style> property.\n+ * matrixSet - {String} The tile matrix set identifier. See the <matrixSet>\n+ * property.\n+ *\n+ * Any other documented layer properties can be provided in the config object.\n */\n- initialize: function(layers, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ initialize: function(config) {\n \n- if (this.scope === null) {\n- this.scope = this;\n- }\n- this.initLayer(layers);\n- var callbacks = {\n- click: this.clickFeature,\n- clickout: this.clickoutFeature\n+ // confirm required properties are supplied\n+ var required = {\n+ url: true,\n+ layer: true,\n+ style: true,\n+ matrixSet: true\n };\n- if (this.hover) {\n- callbacks.over = this.overFeature;\n- callbacks.out = this.outFeature;\n+ for (var prop in required) {\n+ if (!(prop in config)) {\n+ throw new Error(\"Missing property '\" + prop + \"' in layer configuration.\");\n+ }\n }\n \n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlers = {\n- feature: new OpenLayers.Handler.Feature(\n- this, this.layer, this.callbacks, {\n- geometryTypes: this.geometryTypes\n- }\n- )\n- };\n+ config.params = OpenLayers.Util.upperCaseObject(config.params);\n+ var args = [config.name, config.url, config.params, config];\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);\n \n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(\n- this, {\n- done: this.selectBox\n- }, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- }\n- );\n+\n+ // determine format suffix (for REST)\n+ if (!this.formatSuffix) {\n+ this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split(\"/\").pop();\n }\n- },\n \n- /**\n- * Method: initLayer\n- * Assign the layer property. If layers is an array, we need to use\n- * a RootContainer.\n- *\n- * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n- */\n- initLayer: function(layers) {\n- if (OpenLayers.Util.isArray(layers)) {\n- this.layers = layers;\n- this.layer = new OpenLayers.Layer.Vector.RootContainer(\n- this.id + \"_container\", {\n- layers: layers\n+ // expand matrixIds (may be array of string or array of object)\n+ if (this.matrixIds) {\n+ var len = this.matrixIds.length;\n+ if (len && typeof this.matrixIds[0] === \"string\") {\n+ var ids = this.matrixIds;\n+ this.matrixIds = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ this.matrixIds[i] = {\n+ identifier: ids[i]\n+ };\n }\n- );\n- } else {\n- this.layer = layers;\n+ }\n }\n+\n },\n \n /**\n- * Method: destroy\n+ * Method: setMap\n */\n- destroy: function() {\n- if (this.active && this.layers) {\n- this.map.removeLayer(this.layer);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.layers) {\n- this.layer.destroy();\n- }\n+ setMap: function() {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n },\n \n /**\n- * Method: activate\n- * Activates the control.\n- * \n- * Returns:\n- * {Boolean} The control was effectively activated.\n+ * Method: updateMatrixProperties\n+ * Called when map resolution changes to update matrix related properties.\n */\n- activate: function() {\n- if (!this.active) {\n- if (this.layers) {\n- this.map.addLayer(this.layer);\n+ updateMatrixProperties: function() {\n+ this.matrix = this.getMatrix();\n+ if (this.matrix) {\n+ if (this.matrix.topLeftCorner) {\n+ this.tileOrigin = this.matrix.topLeftCorner;\n }\n- this.handlers.feature.activate();\n- if (this.box && this.handlers.box) {\n- this.handlers.box.activate();\n+ if (this.matrix.tileWidth && this.matrix.tileHeight) {\n+ this.tileSize = new OpenLayers.Size(\n+ this.matrix.tileWidth, this.matrix.tileHeight\n+ );\n }\n- }\n- return OpenLayers.Control.prototype.activate.apply(\n- this, arguments\n- );\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivates the control.\n- * \n- * Returns:\n- * {Boolean} The control was effectively deactivated.\n- */\n- deactivate: function() {\n- if (this.active) {\n- this.handlers.feature.deactivate();\n- if (this.handlers.box) {\n- this.handlers.box.deactivate();\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(\n+ this.maxExtent.left, this.maxExtent.top\n+ );\n }\n- if (this.layers) {\n- this.map.removeLayer(this.layer);\n+ if (!this.tileFullExtent) {\n+ this.tileFullExtent = this.maxExtent;\n }\n }\n- return OpenLayers.Control.prototype.deactivate.apply(\n- this, arguments\n- );\n },\n \n /**\n- * Method: unselectAll\n- * Unselect all selected features. To unselect all except for a single\n- * feature, set the options.except property to the feature.\n- *\n+ * Method: moveTo\n+ * \n * Parameters:\n- * options - {Object} Optional configuration object.\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n+ * do some init work in that case.\n+ * dragging - {Boolean}\n */\n- unselectAll: function(options) {\n- // we'll want an option to supress notification here\n- var layers = this.layers || [this.layer],\n- layer, feature, l, numExcept;\n- for (l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- numExcept = 0;\n- //layer.selectedFeatures is null when layer is destroyed and \n- //one of it's preremovelayer listener calls setLayer \n- //with another layer on this control\n- if (layer.selectedFeatures != null) {\n- while (layer.selectedFeatures.length > numExcept) {\n- feature = layer.selectedFeatures[numExcept];\n- if (!options || options.except != feature) {\n- this.unselect(feature);\n- } else {\n- ++numExcept;\n- }\n- }\n- }\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ if (zoomChanged || !this.matrix) {\n+ this.updateMatrixProperties();\n }\n+ return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);\n },\n \n /**\n- * Method: clickFeature\n- * Called on click in a feature\n- * Only responds if this.hover is false.\n- *\n+ * APIMethod: clone\n+ * \n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- clickFeature: function(feature) {\n- if (!this.hover) {\n- var selected = (OpenLayers.Util.indexOf(\n- feature.layer.selectedFeatures, feature) > -1);\n- if (selected) {\n- if (this.toggleSelect()) {\n- this.unselect(feature);\n- } else if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- });\n- }\n- } else {\n- if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- });\n- }\n- this.select(feature);\n- }\n- }\n- },\n-\n- /**\n- * Method: multipleSelect\n- * Allow for multiple selected features based on <multiple> property and\n- * <multipleKey> event modifier.\n- *\n- * Returns:\n- * {Boolean} Allow for multiple selected features.\n- */\n- multipleSelect: function() {\n- return this.multiple || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.multipleKey]);\n- },\n-\n- /**\n- * Method: toggleSelect\n- * Event should toggle the selected state of a feature based on <toggle>\n- * property and <toggleKey> event modifier.\n- *\n+ * obj - {Object}\n+ * \n * Returns:\n- * {Boolean} Toggle the selected state of a feature.\n- */\n- toggleSelect: function() {\n- return this.toggle || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.toggleKey]);\n- },\n-\n- /**\n- * Method: clickoutFeature\n- * Called on click outside a previously clicked (selected) feature.\n- * Only responds if this.hover is false.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Vector.Feature>} \n+ * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>\n */\n- clickoutFeature: function(feature) {\n- if (!this.hover && this.clickout) {\n- this.unselectAll();\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMTS(this.options);\n }\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ // copy/set any non-init, non-simple values here\n+ return obj;\n },\n \n /**\n- * Method: overFeature\n- * Called on over a feature.\n- * Only responds if this.hover is true.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * Method: getIdentifier\n+ * Get the current index in the matrixIds array.\n */\n- overFeature: function(feature) {\n- var layer = feature.layer;\n- if (this.hover) {\n- if (this.highlightOnly) {\n- this.highlight(feature);\n- } else if (OpenLayers.Util.indexOf(\n- layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n- }\n- }\n+ getIdentifier: function() {\n+ return this.getServerZoom();\n },\n \n /**\n- * Method: outFeature\n- * Called on out of a selected feature.\n- * Only responds if this.hover is true.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * Method: getMatrix\n+ * Get the appropriate matrix definition for the current map resolution.\n */\n- outFeature: function(feature) {\n- if (this.hover) {\n- if (this.highlightOnly) {\n- // we do nothing if we're not the last highlighter of the\n- // feature\n- if (feature._lastHighlighter == this.id) {\n- // if another select control had highlighted the feature before\n- // we did it ourself then we use that control to highlight the\n- // feature as it was before we highlighted it, else we just\n- // unhighlight it\n- if (feature._prevHighlighter &&\n- feature._prevHighlighter != this.id) {\n- delete feature._lastHighlighter;\n- var control = this.map.getControl(\n- feature._prevHighlighter);\n- if (control) {\n- control.highlight(feature);\n- }\n- } else {\n- this.unhighlight(feature);\n+ getMatrix: function() {\n+ var matrix;\n+ if (!this.matrixIds || this.matrixIds.length === 0) {\n+ matrix = {\n+ identifier: this.getIdentifier()\n+ };\n+ } else {\n+ // get appropriate matrix given the map scale if possible\n+ if (\"scaleDenominator\" in this.matrixIds[0]) {\n+ // scale denominator calculation based on WMTS spec\n+ var denom =\n+ OpenLayers.METERS_PER_INCH *\n+ OpenLayers.INCHES_PER_UNIT[this.units] *\n+ this.getServerResolution() / 0.28E-3;\n+ var diff = Number.POSITIVE_INFINITY;\n+ var delta;\n+ for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {\n+ delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));\n+ if (delta < diff) {\n+ diff = delta;\n+ matrix = this.matrixIds[i];\n }\n }\n } else {\n- this.unselect(feature);\n+ // fall back on zoom as index\n+ matrix = this.matrixIds[this.getIdentifier()];\n }\n }\n+ return matrix;\n },\n \n- /**\n- * Method: highlight\n- * Redraw feature with the select style.\n+ /** \n+ * Method: getTileInfo\n+ * Get tile information for a given location at the current map resolution.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- highlight: function(feature) {\n- var layer = feature.layer;\n- var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- feature._prevHighlighter = feature._lastHighlighter;\n- feature._lastHighlighter = this.id;\n- var style = this.selectStyle || this.renderIntent;\n- layer.drawFeature(feature, style);\n- this.events.triggerEvent(\"featurehighlighted\", {\n- feature: feature\n- });\n- }\n- },\n-\n- /**\n- * Method: unhighlight\n- * Redraw feature with the \"default\" style\n+ * loc - {<OpenLayers.LonLat} A location in map coordinates.\n *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * Returns:\n+ * {Object} An object with \"col\", \"row\", \"i\", and \"j\" properties. The col\n+ * and row values are zero based tile indexes from the top left. The\n+ * i and j values are the number of pixels to the left and top \n+ * (respectively) of the given location within the target tile.\n */\n- unhighlight: function(feature) {\n- var layer = feature.layer;\n- // three cases:\n- // 1. there's no other highlighter, in that case _prev is undefined,\n- // and we just need to undef _last\n- // 2. another control highlighted the feature after we did it, in\n- // that case _last references this other control, and we just\n- // need to undef _prev\n- // 3. another control highlighted the feature before we did it, in\n- // that case _prev references this other control, and we need to\n- // set _last to _prev and undef _prev\n- if (feature._prevHighlighter == undefined) {\n- delete feature._lastHighlighter;\n- } else if (feature._prevHighlighter == this.id) {\n- delete feature._prevHighlighter;\n- } else {\n- feature._lastHighlighter = feature._prevHighlighter;\n- delete feature._prevHighlighter;\n- }\n- layer.drawFeature(feature, feature.style || feature.layer.style ||\n- \"default\");\n- this.events.triggerEvent(\"featureunhighlighted\", {\n- feature: feature\n- });\n- },\n+ getTileInfo: function(loc) {\n+ var res = this.getServerResolution();\n \n- /**\n- * Method: select\n- * Add feature to the layer's selectedFeature array, render the feature as\n- * selected, and call the onSelect function.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- select: function(feature) {\n- var cont = this.onBeforeSelect.call(this.scope, feature);\n- var layer = feature.layer;\n- if (cont !== false) {\n- cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- layer.selectedFeatures.push(feature);\n- this.highlight(feature);\n- // if the feature handler isn't involved in the feature\n- // selection (because the box handler is used or the\n- // feature is selected programatically) we fake the\n- // feature handler to allow unselecting on click\n- if (!this.handlers.feature.lastFeature) {\n- this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n- }\n- layer.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- });\n- this.onSelect.call(this.scope, feature);\n- }\n- }\n- },\n+ var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);\n+ var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);\n \n- /**\n- * Method: unselect\n- * Remove feature from the layer's selectedFeature array, render the feature as\n- * normal, and call the onUnselect function.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- */\n- unselect: function(feature) {\n- var layer = feature.layer;\n- // Store feature style for restoration later\n- this.unhighlight(feature);\n- OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n- layer.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n- });\n- this.onUnselect.call(this.scope, feature);\n+ var col = Math.floor(fx);\n+ var row = Math.floor(fy);\n+\n+ return {\n+ col: col,\n+ row: row,\n+ i: Math.floor((fx - col) * this.tileSize.w),\n+ j: Math.floor((fy - row) * this.tileSize.h)\n+ };\n },\n \n /**\n- * Method: selectBox\n- * Callback from the handlers.box set up when <box> selection is true\n- * on.\n- *\n+ * Method: getURL\n+ * \n * Parameters:\n- * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n+ * bounds - {<OpenLayers.Bounds>}\n+ * \n+ * Returns:\n+ * {String} A URL for the tile corresponding to the given bounds.\n */\n- selectBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- var bounds = new OpenLayers.Bounds(\n- minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n- );\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var url = \"\";\n+ if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {\n \n- // if multiple is false, first deselect currently selected features\n- if (!this.multipleSelect()) {\n- this.unselectAll();\n+ var center = bounds.getCenterLonLat();\n+ var info = this.getTileInfo(center);\n+ var matrixId = this.matrix.identifier;\n+ var dimensions = this.dimensions,\n+ params;\n+\n+ if (OpenLayers.Util.isArray(this.url)) {\n+ url = this.selectUrl([\n+ this.version, this.style, this.matrixSet,\n+ this.matrix.identifier, info.row, info.col\n+ ].join(\",\"), this.url);\n+ } else {\n+ url = this.url;\n }\n \n- // because we're using a box, we consider we want multiple selection\n- var prevMultiple = this.multiple;\n- this.multiple = true;\n- var layers = this.layers || [this.layer];\n- this.events.triggerEvent(\"boxselectionstart\", {\n- layers: layers\n- });\n- var layer;\n- for (var l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- for (var i = 0, len = layer.features.length; i < len; ++i) {\n- var feature = layer.features[i];\n- // check if the feature is displayed\n- if (!feature.getVisibility()) {\n- continue;\n+ if (this.requestEncoding.toUpperCase() === \"REST\") {\n+ params = this.params;\n+ if (url.indexOf(\"{\") !== -1) {\n+ var template = url.replace(/\\{/g, \"${\");\n+ var context = {\n+ // spec does not make clear if capital S or not\n+ style: this.style,\n+ Style: this.style,\n+ TileMatrixSet: this.matrixSet,\n+ TileMatrix: this.matrix.identifier,\n+ TileRow: info.row,\n+ TileCol: info.col\n+ };\n+ if (dimensions) {\n+ var dimension, i;\n+ for (i = dimensions.length - 1; i >= 0; --i) {\n+ dimension = dimensions[i];\n+ context[dimension] = params[dimension.toUpperCase()];\n+ }\n }\n+ url = OpenLayers.String.format(template, context);\n+ } else {\n+ // include 'version', 'layer' and 'style' in tile resource url\n+ var path = this.version + \"/\" + this.layer + \"/\" + this.style + \"/\";\n \n- if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n- this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n- if (bounds.toGeometry().intersects(feature.geometry)) {\n- if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n+ // append optional dimension path elements\n+ if (dimensions) {\n+ for (var i = 0; i < dimensions.length; i++) {\n+ if (params[dimensions[i]]) {\n+ path = path + params[dimensions[i]] + \"/\";\n }\n }\n }\n+\n+ // append other required path elements\n+ path = path + this.matrixSet + \"/\" + this.matrix.identifier +\n+ \"/\" + info.row + \"/\" + info.col + \".\" + this.formatSuffix;\n+\n+ if (!url.match(/\\/$/)) {\n+ url = url + \"/\";\n+ }\n+ url = url + path;\n }\n- }\n- this.multiple = prevMultiple;\n- this.events.triggerEvent(\"boxselectionend\", {\n- layers: layers\n- });\n- }\n- },\n+ } else if (this.requestEncoding.toUpperCase() === \"KVP\") {\n \n- /** \n- * Method: setMap\n- * Set the map property for the control. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- this.handlers.feature.setMap(map);\n- if (this.box) {\n- this.handlers.box.setMap(map);\n+ // assemble all required parameters\n+ params = {\n+ SERVICE: \"WMTS\",\n+ REQUEST: \"GetTile\",\n+ VERSION: this.version,\n+ LAYER: this.layer,\n+ STYLE: this.style,\n+ TILEMATRIXSET: this.matrixSet,\n+ TILEMATRIX: this.matrix.identifier,\n+ TILEROW: info.row,\n+ TILECOL: info.col,\n+ FORMAT: this.format\n+ };\n+ url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);\n+\n+ }\n }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ return url;\n },\n \n /**\n- * APIMethod: setLayer\n- * Attach a new layer to the control, overriding any existing layers.\n- *\n+ * APIMethod: mergeNewParams\n+ * Extend the existing layer <params> with new properties. Tiles will be\n+ * reloaded with updated params in the request.\n+ * \n * Parameters:\n- * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n- * {<OpenLayers.Layer.Vector>}\n+ * newParams - {Object} Properties to extend to existing <params>.\n */\n- setLayer: function(layers) {\n- var isActive = this.active;\n- this.unselectAll();\n- this.deactivate();\n- if (this.layers) {\n- this.layer.destroy();\n- this.layers = null;\n- }\n- this.initLayer(layers);\n- this.handlers.feature.layer = this.layer;\n- if (isActive) {\n- this.activate();\n+ mergeNewParams: function(newParams) {\n+ if (this.requestEncoding.toUpperCase() === \"KVP\") {\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(\n+ this, [OpenLayers.Util.upperCaseObject(newParams)]\n+ );\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n+ CLASS_NAME: \"OpenLayers.Layer.WMTS\"\n });\n /* ======================================================================\n- OpenLayers/Control/NavigationHistory.js\n+ OpenLayers/Layer/PointGrid.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Control/Button.js\n+ * @requires OpenLayers/Layer/Vector.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n */\n \n /**\n- * Class: OpenLayers.Control.NavigationHistory\n- * A navigation history control. This is a meta-control, that creates two\n- * dependent controls: <previous> and <next>. Call the trigger method\n- * on the <previous> and <next> controls to restore previous and next\n- * history states. The previous and next controls will become active\n- * when there are available states to restore and will become deactive\n- * when there are no states to restore.\n+ * Class: OpenLayers.Layer.PointGrid\n+ * A point grid layer dynamically generates a regularly spaced grid of point\n+ * features. This is a specialty layer for cases where an application needs\n+ * a regular grid of points. It can be used, for example, in an editing\n+ * environment to snap to a grid.\n+ *\n+ * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor.\n+ * (code)\n+ * // create a grid with points spaced at 10 map units\n+ * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10});\n+ *\n+ * // create a grid with different x/y spacing rotated 15 degrees clockwise.\n+ * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15});\n+ * (end)\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Vector>\n */\n-OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {\n-\n- /**\n- * Property: type\n- * {String} Note that this control is not intended to be added directly\n- * to a control panel. Instead, add the sub-controls previous and\n- * next. These sub-controls are button type controls that activate\n- * and deactivate themselves. If this parent control is added to\n- * a panel, it will act as a toggle.\n- */\n- type: OpenLayers.Control.TYPE_TOGGLE,\n-\n- /**\n- * APIProperty: previous\n- * {<OpenLayers.Control>} A button type control whose trigger method restores\n- * the previous state managed by this control.\n- */\n- previous: null,\n-\n- /**\n- * APIProperty: previousOptions\n- * {Object} Set this property on the options argument of the constructor\n- * to set optional properties on the <previous> control.\n- */\n- previousOptions: null,\n-\n- /**\n- * APIProperty: next\n- * {<OpenLayers.Control>} A button type control whose trigger method restores\n- * the next state managed by this control.\n- */\n- next: null,\n-\n- /**\n- * APIProperty: nextOptions\n- * {Object} Set this property on the options argument of the constructor\n- * to set optional properties on the <next> control.\n- */\n- nextOptions: null,\n+OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {\n \n /**\n- * APIProperty: limit\n- * {Integer} Optional limit on the number of history items to retain. If\n- * null, there is no limit. Default is 50.\n+ * APIProperty: dx\n+ * {Number} Point grid spacing in the x-axis direction (map units). \n+ * Read-only. Use the <setSpacing> method to modify this value.\n */\n- limit: 50,\n+ dx: null,\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * APIProperty: dy\n+ * {Number} Point grid spacing in the y-axis direction (map units). \n+ * Read-only. Use the <setSpacing> method to modify this value.\n */\n- autoActivate: true,\n+ dy: null,\n \n /**\n- * Property: clearOnDeactivate\n- * {Boolean} Clear the history when the control is deactivated. Default\n- * is false.\n+ * APIProperty: ratio\n+ * {Number} Ratio of the desired grid size to the map viewport size. \n+ * Default is 1.5. Larger ratios mean the grid is recalculated less often \n+ * while panning. The <maxFeatures> setting has precedence when determining\n+ * grid size. Read-only. Use the <setRatio> method to modify this value.\n */\n- clearOnDeactivate: false,\n+ ratio: 1.5,\n \n /**\n- * Property: registry\n- * {Object} An object with keys corresponding to event types. Values\n- * are functions that return an object representing the current state.\n+ * APIProperty: maxFeatures\n+ * {Number} The maximum number of points to generate in the grid. Default\n+ * is 250. Read-only. Use the <setMaxFeatures> method to modify this value.\n */\n- registry: null,\n+ maxFeatures: 250,\n \n /**\n- * Property: nextStack\n- * {Array} Array of items in the history.\n+ * APIProperty: rotation\n+ * {Number} Grid rotation (in degrees clockwise from the positive x-axis).\n+ * Default is 0. Read-only. Use the <setRotation> method to modify this\n+ * value.\n */\n- nextStack: null,\n+ rotation: 0,\n \n /**\n- * Property: previousStack\n- * {Array} List of items in the history. First item represents the current\n- * state.\n+ * APIProperty: origin\n+ * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with \n+ * the origin. If not set at construction, the center of the map's maximum \n+ * extent is used. Read-only. Use the <setOrigin> method to modify this \n+ * value.\n */\n- previousStack: null,\n+ origin: null,\n \n /**\n- * Property: listeners\n- * {Object} An object containing properties corresponding to event types.\n- * This object is used to configure the control and is modified on\n- * construction.\n+ * Property: gridBounds\n+ * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional \n+ * rotation applied).\n */\n- listeners: null,\n+ gridBounds: null,\n \n /**\n- * Property: restoring\n- * {Boolean} Currently restoring a history state. This is set to true\n- * before calling restore and set to false after restore returns.\n+ * Constructor: OpenLayers.Layer.PointGrid\n+ * Creates a new point grid layer.\n+ *\n+ * Parameters:\n+ * config - {Object} An object containing all configuration properties for\n+ * the layer. The <dx> and <dy> properties are required to be set at \n+ * construction. Any other layer properties may be set in this object.\n */\n- restoring: false,\n+ initialize: function(config) {\n+ config = config || {};\n+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);\n+ },\n \n- /**\n- * Constructor: OpenLayers.Control.NavigationHistory \n+ /** \n+ * Method: setMap\n+ * The layer has been added to the map. \n * \n * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n+ * map - {<OpenLayers.Map>} \n */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n-\n- this.registry = OpenLayers.Util.extend({\n- \"moveend\": this.getState\n- }, this.registry);\n-\n- var previousOptions = {\n- trigger: OpenLayers.Function.bind(this.previousTrigger, this),\n- displayClass: this.displayClass + \" \" + this.displayClass + \"Previous\"\n- };\n- OpenLayers.Util.extend(previousOptions, this.previousOptions);\n- this.previous = new OpenLayers.Control.Button(previousOptions);\n-\n- var nextOptions = {\n- trigger: OpenLayers.Function.bind(this.nextTrigger, this),\n- displayClass: this.displayClass + \" \" + this.displayClass + \"Next\"\n- };\n- OpenLayers.Util.extend(nextOptions, this.nextOptions);\n- this.next = new OpenLayers.Control.Button(nextOptions);\n-\n- this.clear();\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ map.events.register(\"moveend\", this, this.onMoveEnd);\n },\n \n /**\n- * Method: onPreviousChange\n- * Called when the previous history stack changes.\n+ * Method: removeMap\n+ * The layer has been removed from the map.\n *\n * Parameters:\n- * state - {Object} An object representing the state to be restored\n- * if previous is triggered again or null if no previous states remain.\n- * length - {Integer} The number of remaining previous states that can\n- * be restored.\n+ * map - {<OpenLayers.Map>}\n */\n- onPreviousChange: function(state, length) {\n- if (state && !this.previous.active) {\n- this.previous.activate();\n- } else if (!state && this.previous.active) {\n- this.previous.deactivate();\n- }\n+ removeMap: function(map) {\n+ map.events.unregister(\"moveend\", this, this.onMoveEnd);\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n },\n \n /**\n- * Method: onNextChange\n- * Called when the next history stack changes.\n+ * APIMethod: setRatio\n+ * Set the grid <ratio> property and update the grid. Can only be called\n+ * after the layer has been added to a map with a center/extent.\n *\n * Parameters:\n- * state - {Object} An object representing the state to be restored\n- * if next is triggered again or null if no next states remain.\n- * length - {Integer} The number of remaining next states that can\n- * be restored.\n+ * ratio - {Number}\n */\n- onNextChange: function(state, length) {\n- if (state && !this.next.active) {\n- this.next.activate();\n- } else if (!state && this.next.active) {\n- this.next.deactivate();\n- }\n+ setRatio: function(ratio) {\n+ this.ratio = ratio;\n+ this.updateGrid(true);\n },\n \n /**\n- * APIMethod: destroy\n- * Destroy the control.\n+ * APIMethod: setMaxFeatures\n+ * Set the grid <maxFeatures> property and update the grid. Can only be \n+ * called after the layer has been added to a map with a center/extent.\n+ *\n+ * Parameters:\n+ * maxFeatures - {Number}\n */\n- destroy: function() {\n- OpenLayers.Control.prototype.destroy.apply(this);\n- this.previous.destroy();\n- this.next.destroy();\n- this.deactivate();\n- for (var prop in this) {\n- this[prop] = null;\n- }\n+ setMaxFeatures: function(maxFeatures) {\n+ this.maxFeatures = maxFeatures;\n+ this.updateGrid(true);\n },\n \n- /** \n- * Method: setMap\n- * Set the map property for the control and <previous> and <next> child\n- * controls.\n+ /**\n+ * APIMethod: setSpacing\n+ * Set the grid <dx> and <dy> properties and update the grid. If only one\n+ * argument is provided, it will be set as <dx> and <dy>. Can only be \n+ * called after the layer has been added to a map with a center/extent.\n *\n * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * dx - {Number}\n+ * dy - {Number}\n */\n- setMap: function(map) {\n- this.map = map;\n- this.next.setMap(map);\n- this.previous.setMap(map);\n+ setSpacing: function(dx, dy) {\n+ this.dx = dx;\n+ this.dy = dy || dx;\n+ this.updateGrid(true);\n },\n \n /**\n- * Method: draw\n- * Called when the control is added to the map.\n+ * APIMethod: setOrigin\n+ * Set the grid <origin> property and update the grid. Can only be called\n+ * after the layer has been added to a map with a center/extent.\n+ *\n+ * Parameters:\n+ * origin - {<OpenLayers.LonLat>}\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- this.next.draw();\n- this.previous.draw();\n+ setOrigin: function(origin) {\n+ this.origin = origin;\n+ this.updateGrid(true);\n },\n \n /**\n- * Method: previousTrigger\n- * Restore the previous state. If no items are in the previous history\n- * stack, this has no effect.\n+ * APIMethod: getOrigin\n+ * Get the grid <origin> property.\n *\n * Returns:\n- * {Object} Item representing state that was restored. Undefined if no\n- * items are in the previous history stack.\n+ * {<OpenLayers.LonLat>} The grid origin.\n */\n- previousTrigger: function() {\n- var current = this.previousStack.shift();\n- var state = this.previousStack.shift();\n- if (state != undefined) {\n- this.nextStack.unshift(current);\n- this.previousStack.unshift(state);\n- this.restoring = true;\n- this.restore(state);\n- this.restoring = false;\n- this.onNextChange(this.nextStack[0], this.nextStack.length);\n- this.onPreviousChange(\n- this.previousStack[1], this.previousStack.length - 1\n- );\n- } else {\n- this.previousStack.unshift(current);\n+ getOrigin: function() {\n+ if (!this.origin) {\n+ this.origin = this.map.getExtent().getCenterLonLat();\n }\n- return state;\n+ return this.origin;\n },\n \n /**\n- * APIMethod: nextTrigger\n- * Restore the next state. If no items are in the next history\n- * stack, this has no effect. The next history stack is populated\n- * as states are restored from the previous history stack.\n+ * APIMethod: setRotation\n+ * Set the grid <rotation> property and update the grid. Rotation values\n+ * are in degrees clockwise from the positive x-axis (negative values\n+ * for counter-clockwise rotation). Can only be called after the layer \n+ * has been added to a map with a center/extent.\n *\n- * Returns:\n- * {Object} Item representing state that was restored. Undefined if no\n- * items are in the next history stack.\n+ * Parameters:\n+ * rotation - {Number} Degrees clockwise from the positive x-axis.\n */\n- nextTrigger: function() {\n- var state = this.nextStack.shift();\n- if (state != undefined) {\n- this.previousStack.unshift(state);\n- this.restoring = true;\n- this.restore(state);\n- this.restoring = false;\n- this.onNextChange(this.nextStack[0], this.nextStack.length);\n- this.onPreviousChange(\n- this.previousStack[1], this.previousStack.length - 1\n- );\n- }\n- return state;\n+ setRotation: function(rotation) {\n+ this.rotation = rotation;\n+ this.updateGrid(true);\n },\n \n /**\n- * APIMethod: clear\n- * Clear history.\n+ * Method: onMoveEnd\n+ * Listener for map \"moveend\" events.\n */\n- clear: function() {\n- this.previousStack = [];\n- this.previous.deactivate();\n- this.nextStack = [];\n- this.next.deactivate();\n+ onMoveEnd: function() {\n+ this.updateGrid();\n },\n \n /**\n- * Method: getState\n- * Get the current state and return it.\n+ * Method: getViewBounds\n+ * Gets the (potentially rotated) view bounds for grid calculations.\n *\n * Returns:\n- * {Object} An object representing the current state.\n+ * {<OpenLayers.Bounds>}\n */\n- getState: function() {\n- return {\n- center: this.map.getCenter(),\n- resolution: this.map.getResolution(),\n- projection: this.map.getProjectionObject(),\n- units: this.map.getProjectionObject().getUnits() ||\n- this.map.units || this.map.baseLayer.units\n- };\n+ getViewBounds: function() {\n+ var bounds = this.map.getExtent();\n+ if (this.rotation) {\n+ var origin = this.getOrigin();\n+ var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n+ var rect = bounds.toGeometry();\n+ rect.rotate(-this.rotation, rotationOrigin);\n+ bounds = rect.getBounds();\n+ }\n+ return bounds;\n },\n \n /**\n- * Method: restore\n- * Update the state with the given object.\n+ * Method: updateGrid\n+ * Update the grid.\n *\n * Parameters:\n- * state - {Object} An object representing the state to restore.\n- */\n- restore: function(state) {\n- var center, zoom;\n- if (this.map.getProjectionObject() == state.projection) {\n- zoom = this.map.getZoomForResolution(state.resolution);\n- center = state.center;\n- } else {\n- center = state.center.clone();\n- center.transform(state.projection, this.map.getProjectionObject());\n- var sourceUnits = state.units;\n- var targetUnits = this.map.getProjectionObject().getUnits() ||\n- this.map.units || this.map.baseLayer.units;\n- var resolutionFactor = sourceUnits && targetUnits ?\n- OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n- zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution);\n- }\n- this.map.setCenter(center, zoom);\n- },\n-\n- /**\n- * Method: setListeners\n- * Sets functions to be registered in the listeners object.\n+ * force - {Boolean} Update the grid even if the previous bounds are still\n+ * valid.\n */\n- setListeners: function() {\n- this.listeners = {};\n- for (var type in this.registry) {\n- this.listeners[type] = OpenLayers.Function.bind(function() {\n- if (!this.restoring) {\n- var state = this.registry[type].apply(this, arguments);\n- this.previousStack.unshift(state);\n- if (this.previousStack.length > 1) {\n- this.onPreviousChange(\n- this.previousStack[1], this.previousStack.length - 1\n- );\n- }\n- if (this.previousStack.length > (this.limit + 1)) {\n- this.previousStack.pop();\n- }\n- if (this.nextStack.length > 0) {\n- this.nextStack = [];\n- this.onNextChange(null, 0);\n+ updateGrid: function(force) {\n+ if (force || this.invalidBounds()) {\n+ var viewBounds = this.getViewBounds();\n+ var origin = this.getOrigin();\n+ var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n+ var viewBoundsWidth = viewBounds.getWidth();\n+ var viewBoundsHeight = viewBounds.getHeight();\n+ var aspectRatio = viewBoundsWidth / viewBoundsHeight;\n+ var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);\n+ var maxWidth = maxHeight * aspectRatio;\n+ var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);\n+ var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);\n+ var center = viewBounds.getCenterLonLat();\n+ this.gridBounds = new OpenLayers.Bounds(\n+ center.lon - (gridWidth / 2),\n+ center.lat - (gridHeight / 2),\n+ center.lon + (gridWidth / 2),\n+ center.lat + (gridHeight / 2)\n+ );\n+ var rows = Math.floor(gridHeight / this.dy);\n+ var cols = Math.floor(gridWidth / this.dx);\n+ var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));\n+ var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));\n+ var features = new Array(rows * cols);\n+ var x, y, point;\n+ for (var i = 0; i < cols; ++i) {\n+ x = gridLeft + (i * this.dx);\n+ for (var j = 0; j < rows; ++j) {\n+ y = gridBottom + (j * this.dy);\n+ point = new OpenLayers.Geometry.Point(x, y);\n+ if (this.rotation) {\n+ point.rotate(this.rotation, rotationOrigin);\n }\n+ features[(i * rows) + j] = new OpenLayers.Feature.Vector(point);\n }\n- return true;\n- }, this);\n+ }\n+ this.destroyFeatures(this.features, {\n+ silent: true\n+ });\n+ this.addFeatures(features, {\n+ silent: true\n+ });\n }\n },\n \n /**\n- * APIMethod: activate\n- * Activate the control. This registers any listeners.\n+ * Method: invalidBounds\n+ * Determine whether the previously generated point grid is invalid. \n+ * This occurs when the map bounds extends beyond the previously \n+ * generated grid bounds.\n *\n * Returns:\n- * {Boolean} Control successfully activated.\n+ * {Boolean} \n */\n- activate: function() {\n- var activated = false;\n- if (this.map) {\n- if (OpenLayers.Control.prototype.activate.apply(this)) {\n- if (this.listeners == null) {\n- this.setListeners();\n- }\n- for (var type in this.listeners) {\n- this.map.events.register(type, this, this.listeners[type]);\n- }\n- activated = true;\n- if (this.previousStack.length == 0) {\n- this.initStack();\n- }\n- }\n- }\n- return activated;\n+ invalidBounds: function() {\n+ return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.PointGrid\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/PointTrack.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.PointTrack\n+ * Vector layer to display ordered point features as a line, creating one\n+ * LineString feature for each pair of two points.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Vector> \n+ */\n+OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+\n /**\n- * Method: initStack\n- * Called after the control is activated if the previous history stack is\n- * empty.\n+ * APIProperty: dataFrom\n+ * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n+ * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n+ * should get the data/attributes from one of the two points it is\n+ * composed of, which one should it be?\n */\n- initStack: function() {\n- if (this.map.getCenter()) {\n- this.listeners.moveend();\n- }\n- },\n+ dataFrom: null,\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the control. This unregisters any listeners.\n+ * APIProperty: styleFrom\n+ * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n+ * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n+ * should get the style from one of the two points it is composed of,\n+ * which one should it be?\n+ */\n+ styleFrom: null,\n+\n+ /**\n+ * Constructor: OpenLayers.PointTrack\n+ * Constructor for a new OpenLayers.PointTrack instance.\n *\n- * Returns:\n- * {Boolean} Control successfully deactivated.\n+ * Parameters:\n+ * name - {String} name of the layer\n+ * options - {Object} Optional object with properties to tag onto the\n+ * instance.\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (this.map) {\n- if (OpenLayers.Control.prototype.deactivate.apply(this)) {\n- for (var type in this.listeners) {\n- this.map.events.unregister(\n- type, this, this.listeners[type]\n- );\n- }\n- if (this.clearOnDeactivate) {\n- this.clear();\n- }\n- deactivated = true;\n+\n+ /**\n+ * APIMethod: addNodes\n+ * Adds point features that will be used to create lines from, using point\n+ * pairs. The first point of a pair will be the source node, the second\n+ * will be the target node.\n+ * \n+ * Parameters:\n+ * pointFeatures - {Array(<OpenLayers.Feature>)}\n+ * options - {Object}\n+ * \n+ * Supported options:\n+ * silent - {Boolean} true to suppress (before)feature(s)added events\n+ */\n+ addNodes: function(pointFeatures, options) {\n+ if (pointFeatures.length < 2) {\n+ throw new Error(\"At least two point features have to be added to \" +\n+ \"create a line from\");\n+ }\n+\n+ var lines = new Array(pointFeatures.length - 1);\n+\n+ var pointFeature, startPoint, endPoint;\n+ for (var i = 0, len = pointFeatures.length; i < len; i++) {\n+ pointFeature = pointFeatures[i];\n+ endPoint = pointFeature.geometry;\n+\n+ if (!endPoint) {\n+ var lonlat = pointFeature.lonlat;\n+ endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);\n+ } else if (endPoint.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n+ throw new TypeError(\"Only features with point geometries are supported.\");\n+ }\n+\n+ if (i > 0) {\n+ var attributes = (this.dataFrom != null) ?\n+ (pointFeatures[i + this.dataFrom].data ||\n+ pointFeatures[i + this.dataFrom].attributes) :\n+ null;\n+ var style = (this.styleFrom != null) ?\n+ (pointFeatures[i + this.styleFrom].style) :\n+ null;\n+ var line = new OpenLayers.Geometry.LineString([startPoint,\n+ endPoint\n+ ]);\n+\n+ lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes,\n+ style);\n }\n+\n+ startPoint = endPoint;\n }\n- return deactivated;\n+\n+ this.addFeatures(lines, options);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.NavigationHistory\"\n+ CLASS_NAME: \"OpenLayers.Layer.PointTrack\"\n });\n \n+/**\n+ * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE\n+ * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n+ * <OpenLayers.Layer.PointTrack.styleFrom>\n+ */\n+OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;\n+\n+/**\n+ * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE\n+ * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n+ * <OpenLayers.Layer.PointTrack.styleFrom>\n+ */\n+OpenLayers.Layer.PointTrack.TARGET_NODE = 0;\n+\n+/**\n+ * Constant: OpenLayers.Layer.PointTrack.dataFrom\n+ * {Object} with the following keys - *deprecated*\n+ * - SOURCE_NODE: take data/attributes from the source node of the line\n+ * - TARGET_NODE: take data/attributes from the target node of the line\n+ */\n+OpenLayers.Layer.PointTrack.dataFrom = {\n+ 'SOURCE_NODE': -1,\n+ 'TARGET_NODE': 0\n+};\n /* ======================================================================\n- OpenLayers/Control/TouchNavigation.js\n+ OpenLayers/Layer/Google.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control/DragPan.js\n- * @requires OpenLayers/Control/PinchZoom.js\n- * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Layer/SphericalMercator.js\n+ * @requires OpenLayers/Layer/EventPane.js\n+ * @requires OpenLayers/Layer/FixedZoomLevels.js\n+ * @requires OpenLayers/Lang.js\n */\n \n /**\n- * Class: OpenLayers.Control.TouchNavigation\n- * The navigation control handles map browsing with touch events (dragging,\n- * double-tapping, tap with two fingers, and pinch zoom). Create a new \n- * control with the <OpenLayers.Control.TouchNavigation> constructor.\n- *\n- * If you\u2019re only targeting touch enabled devices with your mapping application,\n- * you can create a map with only a TouchNavigation control. The \n- * <OpenLayers.Control.Navigation> control is mobile ready by default, but \n- * you can generate a smaller build of the library by only including this\n- * touch navigation control if you aren't concerned about mouse interaction.\n- *\n- * Inherits:\n- * - <OpenLayers.Control>\n+ * Class: OpenLayers.Layer.Google\n+ * \n+ * Provides a wrapper for Google's Maps API\n+ * Normally the Terms of Use for this API do not allow wrapping, but Google\n+ * have provided written consent to OpenLayers for this - see email in \n+ * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.SphericalMercator>\n+ * - <OpenLayers.Layer.EventPane>\n+ * - <OpenLayers.Layer.FixedZoomLevels>\n */\n-OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Google = OpenLayers.Class(\n+ OpenLayers.Layer.EventPane,\n+ OpenLayers.Layer.FixedZoomLevels, {\n \n- /**\n- * Property: dragPan\n- * {<OpenLayers.Control.DragPan>}\n- */\n- dragPan: null,\n+ /** \n+ * Constant: MIN_ZOOM_LEVEL\n+ * {Integer} 0 \n+ */\n+ MIN_ZOOM_LEVEL: 0,\n \n- /**\n- * APIProperty: dragPanOptions\n- * {Object} Options passed to the DragPan control.\n- */\n- dragPanOptions: null,\n+ /** \n+ * Constant: MAX_ZOOM_LEVEL\n+ * {Integer} 21\n+ */\n+ MAX_ZOOM_LEVEL: 21,\n+\n+ /** \n+ * Constant: RESOLUTIONS\n+ * {Array(Float)} Hardcode these resolutions so that they are more closely\n+ * tied with the standard wms projection\n+ */\n+ RESOLUTIONS: [\n+ 1.40625,\n+ 0.703125,\n+ 0.3515625,\n+ 0.17578125,\n+ 0.087890625,\n+ 0.0439453125,\n+ 0.02197265625,\n+ 0.010986328125,\n+ 0.0054931640625,\n+ 0.00274658203125,\n+ 0.001373291015625,\n+ 0.0006866455078125,\n+ 0.00034332275390625,\n+ 0.000171661376953125,\n+ 0.0000858306884765625,\n+ 0.00004291534423828125,\n+ 0.00002145767211914062,\n+ 0.00001072883605957031,\n+ 0.00000536441802978515,\n+ 0.00000268220901489257,\n+ 0.0000013411045074462891,\n+ 0.00000067055225372314453\n+ ],\n+\n+ /**\n+ * APIProperty: type\n+ * {GMapType}\n+ */\n+ type: null,\n+\n+ /**\n+ * APIProperty: wrapDateLine\n+ * {Boolean} Allow user to pan forever east/west. Default is true. \n+ * Setting this to false only restricts panning if \n+ * <sphericalMercator> is true. \n+ */\n+ wrapDateLine: true,\n+\n+ /**\n+ * APIProperty: sphericalMercator\n+ * {Boolean} Should the map act as a mercator-projected map? This will\n+ * cause all interactions with the map to be in the actual map \n+ * projection, which allows support for vector drawing, overlaying \n+ * other maps, etc. \n+ */\n+ sphericalMercator: false,\n+\n+ /**\n+ * Property: version\n+ * {Number} The version of the Google Maps API\n+ */\n+ version: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Layer.Google\n+ * \n+ * Parameters:\n+ * name - {String} A name for the layer.\n+ * options - {Object} An optional object whose properties will be set\n+ * on the layer.\n+ */\n+ initialize: function(name, options) {\n+ options = options || {};\n+ if (!options.version) {\n+ options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\";\n+ }\n+ var mixin = OpenLayers.Layer.Google[\"v\" +\n+ options.version.replace(/\\./g, \"_\")];\n+ if (mixin) {\n+ OpenLayers.Util.applyDefaults(options, mixin);\n+ } else {\n+ throw \"Unsupported Google Maps API version: \" + options.version;\n+ }\n+\n+ OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n+ if (options.maxExtent) {\n+ options.maxExtent = options.maxExtent.clone();\n+ }\n+\n+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this,\n+ [name, options]);\n+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,\n+ [name, options]);\n+\n+ if (this.sphericalMercator) {\n+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n+ this.initMercatorParameters();\n+ }\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Google>} An exact clone of this layer\n+ */\n+ clone: function() {\n+ /**\n+ * This method isn't intended to be called by a subclass and it\n+ * doesn't call the same method on the superclass. We don't call\n+ * the super's clone because we don't want properties that are set\n+ * on this layer after initialize (i.e. this.mapObject etc.).\n+ */\n+ return new OpenLayers.Layer.Google(\n+ this.name, this.getOptions()\n+ );\n+ },\n+\n+ /**\n+ * APIMethod: setVisibility\n+ * Set the visibility flag for the layer and hide/show & redraw \n+ * accordingly. Fire event unless otherwise specified\n+ * \n+ * Note that visibility is no longer simply whether or not the layer's\n+ * style.display is set to \"block\". Now we store a 'visibility' state \n+ * property on the layer class, this allows us to remember whether or \n+ * not we *desire* for a layer to be visible. In the case where the \n+ * map's resolution is out of the layer's range, this desire may be \n+ * subverted.\n+ * \n+ * Parameters:\n+ * visible - {Boolean} Display the layer (if in range)\n+ */\n+ setVisibility: function(visible) {\n+ // sharing a map container, opacity has to be set per layer\n+ var opacity = this.opacity == null ? 1 : this.opacity;\n+ OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n+ this.setOpacity(opacity);\n+ },\n+\n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer\n+ * \n+ * Parameters:\n+ * visible - {Boolean}\n+ */\n+ display: function(visible) {\n+ if (!this._dragging) {\n+ this.setGMapVisibility(visible);\n+ }\n+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n+ * do some init work in that case.\n+ * dragging - {Boolean}\n+ */\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ this._dragging = dragging;\n+ OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n+ delete this._dragging;\n+ },\n+\n+ /**\n+ * APIMethod: setOpacity\n+ * Sets the opacity for the entire layer (all images)\n+ * \n+ * Parameters:\n+ * opacity - {Float}\n+ */\n+ setOpacity: function(opacity) {\n+ if (opacity !== this.opacity) {\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ });\n+ }\n+ this.opacity = opacity;\n+ }\n+ // Though this layer's opacity may not change, we're sharing a container\n+ // and need to update the opacity for the entire container.\n+ if (this.getVisibility()) {\n+ var container = this.getMapContainer();\n+ OpenLayers.Util.modifyDOMElement(\n+ container, null, null, null, null, null, null, opacity\n+ );\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Clean up this layer.\n+ */\n+ destroy: function() {\n+ /**\n+ * We have to override this method because the event pane destroy\n+ * deletes the mapObject reference before removing this layer from\n+ * the map.\n+ */\n+ if (this.map) {\n+ this.setGMapVisibility(false);\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache && cache.count <= 1) {\n+ this.removeGMapElements();\n+ }\n+ }\n+ OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: removeGMapElements\n+ * Remove all elements added to the dom. This should only be called if\n+ * this is the last of the Google layers for the given map.\n+ */\n+ removeGMapElements: function() {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // remove shared elements from dom\n+ var container = this.mapObject && this.getMapContainer();\n+ if (container && container.parentNode) {\n+ container.parentNode.removeChild(container);\n+ }\n+ var termsOfUse = cache.termsOfUse;\n+ if (termsOfUse && termsOfUse.parentNode) {\n+ termsOfUse.parentNode.removeChild(termsOfUse);\n+ }\n+ var poweredBy = cache.poweredBy;\n+ if (poweredBy && poweredBy.parentNode) {\n+ poweredBy.parentNode.removeChild(poweredBy);\n+ }\n+ if (this.mapObject && window.google && google.maps &&\n+ google.maps.event && google.maps.event.clearListeners) {\n+ google.maps.event.clearListeners(this.mapObject, 'tilesloaded');\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: removeMap\n+ * On being removed from the map, also remove termsOfUse and poweredBy divs\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ removeMap: function(map) {\n+ // hide layer before removing\n+ if (this.visibility && this.mapObject) {\n+ this.setGMapVisibility(false);\n+ }\n+ // check to see if last Google layer in this map\n+ var cache = OpenLayers.Layer.Google.cache[map.id];\n+ if (cache) {\n+ if (cache.count <= 1) {\n+ this.removeGMapElements();\n+ delete OpenLayers.Layer.Google.cache[map.id];\n+ } else {\n+ // decrement the layer count\n+ --cache.count;\n+ }\n+ }\n+ // remove references to gmap elements\n+ delete this.termsOfUse;\n+ delete this.poweredBy;\n+ delete this.mapObject;\n+ delete this.dragObject;\n+ OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);\n+ },\n+\n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n+\n+ /**\n+ * APIMethod: getOLBoundsFromMapObjectBounds\n+ * \n+ * Parameters:\n+ * moBounds - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the \n+ * passed-in MapObject Bounds.\n+ * Returns null if null value is passed in.\n+ */\n+ getOLBoundsFromMapObjectBounds: function(moBounds) {\n+ var olBounds = null;\n+ if (moBounds != null) {\n+ var sw = moBounds.getSouthWest();\n+ var ne = moBounds.getNorthEast();\n+ if (this.sphericalMercator) {\n+ sw = this.forwardMercator(sw.lng(), sw.lat());\n+ ne = this.forwardMercator(ne.lng(), ne.lat());\n+ } else {\n+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat());\n+ }\n+ olBounds = new OpenLayers.Bounds(sw.lon,\n+ sw.lat,\n+ ne.lon,\n+ ne.lat);\n+ }\n+ return olBounds;\n+ },\n+\n+ /** \n+ * APIMethod: getWarningHTML\n+ * \n+ * Returns: \n+ * {String} String with information on why layer is broken, how to get\n+ * it working.\n+ */\n+ getWarningHTML: function() {\n+ return OpenLayers.i18n(\"googleWarning\");\n+ },\n+\n+\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n+\n+\n+ // Get&Set Center, Zoom\n+\n+ /**\n+ * APIMethod: getMapObjectCenter\n+ * \n+ * Returns: \n+ * {Object} The mapObject's current center in Map Object format\n+ */\n+ getMapObjectCenter: function() {\n+ return this.mapObject.getCenter();\n+ },\n+\n+ /** \n+ * APIMethod: getMapObjectZoom\n+ * \n+ * Returns:\n+ * {Integer} The mapObject's current zoom, in Map Object format\n+ */\n+ getMapObjectZoom: function() {\n+ return this.mapObject.getZoom();\n+ },\n+\n+\n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n+\n+\n+ // LonLat\n+\n+ /**\n+ * APIMethod: getLongitudeFromMapObjectLonLat\n+ * \n+ * Parameters:\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n+ * Returns:\n+ * {Float} Longitude of the given MapObject LonLat\n+ */\n+ getLongitudeFromMapObjectLonLat: function(moLonLat) {\n+ return this.sphericalMercator ?\n+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :\n+ moLonLat.lng();\n+ },\n+\n+ /**\n+ * APIMethod: getLatitudeFromMapObjectLonLat\n+ * \n+ * Parameters:\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n+ * Returns:\n+ * {Float} Latitude of the given MapObject LonLat\n+ */\n+ getLatitudeFromMapObjectLonLat: function(moLonLat) {\n+ var lat = this.sphericalMercator ?\n+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :\n+ moLonLat.lat();\n+ return lat;\n+ },\n+\n+ // Pixel\n+\n+ /**\n+ * APIMethod: getXFromMapObjectPixel\n+ * \n+ * Parameters:\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Integer} X value of the MapObject Pixel\n+ */\n+ getXFromMapObjectPixel: function(moPixel) {\n+ return moPixel.x;\n+ },\n+\n+ /**\n+ * APIMethod: getYFromMapObjectPixel\n+ * \n+ * Parameters:\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Integer} Y value of the MapObject Pixel\n+ */\n+ getYFromMapObjectPixel: function(moPixel) {\n+ return moPixel.y;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Google\"\n+ });\n+\n+/**\n+ * Property: OpenLayers.Layer.Google.cache\n+ * {Object} Cache for elements that should only be created once per map.\n+ */\n+OpenLayers.Layer.Google.cache = {};\n+\n+\n+/**\n+ * Constant: OpenLayers.Layer.Google.v2\n+ * \n+ * Mixin providing functionality specific to the Google Maps API v2.\n+ * \n+ * This API has been deprecated by Google.\n+ * Developers are encouraged to migrate to v3 of the API; support for this\n+ * is provided by <OpenLayers.Layer.Google.v3>\n+ */\n+OpenLayers.Layer.Google.v2 = {\n \n /**\n- * Property: pinchZoom\n- * {<OpenLayers.Control.PinchZoom>}\n+ * Property: termsOfUse\n+ * {DOMElement} Div for Google's copyright and terms of use link\n */\n- pinchZoom: null,\n+ termsOfUse: null,\n \n /**\n- * APIProperty: pinchZoomOptions\n- * {Object} Options passed to the PinchZoom control.\n+ * Property: poweredBy\n+ * {DOMElement} Div for Google's powered by logo and link\n */\n- pinchZoomOptions: null,\n+ poweredBy: null,\n \n /**\n- * APIProperty: clickHandlerOptions\n- * {Object} Options passed to the Click handler.\n+ * Property: dragObject\n+ * {GDraggableObject} Since 2.93, Google has exposed the ability to get\n+ * the maps GDraggableObject. We can now use this for smooth panning\n */\n- clickHandlerOptions: null,\n+ dragObject: null,\n \n- /**\n- * APIProperty: documentDrag\n- * {Boolean} Allow panning of the map by dragging outside map viewport.\n- * Default is false.\n+ /** \n+ * Method: loadMapObject\n+ * Load the GMap and register appropriate event listeners. If we can't \n+ * load GMap2, then display a warning message.\n */\n- documentDrag: false,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = G_NORMAL_MAP;\n+ }\n+ var mapObject, termsOfUse, poweredBy;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // there are already Google layers added to this map\n+ mapObject = cache.mapObject;\n+ termsOfUse = cache.termsOfUse;\n+ poweredBy = cache.poweredBy;\n+ // increment the layer count\n+ ++cache.count;\n+ } else {\n+ // this is the first Google layer for this map\n+\n+ var container = this.map.viewPortDiv;\n+ var div = document.createElement(\"div\");\n+ div.id = this.map.id + \"_GMap2Container\";\n+ div.style.position = \"absolute\";\n+ div.style.width = \"100%\";\n+ div.style.height = \"100%\";\n+ container.appendChild(div);\n+\n+ // create GMap and shuffle elements\n+ try {\n+ mapObject = new GMap2(div);\n+\n+ // move the ToS and branding stuff up to the container div\n+ termsOfUse = div.lastChild;\n+ container.appendChild(termsOfUse);\n+ termsOfUse.style.zIndex = \"1100\";\n+ termsOfUse.style.right = \"\";\n+ termsOfUse.style.bottom = \"\";\n+ termsOfUse.className = \"olLayerGoogleCopyright\";\n+\n+ poweredBy = div.lastChild;\n+ container.appendChild(poweredBy);\n+ poweredBy.style.zIndex = \"1100\";\n+ poweredBy.style.right = \"\";\n+ poweredBy.style.bottom = \"\";\n+ poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\";\n+\n+ } catch (e) {\n+ throw (e);\n+ }\n+ // cache elements for use by any other google layers added to\n+ // this same map\n+ OpenLayers.Layer.Google.cache[this.map.id] = {\n+ mapObject: mapObject,\n+ termsOfUse: termsOfUse,\n+ poweredBy: poweredBy,\n+ count: 1\n+ };\n+ }\n+\n+ this.mapObject = mapObject;\n+ this.termsOfUse = termsOfUse;\n+ this.poweredBy = poweredBy;\n+\n+ // ensure this layer type is one of the mapObject types\n+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),\n+ this.type) === -1) {\n+ this.mapObject.addMapType(this.type);\n+ }\n+\n+ //since v 2.93 getDragObject is now available.\n+ if (typeof mapObject.getDragObject == \"function\") {\n+ this.dragObject = mapObject.getDragObject();\n+ } else {\n+ this.dragPanMapObject = null;\n+ }\n+\n+ if (this.isBaseLayer === false) {\n+ this.setGMapVisibility(this.div.style.display !== \"none\");\n+ }\n+\n+ },\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n+ * APIMethod: onMapResize\n */\n- autoActivate: true,\n+ onMapResize: function() {\n+ // workaround for resizing of invisible or not yet fully loaded layers\n+ // where GMap2.checkResize() does not work. We need to load the GMap\n+ // for the old div size, then checkResize(), and then call\n+ // layer.moveTo() to trigger GMap.setCenter() (which will finish\n+ // the GMap initialization).\n+ if (this.visibility && this.mapObject.isLoaded()) {\n+ this.mapObject.checkResize();\n+ } else {\n+ if (!this._resized) {\n+ var layer = this;\n+ var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n+ GEvent.removeListener(handle);\n+ delete layer._resized;\n+ layer.mapObject.checkResize();\n+ layer.moveTo(layer.map.getCenter(), layer.map.getZoom());\n+ });\n+ }\n+ this._resized = true;\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Control.TouchNavigation\n- * Create a new navigation control\n- *\n+ * Method: setGMapVisibility\n+ * Display the GMap container and associated elements.\n+ * \n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * the control\n+ * visible - {Boolean} Display the GMap elements.\n */\n- initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject.getContainer();\n+ if (visible === true) {\n+ this.mapObject.setMapType(this.type);\n+ container.style.display = \"\";\n+ this.termsOfUse.style.left = \"\";\n+ this.termsOfUse.style.display = \"\";\n+ this.poweredBy.style.display = \"\";\n+ cache.displayed = this.id;\n+ } else {\n+ if (cache.displayed === this.id) {\n+ delete cache.displayed;\n+ }\n+ if (!cache.displayed) {\n+ container.style.display = \"none\";\n+ this.termsOfUse.style.display = \"none\";\n+ // move ToU far to the left in addition to setting display\n+ // to \"none\", because at the end of the GMap2 load\n+ // sequence, display: none will be unset and ToU would be\n+ // visible after loading a map with a google layer that is\n+ // initially hidden. \n+ this.termsOfUse.style.left = \"-9999px\";\n+ this.poweredBy.style.display = \"none\";\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ * Method: getMapContainer\n+ * \n+ * Returns:\n+ * {DOMElement} the GMap container's div\n */\n- destroy: function() {\n- this.deactivate();\n- if (this.dragPan) {\n- this.dragPan.destroy();\n- }\n- this.dragPan = null;\n- if (this.pinchZoom) {\n- this.pinchZoom.destroy();\n- delete this.pinchZoom;\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ getMapContainer: function() {\n+ return this.mapObject.getContainer();\n },\n \n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n+\n /**\n- * Method: activate\n+ * APIMethod: getMapObjectBoundsFromOLBounds\n+ * \n+ * Parameters:\n+ * olBounds - {<OpenLayers.Bounds>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Bounds, translated from olBounds\n+ * Returns null if null value is passed in\n */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.dragPan.activate();\n- this.handlers.click.activate();\n- this.pinchZoom.activate();\n- return true;\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.bottom, olBounds.left) :\n+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.top, olBounds.right) :\n+ new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),\n+ new GLatLng(ne.lat, ne.lon));\n }\n- return false;\n+ return moBounds;\n },\n \n- /**\n- * Method: deactivate\n+\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n+\n+\n+ // Get&Set Center, Zoom\n+\n+ /** \n+ * APIMethod: setMapObjectCenter\n+ * Set the mapObject to the specified center and zoom\n+ * \n+ * Parameters:\n+ * center - {Object} MapObject LonLat format\n+ * zoom - {int} MapObject zoom format\n */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.pinchZoom.deactivate();\n- return true;\n- }\n- return false;\n+ setMapObjectCenter: function(center, zoom) {\n+ this.mapObject.setCenter(center, zoom);\n },\n \n /**\n- * Method: draw\n+ * APIMethod: dragPanMapObject\n+ * \n+ * Parameters:\n+ * dX - {Integer}\n+ * dY - {Integer}\n */\n- draw: function() {\n- var clickCallbacks = {\n- click: this.defaultClick,\n- dblclick: this.defaultDblClick\n- };\n- var clickOptions = OpenLayers.Util.extend({\n- \"double\": true,\n- stopDouble: true,\n- pixelTolerance: 2\n- }, this.clickHandlerOptions);\n- this.handlers.click = new OpenLayers.Handler.Click(\n- this, clickCallbacks, clickOptions\n- );\n- this.dragPan = new OpenLayers.Control.DragPan(\n- OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions)\n- );\n- this.dragPan.draw();\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(\n- OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions)\n- );\n+ dragPanMapObject: function(dX, dY) {\n+ this.dragObject.moveBy(new GSize(-dX, dY));\n },\n \n+\n+ // LonLat - Pixel Translation\n+\n /**\n- * Method: defaultClick\n- *\n+ * APIMethod: getMapObjectLonLatFromMapObjectPixel\n+ * \n * Parameters:\n- * evt - {Event}\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Object} MapObject LonLat translated from MapObject Pixel\n */\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut();\n- }\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ return this.mapObject.fromContainerPixelToLatLng(moPixel);\n },\n \n /**\n- * Method: defaultDblClick\n- *\n+ * APIMethod: getMapObjectPixelFromMapObjectLonLat\n+ * \n * Parameters:\n- * evt - {Event}\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n+ * Returns:\n+ * {Object} MapObject Pixel transtlated from MapObject LonLat\n */\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy);\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ return this.mapObject.fromLatLngToContainerPixel(moLonLat);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n-});\n-/* ======================================================================\n- OpenLayers/Control/LayerSwitcher.js\n- ====================================================================== */\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // Bounds\n \n-/**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Lang.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Events/buttonclick.js\n- */\n+ /** \n+ * APIMethod: getMapObjectZoomFromMapObjectBounds\n+ * \n+ * Parameters:\n+ * moBounds - {Object} MapObject Bounds format\n+ * \n+ * Returns:\n+ * {Object} MapObject Zoom for specified MapObject Bounds\n+ */\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds);\n+ },\n \n-/**\n- * Class: OpenLayers.Control.LayerSwitcher\n- * The LayerSwitcher control displays a table of contents for the map. This\n- * allows the user interface to switch between BaseLasyers and to show or hide\n- * Overlays. By default the switcher is shown minimized on the right edge of\n- * the map, the user may expand it by clicking on the handle.\n- *\n- * To create the LayerSwitcher outside of the map, pass the Id of a html div\n- * as the first argument to the constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n \n- /** \n- * Property: layerStates \n- * {Array(Object)} Basically a copy of the \"state\" of the map's layers \n- * the last time the control was drawn. We have this in order to avoid\n- * unnecessarily redrawing the control.\n- */\n- layerStates: null,\n \n- // DOM Elements\n+ // LonLat\n \n /**\n- * Property: layersDiv\n- * {DOMElement}\n+ * APIMethod: getMapObjectLonLatFromLonLat\n+ * \n+ * Parameters:\n+ * lon - {Float}\n+ * lat - {Float}\n+ * \n+ * Returns:\n+ * {Object} MapObject LonLat built from lon and lat params\n */\n- layersDiv: null,\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon);\n+ } else {\n+ gLatLng = new GLatLng(lat, lon);\n+ }\n+ return gLatLng;\n+ },\n+\n+ // Pixel\n \n /**\n- * Property: baseLayersDiv\n- * {DOMElement}\n+ * APIMethod: getMapObjectPixelFromXY\n+ * \n+ * Parameters:\n+ * x - {Integer}\n+ * y - {Integer}\n+ * \n+ * Returns:\n+ * {Object} MapObject Pixel from x and y parameters\n */\n- baseLayersDiv: null,\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new GPoint(x, y);\n+ }\n+\n+};\n+/* ======================================================================\n+ OpenLayers/Layer/Image.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * Property: baseLayers\n- * {Array(Object)}\n- */\n- baseLayers: null,\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Tile/Image.js\n+ */\n \n+/**\n+ * Class: OpenLayers.Layer.Image\n+ * Instances of OpenLayers.Layer.Image are used to display data from a web\n+ * accessible image as a map layer. Create a new image layer with the\n+ * <OpenLayers.Layer.Image> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer>\n+ */\n+OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {\n \n /**\n- * Property: dataLbl\n- * {DOMElement}\n+ * Property: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is true. Set this property\n+ * in the layer options\n */\n- dataLbl: null,\n+ isBaseLayer: true,\n \n /**\n- * Property: dataLayersDiv\n- * {DOMElement}\n+ * Property: url\n+ * {String} URL of the image to use\n */\n- dataLayersDiv: null,\n+ url: null,\n \n /**\n- * Property: dataLayers\n- * {Array(Object)}\n+ * Property: extent\n+ * {<OpenLayers.Bounds>} The image bounds in map units. This extent will\n+ * also be used as the default maxExtent for the layer. If you wish\n+ * to have a maxExtent that is different than the image extent, set the\n+ * maxExtent property of the options argument (as with any other layer).\n */\n- dataLayers: null,\n-\n+ extent: null,\n \n /**\n- * Property: minimizeDiv\n- * {DOMElement}\n+ * Property: size\n+ * {<OpenLayers.Size>} The image size in pixels\n */\n- minimizeDiv: null,\n+ size: null,\n \n /**\n- * Property: maximizeDiv\n- * {DOMElement}\n+ * Property: tile\n+ * {<OpenLayers.Tile.Image>}\n */\n- maximizeDiv: null,\n+ tile: null,\n \n /**\n- * APIProperty: ascending\n- * {Boolean}\n+ * Property: aspectRatio\n+ * {Float} The ratio of height/width represented by a single pixel in the\n+ * graphic\n */\n- ascending: true,\n+ aspectRatio: null,\n \n /**\n- * Constructor: OpenLayers.Control.LayerSwitcher\n+ * Constructor: OpenLayers.Layer.Image\n+ * Create a new image layer\n *\n * Parameters:\n- * options - {Object}\n- */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- this.layerStates = [];\n- },\n-\n- /**\n- * APIMethod: destroy\n+ * name - {String} A name for the layer.\n+ * url - {String} Relative or absolute path to the image\n+ * extent - {<OpenLayers.Bounds>} The extent represented by the image\n+ * size - {<OpenLayers.Size>} The size (in pixels) of the image\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- destroy: function() {\n-\n- //clear out layers info and unregister their events\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n-\n- this.map.events.un({\n- buttonclick: this.onButtonClick,\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n- });\n- this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+ initialize: function(name, url, extent, size, options) {\n+ this.url = url;\n+ this.extent = extent;\n+ this.maxExtent = extent;\n+ this.size = size;\n+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ this.aspectRatio = (this.extent.getHeight() / this.size.h) /\n+ (this.extent.getWidth() / this.size.w);\n },\n \n /**\n- * Method: setMap\n- *\n- * Properties:\n- * map - {<OpenLayers.Map>}\n+ * Method: destroy\n+ * Destroy this layer\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n-\n- this.map.events.on({\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n- });\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick);\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ destroy: function() {\n+ if (this.tile) {\n+ this.removeTileMonitoringHooks(this.tile);\n+ this.tile.destroy();\n+ this.tile = null;\n }\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: draw\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Paramters:\n+ * obj - {Object} An optional layer (is this ever used?)\n *\n * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the\n- * switcher tabs.\n+ * {<OpenLayers.Layer.Image>} An exact copy of this layer\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this);\n-\n- // create layout divs\n- this.loadContents();\n+ clone: function(obj) {\n \n- // set mode to minimize\n- if (!this.outsideViewport) {\n- this.minimizeControl();\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Image(this.name,\n+ this.url,\n+ this.extent,\n+ this.size,\n+ this.getOptions());\n }\n \n- // populate div with current info\n- this.redraw();\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n \n- return this.div;\n- },\n+ // copy/set any non-init, non-simple values here\n \n- /**\n- * Method: onButtonClick\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- onButtonClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.minimizeDiv) {\n- this.minimizeControl();\n- } else if (button === this.maximizeDiv) {\n- this.maximizeControl();\n- } else if (button._layerSwitcher === this.id) {\n- if (button[\"for\"]) {\n- button = document.getElementById(button[\"for\"]);\n- }\n- if (!button.disabled) {\n- if (button.type == \"radio\") {\n- button.checked = true;\n- this.map.setBaseLayer(this.map.getLayer(button._layer));\n- } else {\n- button.checked = !button.checked;\n- this.updateMap();\n- }\n- }\n- }\n+ return obj;\n },\n \n /**\n- * Method: clearLayersArray\n- * User specifies either \"base\" or \"data\". we then clear all the\n- * corresponding listeners, the div, and reinitialize a new array.\n- *\n+ * APIMethod: setMap\n+ * \n * Parameters:\n- * layersType - {String}\n- */\n- clearLayersArray: function(layersType) {\n- this[layersType + \"LayersDiv\"].innerHTML = \"\";\n- this[layersType + \"Layers\"] = [];\n- },\n-\n-\n- /**\n- * Method: checkRedraw\n- * Checks if the layer state has changed since the last redraw() call.\n- *\n- * Returns:\n- * {Boolean} The layer state changed since the last redraw() call.\n+ * map - {<OpenLayers.Map>}\n */\n- checkRedraw: function() {\n- if (!this.layerStates.length ||\n- (this.map.layers.length != this.layerStates.length)) {\n- return true;\n- }\n-\n- for (var i = 0, len = this.layerStates.length; i < len; i++) {\n- var layerState = this.layerStates[i];\n- var layer = this.map.layers[i];\n- if ((layerState.name != layer.name) ||\n- (layerState.inRange != layer.inRange) ||\n- (layerState.id != layer.id) ||\n- (layerState.visibility != layer.visibility)) {\n- return true;\n- }\n+ setMap: function(map) {\n+ /**\n+ * If nothing to do with resolutions has been set, assume a single\n+ * resolution determined by ratio*extent/size - if an image has a\n+ * pixel aspect ratio different than one (as calculated above), the\n+ * image will be stretched in one dimension only.\n+ */\n+ if (this.options.maxResolution == null) {\n+ this.options.maxResolution = this.aspectRatio *\n+ this.extent.getWidth() /\n+ this.size.w;\n }\n-\n- return false;\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n },\n \n- /**\n- * Method: redraw\n- * Goes through and takes the current state of the Map and rebuilds the\n- * control to display that state. Groups base layers into a\n- * radio-button group and lists each data layer with a checkbox.\n- *\n- * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the control\n+ /** \n+ * Method: moveTo\n+ * Create the tile for the image or resize it for the new resolution\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- redraw: function() {\n- //if the state hasn't changed since last redraw, no need\n- // to do anything. Just return the existing div.\n- if (!this.checkRedraw()) {\n- return this.div;\n- }\n-\n- //clear out previous layers\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n-\n- var containsOverlays = false;\n- var containsBaseLayers = false;\n-\n- // Save state -- for checking layer if the map state changed.\n- // We save this before redrawing, because in the process of redrawing\n- // we will trigger more visibility changes, and we want to not redraw\n- // and enter an infinite loop.\n- var len = this.map.layers.length;\n- this.layerStates = new Array(len);\n- for (var i = 0; i < len; i++) {\n- var layer = this.map.layers[i];\n- this.layerStates[i] = {\n- 'name': layer.name,\n- 'visibility': layer.visibility,\n- 'inRange': layer.inRange,\n- 'id': layer.id\n- };\n- }\n-\n- var layers = this.map.layers.slice();\n- if (!this.ascending) {\n- layers.reverse();\n- }\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var baseLayer = layer.isBaseLayer;\n-\n- if (layer.displayInLayerSwitcher) {\n-\n- if (baseLayer) {\n- containsBaseLayers = true;\n- } else {\n- containsOverlays = true;\n- }\n-\n- // only check a baselayer if it is *the* baselayer, check data\n- // layers if they are visible\n- var checked = (baseLayer) ? (layer == this.map.baseLayer) :\n- layer.getVisibility();\n-\n- // create input element\n- var inputElem = document.createElement(\"input\"),\n- // The input shall have an id attribute so we can use\n- // labels to interact with them.\n- inputId = OpenLayers.Util.createUniqueID(\n- this.id + \"_input_\"\n- );\n-\n- inputElem.id = inputId;\n- inputElem.name = (baseLayer) ? this.id + \"_baseLayers\" : layer.name;\n- inputElem.type = (baseLayer) ? \"radio\" : \"checkbox\";\n- inputElem.value = layer.name;\n- inputElem.checked = checked;\n- inputElem.defaultChecked = checked;\n- inputElem.className = \"olButton\";\n- inputElem._layer = layer.id;\n- inputElem._layerSwitcher = this.id;\n-\n- if (!baseLayer && !layer.inRange) {\n- inputElem.disabled = true;\n- }\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n \n- // create span\n- var labelSpan = document.createElement(\"label\");\n- // this isn't the DOM attribute 'for', but an arbitrary name we\n- // use to find the appropriate input element in <onButtonClick>\n- labelSpan[\"for\"] = inputElem.id;\n- OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n- labelSpan._layer = layer.id;\n- labelSpan._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- labelSpan.style.color = \"gray\";\n- }\n- labelSpan.innerHTML = layer.name;\n- labelSpan.style.verticalAlign = (baseLayer) ? \"bottom\" :\n- \"baseline\";\n- // create line break\n- var br = document.createElement(\"br\");\n+ var firstRendering = (this.tile == null);\n \n+ if (zoomChanged || firstRendering) {\n \n- var groupArray = (baseLayer) ? this.baseLayers :\n- this.dataLayers;\n- groupArray.push({\n- 'layer': layer,\n- 'inputElem': inputElem,\n- 'labelSpan': labelSpan\n- });\n+ //determine new tile size\n+ this.setTileSize();\n \n+ //determine new position (upper left corner of new bounds)\n+ var ulPx = this.map.getLayerPxFromLonLat({\n+ lon: this.extent.left,\n+ lat: this.extent.top\n+ });\n \n- var groupDiv = (baseLayer) ? this.baseLayersDiv :\n- this.dataLayersDiv;\n- groupDiv.appendChild(inputElem);\n- groupDiv.appendChild(labelSpan);\n- groupDiv.appendChild(br);\n+ if (firstRendering) {\n+ //create the new tile\n+ this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,\n+ null, this.tileSize);\n+ this.addTileMonitoringHooks(this.tile);\n+ } else {\n+ //just resize the tile and set it's new position\n+ this.tile.size = this.tileSize.clone();\n+ this.tile.position = ulPx.clone();\n }\n+ this.tile.draw();\n }\n-\n- // if no overlays, dont display the overlay label\n- this.dataLbl.style.display = (containsOverlays) ? \"\" : \"none\";\n-\n- // if no baselayers, dont display the baselayer label\n- this.baseLbl.style.display = (containsBaseLayers) ? \"\" : \"none\";\n-\n- return this.div;\n },\n \n /**\n- * Method: updateMap\n- * Cycles through the loaded data and base layer input arrays and makes\n- * the necessary calls to the Map object such that that the map's\n- * visual state corresponds to what the user has selected in\n- * the control.\n+ * Set the tile size based on the map size.\n */\n- updateMap: function() {\n-\n- // set the newly selected base layer\n- for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n- var layerEntry = this.baseLayers[i];\n- if (layerEntry.inputElem.checked) {\n- this.map.setBaseLayer(layerEntry.layer, false);\n- }\n- }\n-\n- // set the correct visibilities for the overlays\n- for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n- var layerEntry = this.dataLayers[i];\n- layerEntry.layer.setVisibility(layerEntry.inputElem.checked);\n- }\n-\n+ setTileSize: function() {\n+ var tileWidth = this.extent.getWidth() / this.map.getResolution();\n+ var tileHeight = this.extent.getHeight() / this.map.getResolution();\n+ this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);\n },\n \n- /**\n- * Method: maximizeControl\n- * Set up the labels and divs for the control\n- *\n- * Parameters:\n- * e - {Event}\n+ /** \n+ * Method: addTileMonitoringHooks\n+ * This function takes a tile as input and adds the appropriate hooks to \n+ * the tile so that the layer can keep track of the loading tiles.\n+ * \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n */\n- maximizeControl: function(e) {\n-\n- // set the div's width and height to empty values, so\n- // the div dimensions can be controlled by CSS\n- this.div.style.width = \"\";\n- this.div.style.height = \"\";\n-\n- this.showControls(false);\n+ addTileMonitoringHooks: function(tile) {\n+ tile.onLoadStart = function() {\n+ this.events.triggerEvent(\"loadstart\");\n+ };\n+ tile.events.register(\"loadstart\", this, tile.onLoadStart);\n \n- if (e != null) {\n- OpenLayers.Event.stop(e);\n- }\n+ tile.onLoadEnd = function() {\n+ this.events.triggerEvent(\"loadend\");\n+ };\n+ tile.events.register(\"loadend\", this, tile.onLoadEnd);\n+ tile.events.register(\"unload\", this, tile.onLoadEnd);\n },\n \n- /**\n- * Method: minimizeControl\n- * Hide all the contents of the control, shrink the size,\n- * add the maximize icon\n- *\n- * Parameters:\n- * e - {Event}\n+ /** \n+ * Method: removeTileMonitoringHooks\n+ * This function takes a tile as input and removes the tile hooks \n+ * that were added in <addTileMonitoringHooks>.\n+ * \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n */\n- minimizeControl: function(e) {\n-\n- // to minimize the control we set its div's width\n- // and height to 0px, we cannot just set \"display\"\n- // to \"none\" because it would hide the maximize\n- // div\n- this.div.style.width = \"0px\";\n- this.div.style.height = \"0px\";\n-\n- this.showControls(true);\n-\n- if (e != null) {\n- OpenLayers.Event.stop(e);\n- }\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: showControls\n- * Hide/Show all LayerSwitcher controls depending on whether we are\n- * minimized or not\n- *\n+ * APIMethod: setUrl\n+ * \n * Parameters:\n- * minimize - {Boolean}\n+ * newUrl - {String}\n */\n- showControls: function(minimize) {\n-\n- this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n- this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n-\n- this.layersDiv.style.display = minimize ? \"none\" : \"\";\n+ setUrl: function(newUrl) {\n+ this.url = newUrl;\n+ this.tile.draw();\n },\n \n- /**\n- * Method: loadContents\n- * Set up the labels and divs for the control\n+ /** \n+ * APIMethod: getURL\n+ * The url we return is always the same (the image itself never changes)\n+ * so we can ignore the bounds parameter (it will always be the same, \n+ * anyways) \n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- loadContents: function() {\n-\n- // layers list div\n- this.layersDiv = document.createElement(\"div\");\n- this.layersDiv.id = this.id + \"_layersDiv\";\n- OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n-\n- this.baseLbl = document.createElement(\"div\");\n- this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n- OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n-\n- this.baseLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n-\n- this.dataLbl = document.createElement(\"div\");\n- this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n- OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n-\n- this.dataLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n-\n- if (this.ascending) {\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- } else {\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n- }\n-\n- this.div.appendChild(this.layersDiv);\n-\n- // maximize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n- this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- \"OpenLayers_Control_MaximizeDiv\",\n- null,\n- null,\n- img,\n- \"absolute\");\n- OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n- this.maximizeDiv.style.display = \"none\";\n-\n- this.div.appendChild(this.maximizeDiv);\n-\n- // minimize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n- this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- \"OpenLayers_Control_MinimizeDiv\",\n- null,\n- null,\n- img,\n- \"absolute\");\n- OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n- this.minimizeDiv.style.display = \"none\";\n-\n- this.div.appendChild(this.minimizeDiv);\n+ getURL: function(bounds) {\n+ return this.url;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+ CLASS_NAME: \"OpenLayers.Layer.Image\"\n });\n /* ======================================================================\n- OpenLayers/Control/Graticule.js\n+ OpenLayers/Layer/Google/v3.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Lang.js\n- * @requires OpenLayers/Rule.js\n- * @requires OpenLayers/StyleMap.js\n- * @requires OpenLayers/Layer/Vector.js\n+ * @requires OpenLayers/Layer/Google.js\n */\n \n /**\n- * Class: OpenLayers.Control.Graticule\n- * The Graticule displays a grid of latitude/longitude lines reprojected on\n- * the map. \n+ * Constant: OpenLayers.Layer.Google.v3\n * \n- * Inherits from:\n- * - <OpenLayers.Control>\n- * \n+ * Mixin providing functionality specific to the Google Maps API v3.\n+ * \n+ * To use this layer, you must include the GMaps v3 API in your html.\n+ * \n+ * Note that this layer configures the google.maps.map object with the\n+ * \"disableDefaultUI\" option set to true. Using UI controls that the Google\n+ * Maps API provides is not supported by the OpenLayers API.\n */\n-OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {\n-\n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true. \n- */\n- autoActivate: true,\n+OpenLayers.Layer.Google.v3 = {\n \n /**\n- * APIProperty: intervals\n- * {Array(Float)} A list of possible graticule widths in degrees.\n+ * Constant: DEFAULTS\n+ * {Object} It is not recommended to change the properties set here. Note\n+ * that Google.v3 layers only work when sphericalMercator is set to true.\n+ * \n+ * (code)\n+ * {\n+ * sphericalMercator: true,\n+ * projection: \"EPSG:900913\"\n+ * }\n+ * (end)\n */\n- intervals: [45, 30, 20, 10, 5, 2, 1,\n- 0.5, 0.2, 0.1, 0.05, 0.01,\n- 0.005, 0.002, 0.001\n- ],\n+ DEFAULTS: {\n+ sphericalMercator: true,\n+ projection: \"EPSG:900913\"\n+ },\n \n /**\n- * APIProperty: displayInLayerSwitcher\n- * {Boolean} Allows the Graticule control to be switched on and off by \n- * LayerSwitcher control. Defaults is true.\n+ * APIProperty: animationEnabled\n+ * {Boolean} If set to true, the transition between zoom levels will be\n+ * animated (if supported by the GMaps API for the device used). Set to\n+ * false to match the zooming experience of other layer types. Default\n+ * is true. Note that the GMaps API does not give us control over zoom\n+ * animation, so if set to false, when zooming, this will make the\n+ * layer temporarily invisible, wait until GMaps reports the map being\n+ * idle, and make it visible again. The result will be a blank layer\n+ * for a few moments while zooming.\n */\n- displayInLayerSwitcher: true,\n+ animationEnabled: true,\n \n- /**\n- * APIProperty: visible\n- * {Boolean} should the graticule be initially visible (default=true)\n+ /** \n+ * Method: loadMapObject\n+ * Load the GMap and register appropriate event listeners.\n */\n- visible: true,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = google.maps.MapTypeId.ROADMAP;\n+ }\n+ var mapObject;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // there are already Google layers added to this map\n+ mapObject = cache.mapObject;\n+ // increment the layer count\n+ ++cache.count;\n+ } else {\n+ // this is the first Google layer for this map\n+ // create GMap\n+ var center = this.map.getCenter();\n+ var container = document.createElement('div');\n+ container.className = \"olForeignContainer\";\n+ container.style.width = '100%';\n+ container.style.height = '100%';\n+ mapObject = new google.maps.Map(container, {\n+ center: center ?\n+ new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n+ zoom: this.map.getZoom() || 0,\n+ mapTypeId: this.type,\n+ disableDefaultUI: true,\n+ keyboardShortcuts: false,\n+ draggable: false,\n+ disableDoubleClickZoom: true,\n+ scrollwheel: false,\n+ streetViewControl: false\n+ });\n+ var googleControl = document.createElement('div');\n+ googleControl.style.width = '100%';\n+ googleControl.style.height = '100%';\n+ mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n \n- /**\n- * APIProperty: numPoints\n- * {Integer} The number of points to use in each graticule line. Higher\n- * numbers result in a smoother curve for projected maps \n- */\n- numPoints: 50,\n+ // cache elements for use by any other google layers added to\n+ // this same map\n+ cache = {\n+ googleControl: googleControl,\n+ mapObject: mapObject,\n+ count: 1\n+ };\n+ OpenLayers.Layer.Google.cache[this.map.id] = cache;\n+ }\n+ this.mapObject = mapObject;\n+ this.setGMapVisibility(this.visibility);\n+ },\n \n /**\n- * APIProperty: targetSize\n- * {Integer} The maximum size of the grid in pixels on the map\n+ * APIMethod: onMapResize\n */\n- targetSize: 200,\n+ onMapResize: function() {\n+ if (this.visibility) {\n+ google.maps.event.trigger(this.mapObject, \"resize\");\n+ }\n+ },\n \n /**\n- * APIProperty: layerName\n- * {String} The name to be displayed in the layer switcher, default is set \n- * by {<OpenLayers.Lang>}.\n+ * Method: setGMapVisibility\n+ * Display the GMap container and associated elements.\n+ * \n+ * Parameters:\n+ * visible - {Boolean} Display the GMap elements.\n */\n- layerName: null,\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ var map = this.map;\n+ if (cache) {\n+ var type = this.type;\n+ var layers = map.layers;\n+ var layer;\n+ for (var i = layers.length - 1; i >= 0; --i) {\n+ layer = layers[i];\n+ if (layer instanceof OpenLayers.Layer.Google &&\n+ layer.visibility === true && layer.inRange === true) {\n+ type = layer.type;\n+ visible = true;\n+ break;\n+ }\n+ }\n+ var container = this.mapObject.getDiv();\n+ if (visible === true) {\n+ if (container.parentNode !== map.div) {\n+ if (!cache.rendered) {\n+ var me = this;\n+ google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {\n+ cache.rendered = true;\n+ me.setGMapVisibility(me.getVisibility());\n+ me.moveTo(me.map.getCenter());\n+ });\n+ } else {\n+ map.div.appendChild(container);\n+ cache.googleControl.appendChild(map.viewPortDiv);\n+ google.maps.event.trigger(this.mapObject, 'resize');\n+ }\n+ }\n+ this.mapObject.setMapTypeId(type);\n+ } else if (cache.googleControl.hasChildNodes()) {\n+ map.div.appendChild(map.viewPortDiv);\n+ map.div.removeChild(container);\n+ }\n+ }\n+ },\n \n /**\n- * APIProperty: labelled\n- * {Boolean} Should the graticule lines be labelled?. default=true\n+ * Method: getMapContainer\n+ * \n+ * Returns:\n+ * {DOMElement} the GMap container's div\n */\n- labelled: true,\n+ getMapContainer: function() {\n+ return this.mapObject.getDiv();\n+ },\n \n- /**\n- * APIProperty: labelFormat\n- * {String} the format of the labels, default = 'dm'. See\n- * <OpenLayers.Util.getFormattedLonLat> for other options.\n- */\n- labelFormat: 'dm',\n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n \n /**\n- * APIProperty: lineSymbolizer\n- * {symbolizer} the symbolizer used to render lines\n+ * APIMethod: getMapObjectBoundsFromOLBounds\n+ * \n+ * Parameters:\n+ * olBounds - {<OpenLayers.Bounds>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Bounds, translated from olBounds\n+ * Returns null if null value is passed in\n */\n- lineSymbolizer: {\n- strokeColor: \"#333\",\n- strokeWidth: 1,\n- strokeOpacity: 0.5\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.bottom, olBounds.left) :\n+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.top, olBounds.right) :\n+ new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new google.maps.LatLngBounds(\n+ new google.maps.LatLng(sw.lat, sw.lon),\n+ new google.maps.LatLng(ne.lat, ne.lon)\n+ );\n+ }\n+ return moBounds;\n },\n \n- /**\n- * APIProperty: labelSymbolizer\n- * {symbolizer} the symbolizer used to render labels\n- */\n- labelSymbolizer: {},\n \n- /**\n- * Property: gratLayer\n- * {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on\n- */\n- gratLayer: null,\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n+\n+\n+ // LonLat - Pixel Translation\n \n /**\n- * Constructor: OpenLayers.Control.Graticule\n- * Create a new graticule control to display a grid of latitude longitude\n- * lines.\n+ * APIMethod: getMapObjectLonLatFromMapObjectPixel\n * \n * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Object} MapObject LonLat translated from MapObject Pixel\n */\n- initialize: function(options) {\n- options = options || {};\n- options.layerName = options.layerName || OpenLayers.i18n(\"Graticule\");\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ var size = this.map.getSize();\n+ var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n+ var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n+ var res = this.map.getResolution();\n \n- this.labelSymbolizer.stroke = false;\n- this.labelSymbolizer.fill = false;\n- this.labelSymbolizer.label = \"${label}\";\n- this.labelSymbolizer.labelAlign = \"${labelAlign}\";\n- this.labelSymbolizer.labelXOffset = \"${xOffset}\";\n- this.labelSymbolizer.labelYOffset = \"${yOffset}\";\n- },\n+ var delta_x = moPixel.x - (size.w / 2);\n+ var delta_y = moPixel.y - (size.h / 2);\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.deactivate();\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.gratLayer) {\n- this.gratLayer.destroy();\n- this.gratLayer = null;\n+ var lonlat = new OpenLayers.LonLat(\n+ lon + delta_x * res,\n+ lat - delta_y * res\n+ );\n+\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent);\n }\n+ return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);\n },\n \n /**\n- * Method: draw\n- *\n- * initializes the graticule layer and does the initial update\n+ * APIMethod: getMapObjectPixelFromMapObjectLonLat\n+ * \n+ * Parameters:\n+ * moLonLat - {Object} MapObject LonLat format\n * \n * Returns:\n- * {DOMElement}\n+ * {Object} MapObject Pixel transtlated from MapObject LonLat\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.gratLayer) {\n- var gratStyle = new OpenLayers.Style({}, {\n- rules: [new OpenLayers.Rule({\n- 'symbolizer': {\n- \"Point\": this.labelSymbolizer,\n- \"Line\": this.lineSymbolizer\n- }\n- })]\n- });\n- this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {\n- styleMap: new OpenLayers.StyleMap({\n- 'default': gratStyle\n- }),\n- visibility: this.visible,\n- displayInLayerSwitcher: this.displayInLayerSwitcher\n- });\n- }\n- return this.div;\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ var res = this.map.getResolution();\n+ var extent = this.map.getExtent();\n+ return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)),\n+ (1 / res * (extent.top - lat)));\n },\n \n- /**\n- * APIMethod: activate\n+\n+ /** \n+ * APIMethod: setMapObjectCenter\n+ * Set the mapObject to the specified center and zoom\n+ * \n+ * Parameters:\n+ * center - {Object} MapObject LonLat format\n+ * zoom - {int} MapObject zoom format\n */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.map.addLayer(this.gratLayer);\n- this.map.events.register('moveend', this, this.update);\n- this.update();\n- return true;\n- } else {\n- return false;\n+ setMapObjectCenter: function(center, zoom) {\n+ if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n+ var mapContainer = this.getMapContainer();\n+ google.maps.event.addListenerOnce(\n+ this.mapObject,\n+ \"idle\",\n+ function() {\n+ mapContainer.style.visibility = \"\";\n+ }\n+ );\n+ mapContainer.style.visibility = \"hidden\";\n }\n+ this.mapObject.setOptions({\n+ center: center,\n+ zoom: zoom\n+ });\n },\n \n- /**\n- * APIMethod: deactivate\n+\n+ // Bounds\n+\n+ /** \n+ * APIMethod: getMapObjectZoomFromMapObjectBounds\n+ * \n+ * Parameters:\n+ * moBounds - {Object} MapObject Bounds format\n+ * \n+ * Returns:\n+ * {Object} MapObject Zoom for specified MapObject Bounds\n */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.map.events.unregister('moveend', this, this.update);\n- this.map.removeLayer(this.gratLayer);\n- return true;\n- } else {\n- return false;\n- }\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds);\n },\n+\n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n+\n+\n+ // LonLat\n+\n /**\n- * Method: update\n- *\n- * calculates the grid to be displayed and actually draws it\n+ * APIMethod: getMapObjectLonLatFromLonLat\n+ * \n+ * Parameters:\n+ * lon - {Float}\n+ * lat - {Float}\n * \n * Returns:\n- * {DOMElement}\n+ * {Object} MapObject LonLat built from lon and lat params\n */\n- update: function() {\n- //wait for the map to be initialized before proceeding\n- var mapBounds = this.map.getExtent();\n- if (!mapBounds) {\n- return;\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);\n+ } else {\n+ gLatLng = new google.maps.LatLng(lat, lon);\n }\n+ return gLatLng;\n+ },\n \n- //clear out the old grid\n- this.gratLayer.destroyFeatures();\n-\n- //get the projection objects required\n- var llProj = new OpenLayers.Projection(\"EPSG:4326\");\n- var mapProj = this.map.getProjectionObject();\n- var mapRes = this.map.getResolution();\n+ // Pixel\n \n- //if the map is in lon/lat, then the lines are straight and only one\n- //point is required\n- if (mapProj.proj && mapProj.proj.projName == \"longlat\") {\n- this.numPoints = 1;\n- }\n+ /**\n+ * APIMethod: getMapObjectPixelFromXY\n+ * \n+ * Parameters:\n+ * x - {Integer}\n+ * y - {Integer}\n+ * \n+ * Returns:\n+ * {Object} MapObject Pixel from x and y parameters\n+ */\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new google.maps.Point(x, y);\n+ }\n \n- //get the map center in EPSG:4326\n- var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y\n- var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);\n- OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);\n+};\n+/* ======================================================================\n+ OpenLayers/Protocol/CSW.js\n+ ====================================================================== */\n \n- /* This block of code determines the lon/lat interval to use for the\n- * grid by calculating the diagonal size of one grid cell at the map\n- * center. Iterates through the intervals array until the diagonal\n- * length is less than the targetSize option.\n- */\n- //find lat/lon interval that results in a grid of less than the target size\n- var testSq = this.targetSize * mapRes;\n- testSq *= testSq; //compare squares rather than doing a square root to save time\n- var llInterval;\n- for (var i = 0; i < this.intervals.length; ++i) {\n- llInterval = this.intervals[i]; //could do this for both x and y??\n- var delta = llInterval / 2;\n- var p1 = mapCenterLL.offset({\n- x: -delta,\n- y: -delta\n- }); //test coords in EPSG:4326 space\n- var p2 = mapCenterLL.offset({\n- x: delta,\n- y: delta\n- });\n- OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection\n- OpenLayers.Projection.transform(p2, llProj, mapProj);\n- var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);\n- if (distSq <= testSq) {\n- break;\n- }\n- }\n- //alert(llInterval);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- //round the LL center to an even number based on the interval\n- mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval;\n- mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval;\n- //TODO adjust for minutses/seconds?\n+/**\n+ * @requires OpenLayers/Protocol.js\n+ */\n \n- /* The following 2 blocks calculate the nodes of the grid along a \n- * line of constant longitude (then latitiude) running through the\n- * center of the map until it reaches the map edge. The calculation\n- * goes from the center in both directions to the edge.\n- */\n- //get the central longitude line, increment the latitude\n- var iter = 0;\n- var centerLonPoints = [mapCenterLL.clone()];\n- var newPoint = mapCenterLL.clone();\n- var mapXY;\n- do {\n- newPoint = newPoint.offset({\n- x: 0,\n- y: llInterval\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLonPoints.unshift(newPoint);\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: 0,\n- y: -llInterval\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLonPoints.push(newPoint);\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n+/**\n+ * Class: OpenLayers.Protocol.CSW\n+ * Used to create a versioned CSW protocol. Default version is 2.0.2.\n+ */\n+OpenLayers.Protocol.CSW = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Protocol.CSW.DEFAULTS\n+ );\n+ var cls = OpenLayers.Protocol.CSW[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSW version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n \n- //get the central latitude line, increment the longitude\n- iter = 0;\n- var centerLatPoints = [mapCenterLL.clone()];\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: -llInterval,\n- y: 0\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLatPoints.unshift(newPoint);\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: llInterval,\n- y: 0\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLatPoints.push(newPoint);\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);\n+/**\n+ * Constant: OpenLayers.Protocol.CSW.DEFAULTS\n+ */\n+OpenLayers.Protocol.CSW.DEFAULTS = {\n+ \"version\": \"2.0.2\"\n+};\n+/* ======================================================================\n+ OpenLayers/Protocol/SOS.js\n+ ====================================================================== */\n \n- //now generate a line for each node in the central lat and lon lines\n- //first loop over constant longitude\n- var lines = [];\n- for (var i = 0; i < centerLatPoints.length; ++i) {\n- var lon = centerLatPoints[i].x;\n- var pointList = [];\n- var labelPoint = null;\n- var latEnd = Math.min(centerLonPoints[0].y, 90);\n- var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);\n- var latDelta = (latEnd - latStart) / this.numPoints;\n- var lat = latStart;\n- for (var j = 0; j <= this.numPoints; ++j) {\n- var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n- gridPoint.transform(llProj, mapProj);\n- pointList.push(gridPoint);\n- lat += latDelta;\n- if (gridPoint.y >= mapBounds.bottom && !labelPoint) {\n- labelPoint = gridPoint;\n- }\n- }\n- if (this.labelled) {\n- //keep track of when this grid line crosses the map bounds to set\n- //the label position\n- //labels along the bottom, add 10 pixel offset up into the map\n- //TODO add option for labels on top\n- var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom);\n- var labelAttrs = {\n- value: lon,\n- label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, \"lon\", this.labelFormat) : \"\",\n- labelAlign: \"cb\",\n- xOffset: 0,\n- yOffset: 2\n- };\n- this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));\n- }\n- var geom = new OpenLayers.Geometry.LineString(pointList);\n- lines.push(new OpenLayers.Feature.Vector(geom));\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- //now draw the lines of constant latitude\n- for (var j = 0; j < centerLonPoints.length; ++j) {\n- lat = centerLonPoints[j].y;\n- if (lat < -90 || lat > 90) { //latitudes only valid between -90 and 90\n- continue;\n- }\n- var pointList = [];\n- var lonStart = centerLatPoints[0].x;\n- var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;\n- var lonDelta = (lonEnd - lonStart) / this.numPoints;\n- var lon = lonStart;\n- var labelPoint = null;\n- for (var i = 0; i <= this.numPoints; ++i) {\n- var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n- gridPoint.transform(llProj, mapProj);\n- pointList.push(gridPoint);\n- lon += lonDelta;\n- if (gridPoint.x < mapBounds.right) {\n- labelPoint = gridPoint;\n- }\n- }\n- if (this.labelled) {\n- //keep track of when this grid line crosses the map bounds to set\n- //the label position\n- //labels along the right, 30 pixel offset left into the map\n- //TODO add option for labels on left\n- var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);\n- var labelAttrs = {\n- value: lat,\n- label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, \"lat\", this.labelFormat) : \"\",\n- labelAlign: \"rb\",\n- xOffset: -2,\n- yOffset: 2\n- };\n- this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));\n- }\n- var geom = new OpenLayers.Geometry.LineString(pointList);\n- lines.push(new OpenLayers.Feature.Vector(geom));\n- }\n- this.gratLayer.addFeatures(lines);\n- },\n+/**\n+ * @requires OpenLayers/Protocol.js\n+ */\n \n- CLASS_NAME: \"OpenLayers.Control.Graticule\"\n-});\n+/**\n+ * Function: OpenLayers.Protocol.SOS\n+ * Used to create a versioned SOS protocol. Default version is 1.0.0.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol>} An SOS protocol for the given version.\n+ */\n+OpenLayers.Protocol.SOS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Protocol.SOS.DEFAULTS\n+ );\n+ var cls = OpenLayers.Protocol.SOS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported SOS version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n \n+/**\n+ * Constant: OpenLayers.Protocol.SOS.DEFAULTS\n+ */\n+OpenLayers.Protocol.SOS.DEFAULTS = {\n+ \"version\": \"1.0.0\"\n+};\n /* ======================================================================\n- OpenLayers/Control/Measure.js\n+ OpenLayers/Protocol/HTTP.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Protocol.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n */\n \n /**\n- * Class: OpenLayers.Control.Measure\n- * Allows for drawing of features for measurements.\n+ * if application uses the query string, for example, for BBOX parameters,\n+ * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.HTTP\n+ * A basic HTTP protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.HTTP> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Protocol>\n */\n-OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n \n /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * measure - Triggered when a measurement sketch is complete. Listeners\n- * will receive an event with measure, units, order, and geometry\n- * properties.\n- * measurepartial - Triggered when a new point is added to the\n- * measurement sketch or if the <immediate> property is true and the\n- * measurement sketch is modified. Listeners receive an event with measure,\n- * units, order, and geometry.\n+ * Property: url\n+ * {String} Service URL, read-only, set through the options\n+ * passed to constructor.\n */\n+ url: null,\n \n /**\n- * APIProperty: handlerOptions\n- * {Object} Used to set non-default properties on the control's handler\n+ * Property: headers\n+ * {Object} HTTP request headers, read-only, set through the options\n+ * passed to the constructor,\n+ * Example: {'Content-Type': 'plain/text'}\n */\n+ headers: null,\n \n /**\n- * Property: callbacks\n- * {Object} The functions that are sent to the handler for callback\n+ * Property: params\n+ * {Object} Parameters of GET requests, read-only, set through the options\n+ * passed to the constructor,\n+ * Example: {'bbox': '5,5,5,5'}\n */\n- callbacks: null,\n+ params: null,\n \n /**\n- * APIProperty: displaySystem\n- * {String} Display system for output measurements. Supported values\n- * are 'english', 'metric', and 'geographic'. Default is 'metric'.\n+ * Property: callback\n+ * {Object} Function to be called when the <read>, <create>,\n+ * <update>, <delete> or <commit> operation completes, read-only,\n+ * set through the options passed to the constructor.\n */\n- displaySystem: 'metric',\n+ callback: null,\n \n /**\n- * APIProperty: geodesic\n- * {Boolean} Calculate geodesic metrics instead of planar metrics. This\n- * requires that geometries can be transformed into Geographic/WGS84\n- * (if that is not already the map projection). Default is false.\n+ * Property: scope\n+ * {Object} Callback execution scope, read-only, set through the\n+ * options passed to the constructor.\n */\n- geodesic: false,\n+ scope: null,\n \n /**\n- * Property: displaySystemUnits\n- * {Object} Units for various measurement systems. Values are arrays\n- * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing\n- * order of length.\n+ * APIProperty: readWithPOST\n+ * {Boolean} true if read operations are done with POST requests\n+ * instead of GET, defaults to false.\n */\n- displaySystemUnits: {\n- geographic: ['dd'],\n- english: ['mi', 'ft', 'in'],\n- metric: ['km', 'm']\n- },\n+ readWithPOST: false,\n \n /**\n- * Property: delay\n- * {Number} Number of milliseconds between clicks before the event is\n- * considered a double-click. The \"measurepartial\" event will not\n- * be triggered if the sketch is completed within this time. This\n- * is required for IE where creating a browser reflow (if a listener\n- * is modifying the DOM by displaying the measurement values) messes\n- * with the dblclick listener in the sketch handler.\n+ * APIProperty: updateWithPOST\n+ * {Boolean} true if update operations are done with POST requests\n+ * defaults to false.\n */\n- partialDelay: 300,\n+ updateWithPOST: false,\n \n /**\n- * Property: delayedTrigger\n- * {Number} Timeout id of trigger for measurepartial.\n+ * APIProperty: deleteWithPOST\n+ * {Boolean} true if delete operations are done with POST requests\n+ * defaults to false.\n+ * if true, POST data is set to output of format.write().\n */\n- delayedTrigger: null,\n+ deleteWithPOST: false,\n \n /**\n- * APIProperty: persist\n- * {Boolean} Keep the temporary measurement sketch drawn after the\n- * measurement is complete. The geometry will persist until a new\n- * measurement is started, the control is deactivated, or <cancel> is\n- * called.\n+ * Property: wildcarded.\n+ * {Boolean} If true percent signs are added around values\n+ * read from LIKE filters, for example if the protocol\n+ * read method is passed a LIKE filter whose property\n+ * is \"foo\" and whose value is \"bar\" the string\n+ * \"foo__ilike=%bar%\" will be sent in the query string;\n+ * defaults to false.\n */\n- persist: false,\n+ wildcarded: false,\n \n /**\n- * APIProperty: immediate\n- * {Boolean} Activates the immediate measurement so that the \"measurepartial\"\n- * event is also fired once the measurement sketch is modified.\n- * Default is false.\n+ * APIProperty: srsInBBOX\n+ * {Boolean} Include the SRS identifier in BBOX query string parameter. \n+ * Default is false. If true and the layer has a projection object set,\n+ * any BBOX filter will be serialized with a fifth item identifying the\n+ * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n */\n- immediate: false,\n+ srsInBBOX: false,\n \n /**\n- * Constructor: OpenLayers.Control.Measure\n+ * Constructor: OpenLayers.Protocol.HTTP\n+ * A class for giving layers generic HTTP protocol.\n *\n * Parameters:\n- * handler - {<OpenLayers.Handler>}\n- * options - {Object}\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options include:\n+ * url - {String}\n+ * headers - {Object} \n+ * params - {Object} URL parameters for GET requests\n+ * format - {<OpenLayers.Format>}\n+ * callback - {Function}\n+ * scope - {Object}\n */\n- initialize: function(handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- var callbacks = {\n- done: this.measureComplete,\n- point: this.measurePartial\n- };\n- if (this.immediate) {\n- callbacks.modify = this.measureImmediate;\n- }\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ initialize: function(options) {\n+ options = options || {};\n+ this.params = {};\n+ this.headers = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n \n- // let the handler options override, so old code that passes 'persist'\n- // directly to the handler does not need an update\n- this.handlerOptions = OpenLayers.Util.extend({\n- persist: this.persist\n- }, this.handlerOptions);\n- this.handler = new handler(this, this.callbacks, this.handlerOptions);\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ wildcarded: this.wildcarded,\n+ srsInBBOX: this.srsInBBOX\n+ });\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params);\n+ };\n+ }\n },\n \n /**\n- * APIMethod: deactivate\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- deactivate: function() {\n- this.cancelDelay();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ destroy: function() {\n+ this.params = null;\n+ this.headers = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n },\n \n /**\n- * APIMethod: cancel\n- * Stop the control from measuring. If <persist> is true, the temporary\n- * sketch will be erased.\n+ * APIMethod: filterToParams\n+ * Optional method to translate an <OpenLayers.Filter> object into an object\n+ * that can be serialized as request query string provided. If a custom\n+ * method is not provided, the filter will be serialized using the \n+ * <OpenLayers.Format.QueryStringFilter> class.\n+ *\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} filter to convert.\n+ * params - {Object} The parameters object.\n+ *\n+ * Returns:\n+ * {Object} The resulting parameters object.\n */\n- cancel: function() {\n- this.cancelDelay();\n- this.handler.cancel();\n- },\n \n /**\n- * APIMethod: setImmediate\n- * Sets the <immediate> property. Changes the activity of immediate\n- * measurement.\n+ * APIMethod: read\n+ * Construct a request for reading new features.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Valid options:\n+ * url - {String} Url for the request.\n+ * params - {Object} Parameters to get serialized as a query string.\n+ * headers - {Object} Headers to be set on the request.\n+ * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n+ * query string.\n+ * readWithPOST - {Boolean} If the request should be done with POST.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n+ * references the HTTP request, this object is also passed to the\n+ * callback function when the request completes, its \"features\" property\n+ * is then populated with the features received from the server.\n */\n- setImmediate: function(immediate) {\n- this.immediate = immediate;\n- if (this.immediate) {\n- this.callbacks.modify = this.measureImmediate;\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = options || {};\n+ options.params = OpenLayers.Util.applyDefaults(\n+ options.params, this.options.params);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(\n+ options.filter, options.params\n+ );\n+ }\n+ var readWithPOST = (options.readWithPOST !== undefined) ?\n+ options.readWithPOST : this.readWithPOST;\n+ var resp = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ if (readWithPOST) {\n+ var headers = options.headers || {};\n+ headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ data: OpenLayers.Util.getParameterString(options.params),\n+ headers: headers\n+ });\n } else {\n- delete this.callbacks.modify;\n+ resp.priv = OpenLayers.Request.GET({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ params: options.params,\n+ headers: options.headers\n+ });\n }\n+ return resp;\n },\n \n /**\n- * Method: updateHandler\n+ * Method: handleRead\n+ * Individual callbacks are created for read, create and update, should\n+ * a subclass need to override each one separately.\n *\n * Parameters:\n- * handler - {Function} One of the sketch handler constructors.\n- * options - {Object} Options for the handler.\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * the user callback.\n+ * options - {Object} The user options passed to the read call.\n */\n- updateHandler: function(handler, options) {\n- var active = this.active;\n- if (active) {\n- this.deactivate();\n- }\n- this.handler = new handler(this, this.callbacks, options);\n- if (active) {\n- this.activate();\n- }\n+ handleRead: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: measureComplete\n- * Called when the measurement sketch is done.\n+ * APIMethod: create\n+ * Construct a request for writing newly created features.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes, its \"features\" property is then populated with the\n+ * the features received from the server.\n */\n- measureComplete: function(geometry) {\n- this.cancelDelay();\n- this.measure(geometry, \"measure\");\n+ create: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: features,\n+ requestType: \"create\"\n+ });\n+\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleCreate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(features)\n+ });\n+\n+ return resp;\n },\n \n /**\n- * Method: measurePartial\n- * Called each time a new point is added to the measurement sketch.\n+ * Method: handleCreate\n+ * Called the the request issued by <create> is complete. May be overridden\n+ * by subclasses.\n *\n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} The last point added.\n- * geometry - {<OpenLayers.Geometry>} The sketch geometry.\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the create call.\n */\n- measurePartial: function(point, geometry) {\n- this.cancelDelay();\n- geometry = geometry.clone();\n- // when we're wating for a dblclick, we have to trigger measurepartial\n- // after some delay to deal with reflow issues in IE\n- if (this.handler.freehandMode(this.handler.evt)) {\n- // no dblclick in freehand mode\n- this.measure(geometry, \"measurepartial\");\n- } else {\n- this.delayedTrigger = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.delayedTrigger = null;\n- this.measure(geometry, \"measurepartial\");\n- }, this),\n- this.partialDelay\n- );\n- }\n+ handleCreate: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: measureImmediate\n- * Called each time the measurement sketch is modified.\n+ * APIMethod: update\n+ * Construct a request updating modified feature.\n *\n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} The point at the mouse position.\n- * feature - {<OpenLayers.Feature.Vector>} The sketch feature.\n- * drawing - {Boolean} Indicates whether we're currently drawing.\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes, its \"features\" property is then populated with the\n+ * the feature received from the server.\n */\n- measureImmediate: function(point, feature, drawing) {\n- if (drawing && !this.handler.freehandMode(this.handler.evt)) {\n- this.cancelDelay();\n- this.measure(feature.geometry, \"measurepartial\");\n- }\n- },\n+ update: function(feature, options) {\n+ options = options || {};\n+ var url = options.url ||\n+ feature.url ||\n+ this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n \n- /**\n- * Method: cancelDelay\n- * Cancels the delay measurement that measurePartial began.\n- */\n- cancelDelay: function() {\n- if (this.delayedTrigger !== null) {\n- window.clearTimeout(this.delayedTrigger);\n- this.delayedTrigger = null;\n- }\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"update\"\n+ });\n+\n+ var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n+ resp.priv = OpenLayers.Request[method]({\n+ url: url,\n+ callback: this.createCallback(this.handleUpdate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(feature)\n+ });\n+\n+ return resp;\n },\n \n /**\n- * Method: measure\n+ * Method: handleUpdate\n+ * Called the the request issued by <update> is complete. May be overridden\n+ * by subclasses.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * eventType - {String}\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the update call.\n */\n- measure: function(geometry, eventType) {\n- var stat, order;\n- if (geometry.CLASS_NAME.indexOf('LineString') > -1) {\n- stat = this.getBestLength(geometry);\n- order = 1;\n- } else {\n- stat = this.getBestArea(geometry);\n- order = 2;\n- }\n- this.events.triggerEvent(eventType, {\n- measure: stat[0],\n- units: stat[1],\n- order: order,\n- geometry: geometry\n- });\n+ handleUpdate: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: getBestArea\n- * Based on the <displaySystem> returns the area of a geometry.\n+ * APIMethod: delete\n+ * Construct a request deleting a removed feature.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n *\n * Returns:\n- * {Array([Float, String])} Returns a two item array containing the\n- * area and the units abbreviation.\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes.\n */\n- getBestArea: function(geometry) {\n- var units = this.displaySystemUnits[this.displaySystem];\n- var unit, area;\n- for (var i = 0, len = units.length; i < len; ++i) {\n- unit = units[i];\n- area = this.getArea(geometry, unit);\n- if (area > 1) {\n- break;\n- }\n+ \"delete\": function(feature, options) {\n+ options = options || {};\n+ var url = options.url ||\n+ feature.url ||\n+ this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"delete\"\n+ });\n+\n+ var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n+ var requestOptions = {\n+ url: url,\n+ callback: this.createCallback(this.handleDelete, resp, options),\n+ headers: options.headers\n+ };\n+ if (this.deleteWithPOST) {\n+ requestOptions.data = this.format.write(feature);\n }\n- return [area, unit];\n+ resp.priv = OpenLayers.Request[method](requestOptions);\n+\n+ return resp;\n },\n \n /**\n- * Method: getArea\n+ * Method: handleDelete\n+ * Called the the request issued by <delete> is complete. May be overridden\n+ * by subclasses.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * units - {String} Unit abbreviation\n- *\n- * Returns:\n- * {Float} The geometry area in the given units.\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the delete call.\n */\n- getArea: function(geometry, units) {\n- var area, geomUnits;\n- if (this.geodesic) {\n- area = geometry.getGeodesicArea(this.map.getProjectionObject());\n- geomUnits = \"m\";\n- } else {\n- area = geometry.getArea();\n- geomUnits = this.map.getUnits();\n- }\n- var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n- if (inPerDisplayUnit) {\n- var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n- area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);\n- }\n- return area;\n+ handleDelete: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: getBestLength\n- * Based on the <displaySystem> returns the length of a geometry.\n+ * Method: handleResponse\n+ * Called by CRUD specific handlers.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- *\n- * Returns:\n- * {Array([Float, String])} Returns a two item array containing the\n- * length and the units abbreviation.\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the create, read, update,\n+ * or delete call.\n */\n- getBestLength: function(geometry) {\n- var units = this.displaySystemUnits[this.displaySystem];\n- var unit, length;\n- for (var i = 0, len = units.length; i < len; ++i) {\n- unit = units[i];\n- length = this.getLength(geometry, unit);\n- if (length > 1) {\n- break;\n+ handleResponse: function(resp, options) {\n+ var request = resp.priv;\n+ if (options.callback) {\n+ if (request.status >= 200 && request.status < 300) {\n+ // success\n+ if (resp.requestType != \"delete\") {\n+ resp.features = this.parseFeatures(request);\n+ }\n+ resp.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ // failure\n+ resp.code = OpenLayers.Protocol.Response.FAILURE;\n }\n+ options.callback.call(options.scope, resp);\n }\n- return [length, unit];\n },\n \n /**\n- * Method: getLength\n+ * Method: parseFeatures\n+ * Read HTTP response body and return features.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * units - {String} Unit abbreviation\n+ * request - {XMLHttpRequest} The request object\n *\n * Returns:\n- * {Float} The geometry length in the given units.\n+ * {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n */\n- getLength: function(geometry, units) {\n- var length, geomUnits;\n- if (this.geodesic) {\n- length = geometry.getGeodesicLength(this.map.getProjectionObject());\n- geomUnits = \"m\";\n- } else {\n- length = geometry.getLength();\n- geomUnits = this.map.getUnits();\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n }\n- var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n- if (inPerDisplayUnit) {\n- var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n- length *= (inPerMapUnit / inPerDisplayUnit);\n+ if (!doc || doc.length <= 0) {\n+ return null;\n }\n- return length;\n+ return this.format.read(doc);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Measure\"\n-});\n-/* ======================================================================\n- OpenLayers/Control/Scale.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n+ /**\n+ * APIMethod: commit\n+ * Iterate over each feature and take action based on the feature state.\n+ * Possible actions are create, update and delete.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})}\n+ * options - {Object} Optional object for setting up intermediate commit\n+ * callbacks.\n+ *\n+ * Valid options:\n+ * create - {Object} Optional object to be passed to the <create> method.\n+ * update - {Object} Optional object to be passed to the <update> method.\n+ * delete - {Object} Optional object to be passed to the <delete> method.\n+ * callback - {Function} Optional function to be called when the commit\n+ * is complete.\n+ * scope - {Object} Optional object to be set as the scope of the callback.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,\n+ * one per request made to the server, each object's \"priv\" property\n+ * references the corresponding HTTP request.\n+ */\n+ commit: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = [],\n+ nResponses = 0;\n \n-/**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Lang.js\n- */\n+ // Divide up features before issuing any requests. This properly\n+ // counts requests in the event that any responses come in before\n+ // all requests have been issued.\n+ var types = {};\n+ types[OpenLayers.State.INSERT] = [];\n+ types[OpenLayers.State.UPDATE] = [];\n+ types[OpenLayers.State.DELETE] = [];\n+ var feature, list, requestFeatures = [];\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ list = types[feature.state];\n+ if (list) {\n+ list.push(feature);\n+ requestFeatures.push(feature);\n+ }\n+ }\n+ // tally up number of requests\n+ var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +\n+ types[OpenLayers.State.UPDATE].length +\n+ types[OpenLayers.State.DELETE].length;\n \n-/**\n- * Class: OpenLayers.Control.Scale\n- * The Scale control displays the current map scale as a ratio (e.g. Scale = \n- * 1:1M). By default it is displayed in the lower right corner of the map.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {\n+ // This response will be sent to the final callback after all the others\n+ // have been fired.\n+ var success = true;\n+ var finalResponse = new OpenLayers.Protocol.Response({\n+ reqFeatures: requestFeatures\n+ });\n \n- /**\n- * Property: element\n- * {DOMElement}\n- */\n- element: null,\n+ function insertCallback(response) {\n+ var len = response.features ? response.features.length : 0;\n+ var fids = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ fids[i] = response.features[i].fid;\n+ }\n+ finalResponse.insertIds = fids;\n+ callback.apply(this, [response]);\n+ }\n \n- /**\n- * APIProperty: geodesic\n- * {Boolean} Use geodesic measurement. Default is false. The recommended\n- * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to\n- * true, the scale will be calculated based on the horizontal size of the\n- * pixel in the center of the map viewport.\n- */\n- geodesic: false,\n+ function callback(response) {\n+ this.callUserCallback(response, options);\n+ success = success && response.success();\n+ nResponses++;\n+ if (nResponses >= nRequests) {\n+ if (options.callback) {\n+ finalResponse.code = success ?\n+ OpenLayers.Protocol.Response.SUCCESS :\n+ OpenLayers.Protocol.Response.FAILURE;\n+ options.callback.apply(options.scope, [finalResponse]);\n+ }\n+ }\n+ }\n \n- /**\n- * Constructor: OpenLayers.Control.Scale\n- * \n- * Parameters:\n- * element - {DOMElement} \n- * options - {Object} \n- */\n- initialize: function(element, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.element = OpenLayers.Util.getElement(element);\n+ // start issuing requests\n+ var queue = types[OpenLayers.State.INSERT];\n+ if (queue.length > 0) {\n+ resp.push(this.create(\n+ queue, OpenLayers.Util.applyDefaults({\n+ callback: insertCallback,\n+ scope: this\n+ }, options.create)\n+ ));\n+ }\n+ queue = types[OpenLayers.State.UPDATE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this.update(\n+ queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options.update)));\n+ }\n+ queue = types[OpenLayers.State.DELETE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this[\"delete\"](\n+ queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options[\"delete\"])));\n+ }\n+ return resp;\n },\n \n /**\n- * Method: draw\n- * \n- * Returns:\n- * {DOMElement}\n+ * APIMethod: abort\n+ * Abort an ongoing request, the response object passed to\n+ * this method must come from this HTTP protocol (as a result\n+ * of a create, read, update, delete or commit operation).\n+ *\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>}\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.element) {\n- this.element = document.createElement(\"div\");\n- this.div.appendChild(this.element);\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort();\n }\n- this.map.events.register('moveend', this, this.updateScale);\n- this.updateScale();\n- return this.div;\n },\n \n /**\n- * Method: updateScale\n+ * Method: callUserCallback\n+ * This method is used from within the commit method each time an\n+ * an HTTP response is received from the server, it is responsible\n+ * for calling the user-supplied callbacks.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>}\n+ * options - {Object} The map of options passed to the commit call.\n */\n- updateScale: function() {\n- var scale;\n- if (this.geodesic === true) {\n- var units = this.map.getUnits();\n- if (!units) {\n- return;\n- }\n- var inches = OpenLayers.INCHES_PER_UNIT;\n- scale = (this.map.getGeodesicPixelSize().w || 0.000001) *\n- inches[\"km\"] * OpenLayers.DOTS_PER_INCH;\n- } else {\n- scale = this.map.getScale();\n- }\n-\n- if (!scale) {\n- return;\n- }\n-\n- if (scale >= 9500 && scale <= 950000) {\n- scale = Math.round(scale / 1000) + \"K\";\n- } else if (scale >= 950000) {\n- scale = Math.round(scale / 1000000) + \"M\";\n- } else {\n- scale = Math.round(scale);\n+ callUserCallback: function(resp, options) {\n+ var opt = options[resp.requestType];\n+ if (opt && opt.callback) {\n+ opt.callback.call(opt.scope, resp);\n }\n-\n- this.element.innerHTML = OpenLayers.i18n(\"Scale = 1 : ${scaleDenom}\", {\n- 'scaleDenom': scale\n- });\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Scale\"\n+ CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n });\n-\n /* ======================================================================\n- OpenLayers/Control/SLDSelect.js\n+ OpenLayers/Protocol/Script.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Layer/WMS.js\n- * @requires OpenLayers/Handler/RegularPolygon.js\n- * @requires OpenLayers/Handler/Polygon.js\n- * @requires OpenLayers/Handler/Path.js\n- * @requires OpenLayers/Handler/Click.js\n- * @requires OpenLayers/Filter/Spatial.js\n- * @requires OpenLayers/Format/SLD/v1_0_0.js\n+ * @requires OpenLayers/Protocol.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Format/GeoJSON.js\n */\n \n /**\n- * Class: OpenLayers.Control.SLDSelect\n- * Perform selections on WMS layers using Styled Layer Descriptor (SLD)\n+ * if application uses the query string, for example, for BBOX parameters,\n+ * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.Script\n+ * A basic Script protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.Script> constructor. A script protocol is used to\n+ * get around the same origin policy. It works with services that return\n+ * JSONP - that is, JSON wrapped in a client-specified callback. The\n+ * protocol handles fetching and parsing of feature data and sends parsed\n+ * features to the <callback> configured with the protocol. The protocol\n+ * expects features serialized as GeoJSON by default, but can be configured\n+ * to work with other formats by setting the <format> property.\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Protocol>\n */\n-OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {\n \n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * selected - Triggered when a selection occurs. Listeners receive an \n- * event with *filters* and *layer* properties. Filters will be an \n- * array of OpenLayers.Filter objects created in order to perform \n- * the particular selection.\n+ /**\n+ * APIProperty: url\n+ * {String} Service URL. The service is expected to return serialized \n+ * features wrapped in a named callback (where the callback name is\n+ * generated by this protocol).\n+ * Read-only, set through the options passed to the constructor.\n */\n+ url: null,\n \n /**\n- * APIProperty: clearOnDeactivate\n- * {Boolean} Should the selection be cleared when the control is \n- * deactivated. Default value is false.\n+ * APIProperty: params\n+ * {Object} Query string parameters to be appended to the URL.\n+ * Read-only, set through the options passed to the constructor.\n+ * Example: {maxFeatures: 50}\n */\n- clearOnDeactivate: false,\n+ params: null,\n \n /**\n- * APIProperty: layers\n- * {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work \n- * on.\n+ * APIProperty: callback\n+ * {Object} Function to be called when the <read> operation completes.\n */\n- layers: null,\n+ callback: null,\n \n /**\n- * Property: callbacks\n- * {Object} The functions that are sent to the handler for callback\n+ * APIProperty: callbackTemplate\n+ * {String} Template for creating a unique callback function name\n+ * for the registry. Should include ${id}. The ${id} variable will be\n+ * replaced with a string identifier prefixed with a \"c\" (e.g. c1, c2).\n+ * Default is \"OpenLayers.Protocol.Script.registry.${id}\".\n */\n- callbacks: null,\n+ callbackTemplate: \"OpenLayers.Protocol.Script.registry.${id}\",\n \n /**\n- * APIProperty: selectionSymbolizer\n- * {Object} Determines the styling of the selected objects. Default is\n- * a selection in red.\n+ * APIProperty: callbackKey\n+ * {String} The name of the query string parameter that the service \n+ * recognizes as the callback identifier. Default is \"callback\".\n+ * This key is used to generate the URL for the script. For example\n+ * setting <callbackKey> to \"myCallback\" would result in a URL like \n+ * http://example.com/?myCallback=...\n */\n- selectionSymbolizer: {\n- 'Polygon': {\n- fillColor: '#FF0000',\n- stroke: false\n- },\n- 'Line': {\n- strokeColor: '#FF0000',\n- strokeWidth: 2\n- },\n- 'Point': {\n- graphicName: 'square',\n- fillColor: '#FF0000',\n- pointRadius: 5\n- }\n- },\n+ callbackKey: \"callback\",\n \n /**\n- * APIProperty: layerOptions\n- * {Object} The options to apply to the selection layer, by default the\n- * selection layer will be kept out of the layer switcher.\n+ * APIProperty: callbackPrefix\n+ * {String} Where a service requires that the callback query string \n+ * parameter value is prefixed by some string, this value may be set.\n+ * For example, setting <callbackPrefix> to \"foo:\" would result in a\n+ * URL like http://example.com/?callback=foo:... Default is \"\".\n */\n- layerOptions: null,\n+ callbackPrefix: \"\",\n \n /**\n- * APIProperty: handlerOptions\n- * {Object} Used to set non-default properties on the control's handler\n+ * APIProperty: scope\n+ * {Object} Optional ``this`` object for the callback. Read-only, set \n+ * through the options passed to the constructor.\n */\n+ scope: null,\n \n /**\n- * APIProperty: sketchStyle\n- * {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch\n- * handler. The recommended way of styling the sketch layer, however, is\n- * to configure an <OpenLayers.StyleMap> in the layerOptions of the\n- * <handlerOptions>:\n- * \n- * (code)\n- * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, {\n- * handlerOptions: {\n- * layerOptions: {\n- * styleMap: new OpenLayers.StyleMap({\n- * \"default\": {strokeColor: \"yellow\"}\n- * })\n- * }\n- * }\n- * });\n- * (end)\n+ * APIProperty: format\n+ * {<OpenLayers.Format>} Format for parsing features. Default is an \n+ * <OpenLayers.Format.GeoJSON> format. If an alternative is provided,\n+ * the format's read method must take an object and return an array\n+ * of features.\n */\n- sketchStyle: null,\n+ format: null,\n \n /**\n- * APIProperty: wfsCache\n- * {Object} Cache to use for storing parsed results from\n- * <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided,\n- * these will be cached on the prototype.\n+ * Property: pendingRequests\n+ * {Object} References all pending requests. Property names are script \n+ * identifiers and property values are script elements.\n */\n- wfsCache: {},\n+ pendingRequests: null,\n \n /**\n- * APIProperty: layerCache\n- * {Object} Cache to use for storing references to the selection layers.\n- * Normally each source layer will have exactly 1 selection layer of\n- * type OpenLayers.Layer.WMS. If not provided, layers will\n- * be cached on the prototype. Note that if <clearOnDeactivate> is\n- * true, the layer will no longer be cached after deactivating the\n- * control.\n+ * APIProperty: srsInBBOX\n+ * {Boolean} Include the SRS identifier in BBOX query string parameter.\n+ * Setting this property has no effect if a custom filterToParams method\n+ * is provided. Default is false. If true and the layer has a \n+ * projection object set, any BBOX filter will be serialized with a \n+ * fifth item identifying the projection. \n+ * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n */\n- layerCache: {},\n+ srsInBBOX: false,\n \n /**\n- * Constructor: OpenLayers.Control.SLDSelect\n- * Create a new control for selecting features in WMS layers using\n- * Styled Layer Descriptor (SLD).\n+ * Constructor: OpenLayers.Protocol.Script\n+ * A class for giving layers generic Script protocol.\n *\n * Parameters:\n- * handler - {<OpenLayers.Class>} A sketch handler class. This determines\n- * the type of selection, e.g. box (<OpenLayers.Handler.Box>), point\n- * (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or\n- * polygon (<OpenLayers.Handler.Polygon>) selection. To use circle\n- * type selection, use <OpenLayers.Handler.RegularPolygon> and pass\n- * the number of desired sides (e.g. 40) as \"sides\" property to the\n- * <handlerOptions>.\n- * options - {Object} An object containing all configuration properties for\n- * the control.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n *\n- * Valid options:\n- * layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the\n- * selection on.\n+ * Valid options include:\n+ * url - {String}\n+ * params - {Object}\n+ * callback - {Function}\n+ * scope - {Object}\n */\n- initialize: function(handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n-\n- this.callbacks = OpenLayers.Util.extend({\n- done: this.select,\n- click: this.select\n- }, this.callbacks);\n- this.handlerOptions = this.handlerOptions || {};\n- this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {\n- displayInLayerSwitcher: false,\n- tileOptions: {\n- maxGetUrlLength: 2048\n- }\n- });\n- if (this.sketchStyle) {\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(\n- this.handlerOptions.layerOptions, {\n- styleMap: new OpenLayers.StyleMap({\n- \"default\": this.sketchStyle\n- })\n- }\n- );\n+ initialize: function(options) {\n+ options = options || {};\n+ this.params = {};\n+ this.pendingRequests = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.GeoJSON();\n }\n- this.handler = new handler(this, this.callbacks, this.handlerOptions);\n- },\n \n- /**\n- * APIMethod: destroy\n- * Take care of things that are not handled in superclass.\n- */\n- destroy: function() {\n- for (var key in this.layerCache) {\n- delete this.layerCache[key];\n- }\n- for (var key in this.wfsCache) {\n- delete this.wfsCache[key];\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ srsInBBOX: this.srsInBBOX\n+ });\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params);\n+ };\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: coupleLayerVisiblity\n- * Couple the selection layer and the source layer with respect to\n- * layer visibility. So if the source layer is turned off, the\n- * selection layer is also turned off.\n- *\n- * Context: \n- * - {<OpenLayers.Layer>}\n+ * APIMethod: read\n+ * Construct a request for reading new features.\n *\n * Parameters:\n- * evt - {Object}\n- */\n- coupleLayerVisiblity: function(evt) {\n- this.setVisibility(evt.object.getVisibility());\n- },\n-\n- /**\n- * Method: createSelectionLayer\n- * Creates a \"clone\" from the source layer in which the selection can\n- * be drawn. This ensures both the source layer and the selection are \n- * visible and not only the selection.\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n *\n- * Parameters:\n- * source - {<OpenLayers.Layer.WMS>} The source layer on which the selection\n- * is performed.\n+ * Valid options:\n+ * url - {String} Url for the request.\n+ * params - {Object} Parameters to get serialized as a query string.\n+ * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n+ * query string.\n *\n * Returns:\n- * {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048\n- * since SLD selections can easily get quite long.\n+ * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n+ * references the injected script. This object is also passed to the\n+ * callback function when the request completes, its \"features\" property\n+ * is then populated with the features received from the server.\n */\n- createSelectionLayer: function(source) {\n- // check if we already have a selection layer for the source layer\n- var selectionLayer;\n- if (!this.layerCache[source.id]) {\n- selectionLayer = new OpenLayers.Layer.WMS(source.name,\n- source.url, source.params,\n- OpenLayers.Util.applyDefaults(\n- this.layerOptions,\n- source.getOptions())\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ options.params = OpenLayers.Util.applyDefaults(\n+ options.params, this.options.params\n+ );\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(\n+ options.filter, options.params\n );\n- this.layerCache[source.id] = selectionLayer;\n- // make sure the layers are coupled wrt visibility, but only\n- // if they are not displayed in the layer switcher, because in\n- // that case the user cannot control visibility.\n- if (this.layerOptions.displayInLayerSwitcher === false) {\n- source.events.on({\n- \"visibilitychanged\": this.coupleLayerVisiblity,\n- scope: selectionLayer\n- });\n- }\n- this.map.addLayer(selectionLayer);\n- } else {\n- selectionLayer = this.layerCache[source.id];\n }\n- return selectionLayer;\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var request = this.createRequest(\n+ options.url,\n+ options.params,\n+ OpenLayers.Function.bind(function(data) {\n+ response.data = data;\n+ this.handleRead(response, options);\n+ }, this)\n+ );\n+ response.priv = request;\n+ return response;\n },\n \n- /**\n- * Method: createSLD\n- * Create the SLD document for the layer using the supplied filters.\n+ /** \n+ * APIMethod: filterToParams \n+ * Optional method to translate an <OpenLayers.Filter> object into an object \n+ * that can be serialized as request query string provided. If a custom \n+ * method is not provided, any filter will not be serialized. \n+ * \n+ * Parameters: \n+ * filter - {<OpenLayers.Filter>} filter to convert. \n+ * params - {Object} The parameters object. \n+ * \n+ * Returns: \n+ * {Object} The resulting parameters object. \n+ */\n+\n+ /** \n+ * Method: createRequest\n+ * Issues a request for features by creating injecting a script in the \n+ * document head.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.WMS>}\n- * filters - Array({<OpenLayers.Filter>}) The filters to be applied.\n- * geometryAttributes - Array({Object}) The geometry attributes of the \n- * layer.\n+ * url - {String} Service URL.\n+ * params - {Object} Query string parameters.\n+ * callback - {Function} Callback to be called with resulting data.\n *\n * Returns:\n- * {String} The SLD document generated as a string.\n+ * {HTMLScriptElement} The script pending execution.\n */\n- createSLD: function(layer, filters, geometryAttributes) {\n- var sld = {\n- version: \"1.0.0\",\n- namedLayers: {}\n- };\n- var layerNames = [layer.params.LAYERS].join(\",\").split(\",\");\n- for (var i = 0, len = layerNames.length; i < len; i++) {\n- var name = layerNames[i];\n- sld.namedLayers[name] = {\n- name: name,\n- userStyles: []\n- };\n- var symbolizer = this.selectionSymbolizer;\n- var geometryAttribute = geometryAttributes[i];\n- if (geometryAttribute.type.indexOf('Polygon') >= 0) {\n- symbolizer = {\n- Polygon: this.selectionSymbolizer['Polygon']\n- };\n- } else if (geometryAttribute.type.indexOf('LineString') >= 0) {\n- symbolizer = {\n- Line: this.selectionSymbolizer['Line']\n- };\n- } else if (geometryAttribute.type.indexOf('Point') >= 0) {\n- symbolizer = {\n- Point: this.selectionSymbolizer['Point']\n- };\n- }\n- var filter = filters[i];\n- sld.namedLayers[name].userStyles.push({\n- name: 'default',\n- rules: [\n- new OpenLayers.Rule({\n- symbolizer: symbolizer,\n- filter: filter,\n- maxScaleDenominator: layer.options.minScale\n- })\n- ]\n- });\n- }\n- return new OpenLayers.Format.SLD({\n- srsName: this.map.getProjection()\n- }).write(sld);\n+ createRequest: function(url, params, callback) {\n+ var id = OpenLayers.Protocol.Script.register(callback);\n+ var name = OpenLayers.String.format(this.callbackTemplate, {\n+ id: id\n+ });\n+ params = OpenLayers.Util.extend({}, params);\n+ params[this.callbackKey] = this.callbackPrefix + name;\n+ url = OpenLayers.Util.urlAppend(\n+ url, OpenLayers.Util.getParameterString(params)\n+ );\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = \"OpenLayers_Protocol_Script_\" + id;\n+ this.pendingRequests[script.id] = script;\n+ var head = document.getElementsByTagName(\"head\")[0];\n+ head.appendChild(script);\n+ return script;\n },\n \n- /**\n- * Method: parseDescribeLayer\n- * Parse the SLD WMS DescribeLayer response and issue the corresponding\n- * WFS DescribeFeatureType request\n+ /** \n+ * Method: destroyRequest\n+ * Remove a script node associated with a response from the document. Also\n+ * unregisters the callback and removes the script from the \n+ * <pendingRequests> object.\n *\n- * request - {XMLHttpRequest} The request object.\n+ * Parameters:\n+ * script - {HTMLScriptElement}\n */\n- parseDescribeLayer: function(request) {\n- var format = new OpenLayers.Format.WMSDescribeLayer();\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- var describeLayer = format.read(doc);\n- var typeNames = [];\n- var url = null;\n- for (var i = 0, len = describeLayer.length; i < len; i++) {\n- // perform a WFS DescribeFeatureType request\n- if (describeLayer[i].owsType == \"WFS\") {\n- typeNames.push(describeLayer[i].typeName);\n- url = describeLayer[i].owsURL;\n- }\n+ destroyRequest: function(script) {\n+ OpenLayers.Protocol.Script.unregister(script.id.split(\"_\").pop());\n+ delete this.pendingRequests[script.id];\n+ if (script.parentNode) {\n+ script.parentNode.removeChild(script);\n }\n- var options = {\n- url: url,\n- params: {\n- SERVICE: \"WFS\",\n- TYPENAME: typeNames.toString(),\n- REQUEST: \"DescribeFeatureType\",\n- VERSION: \"1.0.0\"\n- },\n- callback: function(request) {\n- var format = new OpenLayers.Format.WFSDescribeFeatureType();\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- var describeFeatureType = format.read(doc);\n- this.control.wfsCache[this.layer.id] = describeFeatureType;\n- this.control._queue && this.control.applySelection();\n- },\n- scope: this\n- };\n- OpenLayers.Request.GET(options);\n },\n \n /**\n- * Method: getGeometryAttributes\n- * Look up the geometry attributes from the WFS DescribeFeatureType response\n+ * Method: handleRead\n+ * Individual callbacks are created for read, create and update, should\n+ * a subclass need to override each one separately.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the \n- * geometry attributes.\n- *\n- * Returns:\n- * Array({Object}) Array of geometry attributes\n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * the user callback.\n+ * options - {Object} The user options passed to the read call.\n */\n- getGeometryAttributes: function(layer) {\n- var result = [];\n- var cache = this.wfsCache[layer.id];\n- for (var i = 0, len = cache.featureTypes.length; i < len; i++) {\n- var typeName = cache.featureTypes[i];\n- var properties = typeName.properties;\n- for (var j = 0, lenj = properties.length; j < lenj; j++) {\n- var property = properties[j];\n- var type = property.type;\n- if ((type.indexOf('LineString') >= 0) ||\n- (type.indexOf('GeometryAssociationType') >= 0) ||\n- (type.indexOf('GeometryPropertyType') >= 0) ||\n- (type.indexOf('Point') >= 0) ||\n- (type.indexOf('Polygon') >= 0)) {\n- result.push(property);\n- }\n- }\n- }\n- return result;\n+ handleRead: function(response, options) {\n+ this.handleResponse(response, options);\n },\n \n /**\n- * APIMethod: activate\n- * Activate the control. Activating the control will perform a SLD WMS\n- * DescribeLayer request followed by a WFS DescribeFeatureType request\n- * so that the proper symbolizers can be chosen based on the geometry\n- * type.\n+ * Method: handleResponse\n+ * Called by CRUD specific handlers.\n+ *\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the create, read, update,\n+ * or delete call.\n */\n- activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer && !this.wfsCache[layer.id]) {\n- var options = {\n- url: layer.url,\n- params: {\n- SERVICE: \"WMS\",\n- VERSION: layer.params.VERSION,\n- LAYERS: layer.params.LAYERS,\n- REQUEST: \"DescribeLayer\"\n- },\n- callback: this.parseDescribeLayer,\n- scope: {\n- layer: layer,\n- control: this\n- }\n- };\n- OpenLayers.Request.GET(options);\n- }\n+ handleResponse: function(response, options) {\n+ if (options.callback) {\n+ if (response.data) {\n+ response.features = this.parseFeatures(response.data);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n }\n+ this.destroyRequest(response.priv);\n+ options.callback.call(options.scope, response);\n }\n- return activated;\n },\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the control. If clearOnDeactivate is true, remove the\n- * selection layer(s).\n+ * Method: parseFeatures\n+ * Read Script response body and return features.\n+ *\n+ * Parameters:\n+ * data - {Object} The data sent to the callback function by the server.\n+ *\n+ * Returns:\n+ * {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer && this.clearOnDeactivate === true) {\n- var layerCache = this.layerCache;\n- var selectionLayer = layerCache[layer.id];\n- if (selectionLayer) {\n- layer.events.un({\n- \"visibilitychanged\": this.coupleLayerVisiblity,\n- scope: selectionLayer\n- });\n- selectionLayer.destroy();\n- delete layerCache[layer.id];\n- }\n- }\n- }\n- }\n- return deactivated;\n+ parseFeatures: function(data) {\n+ return this.format.read(data);\n },\n \n /**\n- * APIMethod: setLayers\n- * Set the layers on which the selection should be performed. Call the \n- * setLayers method if the layer(s) to be used change and the same \n- * control should be used on a new set of layers.\n- * If the control is already active, it will be active after the new\n- * set of layers is set.\n+ * APIMethod: abort\n+ * Abort an ongoing request. If no response is provided, all pending \n+ * requests will be aborted.\n *\n * Parameters:\n- * layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which \n- * the selection should be performed.\n+ * response - {<OpenLayers.Protocol.Response>} The response object returned\n+ * from a <read> request.\n */\n- setLayers: function(layers) {\n- if (this.active) {\n- this.deactivate();\n- this.layers = layers;\n- this.activate();\n+ abort: function(response) {\n+ if (response) {\n+ this.destroyRequest(response.priv);\n } else {\n- this.layers = layers;\n+ for (var key in this.pendingRequests) {\n+ this.destroyRequest(this.pendingRequests[key]);\n+ }\n }\n },\n \n /**\n- * Function: createFilter\n- * Create the filter to be used in the SLD.\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n+ */\n+ destroy: function() {\n+ this.abort();\n+ delete this.params;\n+ delete this.format;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Protocol.Script\"\n+});\n+\n+(function() {\n+ var o = OpenLayers.Protocol.Script;\n+ var counter = 0;\n+ o.registry = {};\n+\n+ /**\n+ * Function: OpenLayers.Protocol.Script.register\n+ * Register a callback for a newly created script.\n *\n * Parameters:\n- * geometryAttribute - {Object} Used to get the name of the geometry \n- * attribute which is needed for constructing the spatial filter.\n- * geometry - {<OpenLayers.Geometry>} The geometry to use.\n+ * callback - {Function} The callback to be executed when the newly added\n+ * script loads. This callback will be called with a single argument\n+ * that is the JSON returned by the service.\n *\n * Returns:\n- * {<OpenLayers.Filter.Spatial>} The spatial filter created.\n+ * {Number} An identifier for retrieving the registered callback.\n */\n- createFilter: function(geometryAttribute, geometry) {\n- var filter = null;\n- if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {\n- // box\n- if (this.handler.irregular === true) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- property: geometryAttribute.name,\n- value: geometry.getBounds()\n- });\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- });\n- }\n- } else if (this.handler instanceof OpenLayers.Handler.Polygon) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- });\n- } else if (this.handler instanceof OpenLayers.Handler.Path) {\n- // if source layer is point based, use DWITHIN instead\n- if (geometryAttribute.type.indexOf('Point') >= 0) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.DWITHIN,\n- property: geometryAttribute.name,\n- distance: this.map.getExtent().getWidth() * 0.01,\n- distanceUnits: this.map.getUnits(),\n- value: geometry\n- });\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- });\n- }\n- } else if (this.handler instanceof OpenLayers.Handler.Click) {\n- if (geometryAttribute.type.indexOf('Polygon') >= 0) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- });\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.DWITHIN,\n- property: geometryAttribute.name,\n- distance: this.map.getExtent().getWidth() * 0.01,\n- distanceUnits: this.map.getUnits(),\n- value: geometry\n- });\n- }\n- }\n- return filter;\n- },\n+ o.register = function(callback) {\n+ var id = \"c\" + (++counter);\n+ o.registry[id] = function() {\n+ callback.apply(this, arguments);\n+ };\n+ return id;\n+ };\n \n /**\n- * Method: select\n- * When the handler is done, use SLD_BODY on the selection layer to\n- * display the selection in the map.\n+ * Function: OpenLayers.Protocol.Script.unregister\n+ * Unregister a callback previously registered with the register function.\n *\n * Parameters:\n- * geometry - {Object} or {<OpenLayers.Geometry>}\n+ * id - {Number} The identifer returned by the register function.\n */\n- select: function(geometry) {\n- this._queue = function() {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- var geometryAttributes = this.getGeometryAttributes(layer);\n- var filters = [];\n- for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) {\n- var geometryAttribute = geometryAttributes[j];\n- if (geometryAttribute !== null) {\n- // from the click handler we will not get an actual \n- // geometry so transform\n- if (!(geometry instanceof OpenLayers.Geometry)) {\n- var point = this.map.getLonLatFromPixel(\n- geometry.xy);\n- geometry = new OpenLayers.Geometry.Point(\n- point.lon, point.lat);\n- }\n- var filter = this.createFilter(geometryAttribute,\n- geometry);\n- if (filter !== null) {\n- filters.push(filter);\n- }\n- }\n- }\n-\n- var selectionLayer = this.createSelectionLayer(layer);\n+ o.unregister = function(id) {\n+ delete o.registry[id];\n+ };\n+})();\n+/* ======================================================================\n+ OpenLayers/Protocol/WFS.js\n+ ====================================================================== */\n \n- this.events.triggerEvent(\"selected\", {\n- layer: layer,\n- filters: filters\n- });\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- var sld = this.createSLD(layer, filters, geometryAttributes);\n+/**\n+ * @requires OpenLayers/Protocol.js\n+ */\n \n- selectionLayer.mergeNewParams({\n- SLD_BODY: sld\n- });\n- delete this._queue;\n- }\n- };\n- this.applySelection();\n- },\n+/**\n+ * Class: OpenLayers.Protocol.WFS\n+ * Used to create a versioned WFS protocol. Default version is 1.0.0.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol>} A WFS protocol of the given version.\n+ *\n+ * Example:\n+ * (code)\n+ * var protocol = new OpenLayers.Protocol.WFS({\n+ * version: \"1.1.0\",\n+ * url: \"http://demo.opengeo.org/geoserver/wfs\",\n+ * featureType: \"tasmania_roads\",\n+ * featureNS: \"http://www.openplans.org/topp\",\n+ * geometryName: \"the_geom\"\n+ * });\n+ * (end)\n+ *\n+ * See the protocols for specific WFS versions for more detail.\n+ */\n+OpenLayers.Protocol.WFS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Protocol.WFS.DEFAULTS\n+ );\n+ var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported WFS version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n \n- /**\n- * Method: applySelection\n- * Checks if all required wfs data is cached, and applies the selection\n- */\n- applySelection: function() {\n- var canApply = true;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- if (!this.wfsCache[this.layers[i].id]) {\n- canApply = false;\n- break;\n- }\n- }\n- canApply && this._queue.call(this);\n- },\n+/**\n+ * Function: fromWMSLayer\n+ * Convenience function to create a WFS protocol from a WMS layer. This makes\n+ * the assumption that a WFS requests can be issued at the same URL as\n+ * WMS requests and that a WFS featureType exists with the same name as the\n+ * WMS layer.\n+ * \n+ * This function is designed to auto-configure <url>, <featureType>,\n+ * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that\n+ * srsName matching with the WMS layer will not work with WFS 1.0.0.\n+ * \n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS\n+ * FeatureType at the same server url with the same typename.\n+ * options - {Object} Default properties to be set on the protocol.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.WFS>}\n+ */\n+OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n+ var typeName, featurePrefix;\n+ var param = layer.params[\"LAYERS\"];\n+ var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n+ if (parts.length > 1) {\n+ featurePrefix = parts[0];\n+ }\n+ typeName = parts.pop();\n+ var protocolOptions = {\n+ url: layer.url,\n+ featureType: typeName,\n+ featurePrefix: featurePrefix,\n+ srsName: layer.projection && layer.projection.getCode() ||\n+ layer.map && layer.map.getProjectionObject().getCode(),\n+ version: \"1.1.0\"\n+ };\n+ return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(\n+ options, protocolOptions\n+ ));\n+};\n \n- CLASS_NAME: \"OpenLayers.Control.SLDSelect\"\n-});\n+/**\n+ * Constant: OpenLayers.Protocol.WFS.DEFAULTS\n+ */\n+OpenLayers.Protocol.WFS.DEFAULTS = {\n+ \"version\": \"1.0.0\"\n+};\n /* ======================================================================\n- OpenLayers/Renderer/Canvas.js\n+ OpenLayers/Protocol/WFS/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/Protocol/WFS.js\n */\n \n /**\n- * Class: OpenLayers.Renderer.Canvas \n- * A renderer based on the 2D 'canvas' drawing element.\n- * \n- * Inherits:\n- * - <OpenLayers.Renderer>\n+ * Class: OpenLayers.Protocol.WFS.v1\n+ * Abstract class for for v1.0.0 and v1.1.0 protocol.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Protocol>\n */\n-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n+OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {\n \n /**\n- * APIProperty: hitDetection\n- * {Boolean} Allow for hit detection of features. Default is true.\n+ * Property: version\n+ * {String} WFS version number.\n */\n- hitDetection: true,\n+ version: null,\n \n /**\n- * Property: hitOverflow\n- * {Number} The method for converting feature identifiers to color values\n- * supports 16777215 sequential values. Two features cannot be \n- * predictably detected if their identifiers differ by more than this\n- * value. The hitOverflow allows for bigger numbers (but the \n- * difference in values is still limited).\n+ * Property: srsName\n+ * {String} Name of spatial reference system. Default is \"EPSG:4326\".\n */\n- hitOverflow: 0,\n+ srsName: \"EPSG:4326\",\n \n /**\n- * Property: canvas\n- * {Canvas} The canvas context object.\n+ * Property: featureType\n+ * {String} Local feature typeName.\n */\n- canvas: null,\n+ featureType: null,\n \n /**\n- * Property: features\n- * {Object} Internal object of feature/style pairs for use in redrawing the layer.\n+ * Property: featureNS\n+ * {String} Feature namespace.\n */\n- features: null,\n+ featureNS: null,\n \n /**\n- * Property: pendingRedraw\n- * {Boolean} The renderer needs a redraw call to render features added while\n- * the renderer was locked.\n+ * Property: geometryName\n+ * {String} Name of the geometry attribute for features. Default is\n+ * \"the_geom\" for WFS <version> 1.0, and null for higher versions.\n */\n- pendingRedraw: false,\n+ geometryName: \"the_geom\",\n \n /**\n- * Property: cachedSymbolBounds\n- * {Object} Internal cache of calculated symbol extents.\n+ * Property: maxFeatures\n+ * {Integer} Optional maximum number of features to retrieve.\n */\n- cachedSymbolBounds: {},\n \n /**\n- * Constructor: OpenLayers.Renderer.Canvas\n+ * Property: schema\n+ * {String} Optional schema location that will be included in the\n+ * schemaLocation attribute value. Note that the feature type schema\n+ * is required for a strict XML validator (on transactions with an\n+ * insert for example), but is *not* required by the WFS specification\n+ * (since the server is supposed to know about feature type schemas).\n+ */\n+ schema: null,\n+\n+ /**\n+ * Property: featurePrefix\n+ * {String} Namespace alias for feature type. Default is \"feature\".\n+ */\n+ featurePrefix: \"feature\",\n+\n+ /**\n+ * Property: formatOptions\n+ * {Object} Optional options for the format. If a format is not provided,\n+ * this property can be used to extend the default format options.\n+ */\n+ formatOptions: null,\n+\n+ /** \n+ * Property: readFormat \n+ * {<OpenLayers.Format>} For WFS requests it is possible to get a \n+ * different output format than GML. In that case, we cannot parse \n+ * the response with the default format (WFST) and we need a different \n+ * format for reading. \n+ */\n+ readFormat: null,\n+\n+ /**\n+ * Property: readOptions\n+ * {Object} Optional object to pass to format's read.\n+ */\n+ readOptions: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Protocol.WFS\n+ * A class for giving layers WFS protocol.\n *\n * Parameters:\n- * containerID - {<String>}\n- * options - {Object} Optional properties to be set on the renderer.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * url - {String} URL to send requests to (required).\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (required, but can be autodetected\n+ * during the first query if GML is used as readFormat and\n+ * featurePrefix is provided and matches the prefix used by the server\n+ * for this featureType).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * for writing if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. The default is\n+ * 'the_geom' for WFS <version> 1.0, and null for higher versions. If\n+ * null, it will be set to the name of the first geometry found in the\n+ * first read operation.\n+ * multi - {Boolean} If set to true, geometries will be casted to Multi\n+ * geometries before they are written in a transaction. No casting will\n+ * be done when reading features.\n */\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n- this.root = document.createElement(\"canvas\");\n- this.container.appendChild(this.root);\n- this.canvas = this.root.getContext(\"2d\");\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitCanvas = document.createElement(\"canvas\");\n- this.hitContext = this.hitCanvas.getContext(\"2d\");\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({\n+ version: this.version,\n+ featureType: this.featureType,\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ geometryName: this.geometryName,\n+ srsName: this.srsName,\n+ schema: this.schema\n+ }, this.formatOptions));\n+ }\n+ if (!options.geometryName && parseFloat(this.format.version) > 1.0) {\n+ this.setGeometryName(null);\n }\n },\n \n /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n- *\n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n- *\n- * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- setExtent: function() {\n- OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- // always redraw features\n- return false;\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy();\n+ }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n },\n \n- /** \n- * Method: eraseGeometry\n- * Erase a geometry from the renderer. Because the Canvas renderer has\n- * 'memory' of the features that it has drawn, we have to remove the\n- * feature so it doesn't redraw. \n- * \n+ /**\n+ * APIMethod: read\n+ * Construct a request for reading new features. Since WFS splits the\n+ * basic CRUD operations into GetFeature requests (for read) and\n+ * Transactions (for all others), this method does not make use of the\n+ * format's read method (that is only about reading transaction\n+ * responses).\n+ *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * featureId - {String}\n+ * options - {Object} Options for the read operation, in addition to the\n+ * options set on the instance (options set here will take precedence).\n+ *\n+ * To use a configured protocol to get e.g. a WFS hit count, applications\n+ * could do the following:\n+ *\n+ * (code)\n+ * protocol.read({\n+ * readOptions: {output: \"object\"},\n+ * resultType: \"hits\",\n+ * maxFeatures: null,\n+ * callback: function(resp) {\n+ * // process resp.numberOfFeatures here\n+ * }\n+ * });\n+ * (end)\n+ *\n+ * To use a configured protocol to use WFS paging (if supported by the\n+ * server), applications could do the following:\n+ *\n+ * (code)\n+ * protocol.read({\n+ * startIndex: 0,\n+ * count: 50\n+ * });\n+ * (end)\n+ *\n+ * To limit the attributes returned by the GetFeature request, applications\n+ * can use the propertyNames option to specify the properties to include in\n+ * the response:\n+ *\n+ * (code)\n+ * protocol.read({\n+ * propertyNames: [\"DURATION\", \"INTENSITY\"]\n+ * });\n+ * (end)\n */\n- eraseGeometry: function(geometry, featureId) {\n- this.eraseFeatures(this.features[featureId][0]);\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+\n+ var data = OpenLayers.Format.XML.prototype.write.apply(\n+ this.format, [this.format.writeNode(\"wfs:GetFeature\", options)]\n+ );\n+\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ params: options.params,\n+ headers: options.headers,\n+ data: data\n+ });\n+\n+ return response;\n },\n \n /**\n- * APIMethod: supported\n- * \n- * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n+ * APIMethod: setFeatureType\n+ * Change the feature type on the fly.\n+ *\n+ * Parameters:\n+ * featureType - {String} Local (without prefix) feature typeName.\n */\n- supported: function() {\n- return OpenLayers.CANVAS_SUPPORTED;\n+ setFeatureType: function(featureType) {\n+ this.featureType = featureType;\n+ this.format.featureType = featureType;\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n- *\n- * Once the size is updated, redraw the canvas.\n+ * APIMethod: setGeometryName\n+ * Sets the geometryName option after instantiation.\n *\n * Parameters:\n- * size - {<OpenLayers.Size>} \n+ * geometryName - {String} Name of geometry attribute.\n */\n- setSize: function(size) {\n- this.size = size.clone();\n- var root = this.root;\n- root.style.width = size.w + \"px\";\n- root.style.height = size.h + \"px\";\n- root.width = size.w;\n- root.height = size.h;\n- this.resolution = null;\n- if (this.hitDetection) {\n- var hitCanvas = this.hitCanvas;\n- hitCanvas.style.width = size.w + \"px\";\n- hitCanvas.style.height = size.h + \"px\";\n- hitCanvas.width = size.w;\n- hitCanvas.height = size.h;\n- }\n+ setGeometryName: function(geometryName) {\n+ this.geometryName = geometryName;\n+ this.format.geometryName = geometryName;\n },\n \n /**\n- * Method: drawFeature\n- * Draw the feature. Stores the feature in the features list,\n- * then redraws the layer. \n+ * Method: handleRead\n+ * Deal with response from the read request.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {<Object>} \n- *\n- * Returns:\n- * {Boolean} The feature has been drawn completely. If the feature has no\n- * geometry, undefined will be returned. If the feature is not rendered\n- * for other reasons, false will be returned.\n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass\n+ * to the user callback.\n+ * options - {Object} The user options passed to the read call.\n */\n- drawFeature: function(feature, style) {\n- var rendered;\n- if (feature.geometry) {\n- style = this.applyDefaultSymbolizer(style || feature.style);\n- // don't render if display none or feature outside extent\n- var bounds = feature.geometry.getBounds();\n-\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n- }\n-\n- var intersects = bounds && bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- });\n+ handleRead: function(response, options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n \n- rendered = (style.display !== \"none\") && !!bounds && intersects;\n- if (rendered) {\n- // keep track of what we have rendered for redraw\n- this.features[feature.id] = [feature, style];\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ // success\n+ var result = this.parseResponse(request, options.readOptions);\n+ if (result && result.success !== false) {\n+ if (options.readOptions && options.readOptions.output == \"object\") {\n+ OpenLayers.Util.extend(response, result);\n+ } else {\n+ response.features = result;\n+ }\n+ response.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ // failure (service exception)\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n+ response.error = result;\n+ }\n } else {\n- // remove from features tracked for redraw\n- delete(this.features[feature.id]);\n+ // failure\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n }\n- this.pendingRedraw = true;\n- }\n- if (this.pendingRedraw && !this.locked) {\n- this.redraw();\n- this.pendingRedraw = false;\n+ options.callback.call(options.scope, response);\n }\n- return rendered;\n },\n \n- /** \n- * Method: drawGeometry\n- * Used when looping (in redraw) over the features; draws\n- * the canvas. \n+ /**\n+ * Method: parseResponse\n+ * Read HTTP response body and return features\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * style - {Object} \n+ * request - {XMLHttpRequest} The request object\n+ * options - {Object} Optional object to pass to format's read\n+ *\n+ * Returns:\n+ * {Object} or {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>} \n+ * An object with a features property, an array of features or a single \n+ * feature.\n */\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- if ((className == \"OpenLayers.Geometry.Collection\") ||\n- (className == \"OpenLayers.Geometry.MultiPoint\") ||\n- (className == \"OpenLayers.Geometry.MultiLineString\") ||\n- (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n- for (var i = 0; i < geometry.components.length; i++) {\n- this.drawGeometry(geometry.components[i], style, featureId);\n- }\n- return;\n+ parseResponse: function(request, options) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n }\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- this.drawPoint(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- this.drawLineString(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- this.drawLinearRing(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- this.drawPolygon(geometry, style, featureId);\n- break;\n- default:\n- break;\n+ if (!doc || doc.length <= 0) {\n+ return null;\n+ }\n+ var result = (this.readFormat !== null) ? this.readFormat.read(doc) :\n+ this.format.read(doc, options);\n+ if (!this.featureNS) {\n+ var format = this.readFormat || this.format;\n+ this.featureNS = format.featureNS;\n+ // no need to auto-configure again on subsequent reads\n+ format.autoConfig = false;\n+ if (!this.geometryName) {\n+ this.setGeometryName(format.geometryName);\n+ }\n }\n+ return result;\n },\n \n /**\n- * Method: drawExternalGraphic\n- * Called to draw External graphics. \n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Method: commit\n+ * Given a list of feature, assemble a batch request for update, create,\n+ * and delete transactions. A commit call on the prototype amounts\n+ * to writing a WFS transaction - so the write method on the format\n+ * is used.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)}\n+ * options - {Object}\n+ *\n+ * Valid options properties:\n+ * nativeElements - {Array({Object})} Array of objects with information for writing\n+ * out <Native> elements, these objects have vendorId, safeToIgnore and\n+ * value properties. The <Native> element is intended to allow access to \n+ * vendor specific capabilities of any particular web feature server or \n+ * datastore.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} A response object with a features\n+ * property containing any insertIds and a priv property referencing\n+ * the XMLHttpRequest object.\n */\n- drawExternalGraphic: function(geometry, style, featureId) {\n- var img = new Image();\n-\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- img.title = title;\n- }\n-\n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = (style.graphicXOffset != undefined) ?\n- style.graphicXOffset : -(0.5 * width);\n- var yOffset = (style.graphicYOffset != undefined) ?\n- style.graphicYOffset : -(0.5 * height);\n+ commit: function(features, options) {\n \n- var opacity = style.graphicOpacity || style.fillOpacity;\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n \n- var onLoad = function() {\n- if (!this.features[featureId]) {\n- return;\n- }\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var x = (p0 + xOffset) | 0;\n- var y = (p1 + yOffset) | 0;\n- var canvas = this.canvas;\n- canvas.globalAlpha = opacity;\n- var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||\n- (OpenLayers.Renderer.Canvas.drawImageScaleFactor =\n- /android 2.1/.test(navigator.userAgent.toLowerCase()) ?\n- // 320 is the screen width of the G1 phone, for\n- // which drawImage works out of the box.\n- 320 / window.screen.width : 1\n- );\n- canvas.drawImage(\n- img, x * factor, y * factor, width * factor, height * factor\n- );\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId);\n- this.hitContext.fillRect(x, y, width, height);\n- }\n- }\n- };\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"commit\",\n+ reqFeatures: features\n+ });\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ headers: options.headers,\n+ data: this.format.write(features, options),\n+ callback: this.createCallback(this.handleCommit, response, options)\n+ });\n \n- img.onload = OpenLayers.Function.bind(onLoad, this);\n- img.src = style.externalGraphic;\n+ return response;\n },\n \n /**\n- * Method: drawNamedSymbol\n- * Called to draw Well Known Graphic Symbol Name. \n- * This method is only called by the renderer itself.\n+ * Method: handleCommit\n+ * Called when the commit request returns.\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass\n+ * to the user callback.\n+ * options - {Object} The user options passed to the commit call.\n */\n- drawNamedSymbol: function(geometry, style, featureId) {\n- var x, y, cx, cy, i, symbolBounds, scaling, angle;\n- var unscaledStrokeWidth;\n- var deg2rad = Math.PI / 180.0;\n-\n- var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n-\n- if (!symbol) {\n- throw new Error(style.graphicName + ' is not a valid symbol name');\n- }\n-\n- if (!symbol.length || symbol.length < 2) return;\n-\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n-\n- if (isNaN(p0) || isNaN(p1)) return;\n-\n- // Use rounded line caps\n- this.canvas.lineCap = \"round\";\n- this.canvas.lineJoin = \"round\";\n-\n- if (this.hitDetection) {\n- this.hitContext.lineCap = \"round\";\n- this.hitContext.lineJoin = \"round\";\n- }\n+ handleCommit: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n \n- // Scale and rotate symbols, using precalculated bounds whenever possible.\n- if (style.graphicName in this.cachedSymbolBounds) {\n- symbolBounds = this.cachedSymbolBounds[style.graphicName];\n- } else {\n- symbolBounds = new OpenLayers.Bounds();\n- for (i = 0; i < symbol.length; i += 2) {\n- symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]));\n+ // ensure that we have an xml doc\n+ var data = request.responseXML;\n+ if (!data || !data.documentElement) {\n+ data = request.responseText;\n }\n- this.cachedSymbolBounds[style.graphicName] = symbolBounds;\n- }\n-\n- // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.\n- // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)\n- this.canvas.save();\n- if (this.hitDetection) {\n- this.hitContext.save();\n- }\n \n- // Step 3: place symbol at the desired location\n- this.canvas.translate(p0, p1);\n- if (this.hitDetection) {\n- this.hitContext.translate(p0, p1);\n- }\n+ var obj = this.format.read(data) || {};\n \n- // Step 2a. rotate the symbol if necessary\n- angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.\n- if (!isNaN(angle)) {\n- this.canvas.rotate(angle);\n- if (this.hitDetection) {\n- this.hitContext.rotate(angle);\n+ response.insertIds = obj.insertIds || [];\n+ if (obj.success) {\n+ response.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n+ response.error = obj;\n }\n+ options.callback.call(options.scope, response);\n }\n+ },\n \n- // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.\n- scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n- this.canvas.scale(scaling, scaling);\n- if (this.hitDetection) {\n- this.hitContext.scale(scaling, scaling);\n- }\n-\n- // Step 1: center the symbol at the origin \n- cx = symbolBounds.getCenterLonLat().lon;\n- cy = symbolBounds.getCenterLonLat().lat;\n- this.canvas.translate(-cx, -cy);\n- if (this.hitDetection) {\n- this.hitContext.translate(-cx, -cy);\n- }\n+ /**\n+ * Method: filterDelete\n+ * Send a request that deletes all features by their filter.\n+ * \n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} filter\n+ */\n+ filterDelete: function(filter, options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n \n- // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)\n- // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.\n- unscaledStrokeWidth = style.strokeWidth;\n- style.strokeWidth = unscaledStrokeWidth / scaling;\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"commit\"\n+ });\n \n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y);\n+ var root = this.format.createElementNSPlus(\"wfs:Transaction\", {\n+ attributes: {\n+ service: \"WFS\",\n+ version: this.version\n }\n- this.canvas.closePath();\n- this.canvas.fill();\n+ });\n \n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.hitContext.lineTo(x, y);\n- }\n- this.hitContext.closePath();\n- this.hitContext.fill();\n+ var deleteNode = this.format.createElementNSPlus(\"wfs:Delete\", {\n+ attributes: {\n+ typeName: (options.featureNS ? this.featurePrefix + \":\" : \"\") +\n+ options.featureType\n }\n+ });\n+\n+ if (options.featureNS) {\n+ deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, options.featureNS);\n }\n+ var filterNode = this.format.writeNode(\"ogc:Filter\", filter);\n \n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y);\n- }\n- this.canvas.closePath();\n- this.canvas.stroke();\n+ deleteNode.appendChild(filterNode);\n \n+ root.appendChild(deleteNode);\n \n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.hitContext.moveTo(x, y);\n- this.hitContext.lineTo(x, y);\n- }\n- this.hitContext.closePath();\n- this.hitContext.stroke();\n- }\n+ var data = OpenLayers.Format.XML.prototype.write.apply(\n+ this.format, [root]\n+ );\n \n- }\n+ return OpenLayers.Request.POST({\n+ url: this.url,\n+ callback: options.callback || function() {},\n+ data: data\n+ });\n \n- style.strokeWidth = unscaledStrokeWidth;\n- this.canvas.restore();\n- if (this.hitDetection) {\n- this.hitContext.restore();\n- }\n- this.setCanvasStyle(\"reset\");\n },\n \n /**\n- * Method: setCanvasStyle\n- * Prepare the canvas for drawing by setting various global settings.\n+ * Method: abort\n+ * Abort an ongoing request, the response object passed to\n+ * this method must come from this protocol (as a result\n+ * of a read, or commit operation).\n *\n * Parameters:\n- * type - {String} one of 'stroke', 'fill', or 'reset'\n- * style - {Object} Symbolizer hash\n+ * response - {<OpenLayers.Protocol.Response>}\n */\n- setCanvasStyle: function(type, style) {\n- if (type === \"fill\") {\n- this.canvas.globalAlpha = style['fillOpacity'];\n- this.canvas.fillStyle = style['fillColor'];\n- } else if (type === \"stroke\") {\n- this.canvas.globalAlpha = style['strokeOpacity'];\n- this.canvas.strokeStyle = style['strokeColor'];\n- this.canvas.lineWidth = style['strokeWidth'];\n- } else {\n- this.canvas.globalAlpha = 0;\n- this.canvas.lineWidth = 1;\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort();\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1\"\n+});\n+/* ======================================================================\n+ OpenLayers/Protocol/WFS/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Protocol/WFS/v1.js\n+ * @requires OpenLayers/Format/WFST/v1_0_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.WFS.v1_0_0\n+ * A WFS v1.0.0 protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.WFS.v1_0_0> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Protocol.WFS.v1>\n+ */\n+OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n+\n /**\n- * Method: featureIdToHex\n- * Convert a feature ID string into an RGB hex string.\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Protocol.WFS.v1_0_0\n+ * A class for giving layers WFS v1.0.0 protocol.\n *\n * Parameters:\n- * featureId - {String} Feature id\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n *\n- * Returns:\n- * {String} RGB hex string.\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n */\n- featureIdToHex: function(featureId) {\n- var id = Number(featureId.split(\"_\").pop()) + 1; // zero for no feature\n- if (id >= 16777216) {\n- this.hitOverflow = id - 16777215;\n- id = id % 16777216 + 1;\n- }\n- var hex = \"000000\" + id.toString(16);\n- var len = hex.length;\n- hex = \"#\" + hex.substring(len - 6, len);\n- return hex;\n- },\n+\n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n+});\n+/* ======================================================================\n+ OpenLayers/Protocol/WFS/v1_1_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Protocol/WFS/v1.js\n+ * @requires OpenLayers/Format/WFST/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.WFS.v1_1_0\n+ * A WFS v1.1.0 protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.WFS.v1_1_0> constructor.\n+ *\n+ * Differences from the v1.0.0 protocol:\n+ * - uses Filter Encoding 1.1.0 instead of 1.0.0\n+ * - uses GML 3 instead of 2 if no format is provided\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Protocol.WFS.v1>\n+ */\n+OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n \n /**\n- * Method: setHitContextStyle\n- * Prepare the hit canvas for drawing by setting various global settings.\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.1.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Protocol.WFS.v1_1_0\n+ * A class for giving layers WFS v1.1.0 protocol.\n *\n * Parameters:\n- * type - {String} one of 'stroke', 'fill', or 'reset'\n- * featureId - {String} The feature id.\n- * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ * outputFormat - {String} Optional output format to use for WFS GetFeature\n+ * requests. This can be any format advertized by the WFS's\n+ * GetCapabilities response. If set, an appropriate readFormat also\n+ * has to be provided, unless outputFormat is GML3, GML2 or JSON.\n+ * readFormat - {<OpenLayers.Format>} An appropriate format parser if\n+ * outputFormat is none of GML3, GML2 or JSON.\n */\n- setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n- var hex = this.featureIdToHex(featureId);\n- if (type == \"fill\") {\n- this.hitContext.globalAlpha = 1.0;\n- this.hitContext.fillStyle = hex;\n- } else if (type == \"stroke\") {\n- this.hitContext.globalAlpha = 1.0;\n- this.hitContext.strokeStyle = hex;\n- // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol \n- // on a transformed canvas, so the antialias width bump has to scale as well.\n- if (typeof strokeScaling === \"undefined\") {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2;\n- } else {\n- if (!isNaN(strokeScaling)) {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling;\n- }\n+ initialize: function(options) {\n+ OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);\n+ if (this.outputFormat && !this.readFormat) {\n+ if (this.outputFormat.toLowerCase() == \"gml2\") {\n+ this.readFormat = new OpenLayers.Format.GML.v2({\n+ featureType: this.featureType,\n+ featureNS: this.featureNS,\n+ geometryName: this.geometryName\n+ });\n+ } else if (this.outputFormat.toLowerCase() == \"json\") {\n+ this.readFormat = new OpenLayers.Format.GeoJSON();\n }\n- } else {\n- this.hitContext.globalAlpha = 0;\n- this.hitContext.lineWidth = 1;\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_1_0\"\n+});\n+/* ======================================================================\n+ OpenLayers/Protocol/CSW/v2_0_2.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Protocol/CSW.js\n+ * @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.CSW.v2_0_2\n+ * CS-W (Catalogue services for the Web) version 2.0.2 protocol.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Protocol>\n+ */\n+OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, {\n+\n /**\n- * Method: drawPoint\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Property: formatOptions\n+ * {Object} Optional options for the format. If a format is not provided,\n+ * this property can be used to extend the default format options.\n */\n- drawPoint: function(geometry, style, featureId) {\n- if (style.graphic !== false) {\n- if (style.externalGraphic) {\n- this.drawExternalGraphic(geometry, style, featureId);\n- } else if (style.graphicName && (style.graphicName != \"circle\")) {\n- this.drawNamedSymbol(geometry, style, featureId);\n- } else {\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var twoPi = Math.PI * 2;\n- var radius = style.pointRadius;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.fill();\n- }\n- }\n+ formatOptions: null,\n \n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.stroke();\n- }\n- this.setCanvasStyle(\"reset\");\n- }\n- }\n- }\n+ /**\n+ * Constructor: OpenLayers.Protocol.CSW.v2_0_2\n+ * A class for CSW version 2.0.2 protocol management.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions));\n }\n },\n \n /**\n- * Method: drawLineString\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- drawLineString: function(geometry, style, featureId) {\n- style = OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style);\n- this.drawLinearRing(geometry, style, featureId);\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy();\n+ }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n },\n \n /**\n- * Method: drawLinearRing\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Method: read\n+ * Construct a request for reading new records from the Catalogue.\n */\n- drawLinearRing: function(geometry, style, featureId) {\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"fill\");\n- }\n- }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\");\n- }\n- }\n- this.setCanvasStyle(\"reset\");\n+ read: function(options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+\n+ var data = this.format.write(options.params || options);\n+\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ params: options.params,\n+ headers: options.headers,\n+ data: data\n+ });\n+\n+ return response;\n },\n \n /**\n- * Method: renderPath\n- * Render a path with stroke and optional fill.\n+ * Method: handleRead\n+ * Deal with response from the read request.\n+ *\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass\n+ * to the user callback.\n+ * This response is given a code property, and optionally a data property.\n+ * The latter represents the CSW records as returned by the call to\n+ * the CSW format read method.\n+ * options - {Object} The user options passed to the read call.\n */\n- renderPath: function(context, geometry, style, featureId, type) {\n- var components = geometry.components;\n- var len = components.length;\n- context.beginPath();\n- var start = this.getLocalXY(components[0]);\n- var x = start[0];\n- var y = start[1];\n- if (!isNaN(x) && !isNaN(y)) {\n- context.moveTo(start[0], start[1]);\n- for (var i = 1; i < len; ++i) {\n- var pt = this.getLocalXY(components[i]);\n- context.lineTo(pt[0], pt[1]);\n- }\n- if (type === \"fill\") {\n- context.fill();\n+ handleRead: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ // success\n+ response.data = this.parseData(request);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS;\n } else {\n- context.stroke();\n+ // failure\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n }\n+ options.callback.call(options.scope, response);\n }\n },\n \n /**\n- * Method: drawPolygon\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Method: parseData\n+ * Read HTTP response body and return records\n+ *\n+ * Parameters:\n+ * request - {XMLHttpRequest} The request object\n+ *\n+ * Returns:\n+ * {Object} The CSW records as returned by the call to the format read method.\n */\n- drawPolygon: function(geometry, style, featureId) {\n- var components = geometry.components;\n- var len = components.length;\n- this.drawLinearRing(components[0], style, featureId);\n- // erase inner rings\n- for (var i = 1; i < len; ++i) {\n- /** \n- * Note that this is overly agressive. Here we punch holes through \n- * all previously rendered features on the same canvas. A better \n- * solution for polygons with interior rings would be to draw the \n- * polygon on a sketch canvas first. We could erase all holes \n- * there and then copy the drawing to the layer canvas. \n- * TODO: http://trac.osgeo.org/openlayers/ticket/3130 \n- */\n- this.canvas.globalCompositeOperation = \"destination-out\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"destination-out\";\n- }\n- this.drawLinearRing(\n- components[i],\n- OpenLayers.Util.applyDefaults({\n- stroke: false,\n- fillOpacity: 1.0\n- }, style),\n- featureId\n- );\n- this.canvas.globalCompositeOperation = \"source-over\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"source-over\";\n- }\n- this.drawLinearRing(\n- components[i],\n- OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style),\n- featureId\n- );\n+ parseData: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ if (!doc || doc.length <= 0) {\n+ return null;\n }\n+ return this.format.read(doc);\n },\n \n+ CLASS_NAME: \"OpenLayers.Protocol.CSW.v2_0_2\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Protocol/SOS/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Protocol/SOS.js\n+ * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.SOS.v1_0_0\n+ * An SOS v1.0.0 Protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.SOS.v1_0_0> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Protocol>\n+ */\n+OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {\n+\n /**\n- * Method: drawText\n- * This method is only called by the renderer itself.\n- *\n- * Parameters:\n- * location - {<OpenLayers.Point>}\n- * style - {Object}\n+ * APIProperty: fois\n+ * {Array(String)} Array of features of interest (foi)\n */\n- drawText: function(location, style) {\n- var pt = this.getLocalXY(location);\n+ fois: null,\n \n- this.setCanvasStyle(\"reset\");\n- this.canvas.fillStyle = style.fontColor;\n- this.canvas.globalAlpha = style.fontOpacity || 1.0;\n- var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\",\n- \"normal\", // \"font-variant\" not supported\n- style.fontWeight ? style.fontWeight : \"normal\",\n- style.fontSize ? style.fontSize : \"1em\",\n- style.fontFamily ? style.fontFamily : \"sans-serif\"\n- ].join(\" \");\n- var labelRows = style.label.split('\\n');\n- var numRows = labelRows.length;\n- if (this.canvas.fillText) {\n- // HTML5\n- this.canvas.font = fontStyle;\n- this.canvas.textAlign =\n- OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||\n- \"center\";\n- this.canvas.textBaseline =\n- OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||\n- \"middle\";\n- var vfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5;\n- }\n- var lineHeight =\n- this.canvas.measureText('Mg').height ||\n- this.canvas.measureText('xx').width;\n- pt[1] += lineHeight * vfactor * (numRows - 1);\n- for (var i = 0; i < numRows; i++) {\n- if (style.labelOutlineWidth) {\n- this.canvas.save();\n- this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;\n- this.canvas.strokeStyle = style.labelOutlineColor;\n- this.canvas.lineWidth = style.labelOutlineWidth;\n- this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1);\n- this.canvas.restore();\n- }\n- this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));\n- }\n- } else if (this.canvas.mozDrawText) {\n- // Mozilla pre-Gecko1.9.1 (<FF3.1)\n- this.canvas.mozTextStyle = fontStyle;\n- // No built-in text alignment, so we measure and adjust the position\n- var hfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n- if (hfactor == null) {\n- hfactor = -.5;\n- }\n- var vfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5;\n- }\n- var lineHeight = this.canvas.mozMeasureText('xx');\n- pt[1] += lineHeight * (1 + (vfactor * numRows));\n- for (var i = 0; i < numRows; i++) {\n- var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i]));\n- var y = pt[1] + (i * lineHeight);\n- this.canvas.translate(x, y);\n- this.canvas.mozDrawText(labelRows[i]);\n- this.canvas.translate(-x, -y);\n- }\n- }\n- this.setCanvasStyle(\"reset\");\n- },\n+ /**\n+ * Property: formatOptions\n+ * {Object} Optional options for the format. If a format is not provided,\n+ * this property can be used to extend the default format options.\n+ */\n+ formatOptions: null,\n \n /**\n- * Method: getLocalXY\n- * transform geographic xy into pixel xy\n+ * Constructor: OpenLayers.Protocol.SOS\n+ * A class for giving layers an SOS protocol.\n *\n- * Parameters: \n- * point - {<OpenLayers.Geometry.Point>}\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * url - {String} URL to send requests to (required).\n+ * fois - {Array} The features of interest (required).\n */\n- getLocalXY: function(point) {\n- var resolution = this.getResolution();\n- var extent = this.extent;\n- var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));\n- var y = ((extent.top / resolution) - point.y / resolution);\n- return [x, y];\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(\n+ this.formatOptions);\n+ }\n },\n \n /**\n- * Method: clear\n- * Clear all vectors from the renderer.\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- clear: function() {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height);\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy();\n }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n },\n \n /**\n- * Method: getFeatureIdFromEvent\n- * Returns a feature id from an event on the renderer. \n- * \n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a \n- * feature instead of a feature id to avoid an unnecessary lookup on the\n- * layer.\n+ * APIMethod: read\n+ * Construct a request for reading new sensor positions. This is done by\n+ * issuing one GetFeatureOfInterest request.\n */\n- getFeatureIdFromEvent: function(evt) {\n- var featureId, feature;\n-\n- if (this.hitDetection && this.root.style.display !== \"none\") {\n- // this dragging check should go in the feature handler\n- if (!this.map.dragging) {\n- var xy = evt.xy;\n- var x = xy.x | 0;\n- var y = xy.y | 0;\n- var data = this.hitContext.getImageData(x, y, 1, 1).data;\n- if (data[3] === 255) { // antialiased\n- var id = data[2] + (256 * (data[1] + (256 * data[0])));\n- if (id) {\n- featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n- try {\n- feature = this.features[featureId][0];\n- } catch (err) {\n- // Because of antialiasing on the canvas, when the hit location is at a point where the edge of\n- // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.\n- // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.\n- }\n- }\n- }\n- }\n- }\n- return feature;\n+ read: function(options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var format = this.format;\n+ var data = OpenLayers.Format.XML.prototype.write.apply(format,\n+ [format.writeNode(\"sos:GetFeatureOfInterest\", {\n+ fois: this.fois\n+ })]\n+ );\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ data: data\n+ });\n+ return response;\n },\n \n /**\n- * Method: eraseFeatures \n- * This is called by the layer to erase features; removes the feature from\n- * the list, then redraws the layer.\n- * \n+ * Method: handleRead\n+ * Deal with response from the read request.\n+ *\n * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * response - {<OpenLayers.Protocol.Response>} The response object to pass\n+ * to the user callback.\n+ * options - {Object} The user options passed to the read call.\n */\n- eraseFeatures: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- for (var i = 0; i < features.length; ++i) {\n- delete this.features[features[i].id];\n+ handleRead: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ // success\n+ response.features = this.parseFeatures(request);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ // failure\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n+ }\n+ options.callback.call(options.scope, response);\n }\n- this.redraw();\n },\n \n /**\n- * Method: redraw\n- * The real 'meat' of the function: any time things have changed,\n- * redraw() can be called to loop over all the data and (you guessed\n- * it) redraw it. Unlike Elements-based Renderers, we can't interact\n- * with things once they're drawn, to remove them, for example, so\n- * instead we have to just clear everything and draw from scratch.\n+ * Method: parseFeatures\n+ * Read HTTP response body and return features\n+ *\n+ * Parameters:\n+ * request - {XMLHttpRequest} The request object\n+ *\n+ * Returns:\n+ * {Array({<OpenLayers.Feature.Vector>})} Array of features\n */\n- redraw: function() {\n- if (!this.locked) {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height);\n- }\n- var labelMap = [];\n- var feature, geometry, style;\n- var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();\n- for (var id in this.features) {\n- if (!this.features.hasOwnProperty(id)) {\n- continue;\n- }\n- feature = this.features[id][0];\n- geometry = feature.geometry;\n- this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n- style = this.features[id][1];\n- this.drawGeometry(geometry, style, feature.id);\n- if (style.label) {\n- labelMap.push([feature, style]);\n- }\n- }\n- var item;\n- for (var i = 0, len = labelMap.length; i < len; ++i) {\n- item = labelMap[i];\n- this.drawText(item[0].geometry.getCentroid(), item[1]);\n- }\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ if (!doc || doc.length <= 0) {\n+ return null;\n }\n+ return this.format.read(doc);\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+ CLASS_NAME: \"OpenLayers.Protocol.SOS.v1_0_0\"\n });\n-\n-/**\n- * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN\n- * {Object}\n- */\n-OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n- \"l\": \"left\",\n- \"r\": \"right\",\n- \"t\": \"top\",\n- \"b\": \"bottom\"\n-};\n-\n-/**\n- * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR\n- * {Object}\n- */\n-OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n- \"l\": 0,\n- \"r\": -1,\n- \"t\": 0,\n- \"b\": -1\n-};\n-\n-/**\n- * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor\n- * {Number} Scale factor to apply to the canvas drawImage arguments. This\n- * is always 1 except for Android 2.1 devices, to work around\n- * http://code.google.com/p/android/issues/detail?id=5141.\n- */\n-OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n /* ======================================================================\n OpenLayers/Renderer/Elements.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -83707,2235 +83126,2816 @@\n * Used to prevent default events (especially opening images in a new tab on\n * ctrl-click) from being executed for externalGraphic symbols\n */\n OpenLayers.Renderer.SVG.preventDefault = function(e) {\n OpenLayers.Event.preventDefault(e);\n };\n /* ======================================================================\n- OpenLayers/Tile/Image/IFrame.js\n+ OpenLayers/Renderer/Canvas.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Tile/Image.js\n+ * @requires OpenLayers/Renderer.js\n */\n \n /**\n- * Constant: OpenLayers.Tile.Image.IFrame\n- * Mixin for tiles that use form-encoded POST requests to get images from\n- * remote services. Images will be loaded using HTTP-POST into an IFrame.\n- *\n- * This mixin will be applied to <OpenLayers.Tile.Image> instances\n- * configured with <OpenLayers.Tile.Image.maxGetUrlLength> set.\n+ * Class: OpenLayers.Renderer.Canvas \n+ * A renderer based on the 2D 'canvas' drawing element.\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Renderer>\n */\n-OpenLayers.Tile.Image.IFrame = {\n+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n \n /**\n- * Property: useIFrame\n- * {Boolean} true if we are currently using an IFrame to render POST\n- * responses, false if we are using an img element to render GET responses.\n+ * APIProperty: hitDetection\n+ * {Boolean} Allow for hit detection of features. Default is true.\n */\n- useIFrame: null,\n+ hitDetection: true,\n \n /**\n- * Property: blankImageUrl\n- * {String} Using a data scheme url is not supported by all browsers, but\n- * we don't care because we either set it as css backgroundImage, or the\n- * image's display style is set to \"none\" when we use it.\n+ * Property: hitOverflow\n+ * {Number} The method for converting feature identifiers to color values\n+ * supports 16777215 sequential values. Two features cannot be \n+ * predictably detected if their identifiers differ by more than this\n+ * value. The hitOverflow allows for bigger numbers (but the \n+ * difference in values is still limited).\n */\n- blankImageUrl: \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7\",\n+ hitOverflow: 0,\n \n /**\n- * Method: draw\n- * Set useIFrame in the instance, and operate the image/iframe switch.\n- * Then call Tile.Image.draw.\n+ * Property: canvas\n+ * {Canvas} The canvas context object.\n+ */\n+ canvas: null,\n+\n+ /**\n+ * Property: features\n+ * {Object} Internal object of feature/style pairs for use in redrawing the layer.\n+ */\n+ features: null,\n+\n+ /**\n+ * Property: pendingRedraw\n+ * {Boolean} The renderer needs a redraw call to render features added while\n+ * the renderer was locked.\n+ */\n+ pendingRedraw: false,\n+\n+ /**\n+ * Property: cachedSymbolBounds\n+ * {Object} Internal cache of calculated symbol extents.\n+ */\n+ cachedSymbolBounds: {},\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.Canvas\n+ *\n+ * Parameters:\n+ * containerID - {<String>}\n+ * options - {Object} Optional properties to be set on the renderer.\n+ */\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.root = document.createElement(\"canvas\");\n+ this.container.appendChild(this.root);\n+ this.canvas = this.root.getContext(\"2d\");\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitCanvas = document.createElement(\"canvas\");\n+ this.hitContext = this.hitCanvas.getContext(\"2d\");\n+ }\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n *\n * Returns:\n- * {Boolean}\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n */\n- draw: function() {\n- var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);\n- if (draw) {\n+ setExtent: function() {\n+ OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ // always redraw features\n+ return false;\n+ },\n \n- // this.url isn't set to the currect value yet, so we call getURL\n- // on the layer and store the result in a local variable\n- var url = this.layer.getURL(this.bounds);\n+ /** \n+ * Method: eraseGeometry\n+ * Erase a geometry from the renderer. Because the Canvas renderer has\n+ * 'memory' of the features that it has drawn, we have to remove the\n+ * feature so it doesn't redraw. \n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * featureId - {String}\n+ */\n+ eraseGeometry: function(geometry, featureId) {\n+ this.eraseFeatures(this.features[featureId][0]);\n+ },\n \n- var usedIFrame = this.useIFrame;\n- this.useIFrame = this.maxGetUrlLength !== null &&\n- !this.layer.async &&\n- url.length > this.maxGetUrlLength;\n+ /**\n+ * APIMethod: supported\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the renderer class\n+ */\n+ supported: function() {\n+ return OpenLayers.CANVAS_SUPPORTED;\n+ },\n \n- var fromIFrame = usedIFrame && !this.useIFrame;\n- var toIFrame = !usedIFrame && this.useIFrame;\n+ /**\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ *\n+ * Once the size is updated, redraw the canvas.\n+ *\n+ * Parameters:\n+ * size - {<OpenLayers.Size>} \n+ */\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ var root = this.root;\n+ root.style.width = size.w + \"px\";\n+ root.style.height = size.h + \"px\";\n+ root.width = size.w;\n+ root.height = size.h;\n+ this.resolution = null;\n+ if (this.hitDetection) {\n+ var hitCanvas = this.hitCanvas;\n+ hitCanvas.style.width = size.w + \"px\";\n+ hitCanvas.style.height = size.h + \"px\";\n+ hitCanvas.width = size.w;\n+ hitCanvas.height = size.h;\n+ }\n+ },\n \n- if (fromIFrame || toIFrame) {\n+ /**\n+ * Method: drawFeature\n+ * Draw the feature. Stores the feature in the features list,\n+ * then redraws the layer. \n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {<Object>} \n+ *\n+ * Returns:\n+ * {Boolean} The feature has been drawn completely. If the feature has no\n+ * geometry, undefined will be returned. If the feature is not rendered\n+ * for other reasons, false will be returned.\n+ */\n+ drawFeature: function(feature, style) {\n+ var rendered;\n+ if (feature.geometry) {\n+ style = this.applyDefaultSymbolizer(style || feature.style);\n+ // don't render if display none or feature outside extent\n+ var bounds = feature.geometry.getBounds();\n \n- // Switching between GET (image) and POST (iframe).\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n \n- // We remove the imgDiv (really either an image or an iframe)\n- // from the frame and set it to null to make sure initImage\n- // will call getImage.\n+ var intersects = bounds && bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ });\n \n- if (this.imgDiv && this.imgDiv.parentNode === this.frame) {\n- this.frame.removeChild(this.imgDiv);\n- }\n- this.imgDiv = null;\n+ rendered = (style.display !== \"none\") && !!bounds && intersects;\n+ if (rendered) {\n+ // keep track of what we have rendered for redraw\n+ this.features[feature.id] = [feature, style];\n+ } else {\n+ // remove from features tracked for redraw\n+ delete(this.features[feature.id]);\n+ }\n+ this.pendingRedraw = true;\n+ }\n+ if (this.pendingRedraw && !this.locked) {\n+ this.redraw();\n+ this.pendingRedraw = false;\n+ }\n+ return rendered;\n+ },\n \n- // And if we had an iframe we also remove the event pane.\n+ /** \n+ * Method: drawGeometry\n+ * Used when looping (in redraw) over the features; draws\n+ * the canvas. \n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * style - {Object} \n+ */\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ if ((className == \"OpenLayers.Geometry.Collection\") ||\n+ (className == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (className == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ this.drawGeometry(geometry.components[i], style, featureId);\n+ }\n+ return;\n+ }\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ this.drawPoint(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ this.drawLineString(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ this.drawLinearRing(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ this.drawPolygon(geometry, style, featureId);\n+ break;\n+ default:\n+ break;\n+ }\n+ },\n \n- if (fromIFrame) {\n- this.frame.removeChild(this.frame.firstChild);\n+ /**\n+ * Method: drawExternalGraphic\n+ * Called to draw External graphics. \n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawExternalGraphic: function(geometry, style, featureId) {\n+ var img = new Image();\n+\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ img.title = title;\n+ }\n+\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n+\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+\n+ var onLoad = function() {\n+ if (!this.features[featureId]) {\n+ return;\n+ }\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var x = (p0 + xOffset) | 0;\n+ var y = (p1 + yOffset) | 0;\n+ var canvas = this.canvas;\n+ canvas.globalAlpha = opacity;\n+ var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||\n+ (OpenLayers.Renderer.Canvas.drawImageScaleFactor =\n+ /android 2.1/.test(navigator.userAgent.toLowerCase()) ?\n+ // 320 is the screen width of the G1 phone, for\n+ // which drawImage works out of the box.\n+ 320 / window.screen.width : 1\n+ );\n+ canvas.drawImage(\n+ img, x * factor, y * factor, width * factor, height * factor\n+ );\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId);\n+ this.hitContext.fillRect(x, y, width, height);\n }\n }\n- }\n- return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments);\n+ };\n+\n+ img.onload = OpenLayers.Function.bind(onLoad, this);\n+ img.src = style.externalGraphic;\n },\n \n /**\n- * Method: getImage\n- * Creates the content for the frame on the tile.\n+ * Method: drawNamedSymbol\n+ * Called to draw Well Known Graphic Symbol Name. \n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- getImage: function() {\n- if (this.useIFrame === true) {\n- if (!this.frame.childNodes.length) {\n- var eventPane = document.createElement(\"div\"),\n- style = eventPane.style;\n- style.position = \"absolute\";\n- style.width = \"100%\";\n- style.height = \"100%\";\n- style.zIndex = 1;\n- style.backgroundImage = \"url(\" + this.blankImageUrl + \")\";\n- this.frame.appendChild(eventPane);\n+ drawNamedSymbol: function(geometry, style, featureId) {\n+ var x, y, cx, cy, i, symbolBounds, scaling, angle;\n+ var unscaledStrokeWidth;\n+ var deg2rad = Math.PI / 180.0;\n+\n+ var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n+\n+ if (!symbol) {\n+ throw new Error(style.graphicName + ' is not a valid symbol name');\n+ }\n+\n+ if (!symbol.length || symbol.length < 2) return;\n+\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+\n+ if (isNaN(p0) || isNaN(p1)) return;\n+\n+ // Use rounded line caps\n+ this.canvas.lineCap = \"round\";\n+ this.canvas.lineJoin = \"round\";\n+\n+ if (this.hitDetection) {\n+ this.hitContext.lineCap = \"round\";\n+ this.hitContext.lineJoin = \"round\";\n+ }\n+\n+ // Scale and rotate symbols, using precalculated bounds whenever possible.\n+ if (style.graphicName in this.cachedSymbolBounds) {\n+ symbolBounds = this.cachedSymbolBounds[style.graphicName];\n+ } else {\n+ symbolBounds = new OpenLayers.Bounds();\n+ for (i = 0; i < symbol.length; i += 2) {\n+ symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]));\n }\n+ this.cachedSymbolBounds[style.graphicName] = symbolBounds;\n+ }\n \n- var id = this.id + '_iFrame',\n- iframe;\n- if (parseFloat(navigator.appVersion.split(\"MSIE\")[1]) < 9) {\n- // Older IE versions do not set the name attribute of an iFrame \n- // properly via DOM manipulation, so we need to do it on our own with\n- // this hack.\n- iframe = document.createElement('<iframe name=\"' + id + '\">');\n+ // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.\n+ // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)\n+ this.canvas.save();\n+ if (this.hitDetection) {\n+ this.hitContext.save();\n+ }\n \n- // IFrames in older IE versions are not transparent, if you set\n- // the backgroundColor transparent. This is a workaround to get \n- // transparent iframes.\n- iframe.style.backgroundColor = '#FFFFFF';\n- iframe.style.filter = 'chroma(color=#FFFFFF)';\n- } else {\n- iframe = document.createElement('iframe');\n- iframe.style.backgroundColor = 'transparent';\n+ // Step 3: place symbol at the desired location\n+ this.canvas.translate(p0, p1);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(p0, p1);\n+ }\n \n- // iframe.name needs to be an unique id, otherwise it \n- // could happen that other iframes are overwritten.\n- iframe.name = id;\n+ // Step 2a. rotate the symbol if necessary\n+ angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.\n+ if (!isNaN(angle)) {\n+ this.canvas.rotate(angle);\n+ if (this.hitDetection) {\n+ this.hitContext.rotate(angle);\n }\n+ }\n \n- // some special properties to avoid scaling the images and scrollbars \n- // in the iframe\n- iframe.scrolling = 'no';\n- iframe.marginWidth = '0px';\n- iframe.marginHeight = '0px';\n- iframe.frameBorder = '0';\n+ // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.\n+ scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n+ this.canvas.scale(scaling, scaling);\n+ if (this.hitDetection) {\n+ this.hitContext.scale(scaling, scaling);\n+ }\n \n- iframe.style.position = \"absolute\";\n- iframe.style.width = \"100%\";\n- iframe.style.height = \"100%\";\n+ // Step 1: center the symbol at the origin \n+ cx = symbolBounds.getCenterLonLat().lon;\n+ cy = symbolBounds.getCenterLonLat().lat;\n+ this.canvas.translate(-cx, -cy);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(-cx, -cy);\n+ }\n \n- if (this.layer.opacity < 1) {\n- OpenLayers.Util.modifyDOMElement(iframe, null, null, null,\n- null, null, null, this.layer.opacity);\n+ // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)\n+ // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.\n+ unscaledStrokeWidth = style.strokeWidth;\n+ style.strokeWidth = unscaledStrokeWidth / scaling;\n+\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n }\n- this.frame.appendChild(iframe);\n- this.imgDiv = iframe;\n- return iframe;\n- } else {\n- return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments);\n+ this.canvas.closePath();\n+ this.canvas.fill();\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n+ }\n+ this.canvas.closePath();\n+ this.canvas.stroke();\n+\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.hitContext.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.stroke();\n+ }\n+\n+ }\n+\n+ style.strokeWidth = unscaledStrokeWidth;\n+ this.canvas.restore();\n+ if (this.hitDetection) {\n+ this.hitContext.restore();\n }\n+ this.setCanvasStyle(\"reset\");\n },\n \n /**\n- * Method: createRequestForm\n- * Create the html <form> element with width, height, bbox and all \n- * parameters specified in the layer params.\n+ * Method: setCanvasStyle\n+ * Prepare the canvas for drawing by setting various global settings.\n *\n- * Returns: \n- * {DOMElement} The form element which sends the HTTP-POST request to the\n- * WMS. \n+ * Parameters:\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * style - {Object} Symbolizer hash\n */\n- createRequestForm: function() {\n- // creation of the form element\n- var form = document.createElement('form');\n- form.method = 'POST';\n- var cacheId = this.layer.params[\"_OLSALT\"];\n- cacheId = (cacheId ? cacheId + \"_\" : \"\") + this.bounds.toBBOX();\n- form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);\n- form.target = this.id + '_iFrame';\n-\n- // adding all parameters in layer params as hidden fields to the html\n- // form element\n- var imageSize = this.layer.getImageSize(),\n- params = OpenLayers.Util.getParameters(this.url),\n- field;\n-\n- for (var par in params) {\n- field = document.createElement('input');\n- field.type = 'hidden';\n- field.name = par;\n- field.value = params[par];\n- form.appendChild(field);\n+ setCanvasStyle: function(type, style) {\n+ if (type === \"fill\") {\n+ this.canvas.globalAlpha = style['fillOpacity'];\n+ this.canvas.fillStyle = style['fillColor'];\n+ } else if (type === \"stroke\") {\n+ this.canvas.globalAlpha = style['strokeOpacity'];\n+ this.canvas.strokeStyle = style['strokeColor'];\n+ this.canvas.lineWidth = style['strokeWidth'];\n+ } else {\n+ this.canvas.globalAlpha = 0;\n+ this.canvas.lineWidth = 1;\n }\n+ },\n \n- return form;\n+ /**\n+ * Method: featureIdToHex\n+ * Convert a feature ID string into an RGB hex string.\n+ *\n+ * Parameters:\n+ * featureId - {String} Feature id\n+ *\n+ * Returns:\n+ * {String} RGB hex string.\n+ */\n+ featureIdToHex: function(featureId) {\n+ var id = Number(featureId.split(\"_\").pop()) + 1; // zero for no feature\n+ if (id >= 16777216) {\n+ this.hitOverflow = id - 16777215;\n+ id = id % 16777216 + 1;\n+ }\n+ var hex = \"000000\" + id.toString(16);\n+ var len = hex.length;\n+ hex = \"#\" + hex.substring(len - 6, len);\n+ return hex;\n },\n \n /**\n- * Method: setImgSrc\n- * Sets the source for the tile image\n+ * Method: setHitContextStyle\n+ * Prepare the hit canvas for drawing by setting various global settings.\n *\n * Parameters:\n- * url - {String}\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * featureId - {String} The feature id.\n+ * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.\n */\n- setImgSrc: function(url) {\n- if (this.useIFrame === true) {\n- if (url) {\n- var form = this.createRequestForm();\n- this.frame.appendChild(form);\n- form.submit();\n- this.frame.removeChild(form);\n- } else if (this.imgDiv.parentNode === this.frame) {\n- // we don't reuse iframes to avoid caching issues\n- this.frame.removeChild(this.imgDiv);\n- this.imgDiv = null;\n+ setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n+ var hex = this.featureIdToHex(featureId);\n+ if (type == \"fill\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.fillStyle = hex;\n+ } else if (type == \"stroke\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.strokeStyle = hex;\n+ // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol \n+ // on a transformed canvas, so the antialias width bump has to scale as well.\n+ if (typeof strokeScaling === \"undefined\") {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2;\n+ } else {\n+ if (!isNaN(strokeScaling)) {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling;\n+ }\n }\n } else {\n- OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments);\n+ this.hitContext.globalAlpha = 0;\n+ this.hitContext.lineWidth = 1;\n }\n },\n \n /**\n- * Method: onImageLoad\n- * Handler for the image onload event\n+ * Method: drawPoint\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- onImageLoad: function() {\n- //TODO de-uglify opacity handling\n- OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments);\n- if (this.useIFrame === true) {\n- this.imgDiv.style.opacity = 1;\n- this.frame.style.opacity = this.layer.opacity;\n+ drawPoint: function(geometry, style, featureId) {\n+ if (style.graphic !== false) {\n+ if (style.externalGraphic) {\n+ this.drawExternalGraphic(geometry, style, featureId);\n+ } else if (style.graphicName && (style.graphicName != \"circle\")) {\n+ this.drawNamedSymbol(geometry, style, featureId);\n+ } else {\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var twoPi = Math.PI * 2;\n+ var radius = style.pointRadius;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.stroke();\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ }\n+ }\n+ }\n }\n },\n \n /**\n- * Method: createBackBuffer\n- * Override createBackBuffer to do nothing when we use an iframe. Moving an\n- * iframe from one element to another makes it necessary to reload the iframe\n- * because its content is lost. So we just give up.\n- *\n- * Returns:\n- * {DOMElement}\n+ * Method: drawLineString\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n */\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.useIFrame === false) {\n- backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this);\n+ drawLineString: function(geometry, style, featureId) {\n+ style = OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style);\n+ this.drawLinearRing(geometry, style, featureId);\n+ },\n+\n+ /**\n+ * Method: drawLinearRing\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawLinearRing: function(geometry, style, featureId) {\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"fill\");\n+ }\n }\n- return backBuffer;\n- }\n-};\n-/* ======================================================================\n- OpenLayers/Protocol/SOS.js\n- ====================================================================== */\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\");\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Method: renderPath\n+ * Render a path with stroke and optional fill.\n+ */\n+ renderPath: function(context, geometry, style, featureId, type) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ context.beginPath();\n+ var start = this.getLocalXY(components[0]);\n+ var x = start[0];\n+ var y = start[1];\n+ if (!isNaN(x) && !isNaN(y)) {\n+ context.moveTo(start[0], start[1]);\n+ for (var i = 1; i < len; ++i) {\n+ var pt = this.getLocalXY(components[i]);\n+ context.lineTo(pt[0], pt[1]);\n+ }\n+ if (type === \"fill\") {\n+ context.fill();\n+ } else {\n+ context.stroke();\n+ }\n+ }\n+ },\n \n-/**\n- * @requires OpenLayers/Protocol.js\n- */\n+ /**\n+ * Method: drawPolygon\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawPolygon: function(geometry, style, featureId) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ this.drawLinearRing(components[0], style, featureId);\n+ // erase inner rings\n+ for (var i = 1; i < len; ++i) {\n+ /** \n+ * Note that this is overly agressive. Here we punch holes through \n+ * all previously rendered features on the same canvas. A better \n+ * solution for polygons with interior rings would be to draw the \n+ * polygon on a sketch canvas first. We could erase all holes \n+ * there and then copy the drawing to the layer canvas. \n+ * TODO: http://trac.osgeo.org/openlayers/ticket/3130 \n+ */\n+ this.canvas.globalCompositeOperation = \"destination-out\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"destination-out\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ stroke: false,\n+ fillOpacity: 1.0\n+ }, style),\n+ featureId\n+ );\n+ this.canvas.globalCompositeOperation = \"source-over\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"source-over\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style),\n+ featureId\n+ );\n+ }\n+ },\n \n-/**\n- * Function: OpenLayers.Protocol.SOS\n- * Used to create a versioned SOS protocol. Default version is 1.0.0.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol>} An SOS protocol for the given version.\n- */\n-OpenLayers.Protocol.SOS = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Protocol.SOS.DEFAULTS\n- );\n- var cls = OpenLayers.Protocol.SOS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported SOS version: \" + options.version;\n- }\n- return new cls(options);\n-};\n+ /**\n+ * Method: drawText\n+ * This method is only called by the renderer itself.\n+ *\n+ * Parameters:\n+ * location - {<OpenLayers.Point>}\n+ * style - {Object}\n+ */\n+ drawText: function(location, style) {\n+ var pt = this.getLocalXY(location);\n \n-/**\n- * Constant: OpenLayers.Protocol.SOS.DEFAULTS\n- */\n-OpenLayers.Protocol.SOS.DEFAULTS = {\n- \"version\": \"1.0.0\"\n-};\n-/* ======================================================================\n- OpenLayers/Protocol/CSW.js\n- ====================================================================== */\n+ this.setCanvasStyle(\"reset\");\n+ this.canvas.fillStyle = style.fontColor;\n+ this.canvas.globalAlpha = style.fontOpacity || 1.0;\n+ var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\",\n+ \"normal\", // \"font-variant\" not supported\n+ style.fontWeight ? style.fontWeight : \"normal\",\n+ style.fontSize ? style.fontSize : \"1em\",\n+ style.fontFamily ? style.fontFamily : \"sans-serif\"\n+ ].join(\" \");\n+ var labelRows = style.label.split('\\n');\n+ var numRows = labelRows.length;\n+ if (this.canvas.fillText) {\n+ // HTML5\n+ this.canvas.font = fontStyle;\n+ this.canvas.textAlign =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||\n+ \"center\";\n+ this.canvas.textBaseline =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||\n+ \"middle\";\n+ var vfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n+ }\n+ var lineHeight =\n+ this.canvas.measureText('Mg').height ||\n+ this.canvas.measureText('xx').width;\n+ pt[1] += lineHeight * vfactor * (numRows - 1);\n+ for (var i = 0; i < numRows; i++) {\n+ if (style.labelOutlineWidth) {\n+ this.canvas.save();\n+ this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;\n+ this.canvas.strokeStyle = style.labelOutlineColor;\n+ this.canvas.lineWidth = style.labelOutlineWidth;\n+ this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1);\n+ this.canvas.restore();\n+ }\n+ this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));\n+ }\n+ } else if (this.canvas.mozDrawText) {\n+ // Mozilla pre-Gecko1.9.1 (<FF3.1)\n+ this.canvas.mozTextStyle = fontStyle;\n+ // No built-in text alignment, so we measure and adjust the position\n+ var hfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n+ if (hfactor == null) {\n+ hfactor = -.5;\n+ }\n+ var vfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n+ }\n+ var lineHeight = this.canvas.mozMeasureText('xx');\n+ pt[1] += lineHeight * (1 + (vfactor * numRows));\n+ for (var i = 0; i < numRows; i++) {\n+ var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i]));\n+ var y = pt[1] + (i * lineHeight);\n+ this.canvas.translate(x, y);\n+ this.canvas.mozDrawText(labelRows[i]);\n+ this.canvas.translate(-x, -y);\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Method: getLocalXY\n+ * transform geographic xy into pixel xy\n+ *\n+ * Parameters: \n+ * point - {<OpenLayers.Geometry.Point>}\n+ */\n+ getLocalXY: function(point) {\n+ var resolution = this.getResolution();\n+ var extent = this.extent;\n+ var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));\n+ var y = ((extent.top / resolution) - point.y / resolution);\n+ return [x, y];\n+ },\n \n-/**\n- * @requires OpenLayers/Protocol.js\n- */\n+ /**\n+ * Method: clear\n+ * Clear all vectors from the renderer.\n+ */\n+ clear: function() {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ },\n \n-/**\n- * Class: OpenLayers.Protocol.CSW\n- * Used to create a versioned CSW protocol. Default version is 2.0.2.\n- */\n-OpenLayers.Protocol.CSW = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Protocol.CSW.DEFAULTS\n- );\n- var cls = OpenLayers.Protocol.CSW[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSW version: \" + options.version;\n- }\n- return new cls(options);\n-};\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * Returns a feature id from an event on the renderer. \n+ * \n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a \n+ * feature instead of a feature id to avoid an unnecessary lookup on the\n+ * layer.\n+ */\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId, feature;\n \n-/**\n- * Constant: OpenLayers.Protocol.CSW.DEFAULTS\n- */\n-OpenLayers.Protocol.CSW.DEFAULTS = {\n- \"version\": \"2.0.2\"\n-};\n-/* ======================================================================\n- OpenLayers/Protocol/WFS.js\n- ====================================================================== */\n+ if (this.hitDetection && this.root.style.display !== \"none\") {\n+ // this dragging check should go in the feature handler\n+ if (!this.map.dragging) {\n+ var xy = evt.xy;\n+ var x = xy.x | 0;\n+ var y = xy.y | 0;\n+ var data = this.hitContext.getImageData(x, y, 1, 1).data;\n+ if (data[3] === 255) { // antialiased\n+ var id = data[2] + (256 * (data[1] + (256 * data[0])));\n+ if (id) {\n+ featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n+ try {\n+ feature = this.features[featureId][0];\n+ } catch (err) {\n+ // Because of antialiasing on the canvas, when the hit location is at a point where the edge of\n+ // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.\n+ // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return feature;\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features; removes the feature from\n+ * the list, then redraws the layer.\n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ eraseFeatures: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ for (var i = 0; i < features.length; ++i) {\n+ delete this.features[features[i].id];\n+ }\n+ this.redraw();\n+ },\n \n-/**\n- * @requires OpenLayers/Protocol.js\n- */\n+ /**\n+ * Method: redraw\n+ * The real 'meat' of the function: any time things have changed,\n+ * redraw() can be called to loop over all the data and (you guessed\n+ * it) redraw it. Unlike Elements-based Renderers, we can't interact\n+ * with things once they're drawn, to remove them, for example, so\n+ * instead we have to just clear everything and draw from scratch.\n+ */\n+ redraw: function() {\n+ if (!this.locked) {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ var labelMap = [];\n+ var feature, geometry, style;\n+ var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();\n+ for (var id in this.features) {\n+ if (!this.features.hasOwnProperty(id)) {\n+ continue;\n+ }\n+ feature = this.features[id][0];\n+ geometry = feature.geometry;\n+ this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n+ style = this.features[id][1];\n+ this.drawGeometry(geometry, style, feature.id);\n+ if (style.label) {\n+ labelMap.push([feature, style]);\n+ }\n+ }\n+ var item;\n+ for (var i = 0, len = labelMap.length; i < len; ++i) {\n+ item = labelMap[i];\n+ this.drawText(item[0].geometry.getCentroid(), item[1]);\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+});\n \n /**\n- * Class: OpenLayers.Protocol.WFS\n- * Used to create a versioned WFS protocol. Default version is 1.0.0.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol>} A WFS protocol of the given version.\n- *\n- * Example:\n- * (code)\n- * var protocol = new OpenLayers.Protocol.WFS({\n- * version: \"1.1.0\",\n- * url: \"http://demo.opengeo.org/geoserver/wfs\",\n- * featureType: \"tasmania_roads\",\n- * featureNS: \"http://www.openplans.org/topp\",\n- * geometryName: \"the_geom\"\n- * });\n- * (end)\n- *\n- * See the protocols for specific WFS versions for more detail.\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN\n+ * {Object}\n */\n-OpenLayers.Protocol.WFS = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Protocol.WFS.DEFAULTS\n- );\n- var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported WFS version: \" + options.version;\n- }\n- return new cls(options);\n+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n+ \"l\": \"left\",\n+ \"r\": \"right\",\n+ \"t\": \"top\",\n+ \"b\": \"bottom\"\n };\n \n /**\n- * Function: fromWMSLayer\n- * Convenience function to create a WFS protocol from a WMS layer. This makes\n- * the assumption that a WFS requests can be issued at the same URL as\n- * WMS requests and that a WFS featureType exists with the same name as the\n- * WMS layer.\n- * \n- * This function is designed to auto-configure <url>, <featureType>,\n- * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that\n- * srsName matching with the WMS layer will not work with WFS 1.0.0.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS\n- * FeatureType at the same server url with the same typename.\n- * options - {Object} Default properties to be set on the protocol.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.WFS>}\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR\n+ * {Object}\n */\n-OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n- var typeName, featurePrefix;\n- var param = layer.params[\"LAYERS\"];\n- var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n- if (parts.length > 1) {\n- featurePrefix = parts[0];\n- }\n- typeName = parts.pop();\n- var protocolOptions = {\n- url: layer.url,\n- featureType: typeName,\n- featurePrefix: featurePrefix,\n- srsName: layer.projection && layer.projection.getCode() ||\n- layer.map && layer.map.getProjectionObject().getCode(),\n- version: \"1.1.0\"\n- };\n- return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(\n- options, protocolOptions\n- ));\n+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n+ \"l\": 0,\n+ \"r\": -1,\n+ \"t\": 0,\n+ \"b\": -1\n };\n \n /**\n- * Constant: OpenLayers.Protocol.WFS.DEFAULTS\n+ * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor\n+ * {Number} Scale factor to apply to the canvas drawImage arguments. This\n+ * is always 1 except for Android 2.1 devices, to work around\n+ * http://code.google.com/p/android/issues/detail?id=5141.\n */\n-OpenLayers.Protocol.WFS.DEFAULTS = {\n- \"version\": \"1.0.0\"\n-};\n+OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n /* ======================================================================\n- OpenLayers/Protocol/HTTP.js\n+ OpenLayers/Events/featureclick.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * if application uses the query string, for example, for BBOX parameters,\n- * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ * @requires OpenLayers/Events.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.HTTP\n- * A basic HTTP protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.HTTP> constructor.\n+ * Class: OpenLayers.Events.featureclick\n *\n- * Inherits from:\n- * - <OpenLayers.Protocol>\n+ * Extension event type for handling feature click events, including overlapping\n+ * features. \n+ * \n+ * Event types provided by this extension:\n+ * - featureclick \n */\n-OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n-\n- /**\n- * Property: url\n- * {String} Service URL, read-only, set through the options\n- * passed to constructor.\n- */\n- url: null,\n-\n- /**\n- * Property: headers\n- * {Object} HTTP request headers, read-only, set through the options\n- * passed to the constructor,\n- * Example: {'Content-Type': 'plain/text'}\n- */\n- headers: null,\n-\n- /**\n- * Property: params\n- * {Object} Parameters of GET requests, read-only, set through the options\n- * passed to the constructor,\n- * Example: {'bbox': '5,5,5,5'}\n- */\n- params: null,\n+OpenLayers.Events.featureclick = OpenLayers.Class({\n \n /**\n- * Property: callback\n- * {Object} Function to be called when the <read>, <create>,\n- * <update>, <delete> or <commit> operation completes, read-only,\n- * set through the options passed to the constructor.\n+ * Property: cache\n+ * {Object} A cache of features under the mouse.\n */\n- callback: null,\n+ cache: null,\n \n /**\n- * Property: scope\n- * {Object} Callback execution scope, read-only, set through the\n- * options passed to the constructor.\n+ * Property: map\n+ * {<OpenLayers.Map>} The map to register browser events on.\n */\n- scope: null,\n+ map: null,\n \n /**\n- * APIProperty: readWithPOST\n- * {Boolean} true if read operations are done with POST requests\n- * instead of GET, defaults to false.\n+ * Property: provides\n+ * {Array(String)} The event types provided by this extension.\n */\n- readWithPOST: false,\n+ provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n \n /**\n- * APIProperty: updateWithPOST\n- * {Boolean} true if update operations are done with POST requests\n- * defaults to false.\n+ * Constructor: OpenLayers.Events.featureclick\n+ * Create a new featureclick event type.\n+ *\n+ * Parameters:\n+ * target - {<OpenLayers.Events>} The events instance to create the events\n+ * for.\n */\n- updateWithPOST: false,\n+ initialize: function(target) {\n+ this.target = target;\n+ if (target.object instanceof OpenLayers.Map) {\n+ this.setMap(target.object);\n+ } else if (target.object instanceof OpenLayers.Layer.Vector) {\n+ if (target.object.map) {\n+ this.setMap(target.object.map);\n+ } else {\n+ target.object.events.register(\"added\", this, function(evt) {\n+ this.setMap(target.object.map);\n+ });\n+ }\n+ } else {\n+ throw (\"Listeners for '\" + this.provides.join(\"', '\") +\n+ \"' events can only be registered for OpenLayers.Layer.Vector \" +\n+ \"or OpenLayers.Map instances\");\n+ }\n+ for (var i = this.provides.length - 1; i >= 0; --i) {\n+ target.extensions[this.provides[i]] = true;\n+ }\n+ },\n \n /**\n- * APIProperty: deleteWithPOST\n- * {Boolean} true if delete operations are done with POST requests\n- * defaults to false.\n- * if true, POST data is set to output of format.write().\n+ * Method: setMap\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} The map to register browser events on.\n */\n- deleteWithPOST: false,\n+ setMap: function(map) {\n+ this.map = map;\n+ this.cache = {};\n+ map.events.register(\"mousedown\", this, this.start, {\n+ extension: true\n+ });\n+ map.events.register(\"mouseup\", this, this.onClick, {\n+ extension: true\n+ });\n+ map.events.register(\"touchstart\", this, this.start, {\n+ extension: true\n+ });\n+ map.events.register(\"touchmove\", this, this.cancel, {\n+ extension: true\n+ });\n+ map.events.register(\"touchend\", this, this.onClick, {\n+ extension: true\n+ });\n+ map.events.register(\"mousemove\", this, this.onMousemove, {\n+ extension: true\n+ });\n+ },\n \n /**\n- * Property: wildcarded.\n- * {Boolean} If true percent signs are added around values\n- * read from LIKE filters, for example if the protocol\n- * read method is passed a LIKE filter whose property\n- * is \"foo\" and whose value is \"bar\" the string\n- * \"foo__ilike=%bar%\" will be sent in the query string;\n- * defaults to false.\n+ * Method: start\n+ * Sets startEvt = evt.\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>}\n */\n- wildcarded: false,\n+ start: function(evt) {\n+ this.startEvt = evt;\n+ },\n \n /**\n- * APIProperty: srsInBBOX\n- * {Boolean} Include the SRS identifier in BBOX query string parameter. \n- * Default is false. If true and the layer has a projection object set,\n- * any BBOX filter will be serialized with a fifth item identifying the\n- * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n+ * Method: cancel\n+ * Deletes the start event.\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>}\n */\n- srsInBBOX: false,\n+ cancel: function(evt) {\n+ delete this.startEvt;\n+ },\n \n /**\n- * Constructor: OpenLayers.Protocol.HTTP\n- * A class for giving layers generic HTTP protocol.\n+ * Method: onClick\n+ * Listener for the click event.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options include:\n- * url - {String}\n- * headers - {Object} \n- * params - {Object} URL parameters for GET requests\n- * format - {<OpenLayers.Format>}\n- * callback - {Function}\n- * scope - {Object}\n+ * evt - {<OpenLayers.Event>}\n */\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.headers = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n-\n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- wildcarded: this.wildcarded,\n- srsInBBOX: this.srsInBBOX\n+ onClick: function(evt) {\n+ if (!this.startEvt || evt.type !== \"touchend\" &&\n+ !OpenLayers.Event.isLeftClick(evt)) {\n+ return;\n+ }\n+ var features = this.getFeatures(this.startEvt);\n+ delete this.startEvt;\n+ // fire featureclick events\n+ var feature, layer, more, clicked = {};\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ layer = feature.layer;\n+ clicked[layer.id] = true;\n+ more = this.triggerEvent(\"featureclick\", {\n+ feature: feature\n });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params);\n- };\n+ if (more === false) {\n+ break;\n+ }\n+ }\n+ // fire nofeatureclick events on all vector layers with no targets\n+ for (i = 0, len = this.map.layers.length; i < len; ++i) {\n+ layer = this.map.layers[i];\n+ if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {\n+ this.triggerEvent(\"nofeatureclick\", {\n+ layer: layer\n+ });\n+ }\n }\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * Method: onMousemove\n+ * Listener for the mousemove event.\n+ *\n+ * Parameters:\n+ * evt - {<OpenLayers.Event>}\n */\n- destroy: function() {\n- this.params = null;\n- this.headers = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n+ onMousemove: function(evt) {\n+ delete this.startEvt;\n+ var features = this.getFeatures(evt);\n+ var over = {},\n+ newly = [],\n+ feature;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ over[feature.id] = feature;\n+ if (!this.cache[feature.id]) {\n+ newly.push(feature);\n+ }\n+ }\n+ // check if already over features\n+ var out = [];\n+ for (var id in this.cache) {\n+ feature = this.cache[id];\n+ if (feature.layer && feature.layer.map) {\n+ if (!over[feature.id]) {\n+ out.push(feature);\n+ }\n+ } else {\n+ // removed\n+ delete this.cache[id];\n+ }\n+ }\n+ // fire featureover events\n+ var more;\n+ for (i = 0, len = newly.length; i < len; ++i) {\n+ feature = newly[i];\n+ this.cache[feature.id] = feature;\n+ more = this.triggerEvent(\"featureover\", {\n+ feature: feature\n+ });\n+ if (more === false) {\n+ break;\n+ }\n+ }\n+ // fire featureout events\n+ for (i = 0, len = out.length; i < len; ++i) {\n+ feature = out[i];\n+ delete this.cache[feature.id];\n+ more = this.triggerEvent(\"featureout\", {\n+ feature: feature\n+ });\n+ if (more === false) {\n+ break;\n+ }\n+ }\n },\n \n /**\n- * APIMethod: filterToParams\n- * Optional method to translate an <OpenLayers.Filter> object into an object\n- * that can be serialized as request query string provided. If a custom\n- * method is not provided, the filter will be serialized using the \n- * <OpenLayers.Format.QueryStringFilter> class.\n+ * Method: triggerEvent\n+ * Determines where to trigger the event and triggers it.\n *\n * Parameters:\n- * filter - {<OpenLayers.Filter>} filter to convert.\n- * params - {Object} The parameters object.\n+ * type - {String} The event type to trigger\n+ * evt - {Object} The listener argument\n *\n * Returns:\n- * {Object} The resulting parameters object.\n+ * {Boolean} The last listener return.\n */\n+ triggerEvent: function(type, evt) {\n+ var layer = evt.feature ? evt.feature.layer : evt.layer,\n+ object = this.target.object;\n+ if (object instanceof OpenLayers.Map || object === layer) {\n+ return this.target.triggerEvent(type, evt);\n+ }\n+ },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n+ * Method: getFeatures\n+ * Get all features at the given screen location.\n *\n * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n- * Valid options:\n- * url - {String} Url for the request.\n- * params - {Object} Parameters to get serialized as a query string.\n- * headers - {Object} Headers to be set on the request.\n- * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n- * query string.\n- * readWithPOST - {Boolean} If the request should be done with POST.\n+ * evt - {Object} Event object.\n *\n * Returns:\n- * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n- * references the HTTP request, this object is also passed to the\n- * callback function when the request completes, its \"features\" property\n- * is then populated with the features received from the server.\n+ * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point.\n */\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = options || {};\n- options.params = OpenLayers.Util.applyDefaults(\n- options.params, this.options.params);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(\n- options.filter, options.params\n- );\n+ getFeatures: function(evt) {\n+ var x = evt.clientX,\n+ y = evt.clientY,\n+ features = [],\n+ targets = [],\n+ layers = [],\n+ layer, target, feature, i, len;\n+ // go through all layers looking for targets\n+ for (i = this.map.layers.length - 1; i >= 0; --i) {\n+ layer = this.map.layers[i];\n+ if (layer.div.style.display !== \"none\") {\n+ if (layer.renderer instanceof OpenLayers.Renderer.Elements) {\n+ if (layer instanceof OpenLayers.Layer.Vector) {\n+ target = document.elementFromPoint(x, y);\n+ while (target && target._featureId) {\n+ feature = layer.getFeatureById(target._featureId);\n+ if (feature) {\n+ features.push(feature);\n+ target.style.display = \"none\";\n+ targets.push(target);\n+ target = document.elementFromPoint(x, y);\n+ } else {\n+ // sketch, all bets off\n+ target = false;\n+ }\n+ }\n+ }\n+ layers.push(layer);\n+ layer.div.style.display = \"none\";\n+ } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {\n+ feature = layer.renderer.getFeatureIdFromEvent(evt);\n+ if (feature) {\n+ features.push(feature);\n+ layers.push(layer);\n+ }\n+ }\n+ }\n }\n- var readWithPOST = (options.readWithPOST !== undefined) ?\n- options.readWithPOST : this.readWithPOST;\n- var resp = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- if (readWithPOST) {\n- var headers = options.headers || {};\n- headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- data: OpenLayers.Util.getParameterString(options.params),\n- headers: headers\n- });\n- } else {\n- resp.priv = OpenLayers.Request.GET({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- params: options.params,\n- headers: options.headers\n- });\n+ // restore feature visibility\n+ for (i = 0, len = targets.length; i < len; ++i) {\n+ targets[i].style.display = \"\";\n }\n- return resp;\n+ // restore layer visibility\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ layers[i].div.style.display = \"block\";\n+ }\n+ return features;\n },\n \n /**\n- * Method: handleRead\n- * Individual callbacks are created for read, create and update, should\n- * a subclass need to override each one separately.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * the user callback.\n- * options - {Object} The user options passed to the read call.\n+ * APIMethod: destroy\n+ * Clean up.\n */\n- handleRead: function(resp, options) {\n- this.handleResponse(resp, options);\n- },\n+ destroy: function() {\n+ for (var i = this.provides.length - 1; i >= 0; --i) {\n+ delete this.target.extensions[this.provides[i]];\n+ }\n+ this.map.events.un({\n+ mousemove: this.onMousemove,\n+ mousedown: this.start,\n+ mouseup: this.onClick,\n+ touchstart: this.start,\n+ touchmove: this.cancel,\n+ touchend: this.onClick,\n+ scope: this\n+ });\n+ delete this.cache;\n+ delete this.map;\n+ delete this.target;\n+ }\n+\n+});\n+\n+/**\n+ * Class: OpenLayers.Events.nofeatureclick\n+ *\n+ * Extension event type for handling click events that do not hit a feature. \n+ * \n+ * Event types provided by this extension:\n+ * - nofeatureclick \n+ */\n+OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;\n+\n+/**\n+ * Class: OpenLayers.Events.featureover\n+ *\n+ * Extension event type for handling hovering over a feature. \n+ * \n+ * Event types provided by this extension:\n+ * - featureover \n+ */\n+OpenLayers.Events.featureover = OpenLayers.Events.featureclick;\n+\n+/**\n+ * Class: OpenLayers.Events.featureout\n+ *\n+ * Extension event type for handling leaving a feature. \n+ * \n+ * Event types provided by this extension:\n+ * - featureout \n+ */\n+OpenLayers.Events.featureout = OpenLayers.Events.featureclick;\n+/* ======================================================================\n+ OpenLayers/Strategy/Save.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Strategy.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Strategy.Save\n+ * A strategy that commits newly created or modified features. By default\n+ * the strategy waits for a call to <save> before persisting changes. By\n+ * configuring the strategy with the <auto> option, changes can be saved\n+ * automatically.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Strategy>\n+ */\n+OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * APIMethod: create\n- * Construct a request for writing newly created features.\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the strategy object.\n *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * strategy.events.register(type, obj, listener);\n+ * (end)\n *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes, its \"features\" property is then populated with the\n- * the features received from the server.\n+ * Supported event types:\n+ * start - Triggered before saving\n+ * success - Triggered after a successful transaction\n+ * fail - Triggered after a failed transaction\n+ * \n */\n- create: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n \n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: features,\n- requestType: \"create\"\n- });\n+ /** \n+ * Property: events\n+ * {<OpenLayers.Events>} Events instance for triggering this protocol\n+ * events.\n+ */\n+ events: null,\n \n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleCreate, resp, options),\n- headers: options.headers,\n- data: this.format.write(features)\n- });\n+ /**\n+ * APIProperty: auto\n+ * {Boolean | Number} Auto-save. Default is false. If true, features will be\n+ * saved immediately after being added to the layer and with each\n+ * modification or deletion. If auto is a number, features will be\n+ * saved on an interval provided by the value (in seconds).\n+ */\n+ auto: false,\n \n- return resp;\n- },\n+ /**\n+ * Property: timer\n+ * {Number} The id of the timer.\n+ */\n+ timer: null,\n \n /**\n- * Method: handleCreate\n- * Called the the request issued by <create> is complete. May be overridden\n- * by subclasses.\n+ * Constructor: OpenLayers.Strategy.Save\n+ * Create a new Save strategy.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the create call.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- handleCreate: function(resp, options) {\n- this.handleResponse(resp, options);\n+ initialize: function(options) {\n+ OpenLayers.Strategy.prototype.initialize.apply(this, [options]);\n+ this.events = new OpenLayers.Events(this);\n },\n \n /**\n- * APIMethod: update\n- * Construct a request updating modified feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n+ * APIMethod: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ * \n * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes, its \"features\" property is then populated with the\n- * the feature received from the server.\n+ * {Boolean} The strategy was successfully activated.\n */\n- update: function(feature, options) {\n- options = options || {};\n- var url = options.url ||\n- feature.url ||\n- this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"update\"\n- });\n-\n- var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n- resp.priv = OpenLayers.Request[method]({\n- url: url,\n- callback: this.createCallback(this.handleUpdate, resp, options),\n- headers: options.headers,\n- data: this.format.write(feature)\n- });\n-\n- return resp;\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.auto) {\n+ if (typeof this.auto === \"number\") {\n+ this.timer = window.setInterval(\n+ OpenLayers.Function.bind(this.save, this),\n+ this.auto * 1000\n+ );\n+ } else {\n+ this.layer.events.on({\n+ \"featureadded\": this.triggerSave,\n+ \"afterfeaturemodified\": this.triggerSave,\n+ scope: this\n+ });\n+ }\n+ }\n+ }\n+ return activated;\n },\n \n /**\n- * Method: handleUpdate\n- * Called the the request issued by <update> is complete. May be overridden\n- * by subclasses.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the update call.\n+ * APIMethod: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- handleUpdate: function(resp, options) {\n- this.handleResponse(resp, options);\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.auto) {\n+ if (typeof this.auto === \"number\") {\n+ window.clearInterval(this.timer);\n+ } else {\n+ this.layer.events.un({\n+ \"featureadded\": this.triggerSave,\n+ \"afterfeaturemodified\": this.triggerSave,\n+ scope: this\n+ });\n+ }\n+ }\n+ }\n+ return deactivated;\n },\n \n /**\n- * APIMethod: delete\n- * Construct a request deleting a removed feature.\n+ * Method: triggerSave\n+ * Registered as a listener. Calls save if a feature has insert, update,\n+ * or delete state.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes.\n+ * event - {Object} The event this function is listening for.\n */\n- \"delete\": function(feature, options) {\n- options = options || {};\n- var url = options.url ||\n- feature.url ||\n- this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"delete\"\n- });\n-\n- var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n- var requestOptions = {\n- url: url,\n- callback: this.createCallback(this.handleDelete, resp, options),\n- headers: options.headers\n- };\n- if (this.deleteWithPOST) {\n- requestOptions.data = this.format.write(feature);\n+ triggerSave: function(event) {\n+ var feature = event.feature;\n+ if (feature.state === OpenLayers.State.INSERT ||\n+ feature.state === OpenLayers.State.UPDATE ||\n+ feature.state === OpenLayers.State.DELETE) {\n+ this.save([event.feature]);\n }\n- resp.priv = OpenLayers.Request[method](requestOptions);\n-\n- return resp;\n },\n \n /**\n- * Method: handleDelete\n- * Called the the request issued by <delete> is complete. May be overridden\n- * by subclasses.\n+ * APIMethod: save\n+ * Tell the layer protocol to commit unsaved features. If the layer\n+ * projection differs from the map projection, features will be\n+ * transformed into the layer projection before being committed.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the delete call.\n+ * features - {Array} Features to be saved. If null, then default is all\n+ * features in the layer. Features are assumed to be in the map\n+ * projection.\n */\n- handleDelete: function(resp, options) {\n- this.handleResponse(resp, options);\n+ save: function(features) {\n+ if (!features) {\n+ features = this.layer.features;\n+ }\n+ this.events.triggerEvent(\"start\", {\n+ features: features\n+ });\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var len = features.length;\n+ var clones = new Array(len);\n+ var orig, clone;\n+ for (var i = 0; i < len; ++i) {\n+ orig = features[i];\n+ clone = orig.clone();\n+ clone.fid = orig.fid;\n+ clone.state = orig.state;\n+ if (orig.url) {\n+ clone.url = orig.url;\n+ }\n+ clone._original = orig;\n+ clone.geometry.transform(local, remote);\n+ clones[i] = clone;\n+ }\n+ features = clones;\n+ }\n+ this.layer.protocol.commit(features, {\n+ callback: this.onCommit,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: handleResponse\n- * Called by CRUD specific handlers.\n+ * Method: onCommit\n+ * Called after protocol commit.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the create, read, update,\n- * or delete call.\n+ * response - {<OpenLayers.Protocol.Response>} A response object.\n */\n- handleResponse: function(resp, options) {\n- var request = resp.priv;\n- if (options.callback) {\n- if (request.status >= 200 && request.status < 300) {\n- // success\n- if (resp.requestType != \"delete\") {\n- resp.features = this.parseFeatures(request);\n+ onCommit: function(response) {\n+ var evt = {\n+ \"response\": response\n+ };\n+ if (response.success()) {\n+ var features = response.reqFeatures;\n+ // deal with inserts, updates, and deletes\n+ var state, feature;\n+ var destroys = [];\n+ var insertIds = response.insertIds || [];\n+ var j = 0;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ // if projection was different, we may be dealing with clones\n+ feature = feature._original || feature;\n+ state = feature.state;\n+ if (state) {\n+ if (state == OpenLayers.State.DELETE) {\n+ destroys.push(feature);\n+ } else if (state == OpenLayers.State.INSERT) {\n+ feature.fid = insertIds[j];\n+ ++j;\n+ }\n+ feature.state = null;\n }\n- resp.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- // failure\n- resp.code = OpenLayers.Protocol.Response.FAILURE;\n }\n- options.callback.call(options.scope, resp);\n+\n+ if (destroys.length > 0) {\n+ this.layer.destroyFeatures(destroys);\n+ }\n+\n+ this.events.triggerEvent(\"success\", evt);\n+\n+ } else {\n+ this.events.triggerEvent(\"fail\", evt);\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Strategy.Save\"\n+});\n+/* ======================================================================\n+ OpenLayers/Strategy/Filter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Filter.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Strategy.Filter\n+ * Strategy for limiting features that get added to a layer by \n+ * evaluating a filter. The strategy maintains a cache of\n+ * all features until removeFeatures is called on the layer.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Strategy>\n+ */\n+OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {\n+\n /**\n- * Method: parseFeatures\n- * Read HTTP response body and return features.\n+ * APIProperty: filter\n+ * {<OpenLayers.Filter>} Filter for limiting features sent to the layer.\n+ * Use the <setFilter> method to update this filter after construction.\n+ */\n+ filter: null,\n+\n+ /**\n+ * Property: cache\n+ * {Array(<OpenLayers.Feature.Vector>)} List of currently cached\n+ * features.\n+ */\n+ cache: null,\n+\n+ /**\n+ * Property: caching\n+ * {Boolean} The filter is currently caching features.\n+ */\n+ caching: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Strategy.Filter\n+ * Create a new filter strategy.\n *\n * Parameters:\n- * request - {XMLHttpRequest} The request object\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ */\n+\n+ /**\n+ * APIMethod: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ * By default, this strategy automatically activates itself when a layer\n+ * is added to a map.\n *\n * Returns:\n- * {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- if (!doc || doc.length <= 0) {\n- return null;\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.cache = [];\n+ this.layer.events.on({\n+ \"beforefeaturesadded\": this.handleAdd,\n+ \"beforefeaturesremoved\": this.handleRemove,\n+ scope: this\n+ });\n }\n- return this.format.read(doc);\n+ return activated;\n },\n \n /**\n- * APIMethod: commit\n- * Iterate over each feature and take action based on the feature state.\n- * Possible actions are create, update and delete.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})}\n- * options - {Object} Optional object for setting up intermediate commit\n- * callbacks.\n- *\n- * Valid options:\n- * create - {Object} Optional object to be passed to the <create> method.\n- * update - {Object} Optional object to be passed to the <update> method.\n- * delete - {Object} Optional object to be passed to the <delete> method.\n- * callback - {Function} Optional function to be called when the commit\n- * is complete.\n- * scope - {Object} Optional object to be set as the scope of the callback.\n+ * APIMethod: deactivate\n+ * Deactivate the strategy. Clear the feature cache.\n *\n * Returns:\n- * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,\n- * one per request made to the server, each object's \"priv\" property\n- * references the corresponding HTTP request.\n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n */\n- commit: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = [],\n- nResponses = 0;\n-\n- // Divide up features before issuing any requests. This properly\n- // counts requests in the event that any responses come in before\n- // all requests have been issued.\n- var types = {};\n- types[OpenLayers.State.INSERT] = [];\n- types[OpenLayers.State.UPDATE] = [];\n- types[OpenLayers.State.DELETE] = [];\n- var feature, list, requestFeatures = [];\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- list = types[feature.state];\n- if (list) {\n- list.push(feature);\n- requestFeatures.push(feature);\n- }\n- }\n- // tally up number of requests\n- var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +\n- types[OpenLayers.State.UPDATE].length +\n- types[OpenLayers.State.DELETE].length;\n-\n- // This response will be sent to the final callback after all the others\n- // have been fired.\n- var success = true;\n- var finalResponse = new OpenLayers.Protocol.Response({\n- reqFeatures: requestFeatures\n- });\n-\n- function insertCallback(response) {\n- var len = response.features ? response.features.length : 0;\n- var fids = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- fids[i] = response.features[i].fid;\n- }\n- finalResponse.insertIds = fids;\n- callback.apply(this, [response]);\n+ deactivate: function() {\n+ this.cache = null;\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.un({\n+ \"beforefeaturesadded\": this.handleAdd,\n+ \"beforefeaturesremoved\": this.handleRemove,\n+ scope: this\n+ });\n }\n+ return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);\n+ },\n \n- function callback(response) {\n- this.callUserCallback(response, options);\n- success = success && response.success();\n- nResponses++;\n- if (nResponses >= nRequests) {\n- if (options.callback) {\n- finalResponse.code = success ?\n- OpenLayers.Protocol.Response.SUCCESS :\n- OpenLayers.Protocol.Response.FAILURE;\n- options.callback.apply(options.scope, [finalResponse]);\n+ /**\n+ * Method: handleAdd\n+ */\n+ handleAdd: function(event) {\n+ if (!this.caching && this.filter) {\n+ var features = event.features;\n+ event.features = [];\n+ var feature;\n+ for (var i = 0, ii = features.length; i < ii; ++i) {\n+ feature = features[i];\n+ if (this.filter.evaluate(feature)) {\n+ event.features.push(feature);\n+ } else {\n+ this.cache.push(feature);\n }\n }\n }\n-\n- // start issuing requests\n- var queue = types[OpenLayers.State.INSERT];\n- if (queue.length > 0) {\n- resp.push(this.create(\n- queue, OpenLayers.Util.applyDefaults({\n- callback: insertCallback,\n- scope: this\n- }, options.create)\n- ));\n- }\n- queue = types[OpenLayers.State.UPDATE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this.update(\n- queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options.update)));\n- }\n- queue = types[OpenLayers.State.DELETE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this[\"delete\"](\n- queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options[\"delete\"])));\n- }\n- return resp;\n },\n \n /**\n- * APIMethod: abort\n- * Abort an ongoing request, the response object passed to\n- * this method must come from this HTTP protocol (as a result\n- * of a create, read, update, delete or commit operation).\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>}\n+ * Method: handleRemove\n */\n- abort: function(response) {\n- if (response) {\n- response.priv.abort();\n+ handleRemove: function(event) {\n+ if (!this.caching) {\n+ this.cache = [];\n }\n },\n \n- /**\n- * Method: callUserCallback\n- * This method is used from within the commit method each time an\n- * an HTTP response is received from the server, it is responsible\n- * for calling the user-supplied callbacks.\n+ /** \n+ * APIMethod: setFilter\n+ * Update the filter for this strategy. This will re-evaluate\n+ * any features on the layer and in the cache. Only features\n+ * for which filter.evalute(feature) returns true will be\n+ * added to the layer. Others will be cached by the strategy.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>}\n- * options - {Object} The map of options passed to the commit call.\n+ * filter - {<OpenLayers.Filter>} A filter for evaluating features.\n */\n- callUserCallback: function(resp, options) {\n- var opt = options[resp.requestType];\n- if (opt && opt.callback) {\n- opt.callback.call(opt.scope, resp);\n+ setFilter: function(filter) {\n+ this.filter = filter;\n+ var previousCache = this.cache;\n+ this.cache = [];\n+ // look through layer for features to remove from layer\n+ this.handleAdd({\n+ features: this.layer.features\n+ });\n+ // cache now contains features to remove from layer\n+ if (this.cache.length > 0) {\n+ this.caching = true;\n+ this.layer.removeFeatures(this.cache.slice());\n+ this.caching = false;\n+ }\n+ // now look through previous cache for features to add to layer\n+ if (previousCache.length > 0) {\n+ var event = {\n+ features: previousCache\n+ };\n+ this.handleAdd(event);\n+ if (event.features.length > 0) {\n+ // event has features to add to layer\n+ this.caching = true;\n+ this.layer.addFeatures(event.features);\n+ this.caching = false;\n+ }\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Filter\"\n+\n });\n /* ======================================================================\n- OpenLayers/Protocol/Script.js\n+ OpenLayers/Strategy/Fixed.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Format/GeoJSON.js\n- */\n-\n-/**\n- * if application uses the query string, for example, for BBOX parameters,\n- * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ * @requires OpenLayers/Strategy.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.Script\n- * A basic Script protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.Script> constructor. A script protocol is used to\n- * get around the same origin policy. It works with services that return\n- * JSONP - that is, JSON wrapped in a client-specified callback. The\n- * protocol handles fetching and parsing of feature data and sends parsed\n- * features to the <callback> configured with the protocol. The protocol\n- * expects features serialized as GeoJSON by default, but can be configured\n- * to work with other formats by setting the <format> property.\n+ * Class: OpenLayers.Strategy.Fixed\n+ * A simple strategy that requests features once and never requests new data.\n *\n * Inherits from:\n- * - <OpenLayers.Protocol>\n+ * - <OpenLayers.Strategy>\n */\n-OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * APIProperty: url\n- * {String} Service URL. The service is expected to return serialized \n- * features wrapped in a named callback (where the callback name is\n- * generated by this protocol).\n- * Read-only, set through the options passed to the constructor.\n+ * APIProperty: preload\n+ * {Boolean} Load data before layer made visible. Enabling this may result\n+ * in considerable overhead if your application loads many data layers\n+ * that are not visible by default. Default is false.\n */\n- url: null,\n+ preload: false,\n \n /**\n- * APIProperty: params\n- * {Object} Query string parameters to be appended to the URL.\n- * Read-only, set through the options passed to the constructor.\n- * Example: {maxFeatures: 50}\n+ * Constructor: OpenLayers.Strategy.Fixed\n+ * Create a new Fixed strategy.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- params: null,\n \n /**\n- * APIProperty: callback\n- * {Object} Function to be called when the <read> operation completes.\n+ * Method: activate\n+ * Activate the strategy: load data or add listener to load when visible\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- callback: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"refresh\": this.load,\n+ scope: this\n+ });\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load();\n+ } else {\n+ this.layer.events.on({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ }\n+ return activated;\n+ },\n \n /**\n- * APIProperty: callbackTemplate\n- * {String} Template for creating a unique callback function name\n- * for the registry. Should include ${id}. The ${id} variable will be\n- * replaced with a string identifier prefixed with a \"c\" (e.g. c1, c2).\n- * Default is \"OpenLayers.Protocol.Script.registry.${id}\".\n+ * Method: deactivate\n+ * Deactivate the strategy. Undo what is done in <activate>.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- callbackTemplate: \"OpenLayers.Protocol.Script.registry.${id}\",\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ \"refresh\": this.load,\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n+ },\n \n /**\n- * APIProperty: callbackKey\n- * {String} The name of the query string parameter that the service \n- * recognizes as the callback identifier. Default is \"callback\".\n- * This key is used to generate the URL for the script. For example\n- * setting <callbackKey> to \"myCallback\" would result in a URL like \n- * http://example.com/?myCallback=...\n+ * Method: load\n+ * Tells protocol to load data and unhooks the visibilitychanged event\n+ *\n+ * Parameters:\n+ * options - {Object} options to pass to protocol read.\n */\n- callbackKey: \"callback\",\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n+ });\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ },\n \n /**\n- * APIProperty: callbackPrefix\n- * {String} Where a service requires that the callback query string \n- * parameter value is prefixed by some string, this value may be set.\n- * For example, setting <callbackPrefix> to \"foo:\" would result in a\n- * URL like http://example.com/?callback=foo:... Default is \"\".\n+ * Method: merge\n+ * Add all features to the layer.\n+ * If the layer projection differs from the map projection, features\n+ * will be transformed from the layer projection to the map projection.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object passed\n+ * by the protocol.\n */\n- callbackPrefix: \"\",\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local);\n+ }\n+ }\n+ }\n+ layer.addFeatures(features);\n+ }\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ });\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n+});\n+/* ======================================================================\n+ OpenLayers/Strategy/Paging.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Strategy.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Strategy.Paging\n+ * Strategy for vector feature paging\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Strategy>\n+ */\n+OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * APIProperty: scope\n- * {Object} Optional ``this`` object for the callback. Read-only, set \n- * through the options passed to the constructor.\n+ * Property: features\n+ * {Array(<OpenLayers.Feature.Vector>)} Cached features.\n */\n- scope: null,\n+ features: null,\n \n /**\n- * APIProperty: format\n- * {<OpenLayers.Format>} Format for parsing features. Default is an \n- * <OpenLayers.Format.GeoJSON> format. If an alternative is provided,\n- * the format's read method must take an object and return an array\n- * of features.\n+ * Property: length\n+ * {Integer} Number of features per page. Default is 10.\n */\n- format: null,\n+ length: 10,\n \n /**\n- * Property: pendingRequests\n- * {Object} References all pending requests. Property names are script \n- * identifiers and property values are script elements.\n+ * Property: num\n+ * {Integer} The currently displayed page number.\n */\n- pendingRequests: null,\n+ num: null,\n \n /**\n- * APIProperty: srsInBBOX\n- * {Boolean} Include the SRS identifier in BBOX query string parameter.\n- * Setting this property has no effect if a custom filterToParams method\n- * is provided. Default is false. If true and the layer has a \n- * projection object set, any BBOX filter will be serialized with a \n- * fifth item identifying the projection. \n- * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n+ * Property: paging\n+ * {Boolean} The strategy is currently changing pages.\n */\n- srsInBBOX: false,\n+ paging: false,\n \n /**\n- * Constructor: OpenLayers.Protocol.Script\n- * A class for giving layers generic Script protocol.\n+ * Constructor: OpenLayers.Strategy.Paging\n+ * Create a new paging strategy.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n- *\n- * Valid options include:\n- * url - {String}\n- * params - {Object}\n- * callback - {Function}\n- * scope - {Object}\n */\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.pendingRequests = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n- if (!this.format) {\n- this.format = new OpenLayers.Format.GeoJSON();\n- }\n \n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- srsInBBOX: this.srsInBBOX\n+ /**\n+ * APIMethod: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully activated.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"beforefeaturesadded\": this.cacheFeatures,\n+ scope: this\n });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params);\n- };\n }\n+ return activated;\n },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n- *\n- * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n- * Valid options:\n- * url - {String} Url for the request.\n- * params - {Object} Parameters to get serialized as a query string.\n- * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n- * query string.\n- *\n+ * APIMethod: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ * \n * Returns:\n- * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n- * references the injected script. This object is also passed to the\n- * callback function when the request completes, its \"features\" property\n- * is then populated with the features received from the server.\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- options.params = OpenLayers.Util.applyDefaults(\n- options.params, this.options.params\n- );\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(\n- options.filter, options.params\n- );\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.clearCache();\n+ this.layer.events.un({\n+ \"beforefeaturesadded\": this.cacheFeatures,\n+ scope: this\n+ });\n }\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var request = this.createRequest(\n- options.url,\n- options.params,\n- OpenLayers.Function.bind(function(data) {\n- response.data = data;\n- this.handleRead(response, options);\n- }, this)\n- );\n- response.priv = request;\n- return response;\n+ return deactivated;\n },\n \n- /** \n- * APIMethod: filterToParams \n- * Optional method to translate an <OpenLayers.Filter> object into an object \n- * that can be serialized as request query string provided. If a custom \n- * method is not provided, any filter will not be serialized. \n- * \n- * Parameters: \n- * filter - {<OpenLayers.Filter>} filter to convert. \n- * params - {Object} The parameters object. \n- * \n- * Returns: \n- * {Object} The resulting parameters object. \n- */\n-\n- /** \n- * Method: createRequest\n- * Issues a request for features by creating injecting a script in the \n- * document head.\n+ /**\n+ * Method: cacheFeatures\n+ * Cache features before they are added to the layer.\n *\n * Parameters:\n- * url - {String} Service URL.\n- * params - {Object} Query string parameters.\n- * callback - {Function} Callback to be called with resulting data.\n- *\n- * Returns:\n- * {HTMLScriptElement} The script pending execution.\n+ * event - {Object} The event that this was listening for. This will come\n+ * with a batch of features to be paged.\n */\n- createRequest: function(url, params, callback) {\n- var id = OpenLayers.Protocol.Script.register(callback);\n- var name = OpenLayers.String.format(this.callbackTemplate, {\n- id: id\n- });\n- params = OpenLayers.Util.extend({}, params);\n- params[this.callbackKey] = this.callbackPrefix + name;\n- url = OpenLayers.Util.urlAppend(\n- url, OpenLayers.Util.getParameterString(params)\n- );\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = \"OpenLayers_Protocol_Script_\" + id;\n- this.pendingRequests[script.id] = script;\n- var head = document.getElementsByTagName(\"head\")[0];\n- head.appendChild(script);\n- return script;\n+ cacheFeatures: function(event) {\n+ if (!this.paging) {\n+ this.clearCache();\n+ this.features = event.features;\n+ this.pageNext(event);\n+ }\n },\n \n- /** \n- * Method: destroyRequest\n- * Remove a script node associated with a response from the document. Also\n- * unregisters the callback and removes the script from the \n- * <pendingRequests> object.\n- *\n- * Parameters:\n- * script - {HTMLScriptElement}\n+ /**\n+ * Method: clearCache\n+ * Clear out the cached features. This destroys features, assuming\n+ * nothing else has a reference.\n */\n- destroyRequest: function(script) {\n- OpenLayers.Protocol.Script.unregister(script.id.split(\"_\").pop());\n- delete this.pendingRequests[script.id];\n- if (script.parentNode) {\n- script.parentNode.removeChild(script);\n+ clearCache: function() {\n+ if (this.features) {\n+ for (var i = 0; i < this.features.length; ++i) {\n+ this.features[i].destroy();\n+ }\n }\n+ this.features = null;\n+ this.num = null;\n },\n \n /**\n- * Method: handleRead\n- * Individual callbacks are created for read, create and update, should\n- * a subclass need to override each one separately.\n+ * APIMethod: pageCount\n+ * Get the total count of pages given the current cache of features.\n *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * the user callback.\n- * options - {Object} The user options passed to the read call.\n+ * Returns:\n+ * {Integer} The page count.\n */\n- handleRead: function(response, options) {\n- this.handleResponse(response, options);\n+ pageCount: function() {\n+ var numFeatures = this.features ? this.features.length : 0;\n+ return Math.ceil(numFeatures / this.length);\n },\n \n /**\n- * Method: handleResponse\n- * Called by CRUD specific handlers.\n+ * APIMethod: pageNum\n+ * Get the zero based page number.\n *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the create, read, update,\n- * or delete call.\n+ * Returns:\n+ * {Integer} The current page number being displayed.\n */\n- handleResponse: function(response, options) {\n- if (options.callback) {\n- if (response.data) {\n- response.features = this.parseFeatures(response.data);\n- response.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- }\n- this.destroyRequest(response.priv);\n- options.callback.call(options.scope, response);\n- }\n+ pageNum: function() {\n+ return this.num;\n },\n \n /**\n- * Method: parseFeatures\n- * Read Script response body and return features.\n+ * APIMethod: pageLength\n+ * Gets or sets page length.\n *\n * Parameters:\n- * data - {Object} The data sent to the callback function by the server.\n+ * newLength - {Integer} Optional length to be set.\n *\n * Returns:\n- * {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n+ * {Integer} The length of a page (number of features per page).\n */\n- parseFeatures: function(data) {\n- return this.format.read(data);\n+ pageLength: function(newLength) {\n+ if (newLength && newLength > 0) {\n+ this.length = newLength;\n+ }\n+ return this.length;\n },\n \n /**\n- * APIMethod: abort\n- * Abort an ongoing request. If no response is provided, all pending \n- * requests will be aborted.\n+ * APIMethod: pageNext\n+ * Display the next page of features.\n *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object returned\n- * from a <read> request.\n+ * Returns:\n+ * {Boolean} A new page was displayed.\n */\n- abort: function(response) {\n- if (response) {\n- this.destroyRequest(response.priv);\n- } else {\n- for (var key in this.pendingRequests) {\n- this.destroyRequest(this.pendingRequests[key]);\n+ pageNext: function(event) {\n+ var changed = false;\n+ if (this.features) {\n+ if (this.num === null) {\n+ this.num = -1;\n }\n+ var start = (this.num + 1) * this.length;\n+ changed = this.page(start, event);\n }\n+ return changed;\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * APIMethod: pagePrevious\n+ * Display the previous page of features.\n+ *\n+ * Returns:\n+ * {Boolean} A new page was displayed.\n */\n- destroy: function() {\n- this.abort();\n- delete this.params;\n- delete this.format;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n+ pagePrevious: function() {\n+ var changed = false;\n+ if (this.features) {\n+ if (this.num === null) {\n+ this.num = this.pageCount();\n+ }\n+ var start = (this.num - 1) * this.length;\n+ changed = this.page(start);\n+ }\n+ return changed;\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.Script\"\n-});\n-\n-(function() {\n- var o = OpenLayers.Protocol.Script;\n- var counter = 0;\n- o.registry = {};\n-\n /**\n- * Function: OpenLayers.Protocol.Script.register\n- * Register a callback for a newly created script.\n- *\n- * Parameters:\n- * callback - {Function} The callback to be executed when the newly added\n- * script loads. This callback will be called with a single argument\n- * that is the JSON returned by the service.\n+ * Method: page\n+ * Display the page starting at the given index from the cache.\n *\n * Returns:\n- * {Number} An identifier for retrieving the registered callback.\n+ * {Boolean} A new page was displayed.\n */\n- o.register = function(callback) {\n- var id = \"c\" + (++counter);\n- o.registry[id] = function() {\n- callback.apply(this, arguments);\n- };\n- return id;\n- };\n+ page: function(start, event) {\n+ var changed = false;\n+ if (this.features) {\n+ if (start >= 0 && start < this.features.length) {\n+ var num = Math.floor(start / this.length);\n+ if (num != this.num) {\n+ this.paging = true;\n+ var features = this.features.slice(start, start + this.length);\n+ this.layer.removeFeatures(this.layer.features);\n+ this.num = num;\n+ // modify the event if any\n+ if (event && event.features) {\n+ // this.was called by an event listener\n+ event.features = features;\n+ } else {\n+ // this was called directly on the strategy\n+ this.layer.addFeatures(features);\n+ }\n+ this.paging = false;\n+ changed = true;\n+ }\n+ }\n+ }\n+ return changed;\n+ },\n \n- /**\n- * Function: OpenLayers.Protocol.Script.unregister\n- * Unregister a callback previously registered with the register function.\n- *\n- * Parameters:\n- * id - {Number} The identifer returned by the register function.\n- */\n- o.unregister = function(id) {\n- delete o.registry[id];\n- };\n-})();\n+ CLASS_NAME: \"OpenLayers.Strategy.Paging\"\n+});\n /* ======================================================================\n- OpenLayers/Protocol/WFS/v1.js\n+ OpenLayers/Strategy/Cluster.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol/WFS.js\n+ * @requires OpenLayers/Strategy.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.WFS.v1\n- * Abstract class for for v1.0.0 and v1.1.0 protocol.\n+ * Class: OpenLayers.Strategy.Cluster\n+ * Strategy for vector feature clustering.\n *\n * Inherits from:\n- * - <OpenLayers.Protocol>\n+ * - <OpenLayers.Strategy>\n */\n-OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {\n-\n- /**\n- * Property: version\n- * {String} WFS version number.\n- */\n- version: null,\n-\n- /**\n- * Property: srsName\n- * {String} Name of spatial reference system. Default is \"EPSG:4326\".\n- */\n- srsName: \"EPSG:4326\",\n-\n- /**\n- * Property: featureType\n- * {String} Local feature typeName.\n- */\n- featureType: null,\n-\n- /**\n- * Property: featureNS\n- * {String} Feature namespace.\n- */\n- featureNS: null,\n+OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * Property: geometryName\n- * {String} Name of the geometry attribute for features. Default is\n- * \"the_geom\" for WFS <version> 1.0, and null for higher versions.\n+ * APIProperty: distance\n+ * {Integer} Pixel distance between features that should be considered a\n+ * single cluster. Default is 20 pixels.\n */\n- geometryName: \"the_geom\",\n+ distance: 20,\n \n /**\n- * Property: maxFeatures\n- * {Integer} Optional maximum number of features to retrieve.\n+ * APIProperty: threshold\n+ * {Integer} Optional threshold below which original features will be\n+ * added to the layer instead of clusters. For example, a threshold\n+ * of 3 would mean that any time there are 2 or fewer features in\n+ * a cluster, those features will be added directly to the layer instead\n+ * of a cluster representing those features. Default is null (which is\n+ * equivalent to 1 - meaning that clusters may contain just one feature).\n */\n+ threshold: null,\n \n /**\n- * Property: schema\n- * {String} Optional schema location that will be included in the\n- * schemaLocation attribute value. Note that the feature type schema\n- * is required for a strict XML validator (on transactions with an\n- * insert for example), but is *not* required by the WFS specification\n- * (since the server is supposed to know about feature type schemas).\n+ * Property: features\n+ * {Array(<OpenLayers.Feature.Vector>)} Cached features.\n */\n- schema: null,\n+ features: null,\n \n /**\n- * Property: featurePrefix\n- * {String} Namespace alias for feature type. Default is \"feature\".\n+ * Property: clusters\n+ * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters.\n */\n- featurePrefix: \"feature\",\n+ clusters: null,\n \n /**\n- * Property: formatOptions\n- * {Object} Optional options for the format. If a format is not provided,\n- * this property can be used to extend the default format options.\n- */\n- formatOptions: null,\n-\n- /** \n- * Property: readFormat \n- * {<OpenLayers.Format>} For WFS requests it is possible to get a \n- * different output format than GML. In that case, we cannot parse \n- * the response with the default format (WFST) and we need a different \n- * format for reading. \n+ * Property: clustering\n+ * {Boolean} The strategy is currently clustering features.\n */\n- readFormat: null,\n+ clustering: false,\n \n /**\n- * Property: readOptions\n- * {Object} Optional object to pass to format's read.\n+ * Property: resolution\n+ * {Float} The resolution (map units per pixel) of the current cluster set.\n */\n- readOptions: null,\n+ resolution: null,\n \n /**\n- * Constructor: OpenLayers.Protocol.WFS\n- * A class for giving layers WFS protocol.\n+ * Constructor: OpenLayers.Strategy.Cluster\n+ * Create a new clustering strategy.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n- *\n- * Valid options properties:\n- * url - {String} URL to send requests to (required).\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (required, but can be autodetected\n- * during the first query if GML is used as readFormat and\n- * featurePrefix is provided and matches the prefix used by the server\n- * for this featureType).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * for writing if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. The default is\n- * 'the_geom' for WFS <version> 1.0, and null for higher versions. If\n- * null, it will be set to the name of the first geometry found in the\n- * first read operation.\n- * multi - {Boolean} If set to true, geometries will be casted to Multi\n- * geometries before they are written in a transaction. No casting will\n- * be done when reading features.\n */\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({\n- version: this.version,\n- featureType: this.featureType,\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- geometryName: this.geometryName,\n- srsName: this.srsName,\n- schema: this.schema\n- }, this.formatOptions));\n- }\n- if (!options.geometryName && parseFloat(this.format.version) > 1.0) {\n- this.setGeometryName(null);\n- }\n- },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * APIMethod: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully activated.\n */\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy();\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"beforefeaturesadded\": this.cacheFeatures,\n+ \"featuresremoved\": this.clearCache,\n+ \"moveend\": this.cluster,\n+ scope: this\n+ });\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n+ return activated;\n },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features. Since WFS splits the\n- * basic CRUD operations into GetFeature requests (for read) and\n- * Transactions (for all others), this method does not make use of the\n- * format's read method (that is only about reading transaction\n- * responses).\n- *\n- * Parameters:\n- * options - {Object} Options for the read operation, in addition to the\n- * options set on the instance (options set here will take precedence).\n- *\n- * To use a configured protocol to get e.g. a WFS hit count, applications\n- * could do the following:\n- *\n- * (code)\n- * protocol.read({\n- * readOptions: {output: \"object\"},\n- * resultType: \"hits\",\n- * maxFeatures: null,\n- * callback: function(resp) {\n- * // process resp.numberOfFeatures here\n- * }\n- * });\n- * (end)\n- *\n- * To use a configured protocol to use WFS paging (if supported by the\n- * server), applications could do the following:\n- *\n- * (code)\n- * protocol.read({\n- * startIndex: 0,\n- * count: 50\n- * });\n- * (end)\n- *\n- * To limit the attributes returned by the GetFeature request, applications\n- * can use the propertyNames option to specify the properties to include in\n- * the response:\n- *\n- * (code)\n- * protocol.read({\n- * propertyNames: [\"DURATION\", \"INTENSITY\"]\n- * });\n- * (end)\n+ * APIMethod: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n-\n- var data = OpenLayers.Format.XML.prototype.write.apply(\n- this.format, [this.format.writeNode(\"wfs:GetFeature\", options)]\n- );\n-\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- params: options.params,\n- headers: options.headers,\n- data: data\n- });\n-\n- return response;\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.clearCache();\n+ this.layer.events.un({\n+ \"beforefeaturesadded\": this.cacheFeatures,\n+ \"featuresremoved\": this.clearCache,\n+ \"moveend\": this.cluster,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n },\n \n /**\n- * APIMethod: setFeatureType\n- * Change the feature type on the fly.\n+ * Method: cacheFeatures\n+ * Cache features before they are added to the layer.\n *\n * Parameters:\n- * featureType - {String} Local (without prefix) feature typeName.\n+ * event - {Object} The event that this was listening for. This will come\n+ * with a batch of features to be clustered.\n+ * \n+ * Returns:\n+ * {Boolean} False to stop features from being added to the layer.\n */\n- setFeatureType: function(featureType) {\n- this.featureType = featureType;\n- this.format.featureType = featureType;\n+ cacheFeatures: function(event) {\n+ var propagate = true;\n+ if (!this.clustering) {\n+ this.clearCache();\n+ this.features = event.features;\n+ this.cluster();\n+ propagate = false;\n+ }\n+ return propagate;\n },\n \n /**\n- * APIMethod: setGeometryName\n- * Sets the geometryName option after instantiation.\n- *\n- * Parameters:\n- * geometryName - {String} Name of geometry attribute.\n+ * Method: clearCache\n+ * Clear out the cached features.\n */\n- setGeometryName: function(geometryName) {\n- this.geometryName = geometryName;\n- this.format.geometryName = geometryName;\n+ clearCache: function() {\n+ if (!this.clustering) {\n+ this.features = null;\n+ }\n },\n \n /**\n- * Method: handleRead\n- * Deal with response from the read request.\n+ * Method: cluster\n+ * Cluster features based on some threshold distance.\n *\n * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass\n- * to the user callback.\n- * options - {Object} The user options passed to the read call.\n+ * event - {Object} The event received when cluster is called as a\n+ * result of a moveend event.\n */\n- handleRead: function(response, options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n-\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- // success\n- var result = this.parseResponse(request, options.readOptions);\n- if (result && result.success !== false) {\n- if (options.readOptions && options.readOptions.output == \"object\") {\n- OpenLayers.Util.extend(response, result);\n- } else {\n- response.features = result;\n+ cluster: function(event) {\n+ if ((!event || event.zoomChanged) && this.features) {\n+ var resolution = this.layer.map.getResolution();\n+ if (resolution != this.resolution || !this.clustersExist()) {\n+ this.resolution = resolution;\n+ var clusters = [];\n+ var feature, clustered, cluster;\n+ for (var i = 0; i < this.features.length; ++i) {\n+ feature = this.features[i];\n+ if (feature.geometry) {\n+ clustered = false;\n+ for (var j = clusters.length - 1; j >= 0; --j) {\n+ cluster = clusters[j];\n+ if (this.shouldCluster(cluster, feature)) {\n+ this.addToCluster(cluster, feature);\n+ clustered = true;\n+ break;\n+ }\n+ }\n+ if (!clustered) {\n+ clusters.push(this.createCluster(this.features[i]));\n+ }\n }\n- response.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- // failure (service exception)\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- response.error = result;\n }\n- } else {\n- // failure\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n+ this.clustering = true;\n+ this.layer.removeAllFeatures();\n+ this.clustering = false;\n+ if (clusters.length > 0) {\n+ if (this.threshold > 1) {\n+ var clone = clusters.slice();\n+ clusters = [];\n+ var candidate;\n+ for (var i = 0, len = clone.length; i < len; ++i) {\n+ candidate = clone[i];\n+ if (candidate.attributes.count < this.threshold) {\n+ Array.prototype.push.apply(clusters, candidate.cluster);\n+ } else {\n+ clusters.push(candidate);\n+ }\n+ }\n+ }\n+ this.clustering = true;\n+ // A legitimate feature addition could occur during this\n+ // addFeatures call. For clustering to behave well, features\n+ // should be removed from a layer before requesting a new batch.\n+ this.layer.addFeatures(clusters);\n+ this.clustering = false;\n+ }\n+ this.clusters = clusters;\n }\n- options.callback.call(options.scope, response);\n }\n },\n \n /**\n- * Method: parseResponse\n- * Read HTTP response body and return features\n- *\n- * Parameters:\n- * request - {XMLHttpRequest} The request object\n- * options - {Object} Optional object to pass to format's read\n+ * Method: clustersExist\n+ * Determine whether calculated clusters are already on the layer.\n *\n * Returns:\n- * {Object} or {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>} \n- * An object with a features property, an array of features or a single \n- * feature.\n+ * {Boolean} The calculated clusters are already on the layer.\n */\n- parseResponse: function(request, options) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- if (!doc || doc.length <= 0) {\n- return null;\n- }\n- var result = (this.readFormat !== null) ? this.readFormat.read(doc) :\n- this.format.read(doc, options);\n- if (!this.featureNS) {\n- var format = this.readFormat || this.format;\n- this.featureNS = format.featureNS;\n- // no need to auto-configure again on subsequent reads\n- format.autoConfig = false;\n- if (!this.geometryName) {\n- this.setGeometryName(format.geometryName);\n+ clustersExist: function() {\n+ var exist = false;\n+ if (this.clusters && this.clusters.length > 0 &&\n+ this.clusters.length == this.layer.features.length) {\n+ exist = true;\n+ for (var i = 0; i < this.clusters.length; ++i) {\n+ if (this.clusters[i] != this.layer.features[i]) {\n+ exist = false;\n+ break;\n+ }\n }\n }\n- return result;\n+ return exist;\n },\n \n /**\n- * Method: commit\n- * Given a list of feature, assemble a batch request for update, create,\n- * and delete transactions. A commit call on the prototype amounts\n- * to writing a WFS transaction - so the write method on the format\n- * is used.\n+ * Method: shouldCluster\n+ * Determine whether to include a feature in a given cluster.\n *\n * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)}\n- * options - {Object}\n- *\n- * Valid options properties:\n- * nativeElements - {Array({Object})} Array of objects with information for writing\n- * out <Native> elements, these objects have vendorId, safeToIgnore and\n- * value properties. The <Native> element is intended to allow access to \n- * vendor specific capabilities of any particular web feature server or \n- * datastore.\n+ * cluster - {<OpenLayers.Feature.Vector>} A cluster.\n+ * feature - {<OpenLayers.Feature.Vector>} A feature.\n *\n * Returns:\n- * {<OpenLayers.Protocol.Response>} A response object with a features\n- * property containing any insertIds and a priv property referencing\n- * the XMLHttpRequest object.\n+ * {Boolean} The feature should be included in the cluster.\n */\n- commit: function(features, options) {\n-\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"commit\",\n- reqFeatures: features\n- });\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- headers: options.headers,\n- data: this.format.write(features, options),\n- callback: this.createCallback(this.handleCommit, response, options)\n- });\n-\n- return response;\n+ shouldCluster: function(cluster, feature) {\n+ var cc = cluster.geometry.getBounds().getCenterLonLat();\n+ var fc = feature.geometry.getBounds().getCenterLonLat();\n+ var distance = (\n+ Math.sqrt(\n+ Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)\n+ ) / this.resolution\n+ );\n+ return (distance <= this.distance);\n },\n \n /**\n- * Method: handleCommit\n- * Called when the commit request returns.\n- * \n+ * Method: addToCluster\n+ * Add a feature to a cluster.\n+ *\n * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass\n- * to the user callback.\n- * options - {Object} The user options passed to the commit call.\n+ * cluster - {<OpenLayers.Feature.Vector>} A cluster.\n+ * feature - {<OpenLayers.Feature.Vector>} A feature.\n */\n- handleCommit: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n-\n- // ensure that we have an xml doc\n- var data = request.responseXML;\n- if (!data || !data.documentElement) {\n- data = request.responseText;\n- }\n-\n- var obj = this.format.read(data) || {};\n-\n- response.insertIds = obj.insertIds || [];\n- if (obj.success) {\n- response.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- response.error = obj;\n- }\n- options.callback.call(options.scope, response);\n- }\n+ addToCluster: function(cluster, feature) {\n+ cluster.cluster.push(feature);\n+ cluster.attributes.count += 1;\n },\n \n /**\n- * Method: filterDelete\n- * Send a request that deletes all features by their filter.\n- * \n+ * Method: createCluster\n+ * Given a feature, create a cluster.\n+ *\n * Parameters:\n- * filter - {<OpenLayers.Filter>} filter\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A cluster.\n */\n- filterDelete: function(filter, options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"commit\"\n- });\n-\n- var root = this.format.createElementNSPlus(\"wfs:Transaction\", {\n- attributes: {\n- service: \"WFS\",\n- version: this.version\n- }\n- });\n-\n- var deleteNode = this.format.createElementNSPlus(\"wfs:Delete\", {\n- attributes: {\n- typeName: (options.featureNS ? this.featurePrefix + \":\" : \"\") +\n- options.featureType\n+ createCluster: function(feature) {\n+ var center = feature.geometry.getBounds().getCenterLonLat();\n+ var cluster = new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Point(center.lon, center.lat), {\n+ count: 1\n }\n- });\n-\n- if (options.featureNS) {\n- deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, options.featureNS);\n- }\n- var filterNode = this.format.writeNode(\"ogc:Filter\", filter);\n-\n- deleteNode.appendChild(filterNode);\n-\n- root.appendChild(deleteNode);\n-\n- var data = OpenLayers.Format.XML.prototype.write.apply(\n- this.format, [root]\n );\n-\n- return OpenLayers.Request.POST({\n- url: this.url,\n- callback: options.callback || function() {},\n- data: data\n- });\n-\n- },\n-\n- /**\n- * Method: abort\n- * Abort an ongoing request, the response object passed to\n- * this method must come from this protocol (as a result\n- * of a read, or commit operation).\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>}\n- */\n- abort: function(response) {\n- if (response) {\n- response.priv.abort();\n- }\n+ cluster.cluster = [feature];\n+ return cluster;\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Cluster\"\n });\n /* ======================================================================\n- OpenLayers/Protocol/WFS/v1_0_0.js\n+ OpenLayers/Strategy/BBOX.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol/WFS/v1.js\n- * @requires OpenLayers/Format/WFST/v1_0_0.js\n+ * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Filter/Spatial.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.WFS.v1_0_0\n- * A WFS v1.0.0 protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.WFS.v1_0_0> constructor.\n+ * Class: OpenLayers.Strategy.BBOX\n+ * A simple strategy that reads new features when the viewport invalidates\n+ * some bounds.\n *\n * Inherits from:\n- * - <OpenLayers.Protocol.WFS.v1>\n+ * - <OpenLayers.Strategy>\n */\n-OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * Property: version\n- * {String} WFS version number.\n+ * Property: bounds\n+ * {<OpenLayers.Bounds>} The current data bounds (in the same projection\n+ * as the layer - not always the same projection as the map).\n */\n- version: \"1.0.0\",\n+ bounds: null,\n \n- /**\n- * Constructor: OpenLayers.Protocol.WFS.v1_0_0\n- * A class for giving layers WFS v1.0.0 protocol.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ /** \n+ * Property: resolution \n+ * {Float} The current data resolution. \n */\n+ resolution: null,\n \n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n-});\n-/* ======================================================================\n- OpenLayers/Protocol/WFS/v1_1_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Protocol/WFS/v1.js\n- * @requires OpenLayers/Format/WFST/v1_1_0.js\n- */\n+ /**\n+ * APIProperty: ratio\n+ * {Float} The ratio of the data bounds to the viewport bounds (in each\n+ * dimension). Default is 2.\n+ */\n+ ratio: 2,\n \n-/**\n- * Class: OpenLayers.Protocol.WFS.v1_1_0\n- * A WFS v1.1.0 protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.WFS.v1_1_0> constructor.\n- *\n- * Differences from the v1.0.0 protocol:\n- * - uses Filter Encoding 1.1.0 instead of 1.0.0\n- * - uses GML 3 instead of 2 if no format is provided\n- * \n- * Inherits from:\n- * - <OpenLayers.Protocol.WFS.v1>\n- */\n-OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n+ /** \n+ * Property: resFactor \n+ * {Float} Optional factor used to determine when previously requested \n+ * features are invalid. If set, the resFactor will be compared to the\n+ * resolution of the previous request to the current map resolution.\n+ * If resFactor > (old / new) and 1/resFactor < (old / new). If you\n+ * set a resFactor of 1, data will be requested every time the\n+ * resolution changes. If you set a resFactor of 3, data will be\n+ * requested if the old resolution is 3 times the new, or if the new is\n+ * 3 times the old. If the old bounds do not contain the new bounds\n+ * new data will always be requested (with or without considering\n+ * resFactor). \n+ */\n+ resFactor: null,\n \n /**\n- * Property: version\n- * {String} WFS version number.\n+ * Property: response\n+ * {<OpenLayers.Protocol.Response>} The protocol response object returned\n+ * by the layer protocol.\n */\n- version: \"1.1.0\",\n+ response: null,\n \n /**\n- * Constructor: OpenLayers.Protocol.WFS.v1_1_0\n- * A class for giving layers WFS v1.1.0 protocol.\n+ * Constructor: OpenLayers.Strategy.BBOX\n+ * Create a new BBOX strategy.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n- * outputFormat - {String} Optional output format to use for WFS GetFeature\n- * requests. This can be any format advertized by the WFS's\n- * GetCapabilities response. If set, an appropriate readFormat also\n- * has to be provided, unless outputFormat is GML3, GML2 or JSON.\n- * readFormat - {<OpenLayers.Format>} An appropriate format parser if\n- * outputFormat is none of GML3, GML2 or JSON.\n */\n- initialize: function(options) {\n- OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);\n- if (this.outputFormat && !this.readFormat) {\n- if (this.outputFormat.toLowerCase() == \"gml2\") {\n- this.readFormat = new OpenLayers.Format.GML.v2({\n- featureType: this.featureType,\n- featureNS: this.featureNS,\n- geometryName: this.geometryName\n- });\n- } else if (this.outputFormat.toLowerCase() == \"json\") {\n- this.readFormat = new OpenLayers.Format.GeoJSON();\n- }\n+\n+ /**\n+ * Method: activate\n+ * Set up strategy with regard to reading new batches of remote data.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully activated.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"moveend\": this.update,\n+ \"refresh\": this.update,\n+ \"visibilitychanged\": this.update,\n+ scope: this\n+ });\n+ this.update();\n }\n+ return activated;\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_1_0\"\n-});\n-/* ======================================================================\n- OpenLayers/Protocol/CSW/v2_0_2.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Protocol/CSW.js\n- * @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js\n- */\n-\n-/**\n- * Class: OpenLayers.Protocol.CSW.v2_0_2\n- * CS-W (Catalogue services for the Web) version 2.0.2 protocol.\n- *\n- * Inherits from:\n- * - <OpenLayers.Protocol>\n- */\n-OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, {\n-\n /**\n- * Property: formatOptions\n- * {Object} Optional options for the format. If a format is not provided,\n- * this property can be used to extend the default format options.\n+ * Method: deactivate\n+ * Tear down strategy with regard to reading new batches of remote data.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- formatOptions: null,\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ \"moveend\": this.update,\n+ \"refresh\": this.update,\n+ \"visibilitychanged\": this.update,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n+ },\n \n /**\n- * Constructor: OpenLayers.Protocol.CSW.v2_0_2\n- * A class for CSW version 2.0.2 protocol management.\n+ * Method: update\n+ * Callback function called on \"moveend\" or \"refresh\" layer events.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * options - {Object} Optional object whose properties will determine\n+ * the behaviour of this Strategy\n+ *\n+ * Valid options include:\n+ * force - {Boolean} if true, new data must be unconditionally read.\n+ * noAbort - {Boolean} if true, do not abort previous requests.\n */\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions));\n+ update: function(options) {\n+ var mapBounds = this.getMapBounds();\n+ if (mapBounds !== null && ((options && options.force) ||\n+ (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {\n+ this.calculateBounds(mapBounds);\n+ this.resolution = this.layer.map.getResolution();\n+ this.triggerRead(options);\n }\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * Method: getMapBounds\n+ * Get the map bounds expressed in the same projection as this layer.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.\n */\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy();\n+ getMapBounds: function() {\n+ if (this.layer.map === null) {\n+ return null;\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n+ var bounds = this.layer.map.getExtent();\n+ if (bounds && !this.layer.projection.equals(\n+ this.layer.map.getProjectionObject())) {\n+ bounds = bounds.clone().transform(\n+ this.layer.map.getProjectionObject(), this.layer.projection\n+ );\n+ }\n+ return bounds;\n },\n \n /**\n- * Method: read\n- * Construct a request for reading new records from the Catalogue.\n+ * Method: invalidBounds\n+ * Determine whether the previously requested set of features is invalid. \n+ * This occurs when the new map bounds do not contain the previously \n+ * requested bounds. In addition, if <resFactor> is set, it will be \n+ * considered.\n+ *\n+ * Parameters:\n+ * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n+ * retrieved from the map object if not provided\n+ *\n+ * Returns:\n+ * {Boolean} \n */\n- read: function(options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n-\n- var data = this.format.write(options.params || options);\n-\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- params: options.params,\n- headers: options.headers,\n- data: data\n- });\n-\n- return response;\n+ invalidBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds();\n+ }\n+ var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n+ if (!invalid && this.resFactor) {\n+ var ratio = this.resolution / this.layer.map.getResolution();\n+ invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));\n+ }\n+ return invalid;\n },\n \n /**\n- * Method: handleRead\n- * Deal with response from the read request.\n+ * Method: calculateBounds\n *\n * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass\n- * to the user callback.\n- * This response is given a code property, and optionally a data property.\n- * The latter represents the CSW records as returned by the call to\n- * the CSW format read method.\n- * options - {Object} The user options passed to the read call.\n+ * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n+ * retrieved from the map object if not provided\n */\n- handleRead: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- // success\n- response.data = this.parseData(request);\n- response.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- // failure\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- }\n- options.callback.call(options.scope, response);\n+ calculateBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds();\n }\n+ var center = mapBounds.getCenterLonLat();\n+ var dataWidth = mapBounds.getWidth() * this.ratio;\n+ var dataHeight = mapBounds.getHeight() * this.ratio;\n+ this.bounds = new OpenLayers.Bounds(\n+ center.lon - (dataWidth / 2),\n+ center.lat - (dataHeight / 2),\n+ center.lon + (dataWidth / 2),\n+ center.lat + (dataHeight / 2)\n+ );\n },\n \n /**\n- * Method: parseData\n- * Read HTTP response body and return records\n+ * Method: triggerRead\n *\n * Parameters:\n- * request - {XMLHttpRequest} The request object\n+ * options - {Object} Additional options for the protocol's read method \n+ * (optional)\n *\n * Returns:\n- * {Object} The CSW records as returned by the call to the format read method.\n+ * {<OpenLayers.Protocol.Response>} The protocol response object\n+ * returned by the layer protocol.\n */\n- parseData: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n+ triggerRead: function(options) {\n+ if (this.response && !(options && options.noAbort === true)) {\n+ this.layer.protocol.abort(this.response);\n+ this.layer.events.triggerEvent(\"loadend\");\n }\n- if (!doc || doc.length <= 0) {\n- return null;\n+ var evt = {\n+ filter: this.createFilter()\n+ };\n+ this.layer.events.triggerEvent(\"loadstart\", evt);\n+ this.response = this.layer.protocol.read(\n+ OpenLayers.Util.applyDefaults({\n+ filter: evt.filter,\n+ callback: this.merge,\n+ scope: this\n+ }, options));\n+ },\n+\n+ /**\n+ * Method: createFilter\n+ * Creates a spatial BBOX filter. If the layer that this strategy belongs\n+ * to has a filter property, this filter will be combined with the BBOX \n+ * filter.\n+ * \n+ * Returns\n+ * {<OpenLayers.Filter>} The filter object.\n+ */\n+ createFilter: function() {\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ value: this.bounds,\n+ projection: this.layer.projection\n+ });\n+ if (this.layer.filter) {\n+ filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.layer.filter, filter]\n+ });\n }\n- return this.format.read(doc);\n+ return filter;\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.CSW.v2_0_2\"\n+ /**\n+ * Method: merge\n+ * Given a list of features, determine which ones to add to the layer.\n+ * If the layer projection differs from the map projection, features\n+ * will be transformed from the layer projection to the map projection.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object passed\n+ * by the protocol.\n+ */\n+ merge: function(resp) {\n+ this.layer.destroyFeatures();\n+ if (resp.success()) {\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local);\n+ }\n+ }\n+ }\n+ this.layer.addFeatures(features);\n+ }\n+ } else {\n+ this.bounds = null;\n+ }\n+ this.response = null;\n+ this.layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ });\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n });\n /* ======================================================================\n- OpenLayers/Protocol/SOS/v1_0_0.js\n+ OpenLayers/Strategy/Refresh.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol/SOS.js\n- * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js\n+ * @requires OpenLayers/Strategy.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.SOS.v1_0_0\n- * An SOS v1.0.0 Protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.SOS.v1_0_0> constructor.\n+ * Class: OpenLayers.Strategy.Refresh\n+ * A strategy that refreshes the layer. By default the strategy waits for a\n+ * call to <refresh> before refreshing. By configuring the strategy with \n+ * the <interval> option, refreshing can take place automatically.\n *\n * Inherits from:\n- * - <OpenLayers.Protocol>\n+ * - <OpenLayers.Strategy>\n */\n-OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {\n+OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * APIProperty: fois\n- * {Array(String)} Array of features of interest (foi)\n+ * Property: force\n+ * {Boolean} Force a refresh on the layer. Default is false.\n */\n- fois: null,\n+ force: false,\n \n /**\n- * Property: formatOptions\n- * {Object} Optional options for the format. If a format is not provided,\n- * this property can be used to extend the default format options.\n+ * Property: interval\n+ * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed \n+ * every N milliseconds.\n */\n- formatOptions: null,\n+ interval: 0,\n \n /**\n- * Constructor: OpenLayers.Protocol.SOS\n- * A class for giving layers an SOS protocol.\n+ * Property: timer\n+ * {Number} The id of the timer.\n+ */\n+ timer: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Strategy.Refresh\n+ * Create a new Refresh strategy.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n- *\n- * Valid options properties:\n- * url - {String} URL to send requests to (required).\n- * fois - {Array} The features of interest (required).\n */\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(\n- this.formatOptions);\n+\n+ /**\n+ * APIMethod: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ * \n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.layer.visibility === true) {\n+ this.start();\n+ }\n+ this.layer.events.on({\n+ \"visibilitychanged\": this.reset,\n+ scope: this\n+ });\n }\n+ return activated;\n },\n \n /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n+ * APIMethod: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ * \n+ * Returns:\n+ * {Boolean} True if the strategy was successfully deactivated.\n */\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy();\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.stop();\n+ this.layer.events.un({\n+ \"visibilitychanged\": this.reset,\n+ scope: this\n+ });\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n+ return deactivated;\n },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new sensor positions. This is done by\n- * issuing one GetFeatureOfInterest request.\n+ * Method: reset\n+ * Start or cancel the refresh interval depending on the visibility of \n+ * the layer.\n */\n- read: function(options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var format = this.format;\n- var data = OpenLayers.Format.XML.prototype.write.apply(format,\n- [format.writeNode(\"sos:GetFeatureOfInterest\", {\n- fois: this.fois\n- })]\n- );\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- data: data\n- });\n- return response;\n+ reset: function() {\n+ if (this.layer.visibility === true) {\n+ this.start();\n+ } else {\n+ this.stop();\n+ }\n },\n \n /**\n- * Method: handleRead\n- * Deal with response from the read request.\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>} The response object to pass\n- * to the user callback.\n- * options - {Object} The user options passed to the read call.\n+ * Method: start\n+ * Start the refresh interval. \n */\n- handleRead: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- // success\n- response.features = this.parseFeatures(request);\n- response.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- // failure\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- }\n- options.callback.call(options.scope, response);\n+ start: function() {\n+ if (this.interval && typeof this.interval === \"number\" &&\n+ this.interval > 0) {\n+\n+ this.timer = window.setInterval(\n+ OpenLayers.Function.bind(this.refresh, this),\n+ this.interval);\n }\n },\n \n /**\n- * Method: parseFeatures\n- * Read HTTP response body and return features\n- *\n- * Parameters:\n- * request - {XMLHttpRequest} The request object\n- *\n- * Returns:\n- * {Array({<OpenLayers.Feature.Vector>})} Array of features\n+ * APIMethod: refresh\n+ * Tell the strategy to refresh which will refresh the layer.\n */\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n+ refresh: function() {\n+ if (this.layer && this.layer.refresh &&\n+ typeof this.layer.refresh == \"function\") {\n+\n+ this.layer.refresh({\n+ force: this.force\n+ });\n }\n- if (!doc || doc.length <= 0) {\n- return null;\n+ },\n+\n+ /**\n+ * Method: stop\n+ * Cancels the refresh interval. \n+ */\n+ stop: function() {\n+ if (this.timer !== null) {\n+ window.clearInterval(this.timer);\n+ this.timer = null;\n }\n- return this.format.read(doc);\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.SOS.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Refresh\"\n });\n /* ======================================================================\n Rico/license.js\n ====================================================================== */\n \n /**\n * @license Apache 2 \n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.light.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.light.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -263,449 +263,14 @@\n source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n destination.toString = source.toString;\n }\n }\n return destination;\n };\n /* ======================================================================\n- OpenLayers/Util/vendorPrefix.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n-\n-OpenLayers.Util = OpenLayers.Util || {};\n-/**\n- * Namespace: OpenLayers.Util.vendorPrefix\n- * A collection of utility functions to detect vendor prefixed features\n- */\n-OpenLayers.Util.vendorPrefix = (function() {\n- \"use strict\";\n-\n- var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n- divStyle = document.createElement(\"div\").style,\n- cssCache = {},\n- jsCache = {};\n-\n-\n- /**\n- * Function: domToCss\n- * Converts a upper camel case DOM style property name to a CSS property\n- * i.e. transformOrigin -> transform-origin\n- * or WebkitTransformOrigin -> -webkit-transform-origin\n- *\n- * Parameters:\n- * prefixedDom - {String} The property to convert\n- *\n- * Returns:\n- * {String} The CSS property\n- */\n- function domToCss(prefixedDom) {\n- if (!prefixedDom) {\n- return null;\n- }\n- return prefixedDom.\n- replace(/([A-Z])/g, function(c) {\n- return \"-\" + c.toLowerCase();\n- }).\n- replace(/^ms-/, \"-ms-\");\n- }\n-\n- /**\n- * APIMethod: css\n- * Detect which property is used for a CSS property\n- *\n- * Parameters:\n- * property - {String} The standard (unprefixed) CSS property name\n- *\n- * Returns:\n- * {String} The standard CSS property, prefixed property or null if not\n- * supported\n- */\n- function css(property) {\n- if (cssCache[property] === undefined) {\n- var domProperty = property.\n- replace(/(-[\\s\\S])/g, function(c) {\n- return c.charAt(1).toUpperCase();\n- });\n- var prefixedDom = style(domProperty);\n- cssCache[property] = domToCss(prefixedDom);\n- }\n- return cssCache[property];\n- }\n-\n- /**\n- * APIMethod: js\n- * Detect which property is used for a JS property/method\n- *\n- * Parameters:\n- * obj - {Object} The object to test on\n- * property - {String} The standard (unprefixed) JS property name\n- *\n- * Returns:\n- * {String} The standard JS property, prefixed property or null if not\n- * supported\n- */\n- function js(obj, property) {\n- if (jsCache[property] === undefined) {\n- var tmpProp,\n- i = 0,\n- l = VENDOR_PREFIXES.length,\n- prefix,\n- isStyleObj = (typeof obj.cssText !== \"undefined\");\n-\n- jsCache[property] = null;\n- for (; i < l; i++) {\n- prefix = VENDOR_PREFIXES[i];\n- if (prefix) {\n- if (!isStyleObj) {\n- // js prefix should be lower-case, while style\n- // properties have upper case on first character\n- prefix = prefix.toLowerCase();\n- }\n- tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);\n- } else {\n- tmpProp = property;\n- }\n-\n- if (obj[tmpProp] !== undefined) {\n- jsCache[property] = tmpProp;\n- break;\n- }\n- }\n- }\n- return jsCache[property];\n- }\n-\n- /**\n- * APIMethod: style\n- * Detect which property is used for a DOM style property\n- *\n- * Parameters:\n- * property - {String} The standard (unprefixed) style property name\n- *\n- * Returns:\n- * {String} The standard style property, prefixed property or null if not\n- * supported\n- */\n- function style(property) {\n- return js(divStyle, property);\n- }\n-\n- return {\n- css: css,\n- js: js,\n- style: style,\n-\n- // used for testing\n- cssCache: cssCache,\n- jsCache: jsCache\n- };\n-}());\n-/* ======================================================================\n- OpenLayers/Animation.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/SingleFile.js\n- * @requires OpenLayers/Util/vendorPrefix.js\n- */\n-\n-/**\n- * Namespace: OpenLayers.Animation\n- * A collection of utility functions for executing methods that repaint a \n- * portion of the browser window. These methods take advantage of the\n- * browser's scheduled repaints where requestAnimationFrame is available.\n- */\n-OpenLayers.Animation = (function(window) {\n-\n- /**\n- * Property: isNative\n- * {Boolean} true if a native requestAnimationFrame function is available\n- */\n- var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n- var isNative = !!(requestAnimationFrame);\n-\n- /**\n- * Function: requestFrame\n- * Schedule a function to be called at the next available animation frame.\n- * Uses the native method where available. Where requestAnimationFrame is\n- * not available, setTimeout will be called with a 16ms delay.\n- *\n- * Parameters:\n- * callback - {Function} The function to be called at the next animation frame.\n- * element - {DOMElement} Optional element that visually bounds the animation.\n- */\n- var requestFrame = (function() {\n- var request = window[requestAnimationFrame] ||\n- function(callback, element) {\n- window.setTimeout(callback, 16);\n- };\n- // bind to window to avoid illegal invocation of native function\n- return function(callback, element) {\n- request.apply(window, [callback, element]);\n- };\n- })();\n-\n- // private variables for animation loops\n- var counter = 0;\n- var loops = {};\n-\n- /**\n- * Function: start\n- * Executes a method with <requestFrame> in series for some \n- * duration.\n- *\n- * Parameters:\n- * callback - {Function} The function to be called at the next animation frame.\n- * duration - {Number} Optional duration for the loop. If not provided, the\n- * animation loop will execute indefinitely.\n- * element - {DOMElement} Optional element that visually bounds the animation.\n- *\n- * Returns:\n- * {Number} Identifier for the animation loop. Used to stop animations with\n- * <stop>.\n- */\n- function start(callback, duration, element) {\n- duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n- var id = ++counter;\n- var start = +new Date;\n- loops[id] = function() {\n- if (loops[id] && +new Date - start <= duration) {\n- callback();\n- if (loops[id]) {\n- requestFrame(loops[id], element);\n- }\n- } else {\n- delete loops[id];\n- }\n- };\n- requestFrame(loops[id], element);\n- return id;\n- }\n-\n- /**\n- * Function: stop\n- * Terminates an animation loop started with <start>.\n- *\n- * Parameters:\n- * id - {Number} Identifier returned from <start>.\n- */\n- function stop(id) {\n- delete loops[id];\n- }\n-\n- return {\n- isNative: isNative,\n- requestFrame: requestFrame,\n- start: start,\n- stop: stop\n- };\n-\n-})(window);\n-/* ======================================================================\n- OpenLayers/Kinetic.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Animation.js\n- */\n-\n-OpenLayers.Kinetic = OpenLayers.Class({\n-\n- /**\n- * Property: threshold\n- * In most cases changing the threshold isn't needed.\n- * In px/ms, default to 0.\n- */\n- threshold: 0,\n-\n- /**\n- * Property: deceleration\n- * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n- */\n- deceleration: 0.0035,\n-\n- /**\n- * Property: nbPoints\n- * {Integer} the number of points we use to calculate the kinetic\n- * initial values.\n- */\n- nbPoints: 100,\n-\n- /**\n- * Property: delay\n- * {Float} time to consider to calculate the kinetic initial values.\n- * In ms, default to 200.\n- */\n- delay: 200,\n-\n- /**\n- * Property: points\n- * List of points use to calculate the kinetic initial values.\n- */\n- points: undefined,\n-\n- /**\n- * Property: timerId\n- * ID of the timer.\n- */\n- timerId: undefined,\n-\n- /**\n- * Constructor: OpenLayers.Kinetic\n- *\n- * Parameters:\n- * options - {Object}\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: begin\n- * Begins the dragging.\n- */\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = [];\n- },\n-\n- /**\n- * Method: update\n- * Updates during the dragging.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The new position.\n- */\n- update: function(xy) {\n- this.points.unshift({\n- xy: xy,\n- tick: new Date().getTime()\n- });\n- if (this.points.length > this.nbPoints) {\n- this.points.pop();\n- }\n- },\n-\n- /**\n- * Method: end\n- * Ends the dragging, start the kinetic.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The last position.\n- *\n- * Returns:\n- * {Object} An object with two properties: \"speed\", and \"theta\". The\n- * \"speed\" and \"theta\" values are to be passed to the move \n- * function when starting the animation.\n- */\n- end: function(xy) {\n- var last, now = new Date().getTime();\n- for (var i = 0, l = this.points.length, point; i < l; i++) {\n- point = this.points[i];\n- if (now - point.tick > this.delay) {\n- break;\n- }\n- last = point;\n- }\n- if (!last) {\n- return;\n- }\n- var time = new Date().getTime() - last.tick;\n- var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n- Math.pow(xy.y - last.xy.y, 2));\n- var speed = dist / time;\n- if (speed == 0 || speed < this.threshold) {\n- return;\n- }\n- var theta = Math.asin((xy.y - last.xy.y) / dist);\n- if (last.xy.x <= xy.x) {\n- theta = Math.PI - theta;\n- }\n- return {\n- speed: speed,\n- theta: theta\n- };\n- },\n-\n- /**\n- * Method: move\n- * Launch the kinetic move pan.\n- *\n- * Parameters:\n- * info - {Object} An object with two properties, \"speed\", and \"theta\".\n- * These values are those returned from the \"end\" call.\n- * callback - {Function} Function called on every step of the animation,\n- * receives x, y (values to pan), end (is the last point).\n- */\n- move: function(info, callback) {\n- var v0 = info.speed;\n- var fx = Math.cos(info.theta);\n- var fy = -Math.sin(info.theta);\n-\n- var initialTime = new Date().getTime();\n-\n- var lastX = 0;\n- var lastY = 0;\n-\n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return;\n- }\n-\n- var t = new Date().getTime() - initialTime;\n-\n- var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\n-\n- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n-\n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true;\n- }\n-\n- args.x = x - lastX;\n- args.y = y - lastY;\n- lastX = x;\n- lastY = y;\n- callback(args.x, args.y, args.end);\n- };\n-\n- this.timerId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(timerCallback, this)\n- );\n- },\n-\n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n-/* ======================================================================\n OpenLayers/BaseTypes.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -4861,14 +4426,2378 @@\n } else {\n str += coordinate < 0 ? OpenLayers.i18n(\"S\") : OpenLayers.i18n(\"N\");\n }\n return str;\n };\n \n /* ======================================================================\n+ OpenLayers/Feature.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Feature\n+ * Features are combinations of geography and attributes. The OpenLayers.Feature\n+ * class specifically combines a marker and a lonlat.\n+ */\n+OpenLayers.Feature = OpenLayers.Class({\n+\n+ /** \n+ * Property: layer \n+ * {<OpenLayers.Layer>} \n+ */\n+ layer: null,\n+\n+ /** \n+ * Property: id \n+ * {String} \n+ */\n+ id: null,\n+\n+ /** \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} \n+ */\n+ lonlat: null,\n+\n+ /** \n+ * Property: data \n+ * {Object} \n+ */\n+ data: null,\n+\n+ /** \n+ * Property: marker \n+ * {<OpenLayers.Marker>} \n+ */\n+ marker: null,\n+\n+ /**\n+ * APIProperty: popupClass\n+ * {<OpenLayers.Class>} The class which will be used to instantiate\n+ * a new Popup. Default is <OpenLayers.Popup.Anchored>.\n+ */\n+ popupClass: null,\n+\n+ /** \n+ * Property: popup \n+ * {<OpenLayers.Popup>} \n+ */\n+ popup: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Feature\n+ * Constructor for features.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer>} \n+ * lonlat - {<OpenLayers.LonLat>} \n+ * data - {Object} \n+ * \n+ * Returns:\n+ * {<OpenLayers.Feature>}\n+ */\n+ initialize: function(layer, lonlat, data) {\n+ this.layer = layer;\n+ this.lonlat = lonlat;\n+ this.data = (data != null) ? data : {};\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n+\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+\n+ //remove the popup from the map\n+ if ((this.layer != null) && (this.layer.map != null)) {\n+ if (this.popup != null) {\n+ this.layer.map.removePopup(this.popup);\n+ }\n+ }\n+ // remove the marker from the layer\n+ if (this.layer != null && this.marker != null) {\n+ this.layer.removeMarker(this.marker);\n+ }\n+\n+ this.layer = null;\n+ this.id = null;\n+ this.lonlat = null;\n+ this.data = null;\n+ if (this.marker != null) {\n+ this.destroyMarker(this.marker);\n+ this.marker = null;\n+ }\n+ if (this.popup != null) {\n+ this.destroyPopup(this.popup);\n+ this.popup = null;\n+ }\n+ },\n+\n+ /**\n+ * Method: onScreen\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the feature is currently visible on screen\n+ * (based on its 'lonlat' property)\n+ */\n+ onScreen: function() {\n+\n+ var onScreen = false;\n+ if ((this.layer != null) && (this.layer.map != null)) {\n+ var screenBounds = this.layer.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat);\n+ }\n+ return onScreen;\n+ },\n+\n+\n+ /**\n+ * Method: createMarker\n+ * Based on the data associated with the Feature, create and return a marker object.\n+ *\n+ * Returns: \n+ * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties\n+ * set in this.data. If no 'lonlat' is set, returns null. If no\n+ * 'icon' is set, OpenLayers.Marker() will load the default image.\n+ * \n+ * Note - this.marker is set to return value\n+ * \n+ */\n+ createMarker: function() {\n+\n+ if (this.lonlat != null) {\n+ this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);\n+ }\n+ return this.marker;\n+ },\n+\n+ /**\n+ * Method: destroyMarker\n+ * Destroys marker.\n+ * If user overrides the createMarker() function, s/he should be able\n+ * to also specify an alternative function for destroying it\n+ */\n+ destroyMarker: function() {\n+ this.marker.destroy();\n+ },\n+\n+ /**\n+ * Method: createPopup\n+ * Creates a popup object created from the 'lonlat', 'popupSize',\n+ * and 'popupContentHTML' properties set in this.data. It uses\n+ * this.marker.icon as default anchor. \n+ * \n+ * If no 'lonlat' is set, returns null. \n+ * If no this.marker has been created, no anchor is sent.\n+ *\n+ * Note - the returned popup object is 'owned' by the feature, so you\n+ * cannot use the popup's destroy method to discard the popup.\n+ * Instead, you must use the feature's destroyPopup\n+ * \n+ * Note - this.popup is set to return value\n+ * \n+ * Parameters: \n+ * closeBox - {Boolean} create popup with closebox or not\n+ * \n+ * Returns:\n+ * {<OpenLayers.Popup>} Returns the created popup, which is also set\n+ * as 'popup' property of this feature. Will be of whatever type\n+ * specified by this feature's 'popupClass' property, but must be\n+ * of type <OpenLayers.Popup>.\n+ * \n+ */\n+ createPopup: function(closeBox) {\n+\n+ if (this.lonlat != null) {\n+ if (!this.popup) {\n+ var anchor = (this.marker) ? this.marker.icon : null;\n+ var popupClass = this.popupClass ?\n+ this.popupClass : OpenLayers.Popup.Anchored;\n+ this.popup = new popupClass(this.id + \"_popup\",\n+ this.lonlat,\n+ this.data.popupSize,\n+ this.data.popupContentHTML,\n+ anchor,\n+ closeBox);\n+ }\n+ if (this.data.overflow != null) {\n+ this.popup.contentDiv.style.overflow = this.data.overflow;\n+ }\n+\n+ this.popup.feature = this;\n+ }\n+ return this.popup;\n+ },\n+\n+\n+ /**\n+ * Method: destroyPopup\n+ * Destroys the popup created via createPopup.\n+ *\n+ * As with the marker, if user overrides the createPopup() function, s/he \n+ * should also be able to override the destruction\n+ */\n+ destroyPopup: function() {\n+ if (this.popup) {\n+ this.popup.feature = null;\n+ this.popup.destroy();\n+ this.popup = null;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Feature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Feature/Vector.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+// TRASH THIS\n+OpenLayers.State = {\n+ /** states */\n+ UNKNOWN: 'Unknown',\n+ INSERT: 'Insert',\n+ UPDATE: 'Update',\n+ DELETE: 'Delete'\n+};\n+\n+/**\n+ * @requires OpenLayers/Feature.js\n+ * @requires OpenLayers/Util.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Feature.Vector\n+ * Vector features use the OpenLayers.Geometry classes as geometry description.\n+ * They have an 'attributes' property, which is the data object, and a 'style'\n+ * property, the default values of which are defined in the \n+ * <OpenLayers.Feature.Vector.style> objects.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Feature>\n+ */\n+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n+\n+ /** \n+ * Property: fid \n+ * {String} \n+ */\n+ fid: null,\n+\n+ /** \n+ * APIProperty: geometry \n+ * {<OpenLayers.Geometry>} \n+ */\n+ geometry: null,\n+\n+ /** \n+ * APIProperty: attributes \n+ * {Object} This object holds arbitrary, serializable properties that\n+ * describe the feature.\n+ */\n+ attributes: null,\n+\n+ /**\n+ * Property: bounds\n+ * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that\n+ * property can be set by an <OpenLayers.Format> object when\n+ * deserializing the feature, so in most cases it represents an\n+ * information set by the server. \n+ */\n+ bounds: null,\n+\n+ /** \n+ * Property: state \n+ * {String} \n+ */\n+ state: null,\n+\n+ /** \n+ * APIProperty: style \n+ * {Object} \n+ */\n+ style: null,\n+\n+ /**\n+ * APIProperty: url\n+ * {String} If this property is set it will be taken into account by\n+ * {<OpenLayers.HTTP>} when upadting or deleting the feature.\n+ */\n+ url: null,\n+\n+ /**\n+ * Property: renderIntent\n+ * {String} rendering intent currently being used\n+ */\n+ renderIntent: \"default\",\n+\n+ /**\n+ * APIProperty: modified\n+ * {Object} An object with the originals of the geometry and attributes of\n+ * the feature, if they were changed. Currently this property is only read\n+ * by <OpenLayers.Format.WFST.v1>, and written by\n+ * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.\n+ * Applications can set the originals of modified attributes in the\n+ * attributes property. Note that applications have to check if this\n+ * object and the attributes property is already created before using it.\n+ * After a change made with ModifyFeature, this object could look like\n+ *\n+ * (code)\n+ * {\n+ * geometry: >Object\n+ * }\n+ * (end)\n+ *\n+ * When an application has made changes to feature attributes, it could\n+ * have set the attributes to something like this:\n+ *\n+ * (code)\n+ * {\n+ * attributes: {\n+ * myAttribute: \"original\"\n+ * }\n+ * }\n+ * (end)\n+ *\n+ * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in\n+ * *modified.geometry* and the attribute names in *modified.attributes*,\n+ * but it is recommended to set the original values (and not just true) as\n+ * attribute value, so applications could use this information to undo\n+ * changes.\n+ */\n+ modified: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Feature.Vector\n+ * Create a vector feature. \n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} The geometry that this feature\n+ * represents.\n+ * attributes - {Object} An optional object that will be mapped to the\n+ * <attributes> property. \n+ * style - {Object} An optional style object.\n+ */\n+ initialize: function(geometry, attributes, style) {\n+ OpenLayers.Feature.prototype.initialize.apply(this,\n+ [null, null, attributes]);\n+ this.lonlat = null;\n+ this.geometry = geometry ? geometry : null;\n+ this.state = null;\n+ this.attributes = {};\n+ if (attributes) {\n+ this.attributes = OpenLayers.Util.extend(this.attributes,\n+ attributes);\n+ }\n+ this.style = style ? style : null;\n+ },\n+\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ if (this.layer) {\n+ this.layer.removeFeatures(this);\n+ this.layer = null;\n+ }\n+\n+ this.geometry = null;\n+ this.modified = null;\n+ OpenLayers.Feature.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this vector feature. Does not set any non-standard\n+ * properties.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.\n+ */\n+ clone: function() {\n+ return new OpenLayers.Feature.Vector(\n+ this.geometry ? this.geometry.clone() : null,\n+ this.attributes,\n+ this.style);\n+ },\n+\n+ /**\n+ * Method: onScreen\n+ * Determine whether the feature is within the map viewport. This method\n+ * tests for an intersection between the geometry and the viewport\n+ * bounds. If a more effecient but less precise geometry bounds\n+ * intersection is desired, call the method with the boundsOnly\n+ * parameter true.\n+ *\n+ * Parameters:\n+ * boundsOnly - {Boolean} Only test whether a feature's bounds intersects\n+ * the viewport bounds. Default is false. If false, the feature's\n+ * geometry must intersect the viewport for onScreen to return true.\n+ * \n+ * Returns:\n+ * {Boolean} The feature is currently visible on screen (optionally\n+ * based on its bounds if boundsOnly is true).\n+ */\n+ onScreen: function(boundsOnly) {\n+ var onScreen = false;\n+ if (this.layer && this.layer.map) {\n+ var screenBounds = this.layer.map.getExtent();\n+ if (boundsOnly) {\n+ var featureBounds = this.geometry.getBounds();\n+ onScreen = screenBounds.intersectsBounds(featureBounds);\n+ } else {\n+ var screenPoly = screenBounds.toGeometry();\n+ onScreen = screenPoly.intersects(this.geometry);\n+ }\n+ }\n+ return onScreen;\n+ },\n+\n+ /**\n+ * Method: getVisibility\n+ * Determine whether the feature is displayed or not. It may not displayed\n+ * because:\n+ * - its style display property is set to 'none',\n+ * - it doesn't belong to any layer,\n+ * - the styleMap creates a symbolizer with display property set to 'none'\n+ * for it,\n+ * - the layer which it belongs to is not visible.\n+ * \n+ * Returns:\n+ * {Boolean} The feature is currently displayed.\n+ */\n+ getVisibility: function() {\n+ return !(this.style && this.style.display == 'none' ||\n+ !this.layer ||\n+ this.layer && this.layer.styleMap &&\n+ this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||\n+ this.layer && !this.layer.getVisibility());\n+ },\n+\n+ /**\n+ * Method: createMarker\n+ * HACK - we need to decide if all vector features should be able to\n+ * create markers\n+ * \n+ * Returns:\n+ * {<OpenLayers.Marker>} For now just returns null\n+ */\n+ createMarker: function() {\n+ return null;\n+ },\n+\n+ /**\n+ * Method: destroyMarker\n+ * HACK - we need to decide if all vector features should be able to\n+ * delete markers\n+ * \n+ * If user overrides the createMarker() function, s/he should be able\n+ * to also specify an alternative function for destroying it\n+ */\n+ destroyMarker: function() {\n+ // pass\n+ },\n+\n+ /**\n+ * Method: createPopup\n+ * HACK - we need to decide if all vector features should be able to\n+ * create popups\n+ * \n+ * Returns:\n+ * {<OpenLayers.Popup>} For now just returns null\n+ */\n+ createPopup: function() {\n+ return null;\n+ },\n+\n+ /**\n+ * Method: atPoint\n+ * Determins whether the feature intersects with the specified location.\n+ * \n+ * Parameters: \n+ * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ * toleranceLon - {float} Optional tolerance in Geometric Coords\n+ * toleranceLat - {float} Optional tolerance in Geographic Coords\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the feature is at the specified location\n+ */\n+ atPoint: function(lonlat, toleranceLon, toleranceLat) {\n+ var atPoint = false;\n+ if (this.geometry) {\n+ atPoint = this.geometry.atPoint(lonlat, toleranceLon,\n+ toleranceLat);\n+ }\n+ return atPoint;\n+ },\n+\n+ /**\n+ * Method: destroyPopup\n+ * HACK - we need to decide if all vector features should be able to\n+ * delete popups\n+ */\n+ destroyPopup: function() {\n+ // pass\n+ },\n+\n+ /**\n+ * Method: move\n+ * Moves the feature and redraws it at its new location\n+ *\n+ * Parameters:\n+ * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the\n+ * location to which to move the feature.\n+ */\n+ move: function(location) {\n+\n+ if (!this.layer || !this.geometry.move) {\n+ //do nothing if no layer or immoveable geometry\n+ return undefined;\n+ }\n+\n+ var pixel;\n+ if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n+ pixel = this.layer.getViewPortPxFromLonLat(location);\n+ } else {\n+ pixel = location;\n+ }\n+\n+ var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n+ var res = this.layer.map.getResolution();\n+ this.geometry.move(res * (pixel.x - lastPixel.x),\n+ res * (lastPixel.y - pixel.y));\n+ this.layer.drawFeature(this);\n+ return lastPixel;\n+ },\n+\n+ /**\n+ * Method: toState\n+ * Sets the new state\n+ *\n+ * Parameters:\n+ * state - {String} \n+ */\n+ toState: function(state) {\n+ if (state == OpenLayers.State.UPDATE) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.DELETE:\n+ this.state = state;\n+ break;\n+ case OpenLayers.State.UPDATE:\n+ case OpenLayers.State.INSERT:\n+ break;\n+ }\n+ } else if (state == OpenLayers.State.INSERT) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ break;\n+ default:\n+ this.state = state;\n+ break;\n+ }\n+ } else if (state == OpenLayers.State.DELETE) {\n+ switch (this.state) {\n+ case OpenLayers.State.INSERT:\n+ // the feature should be destroyed\n+ break;\n+ case OpenLayers.State.DELETE:\n+ break;\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.UPDATE:\n+ this.state = state;\n+ break;\n+ }\n+ } else if (state == OpenLayers.State.UNKNOWN) {\n+ this.state = state;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Feature.Vector\"\n+});\n+\n+\n+/**\n+ * Constant: OpenLayers.Feature.Vector.style\n+ * OpenLayers features can have a number of style attributes. The 'default' \n+ * style will typically be used if no other style is specified. These\n+ * styles correspond for the most part, to the styling properties defined\n+ * by the SVG standard. \n+ * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties\n+ * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties\n+ *\n+ * Symbolizer properties:\n+ * fill - {Boolean} Set to false if no fill is desired.\n+ * fillColor - {String} Hex fill color. Default is \"#ee9900\".\n+ * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 \n+ * stroke - {Boolean} Set to false if no stroke is desired.\n+ * strokeColor - {String} Hex stroke color. Default is \"#ee9900\".\n+ * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.\n+ * strokeWidth - {Number} Pixel stroke width. Default is 1.\n+ * strokeLinecap - {String} Stroke cap type. Default is \"round\". [butt | round | square]\n+ * strokeDashstyle - {String} Stroke dash style. Default is \"solid\". [dot | dash | dashdot | longdash | longdashdot | solid]\n+ * graphic - {Boolean} Set to false if no graphic is desired.\n+ * pointRadius - {Number} Pixel point radius. Default is 6.\n+ * pointerEvents - {String} Default is \"visiblePainted\".\n+ * cursor - {String} Default is \"\".\n+ * externalGraphic - {String} Url to an external graphic that will be used for rendering points.\n+ * graphicWidth - {Number} Pixel width for sizing an external graphic.\n+ * graphicHeight - {Number} Pixel height for sizing an external graphic.\n+ * graphicOpacity - {Number} Opacity (0-1) for an external graphic.\n+ * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.\n+ * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.\n+ * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).\n+ * graphicZIndex - {Number} The integer z-index value to use in rendering.\n+ * graphicName - {String} Named graphic to use when rendering points. Supported values include \"circle\" (default),\n+ * \"square\", \"star\", \"x\", \"cross\", \"triangle\".\n+ * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead\n+ * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.\n+ * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.\n+ * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.\n+ * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.\n+ * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.\n+ * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.\n+ * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.\n+ * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either\n+ * fillText or mozDrawText to be available.\n+ * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string\n+ * composed of two characters. The first character is for the horizontal alignment, the second for the vertical\n+ * alignment. Valid values for horizontal alignment: \"l\"=left, \"c\"=center, \"r\"=right. Valid values for vertical\n+ * alignment: \"t\"=top, \"m\"=middle, \"b\"=bottom. Example values: \"lt\", \"cm\", \"rb\". Default is \"cm\".\n+ * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.\n+ * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.\n+ * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.\n+ * Default is false.\n+ * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.\n+ * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.\n+ * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.\n+ * fontColor - {String} The font color for the label, to be provided like CSS.\n+ * fontOpacity - {Number} Opacity (0-1) for the label\n+ * fontFamily - {String} The font family for the label, to be provided like in CSS.\n+ * fontSize - {String} The font size for the label, to be provided like in CSS.\n+ * fontStyle - {String} The font style for the label, to be provided like in CSS.\n+ * fontWeight - {String} The font weight for the label, to be provided like in CSS.\n+ * display - {String} Symbolizers will have no effect if display is set to \"none\". All other values have no effect.\n+ */\n+OpenLayers.Feature.Vector.style = {\n+ 'default': {\n+ fillColor: \"#ee9900\",\n+ fillOpacity: 0.4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"#ee9900\",\n+ strokeOpacity: 1,\n+ strokeWidth: 1,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+ },\n+ 'select': {\n+ fillColor: \"blue\",\n+ fillOpacity: 0.4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"blue\",\n+ strokeOpacity: 1,\n+ strokeWidth: 2,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"pointer\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+\n+ },\n+ 'temporary': {\n+ fillColor: \"#66cccc\",\n+ fillOpacity: 0.2,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: 0.8,\n+ strokeColor: \"#66cccc\",\n+ strokeOpacity: 1,\n+ strokeLinecap: \"round\",\n+ strokeWidth: 2,\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: 0.2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+\n+ },\n+ 'delete': {\n+ display: \"none\"\n+ }\n+};\n+/* ======================================================================\n+ OpenLayers/Style.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Style\n+ * This class represents a UserStyle obtained\n+ * from a SLD, containing styling rules.\n+ */\n+OpenLayers.Style = OpenLayers.Class({\n+\n+ /**\n+ * Property: id\n+ * {String} A unique id for this session.\n+ */\n+ id: null,\n+\n+ /**\n+ * APIProperty: name\n+ * {String}\n+ */\n+ name: null,\n+\n+ /**\n+ * Property: title\n+ * {String} Title of this style (set if included in SLD)\n+ */\n+ title: null,\n+\n+ /**\n+ * Property: description\n+ * {String} Description of this style (set if abstract is included in SLD)\n+ */\n+ description: null,\n+\n+ /**\n+ * APIProperty: layerName\n+ * {<String>} name of the layer that this style belongs to, usually\n+ * according to the NamedLayer attribute of an SLD document.\n+ */\n+ layerName: null,\n+\n+ /**\n+ * APIProperty: isDefault\n+ * {Boolean}\n+ */\n+ isDefault: false,\n+\n+ /** \n+ * Property: rules \n+ * {Array(<OpenLayers.Rule>)}\n+ */\n+ rules: null,\n+\n+ /**\n+ * APIProperty: context\n+ * {Object} An optional object with properties that symbolizers' property\n+ * values should be evaluated against. If no context is specified,\n+ * feature.attributes will be used\n+ */\n+ context: null,\n+\n+ /**\n+ * Property: defaultStyle\n+ * {Object} hash of style properties to use as default for merging\n+ * rule-based style symbolizers onto. If no rules are defined,\n+ * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n+ * true, the defaultStyle will only be taken into account if there are\n+ * rules defined.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * Property: defaultsPerSymbolizer\n+ * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n+ * of every rule. Properties of the <defaultStyle> will also be used to set\n+ * missing symbolizer properties if the symbolizer has stroke, fill or\n+ * graphic set to true. Default is false.\n+ */\n+ defaultsPerSymbolizer: false,\n+\n+ /**\n+ * Property: propertyStyles\n+ * {Hash of Boolean} cache of style properties that need to be parsed for\n+ * propertyNames. Property names are keys, values won't be used.\n+ */\n+ propertyStyles: null,\n+\n+\n+ /** \n+ * Constructor: OpenLayers.Style\n+ * Creates a UserStyle.\n+ *\n+ * Parameters:\n+ * style - {Object} Optional hash of style properties that will be\n+ * used as default style for this style object. This style\n+ * applies if no rules are specified. Symbolizers defined in\n+ * rules will extend this default style.\n+ * options - {Object} An optional object with properties to set on the\n+ * style.\n+ *\n+ * Valid options:\n+ * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n+ * style.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Style>}\n+ */\n+ initialize: function(style, options) {\n+\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules);\n+ }\n+\n+ // use the default style from OpenLayers.Feature.Vector if no style\n+ // was given in the constructor\n+ this.setDefaultStyle(style ||\n+ OpenLayers.Feature.Vector.style[\"default\"]);\n+\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n+\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null;\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null;\n+ },\n+\n+ /**\n+ * Method: createSymbolizer\n+ * creates a style by applying all feature-dependent rules to the base\n+ * style.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n+ * \n+ * Returns:\n+ * {Object} symbolizer hash\n+ */\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n+ OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+\n+ var rules = this.rules;\n+\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ // does the rule apply?\n+ var applies = rule.evaluate(feature);\n+\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule);\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature);\n+ }\n+ }\n+ }\n+\n+ // if no other rules apply, apply the rules with else filters\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature);\n+ }\n+ }\n+\n+ // don't display if there were rules but none applied\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\";\n+ }\n+\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label);\n+ }\n+\n+ return style;\n+ },\n+\n+ /**\n+ * Method: applySymbolizer\n+ *\n+ * Parameters:\n+ * rule - {<OpenLayers.Rule>}\n+ * style - {Object}\n+ * feature - {<OpenLayer.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {Object} A style with new symbolizer applied.\n+ */\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ?\n+ this.getSymbolizerPrefix(feature.geometry) :\n+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ });\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ });\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ });\n+ }\n+ }\n+\n+ // merge the style with the current style\n+ return this.createLiterals(\n+ OpenLayers.Util.extend(style, symbolizer), feature);\n+ },\n+\n+ /**\n+ * Method: createLiterals\n+ * creates literals for all style properties that have an entry in\n+ * <this.propertyStyles>.\n+ * \n+ * Parameters:\n+ * style - {Object} style to create literals for. Will be modified\n+ * inline.\n+ * feature - {Object}\n+ * \n+ * Returns:\n+ * {Object} the modified style\n+ */\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n+ }\n+ return style;\n+ },\n+\n+ /**\n+ * Method: findPropertyStyles\n+ * Looks into all rules for this style and the defaultStyle to collect\n+ * all the style hash property names containing ${...} strings that have\n+ * to be replaced using the createLiteral method before returning them.\n+ * \n+ * Returns:\n+ * {Object} hash of property names that need createLiteral parsing. The\n+ * name of the property is the key, and the value is true;\n+ */\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+\n+ // check the default style\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+\n+ // walk through all rules to check for properties in their symbolizer\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n+ this.addPropertyStyles(propertyStyles, value);\n+ } else {\n+ // symbolizer is a hash of style properties\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break;\n+ }\n+ }\n+ }\n+ return propertyStyles;\n+ },\n+\n+ /**\n+ * Method: addPropertyStyles\n+ * \n+ * Parameters:\n+ * propertyStyles - {Object} hash to add new property styles to. Will be\n+ * modified inline\n+ * symbolizer - {Object} search this symbolizer for property styles\n+ * \n+ * Returns:\n+ * {Object} propertyStyles hash\n+ */\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" &&\n+ property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true;\n+ }\n+ }\n+ return propertyStyles;\n+ },\n+\n+ /**\n+ * APIMethod: addRules\n+ * Adds rules to this style.\n+ * \n+ * Parameters:\n+ * rules - {Array(<OpenLayers.Rule>)}\n+ */\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles();\n+ },\n+\n+ /**\n+ * APIMethod: setDefaultStyle\n+ * Sets the default style for this style object.\n+ * \n+ * Parameters:\n+ * style - {Object} Hash of style properties\n+ */\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles();\n+ },\n+\n+ /**\n+ * Method: getSymbolizerPrefix\n+ * Returns the correct symbolizer prefix according to the\n+ * geometry type of the passed geometry\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {String} key of the according symbolizer\n+ */\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i];\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this style.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Style>} Clone of this style.\n+ */\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ // clone rules\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone());\n+ }\n+ }\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ //clone default style\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+\n+\n+/**\n+ * Function: createLiteral\n+ * converts a style value holding a combination of PropertyName and Literal\n+ * into a Literal, taking the property values from the passed features.\n+ * \n+ * Parameters:\n+ * value - {String} value to parse. If this string contains a construct like\n+ * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n+ * will be replaced by the value of the \"bar\" attribute of the passed\n+ * feature.\n+ * context - {Object} context to take attribute values from\n+ * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n+ * <OpenLayers.String.format> for evaluating functions in the\n+ * context.\n+ * property - {String} optional, name of the property for which the literal is\n+ * being created for evaluating functions in the context.\n+ * \n+ * Returns:\n+ * {String} the parsed value. In the example of the value parameter above, the\n+ * result would be \"foo valueOfBar\", assuming that the passed feature has an\n+ * attribute named \"bar\" with the value \"valueOfBar\".\n+ */\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = (isNaN(value) || !value) ? value : parseFloat(value);\n+ }\n+ return value;\n+};\n+\n+/**\n+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n+ * {Array} prefixes of the sld symbolizers. These are the\n+ * same as the main geometry types\n+ */\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n+ 'Raster'\n+];\n+/* ======================================================================\n+ OpenLayers/StyleMap.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.StyleMap\n+ */\n+OpenLayers.StyleMap = OpenLayers.Class({\n+\n+ /**\n+ * Property: styles\n+ * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known\n+ * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n+ */\n+ styles: null,\n+\n+ /**\n+ * Property: extendDefault\n+ * {Boolean} if true, every render intent will extend the symbolizers\n+ * specified for the \"default\" intent at rendering time. Otherwise, every\n+ * rendering intent will be treated as a completely independent style.\n+ */\n+ extendDefault: true,\n+\n+ /**\n+ * Constructor: OpenLayers.StyleMap\n+ * \n+ * Parameters:\n+ * style - {Object} Optional. Either a style hash, or a style object, or\n+ * a hash of style objects (style hashes) keyed by rendering\n+ * intent. If just one style hash or style object is passed,\n+ * this will be used for all known render intents (default,\n+ * select, temporary)\n+ * options - {Object} optional hash of additional options for this\n+ * instance\n+ */\n+ initialize: function(style, options) {\n+ this.styles = {\n+ \"default\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"default\"]),\n+ \"select\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"select\"]),\n+ \"temporary\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"temporary\"]),\n+ \"delete\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"delete\"])\n+ };\n+\n+ // take whatever the user passed as style parameter and convert it\n+ // into parts of stylemap.\n+ if (style instanceof OpenLayers.Style) {\n+ // user passed a style object\n+ this.styles[\"default\"] = style;\n+ this.styles[\"select\"] = style;\n+ this.styles[\"temporary\"] = style;\n+ this.styles[\"delete\"] = style;\n+ } else if (typeof style == \"object\") {\n+ for (var key in style) {\n+ if (style[key] instanceof OpenLayers.Style) {\n+ // user passed a hash of style objects\n+ this.styles[key] = style[key];\n+ } else if (typeof style[key] == \"object\") {\n+ // user passsed a hash of style hashes\n+ this.styles[key] = new OpenLayers.Style(style[key]);\n+ } else {\n+ // user passed a style hash (i.e. symbolizer)\n+ this.styles[\"default\"] = new OpenLayers.Style(style);\n+ this.styles[\"select\"] = new OpenLayers.Style(style);\n+ this.styles[\"temporary\"] = new OpenLayers.Style(style);\n+ this.styles[\"delete\"] = new OpenLayers.Style(style);\n+ break;\n+ }\n+ }\n+ }\n+ OpenLayers.Util.extend(this, options);\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var key in this.styles) {\n+ this.styles[key].destroy();\n+ }\n+ this.styles = null;\n+ },\n+\n+ /**\n+ * Method: createSymbolizer\n+ * Creates the symbolizer for a feature for a render intent.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature>} The feature to evaluate the rules\n+ * of the intended style against.\n+ * intent - {String} The intent determines the symbolizer that will be\n+ * used to draw the feature. Well known intents are \"default\"\n+ * (for just drawing the features), \"select\" (for selected\n+ * features) and \"temporary\" (for drawing features).\n+ * \n+ * Returns:\n+ * {Object} symbolizer hash\n+ */\n+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector();\n+ }\n+ if (!this.styles[intent]) {\n+ intent = \"default\";\n+ }\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n+ }\n+ return OpenLayers.Util.extend(defaultSymbolizer,\n+ this.styles[intent].createSymbolizer(feature));\n+ },\n+\n+ /**\n+ * Method: addUniqueValueRules\n+ * Convenience method to create comparison rules for unique values of a\n+ * property. The rules will be added to the style object for a specified\n+ * rendering intent. This method is a shortcut for creating something like\n+ * the \"unique value legends\" familiar from well known desktop GIS systems\n+ * \n+ * Parameters:\n+ * renderIntent - {String} rendering intent to add the rules to\n+ * property - {String} values of feature attributes to create the\n+ * rules for\n+ * symbolizers - {Object} Hash of symbolizers, keyed by the desired\n+ * property values \n+ * context - {Object} An optional object with properties that\n+ * symbolizers' property values should be evaluated\n+ * against. If no context is specified, feature.attributes\n+ * will be used\n+ */\n+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n+ var rules = [];\n+ for (var value in symbolizers) {\n+ rules.push(new OpenLayers.Rule({\n+ symbolizer: symbolizers[value],\n+ context: context,\n+ filter: new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ property: property,\n+ value: value\n+ })\n+ }));\n+ }\n+ this.styles[renderIntent].addRules(rules);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.StyleMap\"\n+});\n+/* ======================================================================\n+ OpenLayers/Util/vendorPrefix.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/SingleFile.js\n+ */\n+\n+OpenLayers.Util = OpenLayers.Util || {};\n+/**\n+ * Namespace: OpenLayers.Util.vendorPrefix\n+ * A collection of utility functions to detect vendor prefixed features\n+ */\n+OpenLayers.Util.vendorPrefix = (function() {\n+ \"use strict\";\n+\n+ var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n+ divStyle = document.createElement(\"div\").style,\n+ cssCache = {},\n+ jsCache = {};\n+\n+\n+ /**\n+ * Function: domToCss\n+ * Converts a upper camel case DOM style property name to a CSS property\n+ * i.e. transformOrigin -> transform-origin\n+ * or WebkitTransformOrigin -> -webkit-transform-origin\n+ *\n+ * Parameters:\n+ * prefixedDom - {String} The property to convert\n+ *\n+ * Returns:\n+ * {String} The CSS property\n+ */\n+ function domToCss(prefixedDom) {\n+ if (!prefixedDom) {\n+ return null;\n+ }\n+ return prefixedDom.\n+ replace(/([A-Z])/g, function(c) {\n+ return \"-\" + c.toLowerCase();\n+ }).\n+ replace(/^ms-/, \"-ms-\");\n+ }\n+\n+ /**\n+ * APIMethod: css\n+ * Detect which property is used for a CSS property\n+ *\n+ * Parameters:\n+ * property - {String} The standard (unprefixed) CSS property name\n+ *\n+ * Returns:\n+ * {String} The standard CSS property, prefixed property or null if not\n+ * supported\n+ */\n+ function css(property) {\n+ if (cssCache[property] === undefined) {\n+ var domProperty = property.\n+ replace(/(-[\\s\\S])/g, function(c) {\n+ return c.charAt(1).toUpperCase();\n+ });\n+ var prefixedDom = style(domProperty);\n+ cssCache[property] = domToCss(prefixedDom);\n+ }\n+ return cssCache[property];\n+ }\n+\n+ /**\n+ * APIMethod: js\n+ * Detect which property is used for a JS property/method\n+ *\n+ * Parameters:\n+ * obj - {Object} The object to test on\n+ * property - {String} The standard (unprefixed) JS property name\n+ *\n+ * Returns:\n+ * {String} The standard JS property, prefixed property or null if not\n+ * supported\n+ */\n+ function js(obj, property) {\n+ if (jsCache[property] === undefined) {\n+ var tmpProp,\n+ i = 0,\n+ l = VENDOR_PREFIXES.length,\n+ prefix,\n+ isStyleObj = (typeof obj.cssText !== \"undefined\");\n+\n+ jsCache[property] = null;\n+ for (; i < l; i++) {\n+ prefix = VENDOR_PREFIXES[i];\n+ if (prefix) {\n+ if (!isStyleObj) {\n+ // js prefix should be lower-case, while style\n+ // properties have upper case on first character\n+ prefix = prefix.toLowerCase();\n+ }\n+ tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);\n+ } else {\n+ tmpProp = property;\n+ }\n+\n+ if (obj[tmpProp] !== undefined) {\n+ jsCache[property] = tmpProp;\n+ break;\n+ }\n+ }\n+ }\n+ return jsCache[property];\n+ }\n+\n+ /**\n+ * APIMethod: style\n+ * Detect which property is used for a DOM style property\n+ *\n+ * Parameters:\n+ * property - {String} The standard (unprefixed) style property name\n+ *\n+ * Returns:\n+ * {String} The standard style property, prefixed property or null if not\n+ * supported\n+ */\n+ function style(property) {\n+ return js(divStyle, property);\n+ }\n+\n+ return {\n+ css: css,\n+ js: js,\n+ style: style,\n+\n+ // used for testing\n+ cssCache: cssCache,\n+ jsCache: jsCache\n+ };\n+}());\n+/* ======================================================================\n+ OpenLayers/Animation.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/SingleFile.js\n+ * @requires OpenLayers/Util/vendorPrefix.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Animation\n+ * A collection of utility functions for executing methods that repaint a \n+ * portion of the browser window. These methods take advantage of the\n+ * browser's scheduled repaints where requestAnimationFrame is available.\n+ */\n+OpenLayers.Animation = (function(window) {\n+\n+ /**\n+ * Property: isNative\n+ * {Boolean} true if a native requestAnimationFrame function is available\n+ */\n+ var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n+ var isNative = !!(requestAnimationFrame);\n+\n+ /**\n+ * Function: requestFrame\n+ * Schedule a function to be called at the next available animation frame.\n+ * Uses the native method where available. Where requestAnimationFrame is\n+ * not available, setTimeout will be called with a 16ms delay.\n+ *\n+ * Parameters:\n+ * callback - {Function} The function to be called at the next animation frame.\n+ * element - {DOMElement} Optional element that visually bounds the animation.\n+ */\n+ var requestFrame = (function() {\n+ var request = window[requestAnimationFrame] ||\n+ function(callback, element) {\n+ window.setTimeout(callback, 16);\n+ };\n+ // bind to window to avoid illegal invocation of native function\n+ return function(callback, element) {\n+ request.apply(window, [callback, element]);\n+ };\n+ })();\n+\n+ // private variables for animation loops\n+ var counter = 0;\n+ var loops = {};\n+\n+ /**\n+ * Function: start\n+ * Executes a method with <requestFrame> in series for some \n+ * duration.\n+ *\n+ * Parameters:\n+ * callback - {Function} The function to be called at the next animation frame.\n+ * duration - {Number} Optional duration for the loop. If not provided, the\n+ * animation loop will execute indefinitely.\n+ * element - {DOMElement} Optional element that visually bounds the animation.\n+ *\n+ * Returns:\n+ * {Number} Identifier for the animation loop. Used to stop animations with\n+ * <stop>.\n+ */\n+ function start(callback, duration, element) {\n+ duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n+ var id = ++counter;\n+ var start = +new Date;\n+ loops[id] = function() {\n+ if (loops[id] && +new Date - start <= duration) {\n+ callback();\n+ if (loops[id]) {\n+ requestFrame(loops[id], element);\n+ }\n+ } else {\n+ delete loops[id];\n+ }\n+ };\n+ requestFrame(loops[id], element);\n+ return id;\n+ }\n+\n+ /**\n+ * Function: stop\n+ * Terminates an animation loop started with <start>.\n+ *\n+ * Parameters:\n+ * id - {Number} Identifier returned from <start>.\n+ */\n+ function stop(id) {\n+ delete loops[id];\n+ }\n+\n+ return {\n+ isNative: isNative,\n+ requestFrame: requestFrame,\n+ start: start,\n+ stop: stop\n+ };\n+\n+})(window);\n+/* ======================================================================\n+ OpenLayers/Kinetic.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.js\n+ */\n+\n+OpenLayers.Kinetic = OpenLayers.Class({\n+\n+ /**\n+ * Property: threshold\n+ * In most cases changing the threshold isn't needed.\n+ * In px/ms, default to 0.\n+ */\n+ threshold: 0,\n+\n+ /**\n+ * Property: deceleration\n+ * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n+ */\n+ deceleration: 0.0035,\n+\n+ /**\n+ * Property: nbPoints\n+ * {Integer} the number of points we use to calculate the kinetic\n+ * initial values.\n+ */\n+ nbPoints: 100,\n+\n+ /**\n+ * Property: delay\n+ * {Float} time to consider to calculate the kinetic initial values.\n+ * In ms, default to 200.\n+ */\n+ delay: 200,\n+\n+ /**\n+ * Property: points\n+ * List of points use to calculate the kinetic initial values.\n+ */\n+ points: undefined,\n+\n+ /**\n+ * Property: timerId\n+ * ID of the timer.\n+ */\n+ timerId: undefined,\n+\n+ /**\n+ * Constructor: OpenLayers.Kinetic\n+ *\n+ * Parameters:\n+ * options - {Object}\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ },\n+\n+ /**\n+ * Method: begin\n+ * Begins the dragging.\n+ */\n+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = [];\n+ },\n+\n+ /**\n+ * Method: update\n+ * Updates during the dragging.\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The new position.\n+ */\n+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: new Date().getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop();\n+ }\n+ },\n+\n+ /**\n+ * Method: end\n+ * Ends the dragging, start the kinetic.\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The last position.\n+ *\n+ * Returns:\n+ * {Object} An object with two properties: \"speed\", and \"theta\". The\n+ * \"speed\" and \"theta\" values are to be passed to the move \n+ * function when starting the animation.\n+ */\n+ end: function(xy) {\n+ var last, now = new Date().getTime();\n+ for (var i = 0, l = this.points.length, point; i < l; i++) {\n+ point = this.points[i];\n+ if (now - point.tick > this.delay) {\n+ break;\n+ }\n+ last = point;\n+ }\n+ if (!last) {\n+ return;\n+ }\n+ var time = new Date().getTime() - last.tick;\n+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n+ Math.pow(xy.y - last.xy.y, 2));\n+ var speed = dist / time;\n+ if (speed == 0 || speed < this.threshold) {\n+ return;\n+ }\n+ var theta = Math.asin((xy.y - last.xy.y) / dist);\n+ if (last.xy.x <= xy.x) {\n+ theta = Math.PI - theta;\n+ }\n+ return {\n+ speed: speed,\n+ theta: theta\n+ };\n+ },\n+\n+ /**\n+ * Method: move\n+ * Launch the kinetic move pan.\n+ *\n+ * Parameters:\n+ * info - {Object} An object with two properties, \"speed\", and \"theta\".\n+ * These values are those returned from the \"end\" call.\n+ * callback - {Function} Function called on every step of the animation,\n+ * receives x, y (values to pan), end (is the last point).\n+ */\n+ move: function(info, callback) {\n+ var v0 = info.speed;\n+ var fx = Math.cos(info.theta);\n+ var fy = -Math.sin(info.theta);\n+\n+ var initialTime = new Date().getTime();\n+\n+ var lastX = 0;\n+ var lastY = 0;\n+\n+ var timerCallback = function() {\n+ if (this.timerId == null) {\n+ return;\n+ }\n+\n+ var t = new Date().getTime() - initialTime;\n+\n+ var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n+ var x = p * fx;\n+ var y = p * fy;\n+\n+ var args = {};\n+ args.end = false;\n+ var v = -this.deceleration * t + v0;\n+\n+ if (v <= 0) {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = null;\n+ args.end = true;\n+ }\n+\n+ args.x = x - lastX;\n+ args.y = y - lastY;\n+ lastX = x;\n+ lastY = y;\n+ callback(args.x, args.y, args.end);\n+ };\n+\n+ this.timerId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(timerCallback, this)\n+ );\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Kinetic\"\n+});\n+/* ======================================================================\n+ OpenLayers/Projection.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ */\n+\n+/**\n+ * Namespace: OpenLayers.Projection\n+ * Methods for coordinate transforms between coordinate systems. By default,\n+ * OpenLayers ships with the ability to transform coordinates between\n+ * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)\n+ * coordinate reference systems. See the <transform> method for details\n+ * on usage.\n+ *\n+ * Additional transforms may be added by using the <proj4js at http://proj4js.org/>\n+ * library. If the proj4js library is included, the <transform> method \n+ * will work between any two coordinate reference systems with proj4js \n+ * definitions.\n+ *\n+ * If the proj4js library is not included, or if you wish to allow transforms\n+ * between arbitrary coordinate reference systems, use the <addTransform>\n+ * method to register a custom transform method.\n+ */\n+OpenLayers.Projection = OpenLayers.Class({\n+\n+ /**\n+ * Property: proj\n+ * {Object} Proj4js.Proj instance.\n+ */\n+ proj: null,\n+\n+ /**\n+ * Property: projCode\n+ * {String}\n+ */\n+ projCode: null,\n+\n+ /**\n+ * Property: titleRegEx\n+ * {RegExp} regular expression to strip the title from a proj4js definition\n+ */\n+ titleRegEx: /\\+title=[^\\+]*/,\n+\n+ /**\n+ * Constructor: OpenLayers.Projection\n+ * This class offers several methods for interacting with a wrapped \n+ * pro4js projection object. \n+ *\n+ * Parameters:\n+ * projCode - {String} A string identifying the Well Known Identifier for\n+ * the projection.\n+ * options - {Object} An optional object to set additional properties\n+ * on the projection.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Projection>} A projection object.\n+ */\n+ initialize: function(projCode, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.projCode = projCode;\n+ if (typeof Proj4js == \"object\") {\n+ this.proj = new Proj4js.Proj(projCode);\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: getCode\n+ * Get the string SRS code.\n+ *\n+ * Returns:\n+ * {String} The SRS code.\n+ */\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode;\n+ },\n+\n+ /**\n+ * APIMethod: getUnits\n+ * Get the units string for the projection -- returns null if \n+ * proj4js is not available.\n+ *\n+ * Returns:\n+ * {String} The units abbreviation.\n+ */\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null;\n+ },\n+\n+ /**\n+ * Method: toString\n+ * Convert projection to string (getCode wrapper).\n+ *\n+ * Returns:\n+ * {String} The projection code.\n+ */\n+ toString: function() {\n+ return this.getCode();\n+ },\n+\n+ /**\n+ * Method: equals\n+ * Test equality of two projection instances. Determines equality based\n+ * soley on the projection code.\n+ *\n+ * Returns:\n+ * {Boolean} The two projections are equivalent.\n+ */\n+ equals: function(projection) {\n+ var p = projection,\n+ equals = false;\n+ if (p) {\n+ if (!(p instanceof OpenLayers.Projection)) {\n+ p = new OpenLayers.Projection(p);\n+ }\n+ if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n+ equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n+ p.proj.defData.replace(this.titleRegEx, \"\");\n+ } else if (p.getCode) {\n+ var source = this.getCode(),\n+ target = p.getCode();\n+ equals = source == target ||\n+ !!OpenLayers.Projection.transforms[source] &&\n+ OpenLayers.Projection.transforms[source][target] ===\n+ OpenLayers.Projection.nullTransform;\n+ }\n+ }\n+ return equals;\n+ },\n+\n+ /* Method: destroy\n+ * Destroy projection object.\n+ */\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Projection\"\n+});\n+\n+/**\n+ * Property: transforms\n+ * {Object} Transforms is an object, with from properties, each of which may\n+ * have a to property. This allows you to define projections without \n+ * requiring support for proj4js to be included.\n+ *\n+ * This object has keys which correspond to a 'source' projection object. The\n+ * keys should be strings, corresponding to the projection.getCode() value.\n+ * Each source projection object should have a set of destination projection\n+ * keys included in the object. \n+ * \n+ * Each value in the destination object should be a transformation function,\n+ * where the function is expected to be passed an object with a .x and a .y\n+ * property. The function should return the object, with the .x and .y\n+ * transformed according to the transformation function.\n+ *\n+ * Note - Properties on this object should not be set directly. To add a\n+ * transform method to this object, use the <addTransform> method. For an\n+ * example of usage, see the OpenLayers.Layer.SphericalMercator file.\n+ */\n+OpenLayers.Projection.transforms = {};\n+\n+/**\n+ * APIProperty: defaults\n+ * {Object} Defaults for the SRS codes known to OpenLayers (currently\n+ * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,\n+ * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,\n+ * maxExtent (the validity extent for the SRS) and yx (true if this SRS is\n+ * known to have a reverse axis order).\n+ */\n+OpenLayers.Projection.defaults = {\n+ \"EPSG:4326\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90],\n+ yx: true\n+ },\n+ \"CRS:84\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90]\n+ },\n+ \"EPSG:900913\": {\n+ units: \"m\",\n+ maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n+ }\n+};\n+\n+/**\n+ * APIMethod: addTransform\n+ * Set a custom transform method between two projections. Use this method in\n+ * cases where the proj4js lib is not available or where custom projections\n+ * need to be handled.\n+ *\n+ * Parameters:\n+ * from - {String} The code for the source projection\n+ * to - {String} the code for the destination projection\n+ * method - {Function} A function that takes a point as an argument and\n+ * transforms that point from the source to the destination projection\n+ * in place. The original point should be modified.\n+ */\n+OpenLayers.Projection.addTransform = function(from, to, method) {\n+ if (method === OpenLayers.Projection.nullTransform) {\n+ var defaults = OpenLayers.Projection.defaults[from];\n+ if (defaults && !OpenLayers.Projection.defaults[to]) {\n+ OpenLayers.Projection.defaults[to] = defaults;\n+ }\n+ }\n+ if (!OpenLayers.Projection.transforms[from]) {\n+ OpenLayers.Projection.transforms[from] = {};\n+ }\n+ OpenLayers.Projection.transforms[from][to] = method;\n+};\n+\n+/**\n+ * APIMethod: transform\n+ * Transform a point coordinate from one projection to another. Note that\n+ * the input point is transformed in place.\n+ * \n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y\n+ * properties representing coordinates in those dimensions.\n+ * source - {OpenLayers.Projection} Source map coordinate system\n+ * dest - {OpenLayers.Projection} Destination map coordinate system\n+ *\n+ * Returns:\n+ * point - {object} A transformed coordinate. The original point is modified.\n+ */\n+OpenLayers.Projection.transform = function(point, source, dest) {\n+ if (source && dest) {\n+ if (!(source instanceof OpenLayers.Projection)) {\n+ source = new OpenLayers.Projection(source);\n+ }\n+ if (!(dest instanceof OpenLayers.Projection)) {\n+ dest = new OpenLayers.Projection(dest);\n+ }\n+ if (source.proj && dest.proj) {\n+ point = Proj4js.transform(source.proj, dest.proj, point);\n+ } else {\n+ var sourceCode = source.getCode();\n+ var destCode = dest.getCode();\n+ var transforms = OpenLayers.Projection.transforms;\n+ if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n+ transforms[sourceCode][destCode](point);\n+ }\n+ }\n+ }\n+ return point;\n+};\n+\n+/**\n+ * APIFunction: nullTransform\n+ * A null transformation - useful for defining projection aliases when\n+ * proj4js is not available:\n+ *\n+ * (code)\n+ * OpenLayers.Projection.addTransform(\"EPSG:3857\", \"EPSG:900913\",\n+ * OpenLayers.Projection.nullTransform);\n+ * OpenLayers.Projection.addTransform(\"EPSG:900913\", \"EPSG:3857\",\n+ * OpenLayers.Projection.nullTransform);\n+ * (end)\n+ */\n+OpenLayers.Projection.nullTransform = function(point) {\n+ return point;\n+};\n+\n+/**\n+ * Note: Transforms for web mercator <-> geographic\n+ * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.\n+ * OpenLayers originally started referring to EPSG:900913 as web mercator.\n+ * The EPSG has declared EPSG:3857 to be web mercator.\n+ * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as\n+ * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.\n+ * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and\n+ * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis\n+ * order for EPSG:4326. \n+ */\n+(function() {\n+\n+ var pole = 20037508.34;\n+\n+ function inverseMercator(xy) {\n+ xy.x = 180 * xy.x / pole;\n+ xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);\n+ return xy;\n+ }\n+\n+ function forwardMercator(xy) {\n+ xy.x = xy.x * pole / 180;\n+ var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n+ xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n+ return xy;\n+ }\n+\n+ function map(base, codes) {\n+ var add = OpenLayers.Projection.addTransform;\n+ var same = OpenLayers.Projection.nullTransform;\n+ var i, len, code, other, j;\n+ for (i = 0, len = codes.length; i < len; ++i) {\n+ code = codes[i];\n+ add(base, code, forwardMercator);\n+ add(code, base, inverseMercator);\n+ for (j = i + 1; j < len; ++j) {\n+ other = codes[j];\n+ add(code, other, same);\n+ add(other, code, same);\n+ }\n+ }\n+ }\n+\n+ // list of equivalent codes for web mercator\n+ var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n+ geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n+ i;\n+ for (i = mercator.length - 1; i >= 0; --i) {\n+ map(mercator[i], geographic);\n+ }\n+ for (i = geographic.length - 1; i >= 0; --i) {\n+ map(geographic[i], mercator);\n+ }\n+\n+})();\n+/* ======================================================================\n+ OpenLayers/Rule.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Rule\n+ * This class represents an SLD Rule, as being used for rule-based SLD styling.\n+ */\n+OpenLayers.Rule = OpenLayers.Class({\n+\n+ /**\n+ * Property: id\n+ * {String} A unique id for this session.\n+ */\n+ id: null,\n+\n+ /**\n+ * APIProperty: name\n+ * {String} name of this rule\n+ */\n+ name: null,\n+\n+ /**\n+ * Property: title\n+ * {String} Title of this rule (set if included in SLD)\n+ */\n+ title: null,\n+\n+ /**\n+ * Property: description\n+ * {String} Description of this rule (set if abstract is included in SLD)\n+ */\n+ description: null,\n+\n+ /**\n+ * Property: context\n+ * {Object} An optional object with properties that the rule should be\n+ * evaluated against. If no context is specified, feature.attributes will\n+ * be used.\n+ */\n+ context: null,\n+\n+ /**\n+ * Property: filter\n+ * {<OpenLayers.Filter>} Optional filter for the rule.\n+ */\n+ filter: null,\n+\n+ /**\n+ * Property: elseFilter\n+ * {Boolean} Determines whether this rule is only to be applied only if\n+ * no other rules match (ElseFilter according to the SLD specification). \n+ * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n+ * false, the rule will always apply. For subclasses, the else property is \n+ * ignored.\n+ */\n+ elseFilter: false,\n+\n+ /**\n+ * Property: symbolizer\n+ * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n+ * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n+ * latter if useful if it is required to style e.g. vertices of a line\n+ * with a point symbolizer. Note, however, that this is not implemented\n+ * yet in OpenLayers, but it is the way how symbolizers are defined in\n+ * SLD.\n+ */\n+ symbolizer: null,\n+\n+ /**\n+ * Property: symbolizers\n+ * {Array} Collection of symbolizers associated with this rule. If \n+ * provided at construction, the symbolizers array has precedence\n+ * over the deprecated symbolizer property. Note that multiple \n+ * symbolizers are not currently supported by the vector renderers.\n+ * Rules with multiple symbolizers are currently only useful for\n+ * maintaining elements in an SLD document.\n+ */\n+ symbolizers: null,\n+\n+ /**\n+ * APIProperty: minScaleDenominator\n+ * {Number} or {String} minimum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n+ */\n+ minScaleDenominator: null,\n+\n+ /**\n+ * APIProperty: maxScaleDenominator\n+ * {Number} or {String} maximum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n+ */\n+ maxScaleDenominator: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Rule\n+ * Creates a Rule.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n+ * \n+ * Returns:\n+ * {<OpenLayers.Rule>}\n+ */\n+ initialize: function(options) {\n+ this.symbolizer = {};\n+ OpenLayers.Util.extend(this, options);\n+ if (this.symbolizers) {\n+ delete this.symbolizer;\n+ }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n+\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ for (var i in this.symbolizer) {\n+ this.symbolizer[i] = null;\n+ }\n+ this.symbolizer = null;\n+ delete this.symbolizers;\n+ },\n+\n+ /**\n+ * APIMethod: evaluate\n+ * evaluates this rule for a specific feature\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n+ * \n+ * Returns:\n+ * {Boolean} true if the rule applies, false if it does not.\n+ * This rule is the default rule and always returns true.\n+ */\n+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n+\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale();\n+ }\n+\n+ // check if within minScale/maxScale bounds\n+ if (this.minScaleDenominator) {\n+ applies = scale >= OpenLayers.Style.createLiteral(\n+ this.minScaleDenominator, context);\n+ }\n+ if (applies && this.maxScaleDenominator) {\n+ applies = scale < OpenLayers.Style.createLiteral(\n+ this.maxScaleDenominator, context);\n+ }\n+\n+ // check if optional filter applies\n+ if (applies && this.filter) {\n+ // feature id filters get the feature, others get the context\n+ if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n+ applies = this.filter.evaluate(feature);\n+ } else {\n+ applies = this.filter.evaluate(context);\n+ }\n+ }\n+\n+ return applies;\n+ },\n+\n+ /**\n+ * Method: getContext\n+ * Gets the context for evaluating this rule\n+ * \n+ * Paramters:\n+ * feature - {<OpenLayers.Feature>} feature to take the context from if\n+ * none is specified.\n+ */\n+ getContext: function(feature) {\n+ var context = this.context;\n+ if (!context) {\n+ context = feature.attributes || feature.data;\n+ }\n+ if (typeof this.context == \"function\") {\n+ context = this.context(feature);\n+ }\n+ return context;\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this rule.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Rule>} Clone of this rule.\n+ */\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.symbolizers) {\n+ // clone symbolizers\n+ var len = this.symbolizers.length;\n+ options.symbolizers = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ options.symbolizers[i] = this.symbolizers[i].clone();\n+ }\n+ } else {\n+ // clone symbolizer\n+ options.symbolizer = {};\n+ var value, type;\n+ for (var key in this.symbolizer) {\n+ value = this.symbolizer[key];\n+ type = typeof value;\n+ if (type === \"object\") {\n+ options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n+ } else if (type === \"string\") {\n+ options.symbolizer[key] = value;\n+ }\n+ }\n+ }\n+ // clone filter\n+ options.filter = this.filter && this.filter.clone();\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ return new OpenLayers.Rule(options);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Rule\"\n+});\n+/* ======================================================================\n OpenLayers/Events.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -6402,342 +8331,14 @@\n if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n return -c / 2 * ((--t) * (t - 2) - 1) + b;\n },\n \n CLASS_NAME: \"OpenLayers.Easing.Quad\"\n };\n /* ======================================================================\n- OpenLayers/Projection.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Namespace: OpenLayers.Projection\n- * Methods for coordinate transforms between coordinate systems. By default,\n- * OpenLayers ships with the ability to transform coordinates between\n- * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)\n- * coordinate reference systems. See the <transform> method for details\n- * on usage.\n- *\n- * Additional transforms may be added by using the <proj4js at http://proj4js.org/>\n- * library. If the proj4js library is included, the <transform> method \n- * will work between any two coordinate reference systems with proj4js \n- * definitions.\n- *\n- * If the proj4js library is not included, or if you wish to allow transforms\n- * between arbitrary coordinate reference systems, use the <addTransform>\n- * method to register a custom transform method.\n- */\n-OpenLayers.Projection = OpenLayers.Class({\n-\n- /**\n- * Property: proj\n- * {Object} Proj4js.Proj instance.\n- */\n- proj: null,\n-\n- /**\n- * Property: projCode\n- * {String}\n- */\n- projCode: null,\n-\n- /**\n- * Property: titleRegEx\n- * {RegExp} regular expression to strip the title from a proj4js definition\n- */\n- titleRegEx: /\\+title=[^\\+]*/,\n-\n- /**\n- * Constructor: OpenLayers.Projection\n- * This class offers several methods for interacting with a wrapped \n- * pro4js projection object. \n- *\n- * Parameters:\n- * projCode - {String} A string identifying the Well Known Identifier for\n- * the projection.\n- * options - {Object} An optional object to set additional properties\n- * on the projection.\n- *\n- * Returns:\n- * {<OpenLayers.Projection>} A projection object.\n- */\n- initialize: function(projCode, options) {\n- OpenLayers.Util.extend(this, options);\n- this.projCode = projCode;\n- if (typeof Proj4js == \"object\") {\n- this.proj = new Proj4js.Proj(projCode);\n- }\n- },\n-\n- /**\n- * APIMethod: getCode\n- * Get the string SRS code.\n- *\n- * Returns:\n- * {String} The SRS code.\n- */\n- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode;\n- },\n-\n- /**\n- * APIMethod: getUnits\n- * Get the units string for the projection -- returns null if \n- * proj4js is not available.\n- *\n- * Returns:\n- * {String} The units abbreviation.\n- */\n- getUnits: function() {\n- return this.proj ? this.proj.units : null;\n- },\n-\n- /**\n- * Method: toString\n- * Convert projection to string (getCode wrapper).\n- *\n- * Returns:\n- * {String} The projection code.\n- */\n- toString: function() {\n- return this.getCode();\n- },\n-\n- /**\n- * Method: equals\n- * Test equality of two projection instances. Determines equality based\n- * soley on the projection code.\n- *\n- * Returns:\n- * {Boolean} The two projections are equivalent.\n- */\n- equals: function(projection) {\n- var p = projection,\n- equals = false;\n- if (p) {\n- if (!(p instanceof OpenLayers.Projection)) {\n- p = new OpenLayers.Projection(p);\n- }\n- if ((typeof Proj4js == \"object\") && this.proj.defData && p.proj.defData) {\n- equals = this.proj.defData.replace(this.titleRegEx, \"\") ==\n- p.proj.defData.replace(this.titleRegEx, \"\");\n- } else if (p.getCode) {\n- var source = this.getCode(),\n- target = p.getCode();\n- equals = source == target ||\n- !!OpenLayers.Projection.transforms[source] &&\n- OpenLayers.Projection.transforms[source][target] ===\n- OpenLayers.Projection.nullTransform;\n- }\n- }\n- return equals;\n- },\n-\n- /* Method: destroy\n- * Destroy projection object.\n- */\n- destroy: function() {\n- delete this.proj;\n- delete this.projCode;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Projection\"\n-});\n-\n-/**\n- * Property: transforms\n- * {Object} Transforms is an object, with from properties, each of which may\n- * have a to property. This allows you to define projections without \n- * requiring support for proj4js to be included.\n- *\n- * This object has keys which correspond to a 'source' projection object. The\n- * keys should be strings, corresponding to the projection.getCode() value.\n- * Each source projection object should have a set of destination projection\n- * keys included in the object. \n- * \n- * Each value in the destination object should be a transformation function,\n- * where the function is expected to be passed an object with a .x and a .y\n- * property. The function should return the object, with the .x and .y\n- * transformed according to the transformation function.\n- *\n- * Note - Properties on this object should not be set directly. To add a\n- * transform method to this object, use the <addTransform> method. For an\n- * example of usage, see the OpenLayers.Layer.SphericalMercator file.\n- */\n-OpenLayers.Projection.transforms = {};\n-\n-/**\n- * APIProperty: defaults\n- * {Object} Defaults for the SRS codes known to OpenLayers (currently\n- * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,\n- * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,\n- * maxExtent (the validity extent for the SRS) and yx (true if this SRS is\n- * known to have a reverse axis order).\n- */\n-OpenLayers.Projection.defaults = {\n- \"EPSG:4326\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90],\n- yx: true\n- },\n- \"CRS:84\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90]\n- },\n- \"EPSG:900913\": {\n- units: \"m\",\n- maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n- }\n-};\n-\n-/**\n- * APIMethod: addTransform\n- * Set a custom transform method between two projections. Use this method in\n- * cases where the proj4js lib is not available or where custom projections\n- * need to be handled.\n- *\n- * Parameters:\n- * from - {String} The code for the source projection\n- * to - {String} the code for the destination projection\n- * method - {Function} A function that takes a point as an argument and\n- * transforms that point from the source to the destination projection\n- * in place. The original point should be modified.\n- */\n-OpenLayers.Projection.addTransform = function(from, to, method) {\n- if (method === OpenLayers.Projection.nullTransform) {\n- var defaults = OpenLayers.Projection.defaults[from];\n- if (defaults && !OpenLayers.Projection.defaults[to]) {\n- OpenLayers.Projection.defaults[to] = defaults;\n- }\n- }\n- if (!OpenLayers.Projection.transforms[from]) {\n- OpenLayers.Projection.transforms[from] = {};\n- }\n- OpenLayers.Projection.transforms[from][to] = method;\n-};\n-\n-/**\n- * APIMethod: transform\n- * Transform a point coordinate from one projection to another. Note that\n- * the input point is transformed in place.\n- * \n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y\n- * properties representing coordinates in those dimensions.\n- * source - {OpenLayers.Projection} Source map coordinate system\n- * dest - {OpenLayers.Projection} Destination map coordinate system\n- *\n- * Returns:\n- * point - {object} A transformed coordinate. The original point is modified.\n- */\n-OpenLayers.Projection.transform = function(point, source, dest) {\n- if (source && dest) {\n- if (!(source instanceof OpenLayers.Projection)) {\n- source = new OpenLayers.Projection(source);\n- }\n- if (!(dest instanceof OpenLayers.Projection)) {\n- dest = new OpenLayers.Projection(dest);\n- }\n- if (source.proj && dest.proj) {\n- point = Proj4js.transform(source.proj, dest.proj, point);\n- } else {\n- var sourceCode = source.getCode();\n- var destCode = dest.getCode();\n- var transforms = OpenLayers.Projection.transforms;\n- if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n- transforms[sourceCode][destCode](point);\n- }\n- }\n- }\n- return point;\n-};\n-\n-/**\n- * APIFunction: nullTransform\n- * A null transformation - useful for defining projection aliases when\n- * proj4js is not available:\n- *\n- * (code)\n- * OpenLayers.Projection.addTransform(\"EPSG:3857\", \"EPSG:900913\",\n- * OpenLayers.Projection.nullTransform);\n- * OpenLayers.Projection.addTransform(\"EPSG:900913\", \"EPSG:3857\",\n- * OpenLayers.Projection.nullTransform);\n- * (end)\n- */\n-OpenLayers.Projection.nullTransform = function(point) {\n- return point;\n-};\n-\n-/**\n- * Note: Transforms for web mercator <-> geographic\n- * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.\n- * OpenLayers originally started referring to EPSG:900913 as web mercator.\n- * The EPSG has declared EPSG:3857 to be web mercator.\n- * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as\n- * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.\n- * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and\n- * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis\n- * order for EPSG:4326. \n- */\n-(function() {\n-\n- var pole = 20037508.34;\n-\n- function inverseMercator(xy) {\n- xy.x = 180 * xy.x / pole;\n- xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);\n- return xy;\n- }\n-\n- function forwardMercator(xy) {\n- xy.x = xy.x * pole / 180;\n- var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n- xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n- return xy;\n- }\n-\n- function map(base, codes) {\n- var add = OpenLayers.Projection.addTransform;\n- var same = OpenLayers.Projection.nullTransform;\n- var i, len, code, other, j;\n- for (i = 0, len = codes.length; i < len; ++i) {\n- code = codes[i];\n- add(base, code, forwardMercator);\n- add(code, base, inverseMercator);\n- for (j = i + 1; j < len; ++j) {\n- other = codes[j];\n- add(code, other, same);\n- add(other, code, same);\n- }\n- }\n- }\n-\n- // list of equivalent codes for web mercator\n- var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n- geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n- i;\n- for (i = mercator.length - 1; i >= 0; --i) {\n- map(mercator[i], geographic);\n- }\n- for (i = geographic.length - 1; i >= 0; --i) {\n- map(geographic[i], mercator);\n- }\n-\n-})();\n-/* ======================================================================\n OpenLayers/Map.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -9645,11048 +11246,14143 @@\n OpenLayers.Map.TILE_WIDTH = 256;\n /**\n * Constant: TILE_HEIGHT\n * {Integer} 256 Default tile height (unless otherwise specified)\n */\n OpenLayers.Map.TILE_HEIGHT = 256;\n /* ======================================================================\n- OpenLayers/Feature.js\n+ OpenLayers/Format.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n * @requires OpenLayers/BaseTypes/Class.js\n * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Feature\n- * Features are combinations of geography and attributes. The OpenLayers.Feature\n- * class specifically combines a marker and a lonlat.\n+ * Class: OpenLayers.Format\n+ * Base class for format reading/writing a variety of formats. Subclasses\n+ * of OpenLayers.Format are expected to have read and write methods.\n */\n-OpenLayers.Feature = OpenLayers.Class({\n+OpenLayers.Format = OpenLayers.Class({\n \n- /** \n- * Property: layer \n- * {<OpenLayers.Layer>} \n+ /**\n+ * Property: options\n+ * {Object} A reference to options passed to the constructor.\n */\n- layer: null,\n+ options: null,\n \n- /** \n- * Property: id \n- * {String} \n+ /**\n+ * APIProperty: externalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The externalProjection is the projection used by\n+ * the content which is passed into read or which comes out of write.\n+ * In order to reproject, a projection transformation function for the\n+ * specified projections must be available. This support may be \n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- id: null,\n+ externalProjection: null,\n \n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} \n+ /**\n+ * APIProperty: internalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The internalProjection is the projection used by\n+ * the geometries which are returned by read or which are passed into\n+ * write. In order to reproject, a projection transformation function\n+ * for the specified projections must be available. This support may be\n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- lonlat: null,\n+ internalProjection: null,\n \n- /** \n- * Property: data \n- * {Object} \n+ /**\n+ * APIProperty: data\n+ * {Object} When <keepData> is true, this is the parsed string sent to\n+ * <read>.\n */\n data: null,\n \n- /** \n- * Property: marker \n- * {<OpenLayers.Marker>} \n+ /**\n+ * APIProperty: keepData\n+ * {Object} Maintain a reference (<data>) to the most recently read data.\n+ * Default is false.\n */\n- marker: null,\n+ keepData: false,\n \n /**\n- * APIProperty: popupClass\n- * {<OpenLayers.Class>} The class which will be used to instantiate\n- * a new Popup. Default is <OpenLayers.Popup.Anchored>.\n+ * Constructor: OpenLayers.Format\n+ * Instances of this class are not useful. See one of the subclasses.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * format\n+ *\n+ * Valid options:\n+ * keepData - {Boolean} If true, upon <read>, the data property will be\n+ * set to the parsed object (e.g. the json or xml object).\n+ *\n+ * Returns:\n+ * An instance of OpenLayers.Format\n */\n- popupClass: null,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ },\n \n- /** \n- * Property: popup \n- * {<OpenLayers.Popup>} \n+ /**\n+ * APIMethod: destroy\n+ * Clean up.\n */\n- popup: null,\n+ destroy: function() {},\n \n- /** \n- * Constructor: OpenLayers.Feature\n- * Constructor for features.\n+ /**\n+ * Method: read\n+ * Read data from a string, and return an object whose type depends on the\n+ * subclass. \n+ * \n+ * Parameters:\n+ * data - {string} Data to read/parse.\n+ *\n+ * Returns:\n+ * Depends on the subclass\n+ */\n+ read: function(data) {\n+ throw new Error('Read not implemented.');\n+ },\n+\n+ /**\n+ * Method: write\n+ * Accept an object, and return a string. \n *\n * Parameters:\n- * layer - {<OpenLayers.Layer>} \n- * lonlat - {<OpenLayers.LonLat>} \n- * data - {Object} \n- * \n+ * object - {Object} Object to be serialized\n+ *\n * Returns:\n- * {<OpenLayers.Feature>}\n+ * {String} A string representation of the object.\n */\n- initialize: function(layer, lonlat, data) {\n- this.layer = layer;\n- this.lonlat = lonlat;\n- this.data = (data != null) ? data : {};\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ write: function(object) {\n+ throw new Error('Write not implemented.');\n },\n \n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n+ CLASS_NAME: \"OpenLayers.Format\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/JSON.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * Note:\n+ * This work draws heavily from the public domain JSON serializer/deserializer\n+ * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n+ * basic data prototypes.\n+ */\n+\n+/**\n+ * @requires OpenLayers/Format.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.JSON\n+ * A parser to read/write JSON safely. Create a new instance with the\n+ * <OpenLayers.Format.JSON> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * APIProperty: indent\n+ * {String} For \"pretty\" printing, the indent string will be used once for\n+ * each indentation level.\n */\n- destroy: function() {\n+ indent: \" \",\n \n- //remove the popup from the map\n- if ((this.layer != null) && (this.layer.map != null)) {\n- if (this.popup != null) {\n- this.layer.map.removePopup(this.popup);\n+ /**\n+ * APIProperty: space\n+ * {String} For \"pretty\" printing, the space string will be used after\n+ * the \":\" separating a name/value pair.\n+ */\n+ space: \" \",\n+\n+ /**\n+ * APIProperty: newline\n+ * {String} For \"pretty\" printing, the newline string will be used at the\n+ * end of each name/value pair or array item.\n+ */\n+ newline: \"\\n\",\n+\n+ /**\n+ * Property: level\n+ * {Integer} For \"pretty\" printing, this is incremented/decremented during\n+ * serialization.\n+ */\n+ level: 0,\n+\n+ /**\n+ * Property: pretty\n+ * {Boolean} Serialize with extra whitespace for structure. This is set\n+ * by the <write> method.\n+ */\n+ pretty: false,\n+\n+ /**\n+ * Property: nativeJSON\n+ * {Boolean} Does the browser support native json?\n+ */\n+ nativeJSON: (function() {\n+ return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n+ })(),\n+\n+ /**\n+ * Constructor: OpenLayers.Format.JSON\n+ * Create a new parser for JSON.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Deserialize a json string.\n+ *\n+ * Parameters:\n+ * json - {String} A JSON string\n+ * filter - {Function} A function which will be called for every key and\n+ * value at every level of the final result. Each value will be\n+ * replaced by the result of the filter function. This can be used to\n+ * reform generic objects into instances of classes, or to transform\n+ * date strings into Date objects.\n+ * \n+ * Returns:\n+ * {Object} An object, array, string, or number .\n+ */\n+ read: function(json, filter) {\n+ var object;\n+ if (this.nativeJSON) {\n+ object = JSON.parse(json, filter);\n+ } else try {\n+ /**\n+ * Parsing happens in three stages. In the first stage, we run the\n+ * text against a regular expression which looks for non-JSON\n+ * characters. We are especially concerned with '()' and 'new'\n+ * because they can cause invocation, and '=' because it can\n+ * cause mutation. But just to be safe, we will reject all\n+ * unexpected characters.\n+ */\n+ if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n+\n+ /**\n+ * In the second stage we use the eval function to compile the\n+ * text into a JavaScript structure. The '{' operator is\n+ * subject to a syntactic ambiguity in JavaScript - it can\n+ * begin a block or an object literal. We wrap the text in\n+ * parens to eliminate the ambiguity.\n+ */\n+ object = eval('(' + json + ')');\n+\n+ /**\n+ * In the optional third stage, we recursively walk the new\n+ * structure, passing each name/value pair to a filter\n+ * function for possible transformation.\n+ */\n+ if (typeof filter === 'function') {\n+ function walk(k, v) {\n+ if (v && typeof v === 'object') {\n+ for (var i in v) {\n+ if (v.hasOwnProperty(i)) {\n+ v[i] = walk(i, v[i]);\n+ }\n+ }\n+ }\n+ return filter(k, v);\n+ }\n+ object = walk('', object);\n+ }\n }\n- }\n- // remove the marker from the layer\n- if (this.layer != null && this.marker != null) {\n- this.layer.removeMarker(this.marker);\n+ } catch (e) {\n+ // Fall through if the regexp test fails.\n }\n \n- this.layer = null;\n- this.id = null;\n- this.lonlat = null;\n- this.data = null;\n- if (this.marker != null) {\n- this.destroyMarker(this.marker);\n- this.marker = null;\n- }\n- if (this.popup != null) {\n- this.destroyPopup(this.popup);\n- this.popup = null;\n+ if (this.keepData) {\n+ this.data = object;\n }\n+\n+ return object;\n },\n \n /**\n- * Method: onScreen\n- * \n+ * APIMethod: write\n+ * Serialize an object into a JSON string.\n+ *\n+ * Parameters:\n+ * value - {String} The object, array, string, number, boolean or date\n+ * to be serialized.\n+ * pretty - {Boolean} Structure the output with newlines and indentation.\n+ * Default is false.\n+ *\n * Returns:\n- * {Boolean} Whether or not the feature is currently visible on screen\n- * (based on its 'lonlat' property)\n+ * {String} The JSON string representation of the input value.\n */\n- onScreen: function() {\n-\n- var onScreen = false;\n- if ((this.layer != null) && (this.layer.map != null)) {\n- var screenBounds = this.layer.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat);\n+ write: function(value, pretty) {\n+ this.pretty = !!pretty;\n+ var json = null;\n+ var type = typeof value;\n+ if (this.serialize[type]) {\n+ try {\n+ json = (!this.pretty && this.nativeJSON) ?\n+ JSON.stringify(value) :\n+ this.serialize[type].apply(this, [value]);\n+ } catch (err) {\n+ OpenLayers.Console.error(\"Trouble serializing: \" + err);\n+ }\n }\n- return onScreen;\n+ return json;\n },\n \n-\n /**\n- * Method: createMarker\n- * Based on the data associated with the Feature, create and return a marker object.\n+ * Method: writeIndent\n+ * Output an indentation string depending on the indentation level.\n *\n- * Returns: \n- * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties\n- * set in this.data. If no 'lonlat' is set, returns null. If no\n- * 'icon' is set, OpenLayers.Marker() will load the default image.\n- * \n- * Note - this.marker is set to return value\n- * \n+ * Returns:\n+ * {String} An appropriate indentation string.\n */\n- createMarker: function() {\n-\n- if (this.lonlat != null) {\n- this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);\n+ writeIndent: function() {\n+ var pieces = [];\n+ if (this.pretty) {\n+ for (var i = 0; i < this.level; ++i) {\n+ pieces.push(this.indent);\n+ }\n }\n- return this.marker;\n+ return pieces.join('');\n },\n \n /**\n- * Method: destroyMarker\n- * Destroys marker.\n- * If user overrides the createMarker() function, s/he should be able\n- * to also specify an alternative function for destroying it\n+ * Method: writeNewline\n+ * Output a string representing a newline if in pretty printing mode.\n+ *\n+ * Returns:\n+ * {String} A string representing a new line.\n */\n- destroyMarker: function() {\n- this.marker.destroy();\n+ writeNewline: function() {\n+ return (this.pretty) ? this.newline : '';\n },\n \n /**\n- * Method: createPopup\n- * Creates a popup object created from the 'lonlat', 'popupSize',\n- * and 'popupContentHTML' properties set in this.data. It uses\n- * this.marker.icon as default anchor. \n- * \n- * If no 'lonlat' is set, returns null. \n- * If no this.marker has been created, no anchor is sent.\n+ * Method: writeSpace\n+ * Output a string representing a space if in pretty printing mode.\n *\n- * Note - the returned popup object is 'owned' by the feature, so you\n- * cannot use the popup's destroy method to discard the popup.\n- * Instead, you must use the feature's destroyPopup\n- * \n- * Note - this.popup is set to return value\n- * \n- * Parameters: \n- * closeBox - {Boolean} create popup with closebox or not\n- * \n * Returns:\n- * {<OpenLayers.Popup>} Returns the created popup, which is also set\n- * as 'popup' property of this feature. Will be of whatever type\n- * specified by this feature's 'popupClass' property, but must be\n- * of type <OpenLayers.Popup>.\n- * \n+ * {String} A space.\n */\n- createPopup: function(closeBox) {\n+ writeSpace: function() {\n+ return (this.pretty) ? this.space : '';\n+ },\n \n- if (this.lonlat != null) {\n- if (!this.popup) {\n- var anchor = (this.marker) ? this.marker.icon : null;\n- var popupClass = this.popupClass ?\n- this.popupClass : OpenLayers.Popup.Anchored;\n- this.popup = new popupClass(this.id + \"_popup\",\n- this.lonlat,\n- this.data.popupSize,\n- this.data.popupContentHTML,\n- anchor,\n- closeBox);\n+ /**\n+ * Property: serialize\n+ * Object with properties corresponding to the serializable data types.\n+ * Property values are functions that do the actual serializing.\n+ */\n+ serialize: {\n+ /**\n+ * Method: serialize.object\n+ * Transform an object into a JSON string.\n+ *\n+ * Parameters:\n+ * object - {Object} The object to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the object.\n+ */\n+ 'object': function(object) {\n+ // three special objects that we want to treat differently\n+ if (object == null) {\n+ return \"null\";\n }\n- if (this.data.overflow != null) {\n- this.popup.contentDiv.style.overflow = this.data.overflow;\n+ if (object.constructor == Date) {\n+ return this.serialize.date.apply(this, [object]);\n+ }\n+ if (object.constructor == Array) {\n+ return this.serialize.array.apply(this, [object]);\n }\n+ var pieces = ['{'];\n+ this.level += 1;\n+ var key, keyJSON, valueJSON;\n \n- this.popup.feature = this;\n- }\n- return this.popup;\n- },\n+ var addComma = false;\n+ for (key in object) {\n+ if (object.hasOwnProperty(key)) {\n+ // recursive calls need to allow for sub-classing\n+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [key, this.pretty]);\n+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [object[key], this.pretty]);\n+ if (keyJSON != null && valueJSON != null) {\n+ if (addComma) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(),\n+ keyJSON, ':', this.writeSpace(), valueJSON);\n+ addComma = true;\n+ }\n+ }\n+ }\n \n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), '}');\n+ return pieces.join('');\n+ },\n \n- /**\n- * Method: destroyPopup\n- * Destroys the popup created via createPopup.\n- *\n- * As with the marker, if user overrides the createPopup() function, s/he \n- * should also be able to override the destruction\n- */\n- destroyPopup: function() {\n- if (this.popup) {\n- this.popup.feature = null;\n- this.popup.destroy();\n- this.popup = null;\n+ /**\n+ * Method: serialize.array\n+ * Transform an array into a JSON string.\n+ *\n+ * Parameters:\n+ * array - {Array} The array to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the array.\n+ */\n+ 'array': function(array) {\n+ var json;\n+ var pieces = ['['];\n+ this.level += 1;\n+\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ // recursive calls need to allow for sub-classing\n+ json = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [array[i], this.pretty]);\n+ if (json != null) {\n+ if (i > 0) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), json);\n+ }\n+ }\n+\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), ']');\n+ return pieces.join('');\n+ },\n+\n+ /**\n+ * Method: serialize.string\n+ * Transform a string into a JSON string.\n+ *\n+ * Parameters:\n+ * string - {String} The string to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the string.\n+ */\n+ 'string': function(string) {\n+ // If the string contains no control characters, no quote characters, and no\n+ // backslash characters, then we can simply slap some quotes around it.\n+ // Otherwise we must also replace the offending characters with safe\n+ // sequences. \n+ var m = {\n+ '\\b': '\\\\b',\n+ '\\t': '\\\\t',\n+ '\\n': '\\\\n',\n+ '\\f': '\\\\f',\n+ '\\r': '\\\\r',\n+ '\"': '\\\\\"',\n+ '\\\\': '\\\\\\\\'\n+ };\n+ if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n+ return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n+ var c = m[b];\n+ if (c) {\n+ return c;\n+ }\n+ c = b.charCodeAt();\n+ return '\\\\u00' +\n+ Math.floor(c / 16).toString(16) +\n+ (c % 16).toString(16);\n+ }) + '\"';\n+ }\n+ return '\"' + string + '\"';\n+ },\n+\n+ /**\n+ * Method: serialize.number\n+ * Transform a number into a JSON string.\n+ *\n+ * Parameters:\n+ * number - {Number} The number to be serialized.\n+ *\n+ * Returns:\n+ * {String} A JSON string representing the number.\n+ */\n+ 'number': function(number) {\n+ return isFinite(number) ? String(number) : \"null\";\n+ },\n+\n+ /**\n+ * Method: serialize.boolean\n+ * Transform a boolean into a JSON string.\n+ *\n+ * Parameters:\n+ * bool - {Boolean} The boolean to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the boolean.\n+ */\n+ 'boolean': function(bool) {\n+ return String(bool);\n+ },\n+\n+ /**\n+ * Method: serialize.object\n+ * Transform a date into a JSON string.\n+ *\n+ * Parameters:\n+ * date - {Date} The date to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the date.\n+ */\n+ 'date': function(date) {\n+ function format(number) {\n+ // Format integers to have at least two digits.\n+ return (number < 10) ? '0' + number : number;\n+ }\n+ return '\"' + date.getFullYear() + '-' +\n+ format(date.getMonth() + 1) + '-' +\n+ format(date.getDate()) + 'T' +\n+ format(date.getHours()) + ':' +\n+ format(date.getMinutes()) + ':' +\n+ format(date.getSeconds()) + '\"';\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Feature\"\n+ CLASS_NAME: \"OpenLayers.Format.JSON\"\n+\n });\n /* ======================================================================\n- OpenLayers/Feature/Vector.js\n+ OpenLayers/Geometry.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-// TRASH THIS\n-OpenLayers.State = {\n- /** states */\n- UNKNOWN: 'Unknown',\n- INSERT: 'Insert',\n- UPDATE: 'Update',\n- DELETE: 'Delete'\n-};\n-\n /**\n- * @requires OpenLayers/Feature.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Feature.Vector\n- * Vector features use the OpenLayers.Geometry classes as geometry description.\n- * They have an 'attributes' property, which is the data object, and a 'style'\n- * property, the default values of which are defined in the \n- * <OpenLayers.Feature.Vector.style> objects.\n- * \n- * Inherits from:\n- * - <OpenLayers.Feature>\n+ * Class: OpenLayers.Geometry\n+ * A Geometry is a description of a geographic object. Create an instance of\n+ * this class with the <OpenLayers.Geometry> constructor. This is a base class,\n+ * typical geometry types are described by subclasses of this class.\n+ *\n+ * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must\n+ * explicitly include the OpenLayers.Format.WKT in your build.\n */\n-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n-\n- /** \n- * Property: fid \n- * {String} \n- */\n- fid: null,\n+OpenLayers.Geometry = OpenLayers.Class({\n \n- /** \n- * APIProperty: geometry \n- * {<OpenLayers.Geometry>} \n+ /**\n+ * Property: id\n+ * {String} A unique identifier for this geometry.\n */\n- geometry: null,\n+ id: null,\n \n- /** \n- * APIProperty: attributes \n- * {Object} This object holds arbitrary, serializable properties that\n- * describe the feature.\n+ /**\n+ * Property: parent\n+ * {<OpenLayers.Geometry>}This is set when a Geometry is added as component\n+ * of another geometry\n */\n- attributes: null,\n+ parent: null,\n \n /**\n- * Property: bounds\n- * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that\n- * property can be set by an <OpenLayers.Format> object when\n- * deserializing the feature, so in most cases it represents an\n- * information set by the server. \n+ * Property: bounds \n+ * {<OpenLayers.Bounds>} The bounds of this geometry\n */\n bounds: null,\n \n- /** \n- * Property: state \n- * {String} \n- */\n- state: null,\n-\n- /** \n- * APIProperty: style \n- * {Object} \n- */\n- style: null,\n-\n /**\n- * APIProperty: url\n- * {String} If this property is set it will be taken into account by\n- * {<OpenLayers.HTTP>} when upadting or deleting the feature.\n+ * Constructor: OpenLayers.Geometry\n+ * Creates a geometry object. \n */\n- url: null,\n+ initialize: function() {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n \n /**\n- * Property: renderIntent\n- * {String} rendering intent currently being used\n+ * Method: destroy\n+ * Destroy this geometry.\n */\n- renderIntent: \"default\",\n+ destroy: function() {\n+ this.id = null;\n+ this.bounds = null;\n+ },\n \n /**\n- * APIProperty: modified\n- * {Object} An object with the originals of the geometry and attributes of\n- * the feature, if they were changed. Currently this property is only read\n- * by <OpenLayers.Format.WFST.v1>, and written by\n- * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.\n- * Applications can set the originals of modified attributes in the\n- * attributes property. Note that applications have to check if this\n- * object and the attributes property is already created before using it.\n- * After a change made with ModifyFeature, this object could look like\n- *\n- * (code)\n- * {\n- * geometry: >Object\n- * }\n- * (end)\n- *\n- * When an application has made changes to feature attributes, it could\n- * have set the attributes to something like this:\n- *\n- * (code)\n- * {\n- * attributes: {\n- * myAttribute: \"original\"\n- * }\n- * }\n- * (end)\n- *\n- * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in\n- * *modified.geometry* and the attribute names in *modified.attributes*,\n- * but it is recommended to set the original values (and not just true) as\n- * attribute value, so applications could use this information to undo\n- * changes.\n- */\n- modified: null,\n-\n- /** \n- * Constructor: OpenLayers.Feature.Vector\n- * Create a vector feature. \n+ * APIMethod: clone\n+ * Create a clone of this geometry. Does not set any non-standard\n+ * properties of the cloned geometry.\n * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The geometry that this feature\n- * represents.\n- * attributes - {Object} An optional object that will be mapped to the\n- * <attributes> property. \n- * style - {Object} An optional style object.\n+ * Returns:\n+ * {<OpenLayers.Geometry>} An exact clone of this geometry.\n */\n- initialize: function(geometry, attributes, style) {\n- OpenLayers.Feature.prototype.initialize.apply(this,\n- [null, null, attributes]);\n- this.lonlat = null;\n- this.geometry = geometry ? geometry : null;\n- this.state = null;\n- this.attributes = {};\n- if (attributes) {\n- this.attributes = OpenLayers.Util.extend(this.attributes,\n- attributes);\n- }\n- this.style = style ? style : null;\n+ clone: function() {\n+ return new OpenLayers.Geometry();\n },\n \n- /** \n- * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n+ /**\n+ * Method: setBounds\n+ * Set the bounds for this Geometry.\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} \n */\n- destroy: function() {\n- if (this.layer) {\n- this.layer.removeFeatures(this);\n- this.layer = null;\n+ setBounds: function(bounds) {\n+ if (bounds) {\n+ this.bounds = bounds.clone();\n }\n-\n- this.geometry = null;\n- this.modified = null;\n- OpenLayers.Feature.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: clone\n- * Create a clone of this vector feature. Does not set any non-standard\n- * properties.\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.\n+ * Method: clearBounds\n+ * Nullify this components bounds and that of its parent as well.\n */\n- clone: function() {\n- return new OpenLayers.Feature.Vector(\n- this.geometry ? this.geometry.clone() : null,\n- this.attributes,\n- this.style);\n+ clearBounds: function() {\n+ this.bounds = null;\n+ if (this.parent) {\n+ this.parent.clearBounds();\n+ }\n },\n \n /**\n- * Method: onScreen\n- * Determine whether the feature is within the map viewport. This method\n- * tests for an intersection between the geometry and the viewport\n- * bounds. If a more effecient but less precise geometry bounds\n- * intersection is desired, call the method with the boundsOnly\n- * parameter true.\n- *\n- * Parameters:\n- * boundsOnly - {Boolean} Only test whether a feature's bounds intersects\n- * the viewport bounds. Default is false. If false, the feature's\n- * geometry must intersect the viewport for onScreen to return true.\n+ * Method: extendBounds\n+ * Extend the existing bounds to include the new bounds. \n+ * If geometry's bounds is not yet set, then set a new Bounds.\n * \n- * Returns:\n- * {Boolean} The feature is currently visible on screen (optionally\n- * based on its bounds if boundsOnly is true).\n+ * Parameters:\n+ * newBounds - {<OpenLayers.Bounds>} \n */\n- onScreen: function(boundsOnly) {\n- var onScreen = false;\n- if (this.layer && this.layer.map) {\n- var screenBounds = this.layer.map.getExtent();\n- if (boundsOnly) {\n- var featureBounds = this.geometry.getBounds();\n- onScreen = screenBounds.intersectsBounds(featureBounds);\n- } else {\n- var screenPoly = screenBounds.toGeometry();\n- onScreen = screenPoly.intersects(this.geometry);\n- }\n+ extendBounds: function(newBounds) {\n+ var bounds = this.getBounds();\n+ if (!bounds) {\n+ this.setBounds(newBounds);\n+ } else {\n+ this.bounds.extend(newBounds);\n }\n- return onScreen;\n },\n \n /**\n- * Method: getVisibility\n- * Determine whether the feature is displayed or not. It may not displayed\n- * because:\n- * - its style display property is set to 'none',\n- * - it doesn't belong to any layer,\n- * - the styleMap creates a symbolizer with display property set to 'none'\n- * for it,\n- * - the layer which it belongs to is not visible.\n+ * APIMethod: getBounds\n+ * Get the bounds for this Geometry. If bounds is not set, it \n+ * is calculated again, this makes queries faster.\n * \n * Returns:\n- * {Boolean} The feature is currently displayed.\n+ * {<OpenLayers.Bounds>}\n */\n- getVisibility: function() {\n- return !(this.style && this.style.display == 'none' ||\n- !this.layer ||\n- this.layer && this.layer.styleMap &&\n- this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||\n- this.layer && !this.layer.getVisibility());\n+ getBounds: function() {\n+ if (this.bounds == null) {\n+ this.calculateBounds();\n+ }\n+ return this.bounds;\n },\n \n- /**\n- * Method: createMarker\n- * HACK - we need to decide if all vector features should be able to\n- * create markers\n- * \n- * Returns:\n- * {<OpenLayers.Marker>} For now just returns null\n+ /** \n+ * APIMethod: calculateBounds\n+ * Recalculate the bounds for the geometry. \n */\n- createMarker: function() {\n- return null;\n+ calculateBounds: function() {\n+ //\n+ // This should be overridden by subclasses.\n+ //\n },\n \n /**\n- * Method: destroyMarker\n- * HACK - we need to decide if all vector features should be able to\n- * delete markers\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n+ *\n+ * Valid options depend on the specific geometry type.\n * \n- * If user overrides the createMarker() function, s/he should be able\n- * to also specify an alternative function for destroying it\n+ * Returns:\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n */\n- destroyMarker: function() {\n- // pass\n- },\n+ distanceTo: function(geometry, options) {},\n \n /**\n- * Method: createPopup\n- * HACK - we need to decide if all vector features should be able to\n- * create popups\n- * \n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n+ *\n+ * Parameters:\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n+ *\n * Returns:\n- * {<OpenLayers.Popup>} For now just returns null\n+ * {Array} A list of all vertices in the geometry.\n */\n- createPopup: function() {\n- return null;\n- },\n+ getVertices: function(nodes) {},\n \n /**\n * Method: atPoint\n- * Determins whether the feature intersects with the specified location.\n+ * Note - This is only an approximation based on the bounds of the \n+ * geometry.\n * \n- * Parameters: \n+ * Parameters:\n * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n * object with a 'lon' and 'lat' properties.\n * toleranceLon - {float} Optional tolerance in Geometric Coords\n * toleranceLat - {float} Optional tolerance in Geographic Coords\n * \n * Returns:\n- * {Boolean} Whether or not the feature is at the specified location\n+ * {Boolean} Whether or not the geometry is at the specified location\n */\n atPoint: function(lonlat, toleranceLon, toleranceLat) {\n var atPoint = false;\n- if (this.geometry) {\n- atPoint = this.geometry.atPoint(lonlat, toleranceLon,\n- toleranceLat);\n+ var bounds = this.getBounds();\n+ if ((bounds != null) && (lonlat != null)) {\n+\n+ var dX = (toleranceLon != null) ? toleranceLon : 0;\n+ var dY = (toleranceLat != null) ? toleranceLat : 0;\n+\n+ var toleranceBounds =\n+ new OpenLayers.Bounds(this.bounds.left - dX,\n+ this.bounds.bottom - dY,\n+ this.bounds.right + dX,\n+ this.bounds.top + dY);\n+\n+ atPoint = toleranceBounds.containsLonLat(lonlat);\n }\n return atPoint;\n },\n \n /**\n- * Method: destroyPopup\n- * HACK - we need to decide if all vector features should be able to\n- * delete popups\n+ * Method: getLength\n+ * Calculate the length of this geometry. This method is defined in\n+ * subclasses.\n+ * \n+ * Returns:\n+ * {Float} The length of the collection by summing its parts\n */\n- destroyPopup: function() {\n- // pass\n+ getLength: function() {\n+ //to be overridden by geometries that actually have a length\n+ //\n+ return 0.0;\n },\n \n /**\n- * Method: move\n- * Moves the feature and redraws it at its new location\n- *\n- * Parameters:\n- * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the\n- * location to which to move the feature.\n+ * Method: getArea\n+ * Calculate the area of this geometry. This method is defined in subclasses.\n+ * \n+ * Returns:\n+ * {Float} The area of the collection by summing its parts\n */\n- move: function(location) {\n-\n- if (!this.layer || !this.geometry.move) {\n- //do nothing if no layer or immoveable geometry\n- return undefined;\n- }\n-\n- var pixel;\n- if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n- pixel = this.layer.getViewPortPxFromLonLat(location);\n- } else {\n- pixel = location;\n- }\n+ getArea: function() {\n+ //to be overridden by geometries that actually have an area\n+ //\n+ return 0.0;\n+ },\n \n- var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n- var res = this.layer.map.getResolution();\n- this.geometry.move(res * (pixel.x - lastPixel.x),\n- res * (lastPixel.y - pixel.y));\n- this.layer.drawFeature(this);\n- return lastPixel;\n+ /**\n+ * APIMethod: getCentroid\n+ * Calculate the centroid of this geometry. This method is defined in subclasses.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>} The centroid of the collection\n+ */\n+ getCentroid: function() {\n+ return null;\n },\n \n /**\n- * Method: toState\n- * Sets the new state\n+ * Method: toString\n+ * Returns a text representation of the geometry. If the WKT format is\n+ * included in a build, this will be the Well-Known Text \n+ * representation.\n *\n- * Parameters:\n- * state - {String} \n+ * Returns:\n+ * {String} String representation of this geometry.\n */\n- toState: function(state) {\n- if (state == OpenLayers.State.UPDATE) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.DELETE:\n- this.state = state;\n- break;\n- case OpenLayers.State.UPDATE:\n- case OpenLayers.State.INSERT:\n- break;\n- }\n- } else if (state == OpenLayers.State.INSERT) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- break;\n- default:\n- this.state = state;\n- break;\n- }\n- } else if (state == OpenLayers.State.DELETE) {\n- switch (this.state) {\n- case OpenLayers.State.INSERT:\n- // the feature should be destroyed\n- break;\n- case OpenLayers.State.DELETE:\n- break;\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.UPDATE:\n- this.state = state;\n- break;\n- }\n- } else if (state == OpenLayers.State.UNKNOWN) {\n- this.state = state;\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ string = OpenLayers.Format.WKT.prototype.write(\n+ new OpenLayers.Feature.Vector(this)\n+ );\n+ } else {\n+ string = Object.prototype.toString.call(this);\n }\n+ return string;\n },\n \n- CLASS_NAME: \"OpenLayers.Feature.Vector\"\n+ CLASS_NAME: \"OpenLayers.Geometry\"\n });\n \n+/**\n+ * Function: OpenLayers.Geometry.fromWKT\n+ * Generate a geometry given a Well-Known Text string. For this method to\n+ * work, you must include the OpenLayers.Format.WKT in your build \n+ * explicitly.\n+ *\n+ * Parameters:\n+ * wkt - {String} A string representing the geometry in Well-Known Text.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry of the appropriate class.\n+ */\n+OpenLayers.Geometry.fromWKT = function(wkt) {\n+ var geom;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ var format = OpenLayers.Geometry.fromWKT.format;\n+ if (!format) {\n+ format = new OpenLayers.Format.WKT();\n+ OpenLayers.Geometry.fromWKT.format = format;\n+ }\n+ var result = format.read(wkt);\n+ if (result instanceof OpenLayers.Feature.Vector) {\n+ geom = result.geometry;\n+ } else if (OpenLayers.Util.isArray(result)) {\n+ var len = result.length;\n+ var components = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ components[i] = result[i].geometry;\n+ }\n+ geom = new OpenLayers.Geometry.Collection(components);\n+ }\n+ }\n+ return geom;\n+};\n \n /**\n- * Constant: OpenLayers.Feature.Vector.style\n- * OpenLayers features can have a number of style attributes. The 'default' \n- * style will typically be used if no other style is specified. These\n- * styles correspond for the most part, to the styling properties defined\n- * by the SVG standard. \n- * Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties\n- * Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties\n+ * Method: OpenLayers.Geometry.segmentsIntersect\n+ * Determine whether two line segments intersect. Optionally calculates\n+ * and returns the intersection point. This function is optimized for\n+ * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those\n+ * obvious cases where there is no intersection, the function should\n+ * not be called.\n *\n- * Symbolizer properties:\n- * fill - {Boolean} Set to false if no fill is desired.\n- * fillColor - {String} Hex fill color. Default is \"#ee9900\".\n- * fillOpacity - {Number} Fill opacity (0-1). Default is 0.4 \n- * stroke - {Boolean} Set to false if no stroke is desired.\n- * strokeColor - {String} Hex stroke color. Default is \"#ee9900\".\n- * strokeOpacity - {Number} Stroke opacity (0-1). Default is 1.\n- * strokeWidth - {Number} Pixel stroke width. Default is 1.\n- * strokeLinecap - {String} Stroke cap type. Default is \"round\". [butt | round | square]\n- * strokeDashstyle - {String} Stroke dash style. Default is \"solid\". [dot | dash | dashdot | longdash | longdashdot | solid]\n- * graphic - {Boolean} Set to false if no graphic is desired.\n- * pointRadius - {Number} Pixel point radius. Default is 6.\n- * pointerEvents - {String} Default is \"visiblePainted\".\n- * cursor - {String} Default is \"\".\n- * externalGraphic - {String} Url to an external graphic that will be used for rendering points.\n- * graphicWidth - {Number} Pixel width for sizing an external graphic.\n- * graphicHeight - {Number} Pixel height for sizing an external graphic.\n- * graphicOpacity - {Number} Opacity (0-1) for an external graphic.\n- * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.\n- * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.\n- * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).\n- * graphicZIndex - {Number} The integer z-index value to use in rendering.\n- * graphicName - {String} Named graphic to use when rendering points. Supported values include \"circle\" (default),\n- * \"square\", \"star\", \"x\", \"cross\", \"triangle\".\n- * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead\n- * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.\n- * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.\n- * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.\n- * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.\n- * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.\n- * backgroundHeight - {Number} The height of the background graphic. If not provided, the graphicHeight will be used.\n- * backgroundWidth - {Number} The width of the background width. If not provided, the graphicWidth will be used.\n- * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either\n- * fillText or mozDrawText to be available.\n- * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string\n- * composed of two characters. The first character is for the horizontal alignment, the second for the vertical\n- * alignment. Valid values for horizontal alignment: \"l\"=left, \"c\"=center, \"r\"=right. Valid values for vertical\n- * alignment: \"t\"=top, \"m\"=middle, \"b\"=bottom. Example values: \"lt\", \"cm\", \"rb\". Default is \"cm\".\n- * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.\n- * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.\n- * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.\n- * Default is false.\n- * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.\n- * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the SVG renderers.\n- * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.\n- * fontColor - {String} The font color for the label, to be provided like CSS.\n- * fontOpacity - {Number} Opacity (0-1) for the label\n- * fontFamily - {String} The font family for the label, to be provided like in CSS.\n- * fontSize - {String} The font size for the label, to be provided like in CSS.\n- * fontStyle - {String} The font style for the label, to be provided like in CSS.\n- * fontWeight - {String} The font weight for the label, to be provided like in CSS.\n- * display - {String} Symbolizers will have no effect if display is set to \"none\". All other values have no effect.\n+ * Parameters:\n+ * seg1 - {Object} Object representing a segment with properties x1, y1, x2,\n+ * and y2. The start point is represented by x1 and y1. The end point\n+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n+ * seg2 - {Object} Object representing a segment with properties x1, y1, x2,\n+ * and y2. The start point is represented by x1 and y1. The end point\n+ * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n+ * options - {Object} Optional properties for calculating the intersection.\n+ *\n+ * Valid options:\n+ * point - {Boolean} Return the intersection point. If false, the actual\n+ * intersection point will not be calculated. If true and the segments\n+ * intersect, the intersection point will be returned. If true and\n+ * the segments do not intersect, false will be returned. If true and\n+ * the segments are coincident, true will be returned.\n+ * tolerance - {Number} If a non-null value is provided, if the segments are\n+ * within the tolerance distance, this will be considered an intersection.\n+ * In addition, if the point option is true and the calculated intersection\n+ * is within the tolerance distance of an end point, the endpoint will be\n+ * returned instead of the calculated intersection. Further, if the\n+ * intersection is within the tolerance of endpoints on both segments, or\n+ * if two segment endpoints are within the tolerance distance of eachother\n+ * (but no intersection is otherwise calculated), an endpoint on the\n+ * first segment provided will be returned.\n+ *\n+ * Returns:\n+ * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.\n+ * If the point argument is true, the return will be the intersection\n+ * point or false if none exists. If point is true and the segments\n+ * are coincident, return will be true (and the instersection is equal\n+ * to the shorter segment).\n */\n-OpenLayers.Feature.Vector.style = {\n- 'default': {\n- fillColor: \"#ee9900\",\n- fillOpacity: 0.4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"#ee9900\",\n- strokeOpacity: 1,\n- strokeWidth: 1,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n- },\n- 'select': {\n- fillColor: \"blue\",\n- fillOpacity: 0.4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"blue\",\n- strokeOpacity: 1,\n- strokeWidth: 2,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"pointer\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n+ var point = options && options.point;\n+ var tolerance = options && options.tolerance;\n+ var intersection = false;\n+ var x11_21 = seg1.x1 - seg2.x1;\n+ var y11_21 = seg1.y1 - seg2.y1;\n+ var x12_11 = seg1.x2 - seg1.x1;\n+ var y12_11 = seg1.y2 - seg1.y1;\n+ var y22_21 = seg2.y2 - seg2.y1;\n+ var x22_21 = seg2.x2 - seg2.x1;\n+ var d = (y22_21 * x12_11) - (x22_21 * y12_11);\n+ var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);\n+ var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);\n+ if (d == 0) {\n+ // parallel\n+ if (n1 == 0 && n2 == 0) {\n+ // coincident\n+ intersection = true;\n+ }\n+ } else {\n+ var along1 = n1 / d;\n+ var along2 = n2 / d;\n+ if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n+ // intersect\n+ if (!point) {\n+ intersection = true;\n+ } else {\n+ // calculate the intersection point\n+ var x = seg1.x1 + (along1 * x12_11);\n+ var y = seg1.y1 + (along1 * y12_11);\n+ intersection = new OpenLayers.Geometry.Point(x, y);\n+ }\n+ }\n+ }\n+ if (tolerance) {\n+ var dist;\n+ if (intersection) {\n+ if (point) {\n+ var segs = [seg1, seg2];\n+ var seg, x, y;\n+ // check segment endpoints for proximity to intersection\n+ // set intersection to first endpoint within the tolerance\n+ outer: for (var i = 0; i < 2; ++i) {\n+ seg = segs[i];\n+ for (var j = 1; j < 3; ++j) {\n+ x = seg[\"x\" + j];\n+ y = seg[\"y\" + j];\n+ dist = Math.sqrt(\n+ Math.pow(x - intersection.x, 2) +\n+ Math.pow(y - intersection.y, 2)\n+ );\n+ if (dist < tolerance) {\n+ intersection.x = x;\n+ intersection.y = y;\n+ break outer;\n+ }\n+ }\n+ }\n \n- },\n- 'temporary': {\n- fillColor: \"#66cccc\",\n- fillOpacity: 0.2,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: 0.8,\n- strokeColor: \"#66cccc\",\n- strokeOpacity: 1,\n- strokeLinecap: \"round\",\n- strokeWidth: 2,\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: 0.2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n+ }\n+ } else {\n+ // no calculated intersection, but segments could be within\n+ // the tolerance of one another\n+ var segs = [seg1, seg2];\n+ var source, target, x, y, p, result;\n+ // check segment endpoints for proximity to intersection\n+ // set intersection to first endpoint within the tolerance\n+ outer: for (var i = 0; i < 2; ++i) {\n+ source = segs[i];\n+ target = segs[(i + 1) % 2];\n+ for (var j = 1; j < 3; ++j) {\n+ p = {\n+ x: source[\"x\" + j],\n+ y: source[\"y\" + j]\n+ };\n+ result = OpenLayers.Geometry.distanceToSegment(p, target);\n+ if (result.distance < tolerance) {\n+ if (point) {\n+ intersection = new OpenLayers.Geometry.Point(p.x, p.y);\n+ } else {\n+ intersection = true;\n+ }\n+ break outer;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return intersection;\n+};\n \n- },\n- 'delete': {\n- display: \"none\"\n+/**\n+ * Function: OpenLayers.Geometry.distanceToSegment\n+ *\n+ * Parameters:\n+ * point - {Object} An object with x and y properties representing the\n+ * point coordinates.\n+ * segment - {Object} An object with x1, y1, x2, and y2 properties\n+ * representing endpoint coordinates.\n+ *\n+ * Returns:\n+ * {Object} An object with distance, along, x, and y properties. The distance\n+ * will be the shortest distance between the input point and segment.\n+ * The x and y properties represent the coordinates along the segment\n+ * where the shortest distance meets the segment. The along attribute\n+ * describes how far between the two segment points the given point is.\n+ */\n+OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n+ var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n+ result.distance = Math.sqrt(result.distance);\n+ return result;\n+};\n+\n+/**\n+ * Function: OpenLayers.Geometry.distanceSquaredToSegment\n+ *\n+ * Usually the distanceToSegment function should be used. This variant however\n+ * can be used for comparisons where the exact distance is not important.\n+ *\n+ * Parameters:\n+ * point - {Object} An object with x and y properties representing the\n+ * point coordinates.\n+ * segment - {Object} An object with x1, y1, x2, and y2 properties\n+ * representing endpoint coordinates.\n+ *\n+ * Returns:\n+ * {Object} An object with squared distance, along, x, and y properties.\n+ * The distance will be the shortest distance between the input point and\n+ * segment. The x and y properties represent the coordinates along the\n+ * segment where the shortest distance meets the segment. The along\n+ * attribute describes how far between the two segment points the given\n+ * point is.\n+ */\n+OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n+ var x0 = point.x;\n+ var y0 = point.y;\n+ var x1 = segment.x1;\n+ var y1 = segment.y1;\n+ var x2 = segment.x2;\n+ var y2 = segment.y2;\n+ var dx = x2 - x1;\n+ var dy = y2 - y1;\n+ var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /\n+ (Math.pow(dx, 2) + Math.pow(dy, 2));\n+ var x, y;\n+ if (along <= 0.0) {\n+ x = x1;\n+ y = y1;\n+ } else if (along >= 1.0) {\n+ x = x2;\n+ y = y2;\n+ } else {\n+ x = x1 + along * dx;\n+ y = y1 + along * dy;\n }\n+ return {\n+ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n+ x: x,\n+ y: y,\n+ along: along\n+ };\n };\n /* ======================================================================\n- OpenLayers/Style.js\n+ OpenLayers/Geometry/Point.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry.js\n */\n \n /**\n- * Class: OpenLayers.Style\n- * This class represents a UserStyle obtained\n- * from a SLD, containing styling rules.\n+ * Class: OpenLayers.Geometry.Point\n+ * Point geometry class. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Geometry> \n */\n-OpenLayers.Style = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n \n- /**\n- * APIProperty: name\n- * {String}\n+ /** \n+ * APIProperty: x \n+ * {float} \n */\n- name: null,\n+ x: null,\n \n- /**\n- * Property: title\n- * {String} Title of this style (set if included in SLD)\n+ /** \n+ * APIProperty: y \n+ * {float} \n */\n- title: null,\n+ y: null,\n \n /**\n- * Property: description\n- * {String} Description of this style (set if abstract is included in SLD)\n+ * Constructor: OpenLayers.Geometry.Point\n+ * Construct a point geometry.\n+ *\n+ * Parameters:\n+ * x - {float} \n+ * y - {float}\n+ * \n */\n- description: null,\n+ initialize: function(x, y) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n \n- /**\n- * APIProperty: layerName\n- * {<String>} name of the layer that this style belongs to, usually\n- * according to the NamedLayer attribute of an SLD document.\n- */\n- layerName: null,\n+ this.x = parseFloat(x);\n+ this.y = parseFloat(y);\n+ },\n \n /**\n- * APIProperty: isDefault\n- * {Boolean}\n- */\n- isDefault: false,\n-\n- /** \n- * Property: rules \n- * {Array(<OpenLayers.Rule>)}\n+ * APIMethod: clone\n+ * \n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point\n */\n- rules: null,\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Geometry.Point(this.x, this.y);\n+ }\n \n- /**\n- * APIProperty: context\n- * {Object} An optional object with properties that symbolizers' property\n- * values should be evaluated against. If no context is specified,\n- * feature.attributes will be used\n- */\n- context: null,\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n- /**\n- * Property: defaultStyle\n- * {Object} hash of style properties to use as default for merging\n- * rule-based style symbolizers onto. If no rules are defined,\n- * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n- * true, the defaultStyle will only be taken into account if there are\n- * rules defined.\n- */\n- defaultStyle: null,\n+ return obj;\n+ },\n \n- /**\n- * Property: defaultsPerSymbolizer\n- * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n- * of every rule. Properties of the <defaultStyle> will also be used to set\n- * missing symbolizer properties if the symbolizer has stroke, fill or\n- * graphic set to true. Default is false.\n+ /** \n+ * Method: calculateBounds\n+ * Create a new Bounds based on the lon/lat\n */\n- defaultsPerSymbolizer: false,\n+ calculateBounds: function() {\n+ this.bounds = new OpenLayers.Bounds(this.x, this.y,\n+ this.x, this.y);\n+ },\n \n /**\n- * Property: propertyStyles\n- * {Hash of Boolean} cache of style properties that need to be parsed for\n- * propertyNames. Property names are keys, values won't be used.\n- */\n- propertyStyles: null,\n-\n-\n- /** \n- * Constructor: OpenLayers.Style\n- * Creates a UserStyle.\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n *\n * Parameters:\n- * style - {Object} Optional hash of style properties that will be\n- * used as default style for this style object. This style\n- * applies if no rules are specified. Symbolizers defined in\n- * rules will extend this default style.\n- * options - {Object} An optional object with properties to set on the\n- * style.\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n *\n * Valid options:\n- * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n- * style.\n- * \n+ * details - {Boolean} Return details from the distance calculation.\n+ * Default is false.\n+ * edge - {Boolean} Calculate the distance from this geometry to the\n+ * nearest edge of the target geometry. Default is true. If true,\n+ * calling distanceTo from a geometry that is wholly contained within\n+ * the target will result in a non-zero distance. If false, whenever\n+ * geometries intersect, calling distanceTo will return 0. If false,\n+ * details cannot be returned.\n+ *\n * Returns:\n- * {<OpenLayers.Style>}\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n */\n- initialize: function(style, options) {\n-\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules);\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var distance, x0, y0, x1, y1, result;\n+ if (geometry instanceof OpenLayers.Geometry.Point) {\n+ x0 = this.x;\n+ y0 = this.y;\n+ x1 = geometry.x;\n+ y1 = geometry.y;\n+ distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));\n+ result = !details ?\n+ distance : {\n+ x0: x0,\n+ y0: y0,\n+ x1: x1,\n+ y1: y1,\n+ distance: distance\n+ };\n+ } else {\n+ result = geometry.distanceTo(this, options);\n+ if (details) {\n+ // switch coord order since this geom is target\n+ result = {\n+ x0: result.x1,\n+ y0: result.y1,\n+ x1: result.x0,\n+ y1: result.y0,\n+ distance: result.distance\n+ };\n+ }\n }\n-\n- // use the default style from OpenLayers.Feature.Vector if no style\n- // was given in the constructor\n- this.setDefaultStyle(style ||\n- OpenLayers.Feature.Vector.style[\"default\"]);\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ return result;\n },\n \n /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null;\n- }\n- this.rules = null;\n- this.defaultStyle = null;\n- },\n-\n- /**\n- * Method: createSymbolizer\n- * creates a style by applying all feature-dependent rules to the base\n- * style.\n+ * APIMethod: equals\n+ * Determine whether another geometry is equivalent to this one. Geometries\n+ * are considered equivalent if all components have the same coordinates.\n * \n * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n- * \n+ * geom - {<OpenLayers.Geometry.Point>} The geometry to test. \n+ *\n * Returns:\n- * {Object} symbolizer hash\n+ * {Boolean} The supplied geometry is equivalent to this geometry.\n */\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n- OpenLayers.Util.extend({}, this.defaultStyle), feature);\n-\n- var rules = this.rules;\n-\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- // does the rule apply?\n- var applies = rule.evaluate(feature);\n-\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule);\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature);\n- }\n- }\n- }\n-\n- // if no other rules apply, apply the rules with else filters\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature);\n- }\n- }\n-\n- // don't display if there were rules but none applied\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\";\n- }\n-\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label);\n+ equals: function(geom) {\n+ var equals = false;\n+ if (geom != null) {\n+ equals = ((this.x == geom.x && this.y == geom.y) ||\n+ (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));\n }\n-\n- return style;\n+ return equals;\n },\n \n /**\n- * Method: applySymbolizer\n- *\n- * Parameters:\n- * rule - {<OpenLayers.Rule>}\n- * style - {Object}\n- * feature - {<OpenLayer.Feature.Vector>}\n+ * Method: toShortString\n *\n * Returns:\n- * {Object} A style with new symbolizer applied.\n+ * {String} Shortened String representation of Point object. \n+ * (ex. <i>\"5, 42\"</i>)\n */\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ?\n- this.getSymbolizerPrefix(feature.geometry) :\n- OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n-\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n-\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- });\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- });\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- });\n- }\n- }\n-\n- // merge the style with the current style\n- return this.createLiterals(\n- OpenLayers.Util.extend(style, symbolizer), feature);\n+ toShortString: function() {\n+ return (this.x + \", \" + this.y);\n },\n \n /**\n- * Method: createLiterals\n- * creates literals for all style properties that have an entry in\n- * <this.propertyStyles>.\n- * \n+ * APIMethod: move\n+ * Moves a geometry by the given displacement along positive x and y axes.\n+ * This modifies the position of the geometry and clears the cached\n+ * bounds.\n+ *\n * Parameters:\n- * style - {Object} style to create literals for. Will be modified\n- * inline.\n- * feature - {Object}\n- * \n- * Returns:\n- * {Object} the modified style\n+ * x - {Float} Distance to move geometry in positive x direction. \n+ * y - {Float} Distance to move geometry in positive y direction.\n */\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n-\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n- }\n- return style;\n+ move: function(x, y) {\n+ this.x = this.x + x;\n+ this.y = this.y + y;\n+ this.clearBounds();\n },\n \n /**\n- * Method: findPropertyStyles\n- * Looks into all rules for this style and the defaultStyle to collect\n- * all the style hash property names containing ${...} strings that have\n- * to be replaced using the createLiteral method before returning them.\n- * \n- * Returns:\n- * {Object} hash of property names that need createLiteral parsing. The\n- * name of the property is the key, and the value is true;\n+ * APIMethod: rotate\n+ * Rotate a point around another.\n+ *\n+ * Parameters:\n+ * angle - {Float} Rotation angle in degrees (measured counterclockwise\n+ * from the positive x-axis)\n+ * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n */\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n-\n- // check the default style\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n-\n- // walk through all rules to check for properties in their symbolizer\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n- this.addPropertyStyles(propertyStyles, value);\n- } else {\n- // symbolizer is a hash of style properties\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break;\n- }\n- }\n- }\n- return propertyStyles;\n+ rotate: function(angle, origin) {\n+ angle *= Math.PI / 180;\n+ var radius = this.distanceTo(origin);\n+ var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);\n+ this.x = origin.x + (radius * Math.cos(theta));\n+ this.y = origin.y + (radius * Math.sin(theta));\n+ this.clearBounds();\n },\n \n /**\n- * Method: addPropertyStyles\n- * \n- * Parameters:\n- * propertyStyles - {Object} hash to add new property styles to. Will be\n- * modified inline\n- * symbolizer - {Object} search this symbolizer for property styles\n- * \n+ * APIMethod: getCentroid\n+ *\n * Returns:\n- * {Object} propertyStyles hash\n+ * {<OpenLayers.Geometry.Point>} The centroid of the collection\n */\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" &&\n- property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true;\n- }\n- }\n- return propertyStyles;\n+ getCentroid: function() {\n+ return new OpenLayers.Geometry.Point(this.x, this.y);\n },\n \n /**\n- * APIMethod: addRules\n- * Adds rules to this style.\n- * \n+ * APIMethod: resize\n+ * Resize a point relative to some origin. For points, this has the effect\n+ * of scaling a vector (from the origin to the point). This method is\n+ * more useful on geometry collection subclasses.\n+ *\n * Parameters:\n- * rules - {Array(<OpenLayers.Rule>)}\n+ * scale - {Float} Ratio of the new distance from the origin to the old\n+ * distance from the origin. A scale of 2 doubles the\n+ * distance between the point and origin.\n+ * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Geometry>} - The current geometry. \n */\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles();\n+ resize: function(scale, origin, ratio) {\n+ ratio = (ratio == undefined) ? 1 : ratio;\n+ this.x = origin.x + (scale * ratio * (this.x - origin.x));\n+ this.y = origin.y + (scale * (this.y - origin.y));\n+ this.clearBounds();\n+ return this;\n },\n \n /**\n- * APIMethod: setDefaultStyle\n- * Sets the default style for this style object.\n- * \n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n+ *\n * Parameters:\n- * style - {Object} Hash of style properties\n+ * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n */\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles();\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.equals(geometry);\n+ } else {\n+ intersect = geometry.intersects(this);\n+ }\n+ return intersect;\n },\n \n /**\n- * Method: getSymbolizerPrefix\n- * Returns the correct symbolizer prefix according to the\n- * geometry type of the passed geometry\n+ * APIMethod: transform\n+ * Translate the x,y properties of the point from source to dest.\n * \n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n+ * source - {<OpenLayers.Projection>} \n+ * dest - {<OpenLayers.Projection>}\n * \n * Returns:\n- * {String} key of the according symbolizer\n+ * {<OpenLayers.Geometry>} \n */\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i];\n- }\n+ transform: function(source, dest) {\n+ if ((source && dest)) {\n+ OpenLayers.Projection.transform(\n+ this, source, dest);\n+ this.bounds = null;\n }\n+ return this;\n },\n \n /**\n- * APIMethod: clone\n- * Clones this style.\n- * \n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n+ *\n+ * Parameters:\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n+ *\n * Returns:\n- * {<OpenLayers.Style>} Clone of this style.\n+ * {Array} A list of all vertices in the geometry.\n */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- // clone rules\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone());\n- }\n- }\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- //clone default style\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options);\n+ getVertices: function(nodes) {\n+ return [this];\n },\n \n- CLASS_NAME: \"OpenLayers.Style\"\n+ CLASS_NAME: \"OpenLayers.Geometry.Point\"\n });\n-\n-\n-/**\n- * Function: createLiteral\n- * converts a style value holding a combination of PropertyName and Literal\n- * into a Literal, taking the property values from the passed features.\n- * \n- * Parameters:\n- * value - {String} value to parse. If this string contains a construct like\n- * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n- * will be replaced by the value of the \"bar\" attribute of the passed\n- * feature.\n- * context - {Object} context to take attribute values from\n- * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n- * <OpenLayers.String.format> for evaluating functions in the\n- * context.\n- * property - {String} optional, name of the property for which the literal is\n- * being created for evaluating functions in the context.\n- * \n- * Returns:\n- * {String} the parsed value. In the example of the value parameter above, the\n- * result would be \"foo valueOfBar\", assuming that the passed feature has an\n- * attribute named \"bar\" with the value \"valueOfBar\".\n- */\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = (isNaN(value) || !value) ? value : parseFloat(value);\n- }\n- return value;\n-};\n-\n-/**\n- * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n- * {Array} prefixes of the sld symbolizers. These are the\n- * same as the main geometry types\n- */\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n- 'Raster'\n-];\n /* ======================================================================\n- OpenLayers/Rule.js\n+ OpenLayers/Geometry/Collection.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Geometry.js\n */\n \n /**\n- * Class: OpenLayers.Rule\n- * This class represents an SLD Rule, as being used for rule-based SLD styling.\n+ * Class: OpenLayers.Geometry.Collection\n+ * A Collection is exactly what it sounds like: A collection of different \n+ * Geometries. These are stored in the local parameter <components> (which\n+ * can be passed as a parameter to the constructor). \n+ * \n+ * As new geometries are added to the collection, they are NOT cloned. \n+ * When removing geometries, they need to be specified by reference (ie you \n+ * have to pass in the *exact* geometry to be removed).\n+ * \n+ * The <getArea> and <getLength> functions here merely iterate through\n+ * the components, summing their respective areas and lengths.\n+ *\n+ * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Geometry> \n */\n-OpenLayers.Rule = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n-\n- /**\n- * APIProperty: name\n- * {String} name of this rule\n- */\n- name: null,\n-\n- /**\n- * Property: title\n- * {String} Title of this rule (set if included in SLD)\n- */\n- title: null,\n-\n- /**\n- * Property: description\n- * {String} Description of this rule (set if abstract is included in SLD)\n- */\n- description: null,\n-\n- /**\n- * Property: context\n- * {Object} An optional object with properties that the rule should be\n- * evaluated against. If no context is specified, feature.attributes will\n- * be used.\n- */\n- context: null,\n-\n- /**\n- * Property: filter\n- * {<OpenLayers.Filter>} Optional filter for the rule.\n- */\n- filter: null,\n-\n- /**\n- * Property: elseFilter\n- * {Boolean} Determines whether this rule is only to be applied only if\n- * no other rules match (ElseFilter according to the SLD specification). \n- * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n- * false, the rule will always apply. For subclasses, the else property is \n- * ignored.\n- */\n- elseFilter: false,\n-\n- /**\n- * Property: symbolizer\n- * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n- * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n- * latter if useful if it is required to style e.g. vertices of a line\n- * with a point symbolizer. Note, however, that this is not implemented\n- * yet in OpenLayers, but it is the way how symbolizers are defined in\n- * SLD.\n- */\n- symbolizer: null,\n+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n \n /**\n- * Property: symbolizers\n- * {Array} Collection of symbolizers associated with this rule. If \n- * provided at construction, the symbolizers array has precedence\n- * over the deprecated symbolizer property. Note that multiple \n- * symbolizers are not currently supported by the vector renderers.\n- * Rules with multiple symbolizers are currently only useful for\n- * maintaining elements in an SLD document.\n+ * APIProperty: components\n+ * {Array(<OpenLayers.Geometry>)} The component parts of this geometry\n */\n- symbolizers: null,\n+ components: null,\n \n /**\n- * APIProperty: minScaleDenominator\n- * {Number} or {String} minimum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n */\n- minScaleDenominator: null,\n+ componentTypes: null,\n \n /**\n- * APIProperty: maxScaleDenominator\n- * {Number} or {String} maximum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n- */\n- maxScaleDenominator: null,\n-\n- /** \n- * Constructor: OpenLayers.Rule\n- * Creates a Rule.\n+ * Constructor: OpenLayers.Geometry.Collection\n+ * Creates a Geometry Collection -- a list of geoms.\n+ *\n+ * Parameters: \n+ * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries\n *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\n- * {<OpenLayers.Rule>}\n */\n- initialize: function(options) {\n- this.symbolizer = {};\n- OpenLayers.Util.extend(this, options);\n- if (this.symbolizers) {\n- delete this.symbolizer;\n+ initialize: function(components) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+ this.components = [];\n+ if (components != null) {\n+ this.addComponents(components);\n }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n- /** \n+ /**\n * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n+ * Destroy this geometry.\n */\n destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null;\n- }\n- this.symbolizer = null;\n- delete this.symbolizers;\n+ this.components.length = 0;\n+ this.components = null;\n+ OpenLayers.Geometry.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * APIMethod: evaluate\n- * evaluates this rule for a specific feature\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n- * \n+ * APIMethod: clone\n+ * Clone this geometry.\n+ *\n * Returns:\n- * {Boolean} true if the rule applies, false if it does not.\n- * This rule is the default rule and always returns true.\n+ * {<OpenLayers.Geometry.Collection>} An exact clone of this collection\n */\n- evaluate: function(feature) {\n- var context = this.getContext(feature);\n- var applies = true;\n-\n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale();\n- }\n-\n- // check if within minScale/maxScale bounds\n- if (this.minScaleDenominator) {\n- applies = scale >= OpenLayers.Style.createLiteral(\n- this.minScaleDenominator, context);\n- }\n- if (applies && this.maxScaleDenominator) {\n- applies = scale < OpenLayers.Style.createLiteral(\n- this.maxScaleDenominator, context);\n- }\n-\n- // check if optional filter applies\n- if (applies && this.filter) {\n- // feature id filters get the feature, others get the context\n- if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n- applies = this.filter.evaluate(feature);\n- } else {\n- applies = this.filter.evaluate(context);\n- }\n+ clone: function() {\n+ var geometry = eval(\"new \" + this.CLASS_NAME + \"()\");\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ geometry.addComponent(this.components[i].clone());\n }\n \n- return applies;\n- },\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(geometry, this);\n \n- /**\n- * Method: getContext\n- * Gets the context for evaluating this rule\n- * \n- * Paramters:\n- * feature - {<OpenLayers.Feature>} feature to take the context from if\n- * none is specified.\n- */\n- getContext: function(feature) {\n- var context = this.context;\n- if (!context) {\n- context = feature.attributes || feature.data;\n- }\n- if (typeof this.context == \"function\") {\n- context = this.context(feature);\n- }\n- return context;\n+ return geometry;\n },\n \n /**\n- * APIMethod: clone\n- * Clones this rule.\n+ * Method: getComponentsString\n+ * Get a string representing the components for this collection\n * \n * Returns:\n- * {<OpenLayers.Rule>} Clone of this rule.\n+ * {String} A string representation of the components of this geometry\n */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.symbolizers) {\n- // clone symbolizers\n- var len = this.symbolizers.length;\n- options.symbolizers = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- options.symbolizers[i] = this.symbolizers[i].clone();\n- }\n- } else {\n- // clone symbolizer\n- options.symbolizer = {};\n- var value, type;\n- for (var key in this.symbolizer) {\n- value = this.symbolizer[key];\n- type = typeof value;\n- if (type === \"object\") {\n- options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n- } else if (type === \"string\") {\n- options.symbolizer[key] = value;\n- }\n- }\n+ getComponentsString: function() {\n+ var strings = [];\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ strings.push(this.components[i].toShortString());\n }\n- // clone filter\n- options.filter = this.filter && this.filter.clone();\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- return new OpenLayers.Rule(options);\n+ return strings.join(\",\");\n },\n \n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n-/* ======================================================================\n- OpenLayers/StyleMap.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Style.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.StyleMap\n- */\n-OpenLayers.StyleMap = OpenLayers.Class({\n-\n- /**\n- * Property: styles\n- * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known\n- * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n- */\n- styles: null,\n-\n- /**\n- * Property: extendDefault\n- * {Boolean} if true, every render intent will extend the symbolizers\n- * specified for the \"default\" intent at rendering time. Otherwise, every\n- * rendering intent will be treated as a completely independent style.\n- */\n- extendDefault: true,\n-\n /**\n- * Constructor: OpenLayers.StyleMap\n- * \n- * Parameters:\n- * style - {Object} Optional. Either a style hash, or a style object, or\n- * a hash of style objects (style hashes) keyed by rendering\n- * intent. If just one style hash or style object is passed,\n- * this will be used for all known render intents (default,\n- * select, temporary)\n- * options - {Object} optional hash of additional options for this\n- * instance\n+ * APIMethod: calculateBounds\n+ * Recalculate the bounds by iterating through the components and \n+ * calling calling extendBounds() on each item.\n */\n- initialize: function(style, options) {\n- this.styles = {\n- \"default\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"default\"]),\n- \"select\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"select\"]),\n- \"temporary\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"temporary\"]),\n- \"delete\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"delete\"])\n- };\n-\n- // take whatever the user passed as style parameter and convert it\n- // into parts of stylemap.\n- if (style instanceof OpenLayers.Style) {\n- // user passed a style object\n- this.styles[\"default\"] = style;\n- this.styles[\"select\"] = style;\n- this.styles[\"temporary\"] = style;\n- this.styles[\"delete\"] = style;\n- } else if (typeof style == \"object\") {\n- for (var key in style) {\n- if (style[key] instanceof OpenLayers.Style) {\n- // user passed a hash of style objects\n- this.styles[key] = style[key];\n- } else if (typeof style[key] == \"object\") {\n- // user passsed a hash of style hashes\n- this.styles[key] = new OpenLayers.Style(style[key]);\n- } else {\n- // user passed a style hash (i.e. symbolizer)\n- this.styles[\"default\"] = new OpenLayers.Style(style);\n- this.styles[\"select\"] = new OpenLayers.Style(style);\n- this.styles[\"temporary\"] = new OpenLayers.Style(style);\n- this.styles[\"delete\"] = new OpenLayers.Style(style);\n- break;\n- }\n+ calculateBounds: function() {\n+ this.bounds = null;\n+ var bounds = new OpenLayers.Bounds();\n+ var components = this.components;\n+ if (components) {\n+ for (var i = 0, len = components.length; i < len; i++) {\n+ bounds.extend(components[i].getBounds());\n }\n }\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy();\n+ // to preserve old behavior, we only set bounds if non-null\n+ // in the future, we could add bounds.isEmpty()\n+ if (bounds.left != null && bounds.bottom != null &&\n+ bounds.right != null && bounds.top != null) {\n+ this.setBounds(bounds);\n }\n- this.styles = null;\n },\n \n /**\n- * Method: createSymbolizer\n- * Creates the symbolizer for a feature for a render intent.\n- * \n+ * APIMethod: addComponents\n+ * Add components to this geometry.\n+ *\n * Parameters:\n- * feature - {<OpenLayers.Feature>} The feature to evaluate the rules\n- * of the intended style against.\n- * intent - {String} The intent determines the symbolizer that will be\n- * used to draw the feature. Well known intents are \"default\"\n- * (for just drawing the features), \"select\" (for selected\n- * features) and \"temporary\" (for drawing features).\n- * \n- * Returns:\n- * {Object} symbolizer hash\n+ * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add\n */\n- createSymbolizer: function(feature, intent) {\n- if (!feature) {\n- feature = new OpenLayers.Feature.Vector();\n- }\n- if (!this.styles[intent]) {\n- intent = \"default\";\n+ addComponents: function(components) {\n+ if (!(OpenLayers.Util.isArray(components))) {\n+ components = [components];\n }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n+ for (var i = 0, len = components.length; i < len; i++) {\n+ this.addComponent(components[i]);\n }\n- return OpenLayers.Util.extend(defaultSymbolizer,\n- this.styles[intent].createSymbolizer(feature));\n },\n \n /**\n- * Method: addUniqueValueRules\n- * Convenience method to create comparison rules for unique values of a\n- * property. The rules will be added to the style object for a specified\n- * rendering intent. This method is a shortcut for creating something like\n- * the \"unique value legends\" familiar from well known desktop GIS systems\n+ * Method: addComponent\n+ * Add a new component (geometry) to the collection. If this.componentTypes\n+ * is set, then the component class name must be in the componentTypes array.\n+ *\n+ * The bounds cache is reset.\n * \n * Parameters:\n- * renderIntent - {String} rendering intent to add the rules to\n- * property - {String} values of feature attributes to create the\n- * rules for\n- * symbolizers - {Object} Hash of symbolizers, keyed by the desired\n- * property values \n- * context - {Object} An optional object with properties that\n- * symbolizers' property values should be evaluated\n- * against. If no context is specified, feature.attributes\n- * will be used\n- */\n- addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n- var rules = [];\n- for (var value in symbolizers) {\n- rules.push(new OpenLayers.Rule({\n- symbolizer: symbolizers[value],\n- context: context,\n- filter: new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- property: property,\n- value: value\n- })\n- }));\n- }\n- this.styles[renderIntent].addRules(rules);\n- },\n-\n- CLASS_NAME: \"OpenLayers.StyleMap\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Map.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer\n- */\n-OpenLayers.Layer = OpenLayers.Class({\n-\n- /**\n- * APIProperty: id\n- * {String}\n- */\n- id: null,\n-\n- /** \n- * APIProperty: name\n- * {String}\n- */\n- name: null,\n-\n- /** \n- * APIProperty: div\n- * {DOMElement}\n- */\n- div: null,\n-\n- /**\n- * APIProperty: opacity\n- * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n- * is 1.\n- */\n- opacity: 1,\n-\n- /**\n- * APIProperty: alwaysInRange\n- * {Boolean} If a layer's display should not be scale-based, this should \n- * be set to true. This will cause the layer, as an overlay, to always \n- * be 'active', by always returning true from the calculateInRange() \n- * function. \n- * \n- * If not explicitly specified for a layer, its value will be \n- * determined on startup in initResolutions() based on whether or not \n- * any scale-specific properties have been set as options on the \n- * layer. If no scale-specific options have been set on the layer, we \n- * assume that it should always be in range.\n- * \n- * See #987 for more info.\n- */\n- alwaysInRange: null,\n-\n- /**\n- * Constant: RESOLUTION_PROPERTIES\n- * {Array} The properties that are used for calculating resolutions\n- * information.\n- */\n- RESOLUTION_PROPERTIES: [\n- 'scales', 'resolutions',\n- 'maxScale', 'minScale',\n- 'maxResolution', 'minResolution',\n- 'numZoomLevels', 'maxZoomLevel'\n- ],\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>}\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n+ * component - {<OpenLayers.Geometry>} A geometry to add\n+ * index - {int} Optional index into the array to insert the component\n *\n- * Supported map event types:\n- * loadstart - Triggered when layer loading starts. When using a Vector \n- * layer with a Fixed or BBOX strategy, the event object includes \n- * a *filter* property holding the OpenLayers.Filter used when \n- * calling read on the protocol.\n- * loadend - Triggered when layer loading ends. When using a Vector layer\n- * with a Fixed or BBOX strategy, the event object includes a \n- * *response* property holding an OpenLayers.Protocol.Response object.\n- * visibilitychanged - Triggered when the layer's visibility property is\n- * changed, e.g. by turning the layer on or off in the layer switcher.\n- * Note that the actual visibility of the layer can also change if it\n- * gets out of range (see <calculateInRange>). If you also want to catch\n- * these cases, register for the map's 'changelayer' event instead.\n- * move - Triggered when layer moves (triggered with every mousemove\n- * during a drag).\n- * moveend - Triggered when layer is done moving, object passed as\n- * argument has a zoomChanged boolean property which tells that the\n- * zoom has changed.\n- * added - Triggered after the layer is added to a map. Listeners will\n- * receive an object with a *map* property referencing the map and a\n- * *layer* property referencing the layer.\n- * removed - Triggered after the layer is removed from the map. Listeners\n- * will receive an object with a *map* property referencing the map and\n- * a *layer* property referencing the layer.\n- */\n- events: null,\n-\n- /**\n- * APIProperty: map\n- * {<OpenLayers.Map>} This variable is set when the layer is added to \n- * the map, via the accessor function setMap().\n- */\n- map: null,\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Whether or not the layer is a base layer. This should be set \n- * individually by all subclasses. Default is false\n- */\n- isBaseLayer: false,\n-\n- /**\n- * Property: alpha\n- * {Boolean} The layer's images have an alpha channel. Default is false.\n- */\n- alpha: false,\n-\n- /** \n- * APIProperty: displayInLayerSwitcher\n- * {Boolean} Display the layer's name in the layer switcher. Default is\n- * true.\n- */\n- displayInLayerSwitcher: true,\n-\n- /**\n- * APIProperty: visibility\n- * {Boolean} The layer should be displayed in the map. Default is true.\n- */\n- visibility: true,\n-\n- /**\n- * APIProperty: attribution\n- * {String} Attribution string, displayed when an \n- * <OpenLayers.Control.Attribution> has been added to the map.\n- */\n- attribution: null,\n-\n- /** \n- * Property: inRange\n- * {Boolean} The current map resolution is within the layer's min/max \n- * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom \n- * changes.\n- */\n- inRange: false,\n-\n- /**\n- * Propery: imageSize\n- * {<OpenLayers.Size>} For layers with a gutter, the image is larger than \n- * the tile by twice the gutter in each dimension.\n- */\n- imageSize: null,\n-\n- // OPTIONS\n-\n- /** \n- * Property: options\n- * {Object} An optional object whose properties will be set on the layer.\n- * Any of the layer properties can be set as a property of the options\n- * object and sent to the constructor when the layer is created.\n- */\n- options: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- */\n- eventListeners: null,\n-\n- /**\n- * APIProperty: gutter\n- * {Integer} Determines the width (in pixels) of the gutter around image\n- * tiles to ignore. By setting this property to a non-zero value,\n- * images will be requested that are wider and taller than the tile\n- * size by a value of 2 x gutter. This allows artifacts of rendering\n- * at tile edges to be ignored. Set a gutter value that is equal to\n- * half the size of the widest symbol that needs to be displayed.\n- * Defaults to zero. Non-tiled layers always have zero gutter.\n- */\n- gutter: 0,\n-\n- /**\n- * APIProperty: projection\n- * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.\n- * Can be set in the layer options. If not specified in the layer options,\n- * it is set to the default projection specified in the map,\n- * when the layer is added to the map.\n- * Projection along with default maxExtent and resolutions\n- * are set automatically with commercial baselayers in EPSG:3857,\n- * such as Google, Bing and OpenStreetMap, and do not need to be specified.\n- * Otherwise, if specifying projection, also set maxExtent,\n- * maxResolution or resolutions as appropriate.\n- * When using vector layers with strategies, layer projection should be set\n- * to the projection of the source data if that is different from the map default.\n- * \n- * Can be either a string or an <OpenLayers.Projection> object;\n- * if a string is passed, will be converted to an object when\n- * the layer is added to the map.\n- * \n- */\n- projection: null,\n-\n- /**\n- * APIProperty: units\n- * {String} The layer map units. Defaults to null. Possible values\n- * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.\n- * Normally taken from the projection.\n- * Only required if both map and layers do not define a projection,\n- * or if they define a projection which does not define units.\n- */\n- units: null,\n-\n- /**\n- * APIProperty: scales\n- * {Array} An array of map scales in descending order. The values in the\n- * array correspond to the map scale denominator. Note that these\n- * values only make sense if the display (monitor) resolution of the\n- * client is correctly guessed by whomever is configuring the\n- * application. In addition, the units property must also be set.\n- * Use <resolutions> instead wherever possible.\n- */\n- scales: null,\n-\n- /**\n- * APIProperty: resolutions\n- * {Array} A list of map resolutions (map units per pixel) in descending\n- * order. If this is not set in the layer constructor, it will be set\n- * based on other resolution related properties (maxExtent,\n- * maxResolution, maxScale, etc.).\n- */\n- resolutions: null,\n-\n- /**\n- * APIProperty: maxExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The maximum extent for the layer. Defaults to null.\n- * \n- * The center of these bounds will not stray outside\n- * of the viewport extent during panning. In addition, if\n- * <displayOutsideMaxExtent> is set to false, data will not be\n- * requested that falls completely outside of these bounds.\n- */\n- maxExtent: null,\n-\n- /**\n- * APIProperty: minExtent\n- * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n- * should consist of four values (left, bottom, right, top).\n- * The minimum extent for the layer. Defaults to null.\n- */\n- minExtent: null,\n-\n- /**\n- * APIProperty: maxResolution\n- * {Float} Default max is 360 deg / 256 px, which corresponds to\n- * zoom level 0 on gmaps. Specify a different value in the layer \n- * options if you are not using the default <OpenLayers.Map.tileSize>\n- * and displaying the whole world.\n- */\n- maxResolution: null,\n-\n- /**\n- * APIProperty: minResolution\n- * {Float}\n- */\n- minResolution: null,\n-\n- /**\n- * APIProperty: numZoomLevels\n- * {Integer}\n- */\n- numZoomLevels: null,\n-\n- /**\n- * APIProperty: minScale\n- * {Float}\n- */\n- minScale: null,\n-\n- /**\n- * APIProperty: maxScale\n- * {Float}\n- */\n- maxScale: null,\n-\n- /**\n- * APIProperty: displayOutsideMaxExtent\n- * {Boolean} Request map tiles that are completely outside of the max \n- * extent for this layer. Defaults to false.\n- */\n- displayOutsideMaxExtent: false,\n-\n- /**\n- * APIProperty: wrapDateLine\n- * {Boolean} Wraps the world at the international dateline, so the map can\n- * be panned infinitely in longitudinal direction. Only use this on the\n- * base layer, and only if the layer's maxExtent equals the world bounds.\n- * #487 for more info. \n+ * Returns:\n+ * {Boolean} The component geometry was successfully added\n */\n- wrapDateLine: false,\n+ addComponent: function(component, index) {\n+ var added = false;\n+ if (component) {\n+ if (this.componentTypes == null ||\n+ (OpenLayers.Util.indexOf(this.componentTypes,\n+ component.CLASS_NAME) > -1)) {\n \n- /**\n- * Property: metadata\n- * {Object} This object can be used to store additional information on a\n- * layer object.\n- */\n- metadata: null,\n+ if (index != null && (index < this.components.length)) {\n+ var components1 = this.components.slice(0, index);\n+ var components2 = this.components.slice(index,\n+ this.components.length);\n+ components1.push(component);\n+ this.components = components1.concat(components2);\n+ } else {\n+ this.components.push(component);\n+ }\n+ component.parent = this;\n+ this.clearBounds();\n+ added = true;\n+ }\n+ }\n+ return added;\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer\n+ * APIMethod: removeComponents\n+ * Remove components from this geometry.\n *\n * Parameters:\n- * name - {String} The layer name\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * components - {Array(<OpenLayers.Geometry>)} The components to be removed\n+ *\n+ * Returns: \n+ * {Boolean} A component was removed.\n */\n- initialize: function(name, options) {\n-\n- this.metadata = {};\n+ removeComponents: function(components) {\n+ var removed = false;\n \n- options = OpenLayers.Util.extend({}, options);\n- // make sure we respect alwaysInRange if set on the prototype\n- if (this.alwaysInRange != null) {\n- options.alwaysInRange = this.alwaysInRange;\n+ if (!(OpenLayers.Util.isArray(components))) {\n+ components = [components];\n }\n- this.addOptions(options);\n-\n- this.name = name;\n-\n- if (this.id == null) {\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n-\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.style.width = \"100%\";\n- this.div.style.height = \"100%\";\n- this.div.dir = \"ltr\";\n-\n- this.events = new OpenLayers.Events(this, this.div);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n-\n+ for (var i = components.length - 1; i >= 0; --i) {\n+ removed = this.removeComponent(components[i]) || removed;\n }\n+ return removed;\n },\n \n /**\n- * Method: destroy\n- * Destroy is a destructor: this is to alleviate cyclic references which\n- * the Javascript garbage cleaner can not take care of on its own.\n+ * Method: removeComponent\n+ * Remove a component from this geometry.\n *\n * Parameters:\n- * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n- * been destroyed. Default is true.\n+ * component - {<OpenLayers.Geometry>} \n+ *\n+ * Returns: \n+ * {Boolean} The component was removed.\n */\n- destroy: function(setNewBaseLayer) {\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true;\n- }\n- if (this.map != null) {\n- this.map.removeLayer(this, setNewBaseLayer);\n- }\n- this.projection = null;\n- this.map = null;\n- this.name = null;\n- this.div = null;\n- this.options = null;\n+ removeComponent: function(component) {\n \n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- }\n- this.eventListeners = null;\n- this.events = null;\n+ OpenLayers.Util.removeItem(this.components, component);\n+\n+ // clearBounds() so that it gets recalculated on the next call\n+ // to this.getBounds();\n+ this.clearBounds();\n+ return true;\n },\n \n /**\n- * Method: clone\n- *\n- * Parameters:\n- * obj - {<OpenLayers.Layer>} The layer to be cloned\n+ * APIMethod: getLength\n+ * Calculate the length of this geometry\n *\n * Returns:\n- * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>\n+ * {Float} The length of the geometry\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer(this.name, this.getOptions());\n+ getLength: function() {\n+ var length = 0.0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ length += this.components[i].getLength();\n }\n-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- // a cloned layer should never have its map property set\n- // because it has not been added to a map yet. \n- obj.map = null;\n-\n- return obj;\n+ return length;\n },\n \n /**\n- * Method: getOptions\n- * Extracts an object from the layer with the properties that were set as\n- * options, but updates them with the values currently set on the\n- * instance.\n- * \n+ * APIMethod: getArea\n+ * Calculate the area of this geometry. Note how this function is overridden\n+ * in <OpenLayers.Geometry.Polygon>.\n+ *\n * Returns:\n- * {Object} the <options> of the layer, representing the current state.\n+ * {Float} The area of the collection by summing its parts\n */\n- getOptions: function() {\n- var options = {};\n- for (var o in this.options) {\n- options[o] = this[o];\n+ getArea: function() {\n+ var area = 0.0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ area += this.components[i].getArea();\n }\n- return options;\n+ return area;\n },\n \n /** \n- * APIMethod: setName\n- * Sets the new layer name for this layer. Can trigger a changelayer event\n- * on the map.\n+ * APIMethod: getGeodesicArea\n+ * Calculate the approximate area of the polygon were it projected onto\n+ * the earth.\n *\n * Parameters:\n- * newName - {String} The new name.\n+ * projection - {<OpenLayers.Projection>} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n+ * \n+ * Reference:\n+ * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n+ * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n+ *\n+ * Returns:\n+ * {float} The approximate geodesic area of the geometry in square meters.\n */\n- setName: function(newName) {\n- if (newName != this.name) {\n- this.name = newName;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"name\"\n- });\n- }\n+ getGeodesicArea: function(projection) {\n+ var area = 0.0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ area += this.components[i].getGeodesicArea(projection);\n }\n+ return area;\n },\n \n /**\n- * APIMethod: addOptions\n- * \n+ * APIMethod: getCentroid\n+ *\n+ * Compute the centroid for this geometry collection.\n+ *\n * Parameters:\n- * newOptions - {Object}\n- * reinitialize - {Boolean} If set to true, and if resolution options of the\n- * current baseLayer were changed, the map will be recentered to make\n- * sure that it is displayed with a valid resolution, and a\n- * changebaselayer event will be triggered.\n+ * weighted - {Boolean} Perform the getCentroid computation recursively,\n+ * returning an area weighted average of all geometries in this collection.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>} The centroid of the collection\n */\n- addOptions: function(newOptions, reinitialize) {\n-\n- if (this.options == null) {\n- this.options = {};\n+ getCentroid: function(weighted) {\n+ if (!weighted) {\n+ return this.components.length && this.components[0].getCentroid();\n+ }\n+ var len = this.components.length;\n+ if (!len) {\n+ return false;\n }\n \n- if (newOptions) {\n- // make sure this.projection references a projection object\n- if (typeof newOptions.projection == \"string\") {\n- newOptions.projection = new OpenLayers.Projection(newOptions.projection);\n- }\n- if (newOptions.projection) {\n- // get maxResolution, units and maxExtent from projection defaults if\n- // they are not defined already\n- OpenLayers.Util.applyDefaults(newOptions,\n- OpenLayers.Projection.defaults[newOptions.projection.getCode()]);\n+ var areas = [];\n+ var centroids = [];\n+ var areaSum = 0;\n+ var minArea = Number.MAX_VALUE;\n+ var component;\n+ for (var i = 0; i < len; ++i) {\n+ component = this.components[i];\n+ var area = component.getArea();\n+ var centroid = component.getCentroid(true);\n+ if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {\n+ continue;\n }\n- // allow array for extents\n- if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n- newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);\n+ areas.push(area);\n+ areaSum += area;\n+ minArea = (area < minArea && area > 0) ? area : minArea;\n+ centroids.push(centroid);\n+ }\n+ len = areas.length;\n+ if (areaSum === 0) {\n+ // all the components in this collection have 0 area\n+ // probably a collection of points -- weight all the points the same\n+ for (var i = 0; i < len; ++i) {\n+ areas[i] = 1;\n }\n- if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n- newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);\n+ areaSum = areas.length;\n+ } else {\n+ // normalize all the areas where the smallest area will get\n+ // a value of 1\n+ for (var i = 0; i < len; ++i) {\n+ areas[i] /= minArea;\n }\n+ areaSum /= minArea;\n }\n \n- // update our copy for clone\n- OpenLayers.Util.extend(this.options, newOptions);\n-\n- // add new options to this\n- OpenLayers.Util.extend(this, newOptions);\n-\n- // get the units from the projection, if we have a projection\n- // and it it has units\n- if (this.projection && this.projection.getUnits()) {\n- this.units = this.projection.getUnits();\n- }\n-\n- // re-initialize resolutions if necessary, i.e. if any of the\n- // properties of the \"properties\" array defined below is set\n- // in the new options\n- if (this.map) {\n- // store current resolution so we can try to restore it later\n- var resolution = this.map.getResolution();\n- var properties = this.RESOLUTION_PROPERTIES.concat(\n- [\"projection\", \"units\", \"minExtent\", \"maxExtent\"]\n- );\n- for (var o in newOptions) {\n- if (newOptions.hasOwnProperty(o) &&\n- OpenLayers.Util.indexOf(properties, o) >= 0) {\n-\n- this.initResolutions();\n- if (reinitialize && this.map.baseLayer === this) {\n- // update map position, and restore previous resolution\n- this.map.setCenter(this.map.getCenter(),\n- this.map.getZoomForResolution(resolution),\n- false, true\n- );\n- // trigger a changebaselayer event to make sure that\n- // all controls (especially\n- // OpenLayers.Control.PanZoomBar) get notified of the\n- // new options\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- });\n- }\n- break;\n- }\n- }\n+ var xSum = 0,\n+ ySum = 0,\n+ centroid, area;\n+ for (var i = 0; i < len; ++i) {\n+ centroid = centroids[i];\n+ area = areas[i];\n+ xSum += centroid.x * area;\n+ ySum += centroid.y * area;\n }\n- },\n \n- /**\n- * APIMethod: onMapResize\n- * This function can be implemented by subclasses\n- */\n- onMapResize: function() {\n- //this function can be implemented by subclasses \n+ return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);\n },\n \n /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ * APIMethod: getGeodesicLength\n+ * Calculate the approximate length of the geometry were it projected onto\n+ * the earth.\n *\n+ * projection - {<OpenLayers.Projection>} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n+ * \n * Returns:\n- * {Boolean} The layer was redrawn.\n+ * {Float} The appoximate geodesic length of the geometry in meters.\n */\n- redraw: function() {\n- var redrawn = false;\n- if (this.map) {\n-\n- // min/max Range may have changed\n- this.inRange = this.calculateInRange();\n-\n- // map's center might not yet be set\n- var extent = this.getExtent();\n-\n- if (extent && this.inRange && this.visibility) {\n- var zoomChanged = true;\n- this.moveTo(extent, zoomChanged, false);\n- this.events.triggerEvent(\"moveend\", {\n- \"zoomChanged\": zoomChanged\n- });\n- redrawn = true;\n- }\n+ getGeodesicLength: function(projection) {\n+ var length = 0.0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ length += this.components[i].getGeodesicLength(projection);\n }\n- return redrawn;\n+ return length;\n },\n \n /**\n- * Method: moveTo\n- * \n+ * APIMethod: move\n+ * Moves a geometry by the given displacement along positive x and y axes.\n+ * This modifies the position of the geometry and clears the cached\n+ * bounds.\n+ *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n+ * x - {Float} Distance to move geometry in positive x direction. \n+ * y - {Float} Distance to move geometry in positive y direction.\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- var display = this.visibility;\n- if (!this.isBaseLayer) {\n- display = display && this.inRange;\n+ move: function(x, y) {\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ this.components[i].move(x, y);\n }\n- this.display(display);\n },\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector. To be implemented by subclasses.\n+ * APIMethod: rotate\n+ * Rotate a geometry around some origin\n *\n * Parameters:\n- * dx - {Number} The x coord of the displacement vector.\n- * dy - {Number} The y coord of the displacement vector.\n- */\n- moveByPx: function(dx, dy) {},\n-\n- /**\n- * Method: setMap\n- * Set the map property for the layer. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- * \n- * Here we take care to bring over any of the necessary default \n- * properties from the map. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * angle - {Float} Rotation angle in degrees (measured counterclockwise\n+ * from the positive x-axis)\n+ * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n */\n- setMap: function(map) {\n- if (this.map == null) {\n-\n- this.map = map;\n-\n- // grab some essential layer data from the map if it hasn't already\n- // been set\n- this.maxExtent = this.maxExtent || this.map.maxExtent;\n- this.minExtent = this.minExtent || this.map.minExtent;\n-\n- this.projection = this.projection || this.map.projection;\n- if (typeof this.projection == \"string\") {\n- this.projection = new OpenLayers.Projection(this.projection);\n- }\n-\n- // Check the projection to see if we can get units -- if not, refer\n- // to properties.\n- this.units = this.projection.getUnits() ||\n- this.units || this.map.units;\n-\n- this.initResolutions();\n-\n- if (!this.isBaseLayer) {\n- this.inRange = this.calculateInRange();\n- var show = ((this.visibility) && (this.inRange));\n- this.div.style.display = show ? \"\" : \"none\";\n- }\n-\n- // deal with gutters\n- this.setTileSize();\n+ rotate: function(angle, origin) {\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ this.components[i].rotate(angle, origin);\n }\n },\n \n /**\n- * Method: afterAdd\n- * Called at the end of the map.addLayer sequence. At this point, the map\n- * will have a base layer. To be overridden by subclasses.\n- */\n- afterAdd: function() {},\n-\n- /**\n- * APIMethod: removeMap\n- * Just as setMap() allows each layer the possibility to take a \n- * personalized action on being added to the map, removeMap() allows\n- * each layer to take a personalized action on being removed from it. \n- * For now, this will be mostly unused, except for the EventPane layer,\n- * which needs this hook so that it can remove the special invisible\n- * pane. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- //to be overridden by subclasses\n- },\n-\n- /**\n- * APIMethod: getImageSize\n+ * APIMethod: resize\n+ * Resize a geometry relative to some origin. Use this method to apply\n+ * a uniform scaling to a geometry.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used\n- * by subclasses that have to deal with different tile sizes at the\n- * layer extent edges (e.g. Zoomify)\n+ * scale - {Float} Factor by which to scale the geometry. A scale of 2\n+ * doubles the size of the geometry in each dimension\n+ * (lines, for example, will be twice as long, and polygons\n+ * will have four times the area).\n+ * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n * \n * Returns:\n- * {<OpenLayers.Size>} The size that the image should be, taking into \n- * account gutters.\n- */\n- getImageSize: function(bounds) {\n- return (this.imageSize || this.tileSize);\n- },\n-\n- /**\n- * APIMethod: setTileSize\n- * Set the tile size based on the map size. This also sets layer.imageSize\n- * or use by Tile.Image.\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>}\n+ * {<OpenLayers.Geometry>} - The current geometry. \n */\n- setTileSize: function(size) {\n- var tileSize = (size) ? size :\n- ((this.tileSize) ? this.tileSize :\n- this.map.getTileSize());\n- this.tileSize = tileSize;\n- if (this.gutter) {\n- // layers with gutters need non-null tile sizes\n- //if(tileSize == null) {\n- // OpenLayers.console.error(\"Error in layer.setMap() for \" +\n- // this.name + \": layers with \" +\n- // \"gutters need non-null tile sizes\");\n- //}\n- this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter),\n- tileSize.h + (2 * this.gutter));\n+ resize: function(scale, origin, ratio) {\n+ for (var i = 0; i < this.components.length; ++i) {\n+ this.components[i].resize(scale, origin, ratio);\n }\n+ return this;\n },\n \n /**\n- * APIMethod: getVisibility\n- * \n- * Returns:\n- * {Boolean} The layer should be displayed (if in range).\n- */\n- getVisibility: function() {\n- return this.visibility;\n- },\n-\n- /** \n- * APIMethod: setVisibility\n- * Set the visibility flag for the layer and hide/show & redraw \n- * accordingly. Fire event unless otherwise specified\n- * \n- * Note that visibility is no longer simply whether or not the layer's\n- * style.display is set to \"block\". Now we store a 'visibility' state \n- * property on the layer class, this allows us to remember whether or \n- * not we *desire* for a layer to be visible. In the case where the \n- * map's resolution is out of the layer's range, this desire may be \n- * subverted.\n- * \n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n+ *\n * Parameters:\n- * visibility - {Boolean} Whether or not to display the layer (if in range)\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n+ *\n+ * Valid options:\n+ * details - {Boolean} Return details from the distance calculation.\n+ * Default is false.\n+ * edge - {Boolean} Calculate the distance from this geometry to the\n+ * nearest edge of the target geometry. Default is true. If true,\n+ * calling distanceTo from a geometry that is wholly contained within\n+ * the target will result in a non-zero distance. If false, whenever\n+ * geometries intersect, calling distanceTo will return 0. If false,\n+ * details cannot be returned.\n+ *\n+ * Returns:\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and y1 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n */\n- setVisibility: function(visibility) {\n- if (visibility != this.visibility) {\n- this.visibility = visibility;\n- this.display(visibility);\n- this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"visibility\"\n- });\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var result, best, distance;\n+ var min = Number.POSITIVE_INFINITY;\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ result = this.components[i].distanceTo(geometry, options);\n+ distance = details ? result.distance : result;\n+ if (distance < min) {\n+ min = distance;\n+ best = result;\n+ if (min == 0) {\n+ break;\n+ }\n }\n- this.events.triggerEvent(\"visibilitychanged\");\n }\n+ return best;\n },\n \n /** \n- * APIMethod: display\n- * Hide or show the Layer. This is designed to be used internally, and \n- * is not generally the way to enable or disable the layer. For that,\n- * use the setVisibility function instead..\n+ * APIMethod: equals\n+ * Determine whether another geometry is equivalent to this one. Geometries\n+ * are considered equivalent if all components have the same coordinates.\n * \n * Parameters:\n- * display - {Boolean}\n- */\n- display: function(display) {\n- if (display != (this.div.style.display != \"none\")) {\n- this.div.style.display = (display && this.calculateInRange()) ? \"block\" : \"none\";\n- }\n- },\n-\n- /**\n- * APIMethod: calculateInRange\n- * \n+ * geometry - {<OpenLayers.Geometry>} The geometry to test. \n+ *\n * Returns:\n- * {Boolean} The layer is displayable at the current map's current\n- * resolution. Note that if 'alwaysInRange' is true for the layer, \n- * this function will always return true.\n+ * {Boolean} The supplied geometry is equivalent to this geometry.\n */\n- calculateInRange: function() {\n- var inRange = false;\n-\n- if (this.alwaysInRange) {\n- inRange = true;\n+ equals: function(geometry) {\n+ var equivalent = true;\n+ if (!geometry || !geometry.CLASS_NAME ||\n+ (this.CLASS_NAME != geometry.CLASS_NAME)) {\n+ equivalent = false;\n+ } else if (!(OpenLayers.Util.isArray(geometry.components)) ||\n+ (geometry.components.length != this.components.length)) {\n+ equivalent = false;\n } else {\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- inRange = ((resolution >= this.minResolution) &&\n- (resolution <= this.maxResolution));\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ if (!this.components[i].equals(geometry.components[i])) {\n+ equivalent = false;\n+ break;\n+ }\n }\n }\n- return inRange;\n+ return equivalent;\n },\n \n- /** \n- * APIMethod: setIsBaseLayer\n+ /**\n+ * APIMethod: transform\n+ * Reproject the components geometry from source to dest.\n * \n * Parameters:\n- * isBaseLayer - {Boolean}\n- */\n- setIsBaseLayer: function(isBaseLayer) {\n- if (isBaseLayer != this.isBaseLayer) {\n- this.isBaseLayer = isBaseLayer;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- });\n- }\n- }\n- },\n-\n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /********************************************************/\n-\n- /** \n- * Method: initResolutions\n- * This method's responsibility is to set up the 'resolutions' array \n- * for the layer -- this array is what the layer will use to interface\n- * between the zoom levels of the map and the resolution display \n- * of the layer.\n+ * source - {<OpenLayers.Projection>} \n+ * dest - {<OpenLayers.Projection>}\n * \n- * The user has several options that determine how the array is set up.\n- * \n- * For a detailed explanation, see the following wiki from the \n- * openlayers.org homepage:\n- * http://trac.openlayers.org/wiki/SettingZoomLevels\n+ * Returns:\n+ * {<OpenLayers.Geometry>} \n */\n- initResolutions: function() {\n-\n- // ok we want resolutions, here's our strategy:\n- //\n- // 1. if resolutions are defined in the layer config, use them\n- // 2. else, if scales are defined in the layer config then derive\n- // resolutions from these scales\n- // 3. else, attempt to calculate resolutions from maxResolution,\n- // minResolution, numZoomLevels, maxZoomLevel set in the\n- // layer config\n- // 4. if we still don't have resolutions, and if resolutions\n- // are defined in the same, use them\n- // 5. else, if scales are defined in the map then derive\n- // resolutions from these scales\n- // 6. else, attempt to calculate resolutions from maxResolution,\n- // minResolution, numZoomLevels, maxZoomLevel set in the\n- // map\n- // 7. hope for the best!\n-\n- var i, len, p;\n- var props = {},\n- alwaysInRange = true;\n-\n- // get resolution data from layer config\n- // (we also set alwaysInRange in the layer as appropriate)\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p];\n- if (alwaysInRange && this.options[p]) {\n- alwaysInRange = false;\n- }\n- }\n- if (this.options.alwaysInRange == null) {\n- this.alwaysInRange = alwaysInRange;\n- }\n-\n- // if we don't have resolutions then attempt to derive them from scales\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales);\n- }\n-\n- // if we still don't have resolutions then attempt to calculate them\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props);\n- }\n-\n- // if we couldn't calculate resolutions then we look at we have\n- // in the map\n- if (props.resolutions == null) {\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p] != null ?\n- this.options[p] : this.map[p];\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales);\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props);\n- }\n- }\n-\n- // ok, we new need to set properties in the instance\n-\n- // get maxResolution from the config if it's defined there\n- var maxResolution;\n- if (this.options.maxResolution &&\n- this.options.maxResolution !== \"auto\") {\n- maxResolution = this.options.maxResolution;\n- }\n- if (this.options.minScale) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(\n- this.options.minScale, this.units);\n- }\n-\n- // get minResolution from the config if it's defined there\n- var minResolution;\n- if (this.options.minResolution &&\n- this.options.minResolution !== \"auto\") {\n- minResolution = this.options.minResolution;\n- }\n- if (this.options.maxScale) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(\n- this.options.maxScale, this.units);\n- }\n-\n- if (props.resolutions) {\n-\n- //sort resolutions array descendingly\n- props.resolutions.sort(function(a, b) {\n- return (b - a);\n- });\n-\n- // if we still don't have a maxResolution get it from the\n- // resolutions array\n- if (!maxResolution) {\n- maxResolution = props.resolutions[0];\n- }\n-\n- // if we still don't have a minResolution get it from the\n- // resolutions array\n- if (!minResolution) {\n- var lastIdx = props.resolutions.length - 1;\n- minResolution = props.resolutions[lastIdx];\n- }\n- }\n-\n- this.resolutions = props.resolutions;\n- if (this.resolutions) {\n- len = this.resolutions.length;\n- this.scales = new Array(len);\n- for (i = 0; i < len; i++) {\n- this.scales[i] = OpenLayers.Util.getScaleFromResolution(\n- this.resolutions[i], this.units);\n+ transform: function(source, dest) {\n+ if (source && dest) {\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ var component = this.components[i];\n+ component.transform(source, dest);\n }\n- this.numZoomLevels = len;\n- }\n- this.minResolution = minResolution;\n- if (minResolution) {\n- this.maxScale = OpenLayers.Util.getScaleFromResolution(\n- minResolution, this.units);\n- }\n- this.maxResolution = maxResolution;\n- if (maxResolution) {\n- this.minScale = OpenLayers.Util.getScaleFromResolution(\n- maxResolution, this.units);\n+ this.bounds = null;\n }\n+ return this;\n },\n \n /**\n- * Method: resolutionsFromScales\n- * Derive resolutions from scales.\n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n *\n * Parameters:\n- * scales - {Array(Number)} Scales\n+ * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n *\n- * Returns\n- * {Array(Number)} Resolutions\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n */\n- resolutionsFromScales: function(scales) {\n- if (scales == null) {\n- return;\n- }\n- var resolutions, i, len;\n- len = scales.length;\n- resolutions = new Array(len);\n- for (i = 0; i < len; i++) {\n- resolutions[i] = OpenLayers.Util.getResolutionFromScale(\n- scales[i], this.units);\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ intersect = geometry.intersects(this.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n }\n- return resolutions;\n+ return intersect;\n },\n \n /**\n- * Method: calculateResolutions\n- * Calculate resolutions based on the provided properties.\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n *\n * Parameters:\n- * props - {Object} Properties\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n *\n * Returns:\n- * {Array({Number})} Array of resolutions.\n+ * {Array} A list of all vertices in the geometry.\n */\n- calculateResolutions: function(props) {\n-\n- var viewSize, wRes, hRes;\n-\n- // determine maxResolution\n- var maxResolution = props.maxResolution;\n- if (props.minScale != null) {\n- maxResolution =\n- OpenLayers.Util.getResolutionFromScale(props.minScale,\n- this.units);\n- } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.maxExtent.getWidth() / viewSize.w;\n- hRes = this.maxExtent.getHeight() / viewSize.h;\n- maxResolution = Math.max(wRes, hRes);\n+ getVertices: function(nodes) {\n+ var vertices = [];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ Array.prototype.push.apply(\n+ vertices, this.components[i].getVertices(nodes)\n+ );\n }\n+ return vertices;\n+ },\n \n- // determine minResolution\n- var minResolution = props.minResolution;\n- if (props.maxScale != null) {\n- minResolution =\n- OpenLayers.Util.getResolutionFromScale(props.maxScale,\n- this.units);\n- } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.minExtent.getWidth() / viewSize.w;\n- hRes = this.minExtent.getHeight() / viewSize.h;\n- minResolution = Math.max(wRes, hRes);\n- }\n \n- if (typeof maxResolution !== \"number\" &&\n- typeof minResolution !== \"number\" &&\n- this.maxExtent != null) {\n- // maxResolution for default grid sets assumes that at zoom\n- // level zero, the whole world fits on one tile.\n- var tileSize = this.map.getTileSize();\n- maxResolution = Math.max(\n- this.maxExtent.getWidth() / tileSize.w,\n- this.maxExtent.getHeight() / tileSize.h\n- );\n- }\n+ CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n+});\n+/* ======================================================================\n+ OpenLayers/Geometry/MultiPoint.js\n+ ====================================================================== */\n \n- // determine numZoomLevels\n- var maxZoomLevel = props.maxZoomLevel;\n- var numZoomLevels = props.numZoomLevels;\n- if (typeof minResolution === \"number\" &&\n- typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n- var ratio = maxResolution / minResolution;\n- numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;\n- } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n- numZoomLevels = maxZoomLevel + 1;\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- // are we able to calculate resolutions?\n- if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 ||\n- (typeof maxResolution !== \"number\" &&\n- typeof minResolution !== \"number\")) {\n- return;\n- }\n+/**\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ */\n \n- // now we have numZoomLevels and at least one of maxResolution\n- // or minResolution, we can populate the resolutions array\n+/**\n+ * Class: OpenLayers.Geometry.MultiPoint\n+ * MultiPoint is a collection of Points. Create a new instance with the\n+ * <OpenLayers.Geometry.MultiPoint> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Geometry.Collection>\n+ * - <OpenLayers.Geometry>\n+ */\n+OpenLayers.Geometry.MultiPoint = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\n \n- var resolutions = new Array(numZoomLevels);\n- var base = 2;\n- if (typeof minResolution == \"number\" &&\n- typeof maxResolution == \"number\") {\n- // if maxResolution and minResolution are set, we calculate\n- // the base for exponential scaling that starts at\n- // maxResolution and ends at minResolution in numZoomLevels\n- // steps.\n- base = Math.pow(\n- (maxResolution / minResolution),\n- (1 / (numZoomLevels - 1))\n- );\n- }\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n \n- var i;\n- if (typeof maxResolution === \"number\") {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[i] = maxResolution / Math.pow(base, i);\n- }\n- } else {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[numZoomLevels - 1 - i] =\n- minResolution * Math.pow(base, i);\n- }\n- }\n+ /**\n+ * Constructor: OpenLayers.Geometry.MultiPoint\n+ * Create a new MultiPoint Geometry\n+ *\n+ * Parameters:\n+ * components - {Array(<OpenLayers.Geometry.Point>)} \n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.MultiPoint>}\n+ */\n \n- return resolutions;\n- },\n+ /**\n+ * APIMethod: addPoint\n+ * Wrapper for <OpenLayers.Geometry.Collection.addComponent>\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} Point to be added\n+ * index - {Integer} Optional index\n+ */\n+ addPoint: function(point, index) {\n+ this.addComponent(point, index);\n+ },\n \n- /**\n- * APIMethod: getResolution\n- * \n- * Returns:\n- * {Float} The currently selected resolution of the map, taken from the\n- * resolutions array, indexed by current zoom level.\n- */\n- getResolution: function() {\n- var zoom = this.map.getZoom();\n- return this.getResolutionForZoom(zoom);\n- },\n+ /**\n+ * APIMethod: removePoint\n+ * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>} Point to be removed\n+ */\n+ removePoint: function(point) {\n+ this.removeComponent(point);\n+ },\n \n- /** \n- * APIMethod: getExtent\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n- * bounds of the current viewPort.\n- */\n- getExtent: function() {\n- // just use stock map calculateBounds function -- passing no arguments\n- // means it will user map's current center & resolution\n- //\n- return this.map.calculateBounds();\n- },\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Geometry/Curve.js\n+ ====================================================================== */\n \n- /**\n- * APIMethod: getZoomForExtent\n- * \n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * closest - {Boolean} Find the zoom level that most closely fits the \n- * specified bounds. Note that this may result in a zoom that does \n- * not exactly contain the entire extent.\n- * Default is false.\n- *\n- * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * for the passed-in extent. We do this by calculating the ideal \n- * resolution for the given extent (based on the map size) and then \n- * calling getZoomForResolution(), passing along the 'closest'\n- * parameter.\n- */\n- getZoomForExtent: function(extent, closest) {\n- var viewSize = this.map.getSize();\n- var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n- extent.getHeight() / viewSize.h);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- return this.getZoomForResolution(idealResolution, closest);\n- },\n+/**\n+ * @requires OpenLayers/Geometry/MultiPoint.js\n+ */\n \n- /** \n- * Method: getDataExtent\n- * Calculates the max extent which includes all of the data for the layer.\n- * This function is to be implemented by subclasses.\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getDataExtent: function() {\n- //to be implemented by subclasses\n- },\n+/**\n+ * Class: OpenLayers.Geometry.Curve\n+ * A Curve is a MultiPoint, whose points are assumed to be connected. To \n+ * this end, we provide a \"getLength()\" function, which iterates through \n+ * the points, summing the distances between them. \n+ * \n+ * Inherits: \n+ * - <OpenLayers.Geometry.MultiPoint>\n+ */\n+OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\n \n /**\n- * APIMethod: getResolutionForZoom\n- * \n- * Parameters:\n- * zoom - {Float}\n- * \n- * Returns:\n- * {Float} A suitable resolution for the specified zoom.\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of \n+ * components that the collection can include. A null \n+ * value means the component types are not restricted.\n */\n- getResolutionForZoom: function(zoom) {\n- zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n- var resolution;\n- if (this.map.fractionalZoom) {\n- var low = Math.floor(zoom);\n- var high = Math.ceil(zoom);\n- resolution = this.resolutions[low] -\n- ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n- } else {\n- resolution = this.resolutions[Math.round(zoom)];\n- }\n- return resolution;\n- },\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n \n /**\n- * APIMethod: getZoomForResolution\n+ * Constructor: OpenLayers.Geometry.Curve\n * \n * Parameters:\n- * resolution - {Float}\n- * closest - {Boolean} Find the zoom level that corresponds to the absolute \n- * closest resolution, which may result in a zoom whose corresponding\n- * resolution is actually smaller than we would have desired (if this\n- * is being called from a getZoomForExtent() call, then this means that\n- * the returned zoom index might not actually contain the entire \n- * extent specified... but it'll be close).\n- * Default is false.\n- * \n- * Returns:\n- * {Integer} The index of the zoomLevel (entry in the resolutions array) \n- * that corresponds to the best fit resolution given the passed in \n- * value and the 'closest' specification.\n+ * point - {Array(<OpenLayers.Geometry.Point>)}\n */\n- getZoomForResolution: function(resolution, closest) {\n- var zoom, i, len;\n- if (this.map.fractionalZoom) {\n- var lowZoom = 0;\n- var highZoom = this.resolutions.length - 1;\n- var highRes = this.resolutions[lowZoom];\n- var lowRes = this.resolutions[highZoom];\n- var res;\n- for (i = 0, len = this.resolutions.length; i < len; ++i) {\n- res = this.resolutions[i];\n- if (res >= resolution) {\n- highRes = res;\n- lowZoom = i;\n- }\n- if (res <= resolution) {\n- lowRes = res;\n- highZoom = i;\n- break;\n- }\n- }\n- var dRes = highRes - lowRes;\n- if (dRes > 0) {\n- zoom = lowZoom + ((highRes - resolution) / dRes);\n- } else {\n- zoom = lowZoom;\n- }\n- } else {\n- var diff;\n- var minDiff = Number.POSITIVE_INFINITY;\n- for (i = 0, len = this.resolutions.length; i < len; i++) {\n- if (closest) {\n- diff = Math.abs(this.resolutions[i] - resolution);\n- if (diff > minDiff) {\n- break;\n- }\n- minDiff = diff;\n- } else {\n- if (this.resolutions[i] < resolution) {\n- break;\n- }\n- }\n- }\n- zoom = Math.max(0, i - 1);\n- }\n- return zoom;\n- },\n \n /**\n- * APIMethod: getLonLatFromViewPortPx\n+ * APIMethod: getLength\n * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n- * an object with a 'x'\n- * and 'y' properties.\n- *\n * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in \n- * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.\n+ * {Float} The length of the curve\n */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- var map = this.map;\n- if (viewPortPx != null && map.minPx) {\n- var res = map.getResolution();\n- var maxExtent = map.getMaxExtent({\n- restricted: true\n- });\n- var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n- var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n- lonlat = new OpenLayers.LonLat(lon, lat);\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n+ getLength: function() {\n+ var length = 0.0;\n+ if (this.components && (this.components.length > 1)) {\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ length += this.components[i - 1].distanceTo(this.components[i]);\n }\n }\n- return lonlat;\n+ return length;\n },\n \n /**\n- * APIMethod: getViewPortPxFromLonLat\n- * Returns a pixel location given a map location. This method will return\n- * fractional pixel values.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or\n- * an object with a 'lon'\n- * and 'lat' properties.\n+ * APIMethod: getGeodesicLength\n+ * Calculate the approximate length of the geometry were it projected onto\n+ * the earth.\n *\n- * Returns: \n- * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in \n- * lonlat translated into view port pixels.\n- */\n- getViewPortPxFromLonLat: function(lonlat, resolution) {\n- var px = null;\n- if (lonlat != null) {\n- resolution = resolution || this.map.getResolution();\n- var extent = this.map.calculateBounds(null, resolution);\n- px = new OpenLayers.Pixel(\n- (1 / resolution * (lonlat.lon - extent.left)),\n- (1 / resolution * (extent.top - lonlat.lat))\n- );\n- }\n- return px;\n- },\n-\n- /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n+ * projection - {<OpenLayers.Projection>} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n * \n- * Parameters:\n- * opacity - {Float}\n+ * Returns:\n+ * {Float} The appoximate geodesic length of the geometry in meters.\n */\n- setOpacity: function(opacity) {\n- if (opacity != this.opacity) {\n- this.opacity = opacity;\n- var childNodes = this.div.childNodes;\n- for (var i = 0, len = childNodes.length; i < len; ++i) {\n- var element = childNodes[i].firstChild || childNodes[i];\n- var lastChild = childNodes[i].lastChild;\n- //TODO de-uglify this\n- if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n- element = lastChild.parentNode;\n- }\n- OpenLayers.Util.modifyDOMElement(element, null, null, null,\n- null, null, null, opacity);\n+ getGeodesicLength: function(projection) {\n+ var geom = this; // so we can work with a clone if needed\n+ if (projection) {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ if (!gg.equals(projection)) {\n+ geom = this.clone().transform(projection, gg);\n }\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n+ }\n+ var length = 0.0;\n+ if (geom.components && (geom.components.length > 1)) {\n+ var p1, p2;\n+ for (var i = 1, len = geom.components.length; i < len; i++) {\n+ p1 = geom.components[i - 1];\n+ p2 = geom.components[i];\n+ // this returns km and requires lon/lat properties\n+ length += OpenLayers.Util.distVincenty({\n+ lon: p1.x,\n+ lat: p1.y\n+ }, {\n+ lon: p2.x,\n+ lat: p2.y\n });\n }\n }\n+ // convert to m\n+ return length * 1000;\n },\n \n- /**\n- * Method: getZIndex\n- * \n- * Returns: \n- * {Integer} the z-index of this layer\n- */\n- getZIndex: function() {\n- return this.div.style.zIndex;\n- },\n-\n- /**\n- * Method: setZIndex\n- * \n- * Parameters: \n- * zIndex - {Integer}\n- */\n- setZIndex: function(zIndex) {\n- this.div.style.zIndex = zIndex;\n- },\n-\n- /**\n- * Method: adjustBounds\n- * This function will take a bounds, and if wrapDateLine option is set\n- * on the layer, it will return a bounds which is wrapped around the \n- * world. We do not wrap for bounds which *cross* the \n- * maxExtent.left/right, only bounds which are entirely to the left \n- * or entirely to the right.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- adjustBounds: function(bounds) {\n-\n- if (this.gutter) {\n- // Adjust the extent of a bounds in map units by the \n- // layer's gutter in pixels.\n- var mapGutter = this.gutter * this.map.getResolution();\n- bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n- bounds.bottom - mapGutter,\n- bounds.right + mapGutter,\n- bounds.top + mapGutter);\n- }\n-\n- if (this.wrapDateLine) {\n- // wrap around the date line, within the limits of rounding error\n- var wrappingOptions = {\n- 'rightTolerance': this.getResolution(),\n- 'leftTolerance': this.getResolution()\n- };\n- bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n-\n- }\n- return bounds;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer\"\n+ CLASS_NAME: \"OpenLayers.Geometry.Curve\"\n });\n /* ======================================================================\n- OpenLayers/Layer/HTTPRequest.js\n+ OpenLayers/Geometry/LineString.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Geometry/Curve.js\n */\n \n /**\n- * Class: OpenLayers.Layer.HTTPRequest\n+ * Class: OpenLayers.Geometry.LineString\n+ * A LineString is a Curve which, once two points have been added to it, can \n+ * never be less than two points long.\n * \n- * Inherits from: \n- * - <OpenLayers.Layer>\n+ * Inherits from:\n+ * - <OpenLayers.Geometry.Curve>\n */\n-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /** \n- * Constant: URL_HASH_FACTOR\n- * {Float} Used to hash URL param strings for multi-WMS server selection.\n- * Set to the Golden Ratio per Knuth's recommendation.\n- */\n- URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n-\n- /** \n- * Property: url\n- * {Array(String) or String} This is either an array of url strings or \n- * a single url string. \n- */\n- url: null,\n+OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {\n \n- /** \n- * Property: params\n- * {Object} Hashtable of key/value parameters\n+ /**\n+ * Constructor: OpenLayers.Geometry.LineString\n+ * Create a new LineString geometry\n+ *\n+ * Parameters:\n+ * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to\n+ * generate the linestring\n+ *\n */\n- params: null,\n \n- /** \n- * APIProperty: reproject\n- * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n- * for information on the replacement for this functionality. \n- * {Boolean} Whether layer should reproject itself based on base layer \n- * locations. This allows reprojection onto commercial layers. \n- * Default is false: Most layers can't reproject, but layers \n- * which can create non-square geographic pixels can, like WMS.\n- * \n+ /**\n+ * APIMethod: removeComponent\n+ * Only allows removal of a point if there are three or more points in \n+ * the linestring. (otherwise the result would be just a single point)\n+ *\n+ * Parameters: \n+ * point - {<OpenLayers.Geometry.Point>} The point to be removed\n+ *\n+ * Returns: \n+ * {Boolean} The component was removed.\n */\n- reproject: false,\n+ removeComponent: function(point) {\n+ var removed = this.components && (this.components.length > 2);\n+ if (removed) {\n+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n+ arguments);\n+ }\n+ return removed;\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.HTTPRequest\n- * \n+ * APIMethod: intersects\n+ * Test for instersection between two geometries. This is a cheapo\n+ * implementation of the Bently-Ottmann algorigithm. It doesn't\n+ * really keep track of a sweep line data structure. It is closer\n+ * to the brute force method, except that segments are sorted and\n+ * potential intersections are only calculated when bounding boxes\n+ * intersect.\n+ *\n * Parameters:\n- * name - {String}\n- * url - {Array(String) or String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this geometry.\n */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.url = url;\n- if (!this.params) {\n- this.params = OpenLayers.Util.extend({}, params);\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ var type = geometry.CLASS_NAME;\n+ if (type == \"OpenLayers.Geometry.LineString\" ||\n+ type == \"OpenLayers.Geometry.LinearRing\" ||\n+ type == \"OpenLayers.Geometry.Point\") {\n+ var segs1 = this.getSortedSegments();\n+ var segs2;\n+ if (type == \"OpenLayers.Geometry.Point\") {\n+ segs2 = [{\n+ x1: geometry.x,\n+ y1: geometry.y,\n+ x2: geometry.x,\n+ y2: geometry.y\n+ }];\n+ } else {\n+ segs2 = geometry.getSortedSegments();\n+ }\n+ var seg1, seg1x1, seg1x2, seg1y1, seg1y2,\n+ seg2, seg2y1, seg2y2;\n+ // sweep right\n+ outer: for (var i = 0, len = segs1.length; i < len; ++i) {\n+ seg1 = segs1[i];\n+ seg1x1 = seg1.x1;\n+ seg1x2 = seg1.x2;\n+ seg1y1 = seg1.y1;\n+ seg1y2 = seg1.y2;\n+ inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {\n+ seg2 = segs2[j];\n+ if (seg2.x1 > seg1x2) {\n+ // seg1 still left of seg2\n+ break;\n+ }\n+ if (seg2.x2 < seg1x1) {\n+ // seg2 still left of seg1\n+ continue;\n+ }\n+ seg2y1 = seg2.y1;\n+ seg2y2 = seg2.y2;\n+ if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {\n+ // seg2 above seg1\n+ continue;\n+ }\n+ if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {\n+ // seg2 below seg1\n+ continue;\n+ }\n+ if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {\n+ intersect = true;\n+ break outer;\n+ }\n+ }\n+ }\n+ } else {\n+ intersect = geometry.intersects(this);\n }\n+ return intersect;\n },\n \n /**\n- * APIMethod: destroy\n+ * Method: getSortedSegments\n+ *\n+ * Returns:\n+ * {Array} An array of segment objects. Segment objects have properties\n+ * x1, y1, x2, and y2. The start point is represented by x1 and y1.\n+ * The end point is represented by x2 and y2. Start and end are\n+ * ordered so that x1 < x2.\n */\n- destroy: function() {\n- this.url = null;\n- this.params = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ getSortedSegments: function() {\n+ var numSeg = this.components.length - 1;\n+ var segments = new Array(numSeg),\n+ point1, point2;\n+ for (var i = 0; i < numSeg; ++i) {\n+ point1 = this.components[i];\n+ point2 = this.components[i + 1];\n+ if (point1.x < point2.x) {\n+ segments[i] = {\n+ x1: point1.x,\n+ y1: point1.y,\n+ x2: point2.x,\n+ y2: point2.y\n+ };\n+ } else {\n+ segments[i] = {\n+ x1: point2.x,\n+ y1: point2.y,\n+ x2: point1.x,\n+ y2: point1.y\n+ };\n+ }\n+ }\n+ // more efficient to define this somewhere static\n+ function byX1(seg1, seg2) {\n+ return seg1.x1 - seg2.x1;\n+ }\n+ return segments.sort(byX1);\n },\n \n /**\n- * APIMethod: clone\n- * \n+ * Method: splitWithSegment\n+ * Split this geometry with the given segment.\n+ *\n * Parameters:\n- * obj - {Object}\n- * \n+ * seg - {Object} An object with x1, y1, x2, and y2 properties referencing\n+ * segment endpoint coordinates.\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n+ *\n+ * Valid options:\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source segment must be within the\n+ * tolerance distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of one of the source segment's\n+ * endpoints will be assumed to occur at the endpoint.\n+ *\n * Returns:\n- * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this \n- * <OpenLayers.Layer.HTTPRequest>\n+ * {Object} An object with *lines* and *points* properties. If the given\n+ * segment intersects this linestring, the lines array will reference\n+ * geometries that result from the split. The points array will contain\n+ * all intersection points. Intersection points are sorted along the\n+ * segment (in order from x1,y1 to x2,y2).\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.HTTPRequest(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n+ splitWithSegment: function(seg, options) {\n+ var edge = !(options && options.edge === false);\n+ var tolerance = options && options.tolerance;\n+ var lines = [];\n+ var verts = this.getVertices();\n+ var points = [];\n+ var intersections = [];\n+ var split = false;\n+ var vert1, vert2, point;\n+ var node, vertex, target;\n+ var interOptions = {\n+ point: true,\n+ tolerance: tolerance\n+ };\n+ var result = null;\n+ for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n+ vert1 = verts[i];\n+ points.push(vert1.clone());\n+ vert2 = verts[i + 1];\n+ target = {\n+ x1: vert1.x,\n+ y1: vert1.y,\n+ x2: vert2.x,\n+ y2: vert2.y\n+ };\n+ point = OpenLayers.Geometry.segmentsIntersect(\n+ seg, target, interOptions\n+ );\n+ if (point instanceof OpenLayers.Geometry.Point) {\n+ if ((point.x === seg.x1 && point.y === seg.y1) ||\n+ (point.x === seg.x2 && point.y === seg.y2) ||\n+ point.equals(vert1) || point.equals(vert2)) {\n+ vertex = true;\n+ } else {\n+ vertex = false;\n+ }\n+ if (vertex || edge) {\n+ // push intersections different than the previous\n+ if (!point.equals(intersections[intersections.length - 1])) {\n+ intersections.push(point.clone());\n+ }\n+ if (i === 0) {\n+ if (point.equals(vert1)) {\n+ continue;\n+ }\n+ }\n+ if (point.equals(vert2)) {\n+ continue;\n+ }\n+ split = true;\n+ if (!point.equals(vert1)) {\n+ points.push(point);\n+ }\n+ lines.push(new OpenLayers.Geometry.LineString(points));\n+ points = [point.clone()];\n+ }\n+ }\n }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n+ if (split) {\n+ points.push(vert2.clone());\n+ lines.push(new OpenLayers.Geometry.LineString(points));\n+ }\n+ if (intersections.length > 0) {\n+ // sort intersections along segment\n+ var xDir = seg.x1 < seg.x2 ? 1 : -1;\n+ var yDir = seg.y1 < seg.y2 ? 1 : -1;\n+ result = {\n+ lines: lines,\n+ points: intersections.sort(function(p1, p2) {\n+ return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);\n+ })\n+ };\n+ }\n+ return result;\n },\n \n- /** \n- * APIMethod: setUrl\n+ /**\n+ * Method: split\n+ * Use this geometry (the source) to attempt to split a target geometry.\n * \n * Parameters:\n- * newUrl - {String}\n+ * target - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n+ *\n+ * Valid options:\n+ * mutual - {Boolean} Split the source geometry in addition to the target\n+ * geometry. Default is false.\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source must be within the tolerance\n+ * distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of an existing vertex on the source\n+ * will be assumed to occur at the vertex.\n+ * \n+ * Returns:\n+ * {Array} A list of geometries (of this same type as the target) that\n+ * result from splitting the target with the source geometry. The\n+ * source and target geometry will remain unmodified. If no split\n+ * results, null will be returned. If mutual is true and a split\n+ * results, return will be an array of two arrays - the first will be\n+ * all geometries that result from splitting the source geometry and\n+ * the second will be all geometries that result from splitting the\n+ * target geometry.\n */\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n+ split: function(target, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var sourceSplit, targetSplit, sourceParts, targetParts;\n+ if (target instanceof OpenLayers.Geometry.LineString) {\n+ var verts = this.getVertices();\n+ var vert1, vert2, seg, splits, lines, point;\n+ var points = [];\n+ sourceParts = [];\n+ for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n+ vert1 = verts[i];\n+ vert2 = verts[i + 1];\n+ seg = {\n+ x1: vert1.x,\n+ y1: vert1.y,\n+ x2: vert2.x,\n+ y2: vert2.y\n+ };\n+ targetParts = targetParts || [target];\n+ if (mutual) {\n+ points.push(vert1.clone());\n+ }\n+ for (var j = 0; j < targetParts.length; ++j) {\n+ splits = targetParts[j].splitWithSegment(seg, options);\n+ if (splits) {\n+ // splice in new features\n+ lines = splits.lines;\n+ if (lines.length > 0) {\n+ lines.unshift(j, 1);\n+ Array.prototype.splice.apply(targetParts, lines);\n+ j += lines.length - 2;\n+ }\n+ if (mutual) {\n+ for (var k = 0, len = splits.points.length; k < len; ++k) {\n+ point = splits.points[k];\n+ if (!point.equals(vert1)) {\n+ points.push(point);\n+ sourceParts.push(new OpenLayers.Geometry.LineString(points));\n+ if (point.equals(vert2)) {\n+ points = [];\n+ } else {\n+ points = [point.clone()];\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ if (mutual && sourceParts.length > 0 && points.length > 0) {\n+ points.push(vert2.clone());\n+ sourceParts.push(new OpenLayers.Geometry.LineString(points));\n+ }\n+ } else {\n+ results = target.splitWith(this, options);\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true;\n+ } else {\n+ targetParts = [];\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true;\n+ } else {\n+ sourceParts = [];\n+ }\n+ if (targetSplit || sourceSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts];\n+ } else {\n+ results = targetParts;\n+ }\n+ }\n+ return results;\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * \n+ * Method: splitWith\n+ * Split this geometry (the target) with the given geometry (the source).\n+ *\n * Parameters:\n- * newParams - {Object}\n+ * geometry - {<OpenLayers.Geometry>} A geometry used to split this\n+ * geometry (the source).\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n *\n+ * Valid options:\n+ * mutual - {Boolean} Split the source geometry in addition to the target\n+ * geometry. Default is false.\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source must be within the tolerance\n+ * distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of an existing vertex on the source\n+ * will be assumed to occur at the vertex.\n+ * \n * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n+ * {Array} A list of geometries (of this same type as the target) that\n+ * result from splitting the target with the source geometry. The\n+ * source and target geometry will remain unmodified. If no split\n+ * results, null will be returned. If mutual is true and a split\n+ * results, return will be an array of two arrays - the first will be\n+ * all geometries that result from splitting the source geometry and\n+ * the second will be all geometries that result from splitting the\n+ * target geometry.\n */\n- mergeNewParams: function(newParams) {\n- this.params = OpenLayers.Util.extend(this.params, newParams);\n- var ret = this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"params\"\n- });\n- }\n- return ret;\n+ splitWith: function(geometry, options) {\n+ return geometry.split(this, options);\n+\n },\n \n /**\n- * APIMethod: redraw\n- * Redraws the layer. Returns true if the layer was redrawn, false if not.\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n *\n * Parameters:\n- * force - {Boolean} Force redraw by adding random parameter.\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n *\n * Returns:\n- * {Boolean} The layer was redrawn.\n+ * {Array} A list of all vertices in the geometry.\n */\n- redraw: function(force) {\n- if (force) {\n- return this.mergeNewParams({\n- \"_olSalt\": Math.random()\n- });\n+ getVertices: function(nodes) {\n+ var vertices;\n+ if (nodes === true) {\n+ vertices = [\n+ this.components[0],\n+ this.components[this.components.length - 1]\n+ ];\n+ } else if (nodes === false) {\n+ vertices = this.components.slice(1, this.components.length - 1);\n } else {\n- return OpenLayers.Layer.prototype.redraw.apply(this, []);\n+ vertices = this.components.slice();\n }\n+ return vertices;\n },\n \n /**\n- * Method: selectUrl\n- * selectUrl() implements the standard floating-point multiplicative\n- * hash function described by Knuth, and hashes the contents of the \n- * given param string into a float between 0 and 1. This float is then\n- * scaled to the size of the provided urls array, and used to select\n- * a URL.\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n *\n * Parameters:\n- * paramString - {String}\n- * urls - {Array(String)}\n- * \n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n+ *\n+ * Valid options:\n+ * details - {Boolean} Return details from the distance calculation.\n+ * Default is false.\n+ * edge - {Boolean} Calculate the distance from this geometry to the\n+ * nearest edge of the target geometry. Default is true. If true,\n+ * calling distanceTo from a geometry that is wholly contained within\n+ * the target will result in a non-zero distance. If false, whenever\n+ * geometries intersect, calling distanceTo will return 0. If false,\n+ * details cannot be returned.\n+ *\n * Returns:\n- * {String} An entry from the urls array, deterministically selected based\n- * on the paramString.\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n */\n- selectUrl: function(paramString, urls) {\n- var product = 1;\n- for (var i = 0, len = paramString.length; i < len; i++) {\n- product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n- product -= Math.floor(product);\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var result, best = {};\n+ var min = Number.POSITIVE_INFINITY;\n+ if (geometry instanceof OpenLayers.Geometry.Point) {\n+ var segs = this.getSortedSegments();\n+ var x = geometry.x;\n+ var y = geometry.y;\n+ var seg;\n+ for (var i = 0, len = segs.length; i < len; ++i) {\n+ seg = segs[i];\n+ result = OpenLayers.Geometry.distanceToSegment(geometry, seg);\n+ if (result.distance < min) {\n+ min = result.distance;\n+ best = result;\n+ if (min === 0) {\n+ break;\n+ }\n+ } else {\n+ // if distance increases and we cross y0 to the right of x0, no need to keep looking.\n+ if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {\n+ break;\n+ }\n+ }\n+ }\n+ if (details) {\n+ best = {\n+ distance: best.distance,\n+ x0: best.x,\n+ y0: best.y,\n+ x1: x,\n+ y1: y\n+ };\n+ } else {\n+ best = best.distance;\n+ }\n+ } else if (geometry instanceof OpenLayers.Geometry.LineString) {\n+ var segs0 = this.getSortedSegments();\n+ var segs1 = geometry.getSortedSegments();\n+ var seg0, seg1, intersection, x0, y0;\n+ var len1 = segs1.length;\n+ var interOptions = {\n+ point: true\n+ };\n+ outer: for (var i = 0, len = segs0.length; i < len; ++i) {\n+ seg0 = segs0[i];\n+ x0 = seg0.x1;\n+ y0 = seg0.y1;\n+ for (var j = 0; j < len1; ++j) {\n+ seg1 = segs1[j];\n+ intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);\n+ if (intersection) {\n+ min = 0;\n+ best = {\n+ distance: 0,\n+ x0: intersection.x,\n+ y0: intersection.y,\n+ x1: intersection.x,\n+ y1: intersection.y\n+ };\n+ break outer;\n+ } else {\n+ result = OpenLayers.Geometry.distanceToSegment({\n+ x: x0,\n+ y: y0\n+ }, seg1);\n+ if (result.distance < min) {\n+ min = result.distance;\n+ best = {\n+ distance: min,\n+ x0: x0,\n+ y0: y0,\n+ x1: result.x,\n+ y1: result.y\n+ };\n+ }\n+ }\n+ }\n+ }\n+ if (!details) {\n+ best = best.distance;\n+ }\n+ if (min !== 0) {\n+ // check the final vertex in this line's sorted segments\n+ if (seg0) {\n+ result = geometry.distanceTo(\n+ new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),\n+ options\n+ );\n+ var dist = details ? result.distance : result;\n+ if (dist < min) {\n+ if (details) {\n+ best = {\n+ distance: min,\n+ x0: result.x1,\n+ y0: result.y1,\n+ x1: result.x0,\n+ y1: result.y0\n+ };\n+ } else {\n+ best = dist;\n+ }\n+ }\n+ }\n+ }\n+ } else {\n+ best = geometry.distanceTo(this, options);\n+ // swap since target comes from this line\n+ if (details) {\n+ best = {\n+ distance: best.distance,\n+ x0: best.x1,\n+ y0: best.y1,\n+ x1: best.x0,\n+ y1: best.y0\n+ };\n+ }\n }\n- return urls[Math.floor(product * urls.length)];\n+ return best;\n },\n \n- /** \n- * Method: getFullRequestString\n- * Combine url with layer's params and these newParams. \n- * \n- * does checking on the serverPath variable, allowing for cases when it \n- * is supplied with trailing ? or &, as well as cases where not. \n+ /**\n+ * APIMethod: simplify\n+ * This function will return a simplified LineString.\n+ * Simplification is based on the Douglas-Peucker algorithm.\n *\n- * return in formatted string like this:\n- * \"server?key1=value1&key2=value2&key3=value3\"\n- * \n- * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n *\n * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n- * Returns: \n- * {String}\n+ * tolerance - {number} threshhold for simplification in map units\n+ *\n+ * Returns:\n+ * {OpenLayers.Geometry.LineString} the simplified LineString\n */\n- getFullRequestString: function(newParams, altUrl) {\n+ simplify: function(tolerance) {\n+ if (this && this !== null) {\n+ var points = this.getVertices();\n+ if (points.length < 3) {\n+ return this;\n+ }\n \n- // if not altUrl passed in, use layer's url\n- var url = altUrl || this.url;\n+ var compareNumbers = function(a, b) {\n+ return (a - b);\n+ };\n \n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ /**\n+ * Private function doing the Douglas-Peucker reduction\n+ */\n+ var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) {\n+ var maxDistance = 0;\n+ var indexFarthest = 0;\n \n- // if url is not a string, it should be an array of strings, \n- // in which case we will deterministically select one of them in \n- // order to evenly distribute requests to different urls.\n- //\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url);\n- }\n+ for (var index = firstPoint, distance; index < lastPoint; index++) {\n+ distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);\n+ if (distance > maxDistance) {\n+ maxDistance = distance;\n+ indexFarthest = index;\n+ }\n+ }\n \n- // ignore parameters that are already in the url search string\n- var urlParams =\n- OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n+ if (maxDistance > tolerance && indexFarthest != firstPoint) {\n+ //Add the largest point that exceeds the tolerance\n+ pointIndexsToKeep.push(indexFarthest);\n+ douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);\n+ douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);\n+ }\n+ };\n+\n+ /**\n+ * Private function calculating the perpendicular distance\n+ * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower\n+ */\n+ var perpendicularDistance = function(point1, point2, point) {\n+ //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle\n+ //Base = v((x1-x2)\u00b2+(x1-x2)\u00b2) *Base of Triangle*\n+ //Area = .5*Base*H *Solve for height\n+ //Height = Area/.5/Base\n+\n+ var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));\n+ var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));\n+ var height = area / bottom * 2;\n+\n+ return height;\n+ };\n+\n+ var firstPoint = 0;\n+ var lastPoint = points.length - 1;\n+ var pointIndexsToKeep = [];\n+\n+ //Add the first and last index to the keepers\n+ pointIndexsToKeep.push(firstPoint);\n+ pointIndexsToKeep.push(lastPoint);\n+\n+ //The first and the last point cannot be the same\n+ while (points[firstPoint].equals(points[lastPoint])) {\n+ lastPoint--;\n+ //Addition: the first point not equal to first point in the LineString is kept as well\n+ pointIndexsToKeep.push(lastPoint);\n }\n- }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n \n- return OpenLayers.Util.urlAppend(url, paramsString);\n+ douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);\n+ var returnPoints = [];\n+ pointIndexsToKeep.sort(compareNumbers);\n+ for (var index = 0; index < pointIndexsToKeep.length; index++) {\n+ returnPoints.push(points[pointIndexsToKeep[index]]);\n+ }\n+ return new OpenLayers.Geometry.LineString(returnPoints);\n+\n+ } else {\n+ return this;\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n+ CLASS_NAME: \"OpenLayers.Geometry.LineString\"\n });\n /* ======================================================================\n- OpenLayers/Tile.js\n+ OpenLayers/Geometry/MultiLineString.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/LineString.js\n */\n \n /**\n- * Class: OpenLayers.Tile \n- * This is a class designed to designate a single tile, however\n- * it is explicitly designed to do relatively little. Tiles store \n- * information about themselves -- such as the URL that they are related\n- * to, and their size - but do not add themselves to the layer div \n- * automatically, for example. Create a new tile with the \n- * <OpenLayers.Tile> constructor, or a subclass. \n- * \n- * TBD 3.0 - remove reference to url in above paragraph\n+ * Class: OpenLayers.Geometry.MultiLineString\n+ * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>\n+ * components.\n * \n+ * Inherits from:\n+ * - <OpenLayers.Geometry.Collection>\n+ * - <OpenLayers.Geometry> \n */\n-OpenLayers.Tile = OpenLayers.Class({\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types:\n- * beforedraw - Triggered before the tile is drawn. Used to defer\n- * drawing to an animation queue. To defer drawing, listeners need\n- * to return false, which will abort drawing. The queue handler needs\n- * to call <draw>(true) to actually draw the tile.\n- * loadstart - Triggered when tile loading starts.\n- * loadend - Triggered when tile loading ends.\n- * loaderror - Triggered before the loadend event (i.e. when the tile is\n- * still hidden) if the tile could not be loaded.\n- * reload - Triggered when an already loading tile is reloaded.\n- * unload - Triggered before a tile is unloaded.\n- */\n- events: null,\n-\n- /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n- *\n- * This options can be set in the ``tileOptions`` option from\n- * <OpenLayers.Layer.Grid>. For example, to be notified of the\n- * ``loadend`` event of each tiles:\n- * (code)\n- * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n- * tileOptions: {\n- * eventListeners: {\n- * 'loadend': function(evt) {\n- * // do something on loadend\n- * }\n- * }\n- * }\n- * });\n- * (end)\n- */\n- eventListeners: null,\n-\n- /**\n- * Property: id \n- * {String} null\n- */\n- id: null,\n-\n- /** \n- * Property: layer \n- * {<OpenLayers.Layer>} layer the tile is attached to \n- */\n- layer: null,\n-\n- /**\n- * Property: url\n- * {String} url of the request.\n- *\n- * TBD 3.0 \n- * Deprecated. The base tile class does not need an url. This should be \n- * handled in subclasses. Does not belong here.\n- */\n- url: null,\n-\n- /** \n- * APIProperty: bounds \n- * {<OpenLayers.Bounds>} null\n- */\n- bounds: null,\n-\n- /** \n- * Property: size \n- * {<OpenLayers.Size>} null\n- */\n- size: null,\n-\n- /** \n- * Property: position \n- * {<OpenLayers.Pixel>} Top Left pixel of the tile\n- */\n- position: null,\n-\n- /**\n- * Property: isLoading\n- * {Boolean} Is the tile loading?\n- */\n- isLoading: false,\n+OpenLayers.Geometry.MultiLineString = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\n \n- /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n- * there is no need for the base tile class to have a url.\n- */\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.LineString\"],\n \n- /** \n- * Constructor: OpenLayers.Tile\n- * Constructor for a new <OpenLayers.Tile> instance.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>}\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n- */\n- initialize: function(layer, position, bounds, url, size, options) {\n- this.layer = layer;\n- this.position = position.clone();\n- this.setBounds(bounds);\n- this.url = url;\n- if (size) {\n- this.size = size.clone();\n- }\n+ /**\n+ * Constructor: OpenLayers.Geometry.MultiLineString\n+ * Constructor for a MultiLineString Geometry.\n+ *\n+ * Parameters: \n+ * components - {Array(<OpenLayers.Geometry.LineString>)} \n+ *\n+ */\n \n- //give the tile a unique id based on its BBOX.\n- this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n+ /**\n+ * Method: split\n+ * Use this geometry (the source) to attempt to split a target geometry.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n+ *\n+ * Valid options:\n+ * mutual - {Boolean} Split the source geometry in addition to the target\n+ * geometry. Default is false.\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source must be within the tolerance\n+ * distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of an existing vertex on the source\n+ * will be assumed to occur at the vertex.\n+ * \n+ * Returns:\n+ * {Array} A list of geometries (of this same type as the target) that\n+ * result from splitting the target with the source geometry. The\n+ * source and target geometry will remain unmodified. If no split\n+ * results, null will be returned. If mutual is true and a split\n+ * results, return will be an array of two arrays - the first will be\n+ * all geometries that result from splitting the source geometry and\n+ * the second will be all geometries that result from splitting the\n+ * target geometry.\n+ */\n+ split: function(geometry, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var splits, sourceLine, sourceLines, sourceSplit, targetSplit;\n+ var sourceParts = [];\n+ var targetParts = [geometry];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ sourceLine = this.components[i];\n+ sourceSplit = false;\n+ for (var j = 0; j < targetParts.length; ++j) {\n+ splits = sourceLine.split(targetParts[j], options);\n+ if (splits) {\n+ if (mutual) {\n+ sourceLines = splits[0];\n+ for (var k = 0, klen = sourceLines.length; k < klen; ++k) {\n+ if (k === 0 && sourceParts.length) {\n+ sourceParts[sourceParts.length - 1].addComponent(\n+ sourceLines[k]\n+ );\n+ } else {\n+ sourceParts.push(\n+ new OpenLayers.Geometry.MultiLineString([\n+ sourceLines[k]\n+ ])\n+ );\n+ }\n+ }\n+ sourceSplit = true;\n+ splits = splits[1];\n+ }\n+ if (splits.length) {\n+ // splice in new target parts\n+ splits.unshift(j, 1);\n+ Array.prototype.splice.apply(targetParts, splits);\n+ break;\n+ }\n+ }\n+ }\n+ if (!sourceSplit) {\n+ // source line was not hit\n+ if (sourceParts.length) {\n+ // add line to existing multi\n+ sourceParts[sourceParts.length - 1].addComponent(\n+ sourceLine.clone()\n+ );\n+ } else {\n+ // create a fresh multi\n+ sourceParts = [\n+ new OpenLayers.Geometry.MultiLineString(\n+ sourceLine.clone()\n+ )\n+ ];\n+ }\n+ }\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true;\n+ } else {\n+ sourceParts = [];\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true;\n+ } else {\n+ targetParts = [];\n+ }\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts];\n+ } else {\n+ results = targetParts;\n+ }\n+ }\n+ return results;\n+ },\n \n- OpenLayers.Util.extend(this, options);\n+ /**\n+ * Method: splitWith\n+ * Split this geometry (the target) with the given geometry (the source).\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} A geometry used to split this\n+ * geometry (the source).\n+ * options - {Object} Properties of this object will be used to determine\n+ * how the split is conducted.\n+ *\n+ * Valid options:\n+ * mutual - {Boolean} Split the source geometry in addition to the target\n+ * geometry. Default is false.\n+ * edge - {Boolean} Allow splitting when only edges intersect. Default is\n+ * true. If false, a vertex on the source must be within the tolerance\n+ * distance of the intersection to be considered a split.\n+ * tolerance - {Number} If a non-null value is provided, intersections\n+ * within the tolerance distance of an existing vertex on the source\n+ * will be assumed to occur at the vertex.\n+ * \n+ * Returns:\n+ * {Array} A list of geometries (of this same type as the target) that\n+ * result from splitting the target with the source geometry. The\n+ * source and target geometry will remain unmodified. If no split\n+ * results, null will be returned. If mutual is true and a split\n+ * results, return will be an array of two arrays - the first will be\n+ * all geometries that result from splitting the source geometry and\n+ * the second will be all geometries that result from splitting the\n+ * target geometry.\n+ */\n+ splitWith: function(geometry, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n+ if (geometry instanceof OpenLayers.Geometry.LineString) {\n+ targetParts = [];\n+ sourceParts = [geometry];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ targetSplit = false;\n+ targetLine = this.components[i];\n+ for (var j = 0; j < sourceParts.length; ++j) {\n+ splits = sourceParts[j].split(targetLine, options);\n+ if (splits) {\n+ if (mutual) {\n+ sourceLines = splits[0];\n+ if (sourceLines.length) {\n+ // splice in new source parts\n+ sourceLines.unshift(j, 1);\n+ Array.prototype.splice.apply(sourceParts, sourceLines);\n+ j += sourceLines.length - 2;\n+ }\n+ splits = splits[1];\n+ if (splits.length === 0) {\n+ splits = [targetLine.clone()];\n+ }\n+ }\n+ for (var k = 0, klen = splits.length; k < klen; ++k) {\n+ if (k === 0 && targetParts.length) {\n+ targetParts[targetParts.length - 1].addComponent(\n+ splits[k]\n+ );\n+ } else {\n+ targetParts.push(\n+ new OpenLayers.Geometry.MultiLineString([\n+ splits[k]\n+ ])\n+ );\n+ }\n+ }\n+ targetSplit = true;\n+ }\n+ }\n+ if (!targetSplit) {\n+ // target component was not hit\n+ if (targetParts.length) {\n+ // add it to any existing multi-line\n+ targetParts[targetParts.length - 1].addComponent(\n+ targetLine.clone()\n+ );\n+ } else {\n+ // or start with a fresh multi-line\n+ targetParts = [\n+ new OpenLayers.Geometry.MultiLineString([\n+ targetLine.clone()\n+ ])\n+ ];\n+ }\n \n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners);\n- }\n- },\n+ }\n+ }\n+ } else {\n+ results = geometry.split(this);\n+ }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true;\n+ } else {\n+ sourceParts = [];\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true;\n+ } else {\n+ targetParts = [];\n+ }\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts];\n+ } else {\n+ results = targetParts;\n+ }\n+ }\n+ return results;\n+ },\n \n- /**\n- * Method: unload\n- * Call immediately before destroying if you are listening to tile\n- * events, so that counters are properly handled if tile is still\n- * loading at destroy-time. Will only fire an event if the tile is\n- * still loading.\n- */\n- unload: function() {\n- if (this.isLoading) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"unload\");\n- }\n- },\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Geometry/LinearRing.js\n+ ====================================================================== */\n \n- /** \n- * APIMethod: destroy\n- * Nullify references to prevent circular references and memory leaks.\n- */\n- destroy: function() {\n- this.layer = null;\n- this.bounds = null;\n- this.size = null;\n- this.position = null;\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- this.eventListeners = null;\n- this.events = null;\n- },\n+/**\n+ * @requires OpenLayers/Geometry/LineString.js\n+ */\n \n- /**\n- * Method: draw\n- * Clear whatever is currently in the tile, then return whether or not \n- * it should actually be re-drawn. This is an example implementation\n- * that can be overridden by subclasses. The minimum thing to do here\n- * is to call <clear> and return the result from <shouldDraw>.\n- *\n- * Parameters:\n- * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n- * event will be fired. This is used for drawing tiles asynchronously\n- * after drawing has been cancelled by returning false from a beforedraw\n- * listener.\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn. Returns null\n- * if a beforedraw listener returned false.\n- */\n- draw: function(force) {\n- if (!force) {\n- //clear tile's contents and mark as not drawn\n- this.clear();\n- }\n- var draw = this.shouldDraw();\n- if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n- draw = null;\n- }\n- return draw;\n- },\n+/**\n+ * Class: OpenLayers.Geometry.LinearRing\n+ * \n+ * A Linear Ring is a special LineString which is closed. It closes itself \n+ * automatically on every addPoint/removePoint by adding a copy of the first\n+ * point as the last point. \n+ * \n+ * Also, as it is the first in the line family to close itself, a getArea()\n+ * function is defined to calculate the enclosed area of the linearRing\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Geometry.LineString>\n+ */\n+OpenLayers.Geometry.LinearRing = OpenLayers.Class(\n+ OpenLayers.Geometry.LineString, {\n \n- /**\n- * Method: shouldDraw\n- * Return whether or not the tile should actually be (re-)drawn. The only\n- * case where we *wouldn't* want to draw the tile is if the tile is outside\n- * its layer's maxExtent\n- * \n- * Returns:\n- * {Boolean} Whether or not the tile should actually be drawn.\n- */\n- shouldDraw: function() {\n- var withinMaxExtent = false,\n- maxExtent = this.layer.maxExtent;\n- if (maxExtent) {\n- var map = this.layer.map;\n- var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n- if (this.bounds.intersectsBounds(maxExtent, {\n- inclusive: false,\n- worldBounds: worldBounds\n- })) {\n- withinMaxExtent = true;\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of \n+ * components that the collection can include. A null \n+ * value means the component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n+\n+ /**\n+ * Constructor: OpenLayers.Geometry.LinearRing\n+ * Linear rings are constructed with an array of points. This array\n+ * can represent a closed or open ring. If the ring is open (the last\n+ * point does not equal the first point), the constructor will close\n+ * the ring. If the ring is already closed (the last point does equal\n+ * the first point), it will be left closed.\n+ * \n+ * Parameters:\n+ * points - {Array(<OpenLayers.Geometry.Point>)} points\n+ */\n+\n+ /**\n+ * APIMethod: addComponent\n+ * Adds a point to geometry components. If the point is to be added to\n+ * the end of the components array and it is the same as the last point\n+ * already in that array, the duplicate point is not added. This has \n+ * the effect of closing the ring if it is not already closed, and \n+ * doing the right thing if it is already closed. This behavior can \n+ * be overridden by calling the method with a non-null index as the \n+ * second argument.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ * index - {Integer} Index into the array to insert the component\n+ * \n+ * Returns:\n+ * {Boolean} Was the Point successfully added?\n+ */\n+ addComponent: function(point, index) {\n+ var added = false;\n+\n+ //remove last point\n+ var lastPoint = this.components.pop();\n+\n+ // given an index, add the point\n+ // without an index only add non-duplicate points\n+ if (index != null || !point.equals(lastPoint)) {\n+ added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ arguments);\n }\n- }\n \n- return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n- },\n+ //append copy of first point\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ [firstPoint]);\n \n- /**\n- * Method: setBounds\n- * Sets the bounds on this instance\n- *\n- * Parameters:\n- * bounds {<OpenLayers.Bounds>}\n- */\n- setBounds: function(bounds) {\n- bounds = bounds.clone();\n- if (this.layer.map.baseLayer.wrapDateLine) {\n- var worldExtent = this.layer.map.getMaxExtent(),\n- tolerance = this.layer.map.getResolution();\n- bounds = bounds.wrapDateLine(worldExtent, {\n- leftTolerance: tolerance,\n- rightTolerance: tolerance\n- });\n- }\n- this.bounds = bounds;\n- },\n+ return added;\n+ },\n \n- /** \n- * Method: moveTo\n- * Reposition the tile.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * position - {<OpenLayers.Pixel>}\n- * redraw - {Boolean} Call draw method on tile after moving.\n- * Default is true\n- */\n- moveTo: function(bounds, position, redraw) {\n- if (redraw == null) {\n- redraw = true;\n- }\n+ /**\n+ * APIMethod: removeComponent\n+ * Removes a point from geometry components.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns: \n+ * {Boolean} The component was removed.\n+ */\n+ removeComponent: function(point) {\n+ var removed = this.components && (this.components.length > 3);\n+ if (removed) {\n+ //remove last point\n+ this.components.pop();\n \n- this.setBounds(bounds);\n- this.position = position.clone();\n- if (redraw) {\n- this.draw();\n- }\n- },\n+ //remove our point\n+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n+ arguments);\n+ //append copy of first point\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n+ [firstPoint]);\n+ }\n+ return removed;\n+ },\n \n- /** \n- * Method: clear\n- * Clear the tile of any bounds/position-related data so that it can \n- * be reused in a new location.\n- */\n- clear: function(draw) {\n- // to be extended by subclasses\n- },\n+ /**\n+ * APIMethod: move\n+ * Moves a geometry by the given displacement along positive x and y axes.\n+ * This modifies the position of the geometry and clears the cached\n+ * bounds.\n+ *\n+ * Parameters:\n+ * x - {Float} Distance to move geometry in positive x direction. \n+ * y - {Float} Distance to move geometry in positive y direction.\n+ */\n+ move: function(x, y) {\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ this.components[i].move(x, y);\n+ }\n+ },\n \n- CLASS_NAME: \"OpenLayers.Tile\"\n-});\n+ /**\n+ * APIMethod: rotate\n+ * Rotate a geometry around some origin\n+ *\n+ * Parameters:\n+ * angle - {Float} Rotation angle in degrees (measured counterclockwise\n+ * from the positive x-axis)\n+ * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n+ */\n+ rotate: function(angle, origin) {\n+ for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n+ this.components[i].rotate(angle, origin);\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: resize\n+ * Resize a geometry relative to some origin. Use this method to apply\n+ * a uniform scaling to a geometry.\n+ *\n+ * Parameters:\n+ * scale - {Float} Factor by which to scale the geometry. A scale of 2\n+ * doubles the size of the geometry in each dimension\n+ * (lines, for example, will be twice as long, and polygons\n+ * will have four times the area).\n+ * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Geometry>} - The current geometry. \n+ */\n+ resize: function(scale, origin, ratio) {\n+ for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n+ this.components[i].resize(scale, origin, ratio);\n+ }\n+ return this;\n+ },\n+\n+ /**\n+ * APIMethod: transform\n+ * Reproject the components geometry from source to dest.\n+ *\n+ * Parameters:\n+ * source - {<OpenLayers.Projection>}\n+ * dest - {<OpenLayers.Projection>}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Geometry>} \n+ */\n+ transform: function(source, dest) {\n+ if (source && dest) {\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ var component = this.components[i];\n+ component.transform(source, dest);\n+ }\n+ this.bounds = null;\n+ }\n+ return this;\n+ },\n+\n+ /**\n+ * APIMethod: getCentroid\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.Point>} The centroid of the collection\n+ */\n+ getCentroid: function() {\n+ if (this.components) {\n+ var len = this.components.length;\n+ if (len > 0 && len <= 2) {\n+ return this.components[0].clone();\n+ } else if (len > 2) {\n+ var sumX = 0.0;\n+ var sumY = 0.0;\n+ var x0 = this.components[0].x;\n+ var y0 = this.components[0].y;\n+ var area = -1 * this.getArea();\n+ if (area != 0) {\n+ for (var i = 0; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n+ sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n+ }\n+ var x = x0 + sumX / (6 * area);\n+ var y = y0 + sumY / (6 * area);\n+ } else {\n+ for (var i = 0; i < len - 1; i++) {\n+ sumX += this.components[i].x;\n+ sumY += this.components[i].y;\n+ }\n+ var x = sumX / (len - 1);\n+ var y = sumY / (len - 1);\n+ }\n+ return new OpenLayers.Geometry.Point(x, y);\n+ } else {\n+ return null;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: getArea\n+ * Note - The area is positive if the ring is oriented CW, otherwise\n+ * it will be negative.\n+ * \n+ * Returns:\n+ * {Float} The signed area for a ring.\n+ */\n+ getArea: function() {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 2)) {\n+ var sum = 0.0;\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sum += (b.x + c.x) * (c.y - b.y);\n+ }\n+ area = -sum / 2.0;\n+ }\n+ return area;\n+ },\n+\n+ /**\n+ * APIMethod: getGeodesicArea\n+ * Calculate the approximate area of the polygon were it projected onto\n+ * the earth. Note that this area will be positive if ring is oriented\n+ * clockwise, otherwise it will be negative.\n+ *\n+ * Parameters:\n+ * projection - {<OpenLayers.Projection>} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n+ * \n+ * Reference:\n+ * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n+ * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n+ *\n+ * Returns:\n+ * {float} The approximate signed geodesic area of the polygon in square\n+ * meters.\n+ */\n+ getGeodesicArea: function(projection) {\n+ var ring = this; // so we can work with a clone if needed\n+ if (projection) {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ if (!gg.equals(projection)) {\n+ ring = this.clone().transform(projection, gg);\n+ }\n+ }\n+ var area = 0.0;\n+ var len = ring.components && ring.components.length;\n+ if (len > 2) {\n+ var p1, p2;\n+ for (var i = 0; i < len - 1; i++) {\n+ p1 = ring.components[i];\n+ p2 = ring.components[i + 1];\n+ area += OpenLayers.Util.rad(p2.x - p1.x) *\n+ (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +\n+ Math.sin(OpenLayers.Util.rad(p2.y)));\n+ }\n+ area = area * 6378137.0 * 6378137.0 / 2.0;\n+ }\n+ return area;\n+ },\n+\n+ /**\n+ * Method: containsPoint\n+ * Test if a point is inside a linear ring. For the case where a point\n+ * is coincident with a linear ring edge, returns 1. Otherwise,\n+ * returns boolean.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns:\n+ * {Boolean | Number} The point is inside the linear ring. Returns 1 if\n+ * the point is coincident with an edge. Returns boolean otherwise.\n+ */\n+ containsPoint: function(point) {\n+ var approx = OpenLayers.Number.limitSigDigs;\n+ var digs = 14;\n+ var px = approx(point.x, digs);\n+ var py = approx(point.y, digs);\n+\n+ function getX(y, x1, y1, x2, y2) {\n+ return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;\n+ }\n+ var numSeg = this.components.length - 1;\n+ var start, end, x1, y1, x2, y2, cx, cy;\n+ var crosses = 0;\n+ for (var i = 0; i < numSeg; ++i) {\n+ start = this.components[i];\n+ x1 = approx(start.x, digs);\n+ y1 = approx(start.y, digs);\n+ end = this.components[i + 1];\n+ x2 = approx(end.x, digs);\n+ y2 = approx(end.y, digs);\n+\n+ /**\n+ * The following conditions enforce five edge-crossing rules:\n+ * 1. points coincident with edges are considered contained;\n+ * 2. an upward edge includes its starting endpoint, and\n+ * excludes its final endpoint;\n+ * 3. a downward edge excludes its starting endpoint, and\n+ * includes its final endpoint;\n+ * 4. horizontal edges are excluded; and\n+ * 5. the edge-ray intersection point must be strictly right\n+ * of the point P.\n+ */\n+ if (y1 == y2) {\n+ // horizontal edge\n+ if (py == y1) {\n+ // point on horizontal line\n+ if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert\n+ x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert\n+ // point on edge\n+ crosses = -1;\n+ break;\n+ }\n+ }\n+ // ignore other horizontal edges\n+ continue;\n+ }\n+ cx = approx(getX(py, x1, y1, x2, y2), digs);\n+ if (cx == px) {\n+ // point on line\n+ if (y1 < y2 && (py >= y1 && py <= y2) || // upward\n+ y1 > y2 && (py <= y1 && py >= y2)) { // downward\n+ // point on edge\n+ crosses = -1;\n+ break;\n+ }\n+ }\n+ if (cx <= px) {\n+ // no crossing to the right\n+ continue;\n+ }\n+ if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n+ // no crossing\n+ continue;\n+ }\n+ if (y1 < y2 && (py >= y1 && py < y2) || // upward\n+ y1 > y2 && (py < y1 && py >= y2)) { // downward\n+ ++crosses;\n+ }\n+ }\n+ var contained = (crosses == -1) ?\n+ // on edge\n+ 1 :\n+ // even (out) or odd (in)\n+ !!(crosses & 1);\n+\n+ return contained;\n+ },\n+\n+ /**\n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n+ */\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n+ intersect = geometry.intersects(this);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(\n+ this, [geometry]\n+ );\n+ } else {\n+ // check for component intersections\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = geometry.components[i].intersects(this);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ return intersect;\n+ },\n+\n+ /**\n+ * APIMethod: getVertices\n+ * Return a list of all points in this geometry.\n+ *\n+ * Parameters:\n+ * nodes - {Boolean} For lines, only return vertices that are\n+ * endpoints. If false, for lines, only vertices that are not\n+ * endpoints will be returned. If not provided, all vertices will\n+ * be returned.\n+ *\n+ * Returns:\n+ * {Array} A list of all vertices in the geometry.\n+ */\n+ getVertices: function(nodes) {\n+ return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n+ });\n /* ======================================================================\n- OpenLayers/Tile/Image.js\n+ OpenLayers/Geometry/Polygon.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+/**\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/LinearRing.js\n+ */\n \n /**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Animation.js\n- * @requires OpenLayers/Util.js\n+ * Class: OpenLayers.Geometry.Polygon \n+ * Polygon is a collection of Geometry.LinearRings. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Geometry.Collection> \n+ * - <OpenLayers.Geometry> \n */\n+OpenLayers.Geometry.Polygon = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\n+\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n+\n+ /**\n+ * Constructor: OpenLayers.Geometry.Polygon\n+ * Constructor for a Polygon geometry. \n+ * The first ring (this.component[0])is the outer bounds of the polygon and \n+ * all subsequent rings (this.component[1-n]) are internal holes.\n+ *\n+ *\n+ * Parameters:\n+ * components - {Array(<OpenLayers.Geometry.LinearRing>)} \n+ */\n+\n+ /** \n+ * APIMethod: getArea\n+ * Calculated by subtracting the areas of the internal holes from the \n+ * area of the outer hole.\n+ * \n+ * Returns:\n+ * {float} The area of the geometry\n+ */\n+ getArea: function() {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 0)) {\n+ area += Math.abs(this.components[0].getArea());\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getArea());\n+ }\n+ }\n+ return area;\n+ },\n+\n+ /** \n+ * APIMethod: getGeodesicArea\n+ * Calculate the approximate area of the polygon were it projected onto\n+ * the earth.\n+ *\n+ * Parameters:\n+ * projection - {<OpenLayers.Projection>} The spatial reference system\n+ * for the geometry coordinates. If not provided, Geographic/WGS84 is\n+ * assumed.\n+ * \n+ * Reference:\n+ * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n+ * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n+ *\n+ * Returns:\n+ * {float} The approximate geodesic area of the polygon in square meters.\n+ */\n+ getGeodesicArea: function(projection) {\n+ var area = 0.0;\n+ if (this.components && (this.components.length > 0)) {\n+ area += Math.abs(this.components[0].getGeodesicArea(projection));\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getGeodesicArea(projection));\n+ }\n+ }\n+ return area;\n+ },\n+\n+ /**\n+ * Method: containsPoint\n+ * Test if a point is inside a polygon. Points on a polygon edge are\n+ * considered inside.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns:\n+ * {Boolean | Number} The point is inside the polygon. Returns 1 if the\n+ * point is on an edge. Returns boolean otherwise.\n+ */\n+ containsPoint: function(point) {\n+ var numRings = this.components.length;\n+ var contained = false;\n+ if (numRings > 0) {\n+ // check exterior ring - 1 means on edge, boolean otherwise\n+ contained = this.components[0].containsPoint(point);\n+ if (contained !== 1) {\n+ if (contained && numRings > 1) {\n+ // check interior rings\n+ var hole;\n+ for (var i = 1; i < numRings; ++i) {\n+ hole = this.components[i].containsPoint(point);\n+ if (hole) {\n+ if (hole === 1) {\n+ // on edge\n+ contained = 1;\n+ } else {\n+ // in hole\n+ contained = false;\n+ }\n+ break;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return contained;\n+ },\n+\n+ /**\n+ * APIMethod: intersects\n+ * Determine if the input geometry intersects this one.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n+ *\n+ * Returns:\n+ * {Boolean} The input geometry intersects this one.\n+ */\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ var i, len;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry);\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n+ geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ // check if rings/linestrings intersect\n+ for (i = 0, len = this.components.length; i < len; ++i) {\n+ intersect = geometry.intersects(this.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ if (!intersect) {\n+ // check if this poly contains points of the ring/linestring\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.containsPoint(geometry.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ } else {\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.intersects(geometry.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ // check case where this poly is wholly contained by another\n+ if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n+ // exterior ring points will be contained in the other geometry\n+ var ring = this.components[0];\n+ for (i = 0, len = ring.components.length; i < len; ++i) {\n+ intersect = geometry.containsPoint(ring.components[i]);\n+ if (intersect) {\n+ break;\n+ }\n+ }\n+ }\n+ return intersect;\n+ },\n+\n+ /**\n+ * APIMethod: distanceTo\n+ * Calculate the closest distance between two geometries (on the x-y plane).\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} The target geometry.\n+ * options - {Object} Optional properties for configuring the distance\n+ * calculation.\n+ *\n+ * Valid options:\n+ * details - {Boolean} Return details from the distance calculation.\n+ * Default is false.\n+ * edge - {Boolean} Calculate the distance from this geometry to the\n+ * nearest edge of the target geometry. Default is true. If true,\n+ * calling distanceTo from a geometry that is wholly contained within\n+ * the target will result in a non-zero distance. If false, whenever\n+ * geometries intersect, calling distanceTo will return 0. If false,\n+ * details cannot be returned.\n+ *\n+ * Returns:\n+ * {Number | Object} The distance between this geometry and the target.\n+ * If details is true, the return will be an object with distance,\n+ * x0, y0, x1, and y1 properties. The x0 and y0 properties represent\n+ * the coordinates of the closest point on this geometry. The x1 and y1\n+ * properties represent the coordinates of the closest point on the\n+ * target geometry.\n+ */\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var result;\n+ // this is the case where we might not be looking for distance to edge\n+ if (!edge && this.intersects(geometry)) {\n+ result = 0;\n+ } else {\n+ result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(\n+ this, [geometry, options]\n+ );\n+ }\n+ return result;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n+ });\n \n /**\n- * Class: OpenLayers.Tile.Image\n- * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n- * used by various layers. Create a new image tile with the\n- * <OpenLayers.Tile.Image> constructor.\n+ * APIMethod: createRegularPolygon\n+ * Create a regular polygon around a radius. Useful for creating circles \n+ * and the like.\n *\n+ * Parameters:\n+ * origin - {<OpenLayers.Geometry.Point>} center of polygon.\n+ * radius - {Float} distance to vertex, in map units.\n+ * sides - {Integer} Number of sides. 20 approximates a circle.\n+ * rotation - {Float} original angle of rotation, in degrees.\n+ */\n+OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n+ var angle = Math.PI * ((1 / sides) - (1 / 2));\n+ if (rotation) {\n+ angle += (rotation / 180) * Math.PI;\n+ }\n+ var rotatedAngle, x, y;\n+ var points = [];\n+ for (var i = 0; i < sides; ++i) {\n+ rotatedAngle = angle + (i * 2 * Math.PI / sides);\n+ x = origin.x + (radius * Math.cos(rotatedAngle));\n+ y = origin.y + (radius * Math.sin(rotatedAngle));\n+ points.push(new OpenLayers.Geometry.Point(x, y));\n+ }\n+ var ring = new OpenLayers.Geometry.LinearRing(points);\n+ return new OpenLayers.Geometry.Polygon([ring]);\n+};\n+/* ======================================================================\n+ OpenLayers/Geometry/MultiPolygon.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Geometry/Collection.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Geometry.MultiPolygon\n+ * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>\n+ * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>\n+ * constructor.\n+ * \n * Inherits from:\n- * - <OpenLayers.Tile>\n+ * - <OpenLayers.Geometry.Collection>\n */\n-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n+OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(\n+ OpenLayers.Geometry.Collection, {\n \n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the tile.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * tile.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to the <OpenLayers.Tile> events):\n- * beforeload - Triggered before an image is prepared for loading, when the\n- * url for the image is known already. Listeners may call <setImage> on\n- * the tile instance. If they do so, that image will be used and no new\n- * one will be created.\n- */\n+ /**\n+ * Property: componentTypes\n+ * {Array(String)} An array of class names representing the types of\n+ * components that the collection can include. A null value means the\n+ * component types are not restricted.\n+ */\n+ componentTypes: [\"OpenLayers.Geometry.Polygon\"],\n \n- /** \n- * APIProperty: url\n- * {String} The URL of the image being requested. No default. Filled in by\n- * layer.getURL() function. May be modified by loadstart listeners.\n- */\n- url: null,\n+ /**\n+ * Constructor: OpenLayers.Geometry.MultiPolygon\n+ * Create a new MultiPolygon geometry\n+ *\n+ * Parameters:\n+ * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons\n+ * used to generate the MultiPolygon\n+ *\n+ */\n \n- /** \n- * Property: imgDiv\n- * {HTMLImageElement} The image for this tile.\n- */\n- imgDiv: null,\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/GeoJSON.js\n+ ====================================================================== */\n \n- /**\n- * Property: frame\n- * {DOMElement} The image element is appended to the frame. Any gutter on\n- * the image will be hidden behind the frame. If no gutter is set,\n- * this will be null.\n- */\n- frame: null,\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /** \n- * Property: imageReloadAttempts\n- * {Integer} Attempts to load the image.\n- */\n- imageReloadAttempts: null,\n+/**\n+ * @requires OpenLayers/Format/JSON.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/MultiPoint.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/MultiLineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n+ * @requires OpenLayers/Geometry/MultiPolygon.js\n+ * @requires OpenLayers/Console.js\n+ */\n \n- /**\n- * Property: layerAlphaHack\n- * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n- */\n- layerAlphaHack: null,\n+/**\n+ * Class: OpenLayers.Format.GeoJSON\n+ * Read and write GeoJSON. Create a new parser with the\n+ * <OpenLayers.Format.GeoJSON> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.JSON>\n+ */\n+OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n \n /**\n- * Property: asyncRequestId\n- * {Integer} ID of an request to see if request is still valid. This is a\n- * number which increments by 1 for each asynchronous request.\n+ * APIProperty: ignoreExtraDims\n+ * {Boolean} Ignore dimensions higher than 2 when reading geometry\n+ * coordinates.\n */\n- asyncRequestId: null,\n+ ignoreExtraDims: false,\n \n /**\n- * APIProperty: maxGetUrlLength\n- * {Number} If set, requests that would result in GET urls with more\n- * characters than the number provided will be made using form-encoded\n- * HTTP POST. It is good practice to avoid urls that are longer than 2048\n- * characters.\n+ * Constructor: OpenLayers.Format.GeoJSON\n+ * Create a new parser for GeoJSON.\n *\n- * Caution:\n- * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n- * Opera versions do not fully support this option. On all browsers,\n- * transition effects are not supported if POST requests are used.\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n */\n- maxGetUrlLength: null,\n \n /**\n- * Property: canvasContext\n- * {CanvasRenderingContext2D} A canvas context associated with\n- * the tile image.\n+ * APIMethod: read\n+ * Deserialize a GeoJSON string.\n+ *\n+ * Parameters:\n+ * json - {String} A GeoJSON string\n+ * type - {String} Optional string that determines the structure of\n+ * the output. Supported values are \"Geometry\", \"Feature\", and\n+ * \"FeatureCollection\". If absent or null, a default of\n+ * \"FeatureCollection\" is assumed.\n+ * filter - {Function} A function which will be called for every key and\n+ * value at every level of the final result. Each value will be\n+ * replaced by the result of the filter function. This can be used to\n+ * reform generic objects into instances of classes, or to transform\n+ * date strings into Date objects.\n+ *\n+ * Returns: \n+ * {Object} The return depends on the value of the type argument. If type\n+ * is \"FeatureCollection\" (the default), the return will be an array\n+ * of <OpenLayers.Feature.Vector>. If type is \"Geometry\", the input json\n+ * must represent a single geometry, and the return will be an\n+ * <OpenLayers.Geometry>. If type is \"Feature\", the input json must\n+ * represent a single feature, and the return will be an\n+ * <OpenLayers.Feature.Vector>.\n */\n- canvasContext: null,\n+ read: function(json, type, filter) {\n+ type = (type) ? type : \"FeatureCollection\";\n+ var results = null;\n+ var obj = null;\n+ if (typeof json == \"string\") {\n+ obj = OpenLayers.Format.JSON.prototype.read.apply(this,\n+ [json, filter]);\n+ } else {\n+ obj = json;\n+ }\n+ if (!obj) {\n+ OpenLayers.Console.error(\"Bad JSON: \" + json);\n+ } else if (typeof(obj.type) != \"string\") {\n+ OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json);\n+ } else if (this.isValidType(obj, type)) {\n+ switch (type) {\n+ case \"Geometry\":\n+ try {\n+ results = this.parseGeometry(obj);\n+ } catch (err) {\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"Feature\":\n+ try {\n+ results = this.parseFeature(obj);\n+ results.type = \"Feature\";\n+ } catch (err) {\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ // for type FeatureCollection, we allow input to be any type\n+ results = [];\n+ switch (obj.type) {\n+ case \"Feature\":\n+ try {\n+ results.push(this.parseFeature(obj));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ for (var i = 0, len = obj.features.length; i < len; ++i) {\n+ try {\n+ results.push(this.parseFeature(obj.features[i]));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ }\n+ break;\n+ default:\n+ try {\n+ var geom = this.parseGeometry(obj);\n+ results.push(new OpenLayers.Feature.Vector(geom));\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err);\n+ }\n+ }\n+ break;\n+ }\n+ }\n+ return results;\n+ },\n \n /**\n- * APIProperty: crossOriginKeyword\n- * The value of the crossorigin keyword to use when loading images. This is\n- * only relevant when using <getCanvasContext> for tiles from remote\n- * origins and should be set to either 'anonymous' or 'use-credentials'\n- * for servers that send Access-Control-Allow-Origin headers with their\n- * tiles.\n- */\n- crossOriginKeyword: null,\n-\n- /** TBD 3.0 - reorder the parameters to the init function to remove \n- * URL. the getUrl() function on the layer gets called on \n- * each draw(), so no need to specify it here.\n+ * Method: isValidType\n+ * Check if a GeoJSON object is a valid representative of the given type.\n+ *\n+ * Returns:\n+ * {Boolean} The object is valid GeoJSON object of the given type.\n */\n+ isValidType: function(obj, type) {\n+ var valid = false;\n+ switch (type) {\n+ case \"Geometry\":\n+ if (OpenLayers.Util.indexOf(\n+ [\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\",\n+ \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"\n+ ],\n+ obj.type) == -1) {\n+ // unsupported geometry type\n+ OpenLayers.Console.error(\"Unsupported geometry type: \" +\n+ obj.type);\n+ } else {\n+ valid = true;\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ // allow for any type to be converted to a feature collection\n+ valid = true;\n+ break;\n+ default:\n+ // for Feature types must match\n+ if (obj.type == type) {\n+ valid = true;\n+ } else {\n+ OpenLayers.Console.error(\"Cannot convert types from \" +\n+ obj.type + \" to \" + type);\n+ }\n+ }\n+ return valid;\n+ },\n \n- /** \n- * Constructor: OpenLayers.Tile.Image\n- * Constructor for a new <OpenLayers.Tile.Image> instance.\n- * \n+ /**\n+ * Method: parseFeature\n+ * Convert a feature object from GeoJSON into an\n+ * <OpenLayers.Feature.Vector>.\n+ *\n * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>} Deprecated. Remove me in 3.0.\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n+ * obj - {Object} An object created from a GeoJSON object\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A feature.\n */\n- initialize: function(layer, position, bounds, url, size, options) {\n- OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n-\n- this.url = url; //deprecated remove me\n-\n- this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n-\n- if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n- // only create frame if it's needed\n- this.frame = document.createElement(\"div\");\n- this.frame.style.position = \"absolute\";\n- this.frame.style.overflow = \"hidden\";\n+ parseFeature: function(obj) {\n+ var feature, geometry, attributes, bbox;\n+ attributes = (obj.properties) ? obj.properties : {};\n+ bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;\n+ try {\n+ geometry = this.parseGeometry(obj.geometry);\n+ } catch (err) {\n+ // deal with bad geometries\n+ throw err;\n }\n- if (this.maxGetUrlLength != null) {\n- OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n+ feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+ if (bbox) {\n+ feature.bounds = OpenLayers.Bounds.fromArray(bbox);\n }\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- if (this.imgDiv) {\n- this.clear();\n- this.imgDiv = null;\n- this.frame = null;\n+ if (obj.id) {\n+ feature.fid = obj.id;\n }\n- // don't handle async requests any more\n- this.asyncRequestId = null;\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n+ return feature;\n },\n \n /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * \n- * Returns:\n- * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n- * false.\n+ * Method: parseGeometry\n+ * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * obj - {Object} An object created from a GeoJSON object\n+ *\n+ * Returns: \n+ * {<OpenLayers.Geometry>} A geometry.\n */\n- draw: function() {\n- var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (shouldDraw) {\n- // The layer's reproject option is deprecated.\n- if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n- // getBoundsFromBaseLayer is defined in deprecated.js.\n- this.bounds = this.getBoundsFromBaseLayer(this.position);\n+ parseGeometry: function(obj) {\n+ if (obj == null) {\n+ return null;\n+ }\n+ var geometry, collection = false;\n+ if (obj.type == \"GeometryCollection\") {\n+ if (!(OpenLayers.Util.isArray(obj.geometries))) {\n+ throw \"GeometryCollection must have geometries array: \" + obj;\n }\n- if (this.isLoading) {\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this._loadEvent = \"reload\";\n- } else {\n- this.isLoading = true;\n- this._loadEvent = \"loadstart\";\n+ var numGeom = obj.geometries.length;\n+ var components = new Array(numGeom);\n+ for (var i = 0; i < numGeom; ++i) {\n+ components[i] = this.parseGeometry.apply(\n+ this, [obj.geometries[i]]\n+ );\n+ }\n+ geometry = new OpenLayers.Geometry.Collection(components);\n+ collection = true;\n+ } else {\n+ if (!(OpenLayers.Util.isArray(obj.coordinates))) {\n+ throw \"Geometry must have coordinates array: \" + obj;\n+ }\n+ if (!this.parseCoords[obj.type.toLowerCase()]) {\n+ throw \"Unsupported geometry type: \" + obj.type;\n+ }\n+ try {\n+ geometry = this.parseCoords[obj.type.toLowerCase()].apply(\n+ this, [obj.coordinates]\n+ );\n+ } catch (err) {\n+ // deal with bad coordinates\n+ throw err;\n }\n- this.renderTile();\n- this.positionTile();\n- } else if (shouldDraw === false) {\n- this.unload();\n }\n- return shouldDraw;\n+ // We don't reproject collections because the children are reprojected\n+ // for us when they are created.\n+ if (this.internalProjection && this.externalProjection && !collection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ return geometry;\n },\n \n /**\n- * Method: renderTile\n- * Internal function to actually initialize the image tile,\n- * position it correctly, and set its url.\n+ * Property: parseCoords\n+ * Object with properties corresponding to the GeoJSON geometry types.\n+ * Property values are functions that do the actual parsing.\n */\n- renderTile: function() {\n- if (this.layer.async) {\n- // Asynchronous image requests call the asynchronous getURL method\n- // on the layer to fetch an image that covers 'this.bounds'.\n- var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n- this.layer.getURLasync(this.bounds, function(url) {\n- if (id == this.asyncRequestId) {\n- this.url = url;\n- this.initImage();\n+ parseCoords: {\n+ /**\n+ * Method: parseCoords.point\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"point\": function(array) {\n+ if (this.ignoreExtraDims == false &&\n+ array.length != 2) {\n+ throw \"Only 2D points are supported: \" + array;\n+ }\n+ return new OpenLayers.Geometry.Point(array[0], array[1]);\n+ },\n+\n+ /**\n+ * Method: parseCoords.multipoint\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"multipoint\": function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n }\n- }, this);\n- } else {\n- // synchronous image requests get the url immediately.\n- this.url = this.layer.getURL(this.bounds);\n- this.initImage();\n+ points.push(p);\n+ }\n+ return new OpenLayers.Geometry.MultiPoint(points);\n+ },\n+\n+ /**\n+ * Method: parseCoords.linestring\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"linestring\": function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ points.push(p);\n+ }\n+ return new OpenLayers.Geometry.LineString(points);\n+ },\n+\n+ /**\n+ * Method: parseCoords.multilinestring\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"multilinestring\": function(array) {\n+ var lines = [];\n+ var l = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ lines.push(l);\n+ }\n+ return new OpenLayers.Geometry.MultiLineString(lines);\n+ },\n+\n+ /**\n+ * Method: parseCoords.polygon\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"polygon\": function(array) {\n+ var rings = [];\n+ var r, l;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ r = new OpenLayers.Geometry.LinearRing(l.components);\n+ rings.push(r);\n+ }\n+ return new OpenLayers.Geometry.Polygon(rings);\n+ },\n+\n+ /**\n+ * Method: parseCoords.multipolygon\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"multipolygon\": function(array) {\n+ var polys = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"polygon\"].apply(this, [array[i]]);\n+ } catch (err) {\n+ throw err;\n+ }\n+ polys.push(p);\n+ }\n+ return new OpenLayers.Geometry.MultiPolygon(polys);\n+ },\n+\n+ /**\n+ * Method: parseCoords.box\n+ * Convert a coordinate array from GeoJSON into an\n+ * <OpenLayers.Geometry>.\n+ *\n+ * Parameters:\n+ * array - {Object} The coordinates array from the GeoJSON fragment.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry>} A geometry.\n+ */\n+ \"box\": function(array) {\n+ if (array.length != 2) {\n+ throw \"GeoJSON box coordinates must have 2 elements\";\n+ }\n+ return new OpenLayers.Geometry.Polygon([\n+ new OpenLayers.Geometry.LinearRing([\n+ new OpenLayers.Geometry.Point(array[0][0], array[0][1]),\n+ new OpenLayers.Geometry.Point(array[1][0], array[0][1]),\n+ new OpenLayers.Geometry.Point(array[1][0], array[1][1]),\n+ new OpenLayers.Geometry.Point(array[0][0], array[1][1]),\n+ new OpenLayers.Geometry.Point(array[0][0], array[0][1])\n+ ])\n+ ]);\n }\n+\n },\n \n /**\n- * Method: positionTile\n- * Using the properties currenty set on the layer, position the tile correctly.\n- * This method is used both by the async and non-async versions of the Tile.Image\n- * code.\n+ * APIMethod: write\n+ * Serialize a feature, geometry, array of features into a GeoJSON string.\n+ *\n+ * Parameters:\n+ * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,\n+ * or an array of features.\n+ * pretty - {Boolean} Structure the output with newlines and indentation.\n+ * Default is false.\n+ *\n+ * Returns:\n+ * {String} The GeoJSON string representation of the input geometry,\n+ * features, or array of features.\n */\n- positionTile: function() {\n- var style = this.getTile().style,\n- size = this.frame ? this.size :\n- this.layer.getImageSize(this.bounds),\n- ratio = 1;\n- if (this.layer instanceof OpenLayers.Layer.Grid) {\n- ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n+ write: function(obj, pretty) {\n+ var geojson = {\n+ \"type\": null\n+ };\n+ if (OpenLayers.Util.isArray(obj)) {\n+ geojson.type = \"FeatureCollection\";\n+ var numFeatures = obj.length;\n+ geojson.features = new Array(numFeatures);\n+ for (var i = 0; i < numFeatures; ++i) {\n+ var element = obj[i];\n+ if (!element instanceof OpenLayers.Feature.Vector) {\n+ var msg = \"FeatureCollection only supports collections \" +\n+ \"of features: \" + element;\n+ throw msg;\n+ }\n+ geojson.features[i] = this.extract.feature.apply(\n+ this, [element]\n+ );\n+ }\n+ } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n+ geojson = this.extract.geometry.apply(this, [obj]);\n+ } else if (obj instanceof OpenLayers.Feature.Vector) {\n+ geojson = this.extract.feature.apply(this, [obj]);\n+ if (obj.layer && obj.layer.projection) {\n+ geojson.crs = this.createCRSObject(obj);\n+ }\n }\n- style.left = this.position.x + \"px\";\n- style.top = this.position.y + \"px\";\n- style.width = Math.round(ratio * size.w) + \"px\";\n- style.height = Math.round(ratio * size.h) + \"px\";\n+ return OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [geojson, pretty]);\n },\n \n- /** \n- * Method: clear\n- * Remove the tile from the DOM, clear it of any image related data so that\n- * it can be reused in a new location.\n+ /**\n+ * Method: createCRSObject\n+ * Create the CRS object for an object.\n+ *\n+ * Parameters:\n+ * object - {<OpenLayers.Feature.Vector>} \n+ *\n+ * Returns:\n+ * {Object} An object which can be assigned to the crs property\n+ * of a GeoJSON object.\n */\n- clear: function() {\n- OpenLayers.Tile.prototype.clear.apply(this, arguments);\n- var img = this.imgDiv;\n- if (img) {\n- var tile = this.getTile();\n- if (tile.parentNode === this.layer.div) {\n- this.layer.div.removeChild(tile);\n- }\n- this.setImgSrc();\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"\";\n+ createCRSObject: function(object) {\n+ var proj = object.layer.projection.toString();\n+ var crs = {};\n+ if (proj.match(/epsg:/i)) {\n+ var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n+ if (code == 4326) {\n+ crs = {\n+ \"type\": \"name\",\n+ \"properties\": {\n+ \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n+ }\n+ };\n+ } else {\n+ crs = {\n+ \"type\": \"name\",\n+ \"properties\": {\n+ \"name\": \"EPSG:\" + code\n+ }\n+ };\n }\n- OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n }\n- this.canvasContext = null;\n+ return crs;\n },\n \n /**\n- * Method: getImage\n- * Returns or creates and returns the tile image.\n+ * Property: extract\n+ * Object with properties corresponding to the GeoJSON types.\n+ * Property values are functions that do the actual value extraction.\n */\n- getImage: function() {\n- if (!this.imgDiv) {\n- this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n+ extract: {\n+ /**\n+ * Method: extract.feature\n+ * Return a partial GeoJSON object representing a single feature.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {Object} An object representing the point.\n+ */\n+ 'feature': function(feature) {\n+ var geom = this.extract.geometry.apply(this, [feature.geometry]);\n+ var json = {\n+ \"type\": \"Feature\",\n+ \"properties\": feature.attributes,\n+ \"geometry\": geom\n+ };\n+ if (feature.fid != null) {\n+ json.id = feature.fid;\n+ }\n+ return json;\n+ },\n \n- var style = this.imgDiv.style;\n- if (this.frame) {\n- var left = 0,\n- top = 0;\n- if (this.layer.gutter) {\n- left = this.layer.gutter / this.layer.tileSize.w * 100;\n- top = this.layer.gutter / this.layer.tileSize.h * 100;\n- }\n- style.left = -left + \"%\";\n- style.top = -top + \"%\";\n- style.width = (2 * left + 100) + \"%\";\n- style.height = (2 * top + 100) + \"%\";\n+ /**\n+ * Method: extract.geometry\n+ * Return a GeoJSON object representing a single geometry.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ *\n+ * Returns:\n+ * {Object} An object representing the geometry.\n+ */\n+ 'geometry': function(geometry) {\n+ if (geometry == null) {\n+ return null;\n }\n- style.visibility = \"hidden\";\n- style.opacity = 0;\n- if (this.layer.opacity < 1) {\n- style.filter = 'alpha(opacity=' +\n- (this.layer.opacity * 100) +\n- ')';\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n }\n- style.position = \"absolute\";\n- if (this.layerAlphaHack) {\n- // move the image out of sight\n- style.paddingTop = style.height;\n- style.height = \"0\";\n- style.width = \"100%\";\n+ var geometryType = geometry.CLASS_NAME.split('.')[2];\n+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n+ var json;\n+ if (geometryType == \"Collection\") {\n+ json = {\n+ \"type\": \"GeometryCollection\",\n+ \"geometries\": data\n+ };\n+ } else {\n+ json = {\n+ \"type\": geometryType,\n+ \"coordinates\": data\n+ };\n }\n- if (this.frame) {\n- this.frame.appendChild(this.imgDiv);\n+\n+ return json;\n+ },\n+\n+ /**\n+ * Method: extract.point\n+ * Return an array of coordinates from a point.\n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ *\n+ * Returns: \n+ * {Array} An array of coordinates representing the point.\n+ */\n+ 'point': function(point) {\n+ return [point.x, point.y];\n+ },\n+\n+ /**\n+ * Method: extract.multipoint\n+ * Return an array of point coordinates from a multipoint.\n+ *\n+ * Parameters:\n+ * multipoint - {<OpenLayers.Geometry.MultiPoint>}\n+ *\n+ * Returns:\n+ * {Array} An array of point coordinate arrays representing\n+ * the multipoint.\n+ */\n+ 'multipoint': function(multipoint) {\n+ var array = [];\n+ for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [multipoint.components[i]]));\n+ }\n+ return array;\n+ },\n+\n+ /**\n+ * Method: extract.linestring\n+ * Return an array of coordinate arrays from a linestring.\n+ *\n+ * Parameters:\n+ * linestring - {<OpenLayers.Geometry.LineString>}\n+ *\n+ * Returns:\n+ * {Array} An array of coordinate arrays representing\n+ * the linestring.\n+ */\n+ 'linestring': function(linestring) {\n+ var array = [];\n+ for (var i = 0, len = linestring.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [linestring.components[i]]));\n+ }\n+ return array;\n+ },\n+\n+ /**\n+ * Method: extract.multilinestring\n+ * Return an array of linestring arrays from a linestring.\n+ * \n+ * Parameters:\n+ * multilinestring - {<OpenLayers.Geometry.MultiLineString>}\n+ * \n+ * Returns:\n+ * {Array} An array of linestring arrays representing\n+ * the multilinestring.\n+ */\n+ 'multilinestring': function(multilinestring) {\n+ var array = [];\n+ for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));\n+ }\n+ return array;\n+ },\n+\n+ /**\n+ * Method: extract.polygon\n+ * Return an array of linear ring arrays from a polygon.\n+ *\n+ * Parameters:\n+ * polygon - {<OpenLayers.Geometry.Polygon>}\n+ * \n+ * Returns:\n+ * {Array} An array of linear ring arrays representing the polygon.\n+ */\n+ 'polygon': function(polygon) {\n+ var array = [];\n+ for (var i = 0, len = polygon.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [polygon.components[i]]));\n+ }\n+ return array;\n+ },\n+\n+ /**\n+ * Method: extract.multipolygon\n+ * Return an array of polygon arrays from a multipolygon.\n+ * \n+ * Parameters:\n+ * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}\n+ * \n+ * Returns:\n+ * {Array} An array of polygon arrays representing\n+ * the multipolygon\n+ */\n+ 'multipolygon': function(multipolygon) {\n+ var array = [];\n+ for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n+ array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));\n+ }\n+ return array;\n+ },\n+\n+ /**\n+ * Method: extract.collection\n+ * Return an array of geometries from a geometry collection.\n+ * \n+ * Parameters:\n+ * collection - {<OpenLayers.Geometry.Collection>}\n+ * \n+ * Returns:\n+ * {Array} An array of geometry objects representing the geometry\n+ * collection.\n+ */\n+ 'collection': function(collection) {\n+ var len = collection.components.length;\n+ var array = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ array[i] = this.extract.geometry.apply(\n+ this, [collection.components[i]]\n+ );\n }\n+ return array;\n }\n \n- return this.imgDiv;\n+\n },\n \n- /**\n- * APIMethod: setImage\n- * Sets the image element for this tile. This method should only be called\n- * from beforeload listeners.\n+ CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Filter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter\n+ * This class represents an OGC Filter.\n+ */\n+OpenLayers.Filter = OpenLayers.Class({\n+\n+ /** \n+ * Constructor: OpenLayers.Filter\n+ * This class represents a generic filter.\n *\n- * Parameters\n- * img - {HTMLImageElement} The image to use for this tile.\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter>}\n */\n- setImage: function(img) {\n- this.imgDiv = img;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n- /**\n- * Method: initImage\n- * Creates the content for the frame on the tile.\n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to anything added.\n */\n- initImage: function() {\n- if (!this.url && !this.imgDiv) {\n- // fast path out - if there is no tile url and no previous image\n- this.isLoading = false;\n- return;\n- }\n- this.events.triggerEvent('beforeload');\n- this.layer.div.appendChild(this.getTile());\n- this.events.triggerEvent(this._loadEvent);\n- var img = this.getImage();\n- var src = img.getAttribute('src') || '';\n- if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n- this._loadTimeout = window.setTimeout(\n- OpenLayers.Function.bind(this.onImageLoad, this), 0\n- );\n- } else {\n- this.stopLoading();\n- if (this.crossOriginKeyword) {\n- img.removeAttribute(\"crossorigin\");\n- }\n- OpenLayers.Event.observe(img, \"load\",\n- OpenLayers.Function.bind(this.onImageLoad, this)\n- );\n- OpenLayers.Event.observe(img, \"error\",\n- OpenLayers.Function.bind(this.onImageError, this)\n- );\n- this.imageReloadAttempts = 0;\n- this.setImgSrc(this.url);\n- }\n- },\n+ destroy: function() {},\n \n /**\n- * Method: setImgSrc\n- * Sets the source for the tile image\n- *\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context. Instances or subclasses\n+ * are supposed to override this method.\n+ * \n * Parameters:\n- * url - {String} or undefined to hide the image\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n */\n- setImgSrc: function(url) {\n- var img = this.imgDiv;\n- if (url) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- // don't set crossOrigin if the url is a data URL\n- if (this.crossOriginKeyword) {\n- if (url.substr(0, 5) !== 'data:') {\n- img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n- } else {\n- img.removeAttribute(\"crossorigin\");\n- }\n- }\n- img.src = url;\n- } else {\n- // Remove reference to the image, and leave it to the browser's\n- // caching and garbage collection.\n- this.stopLoading();\n- this.imgDiv = null;\n- if (img.parentNode) {\n- img.parentNode.removeChild(img);\n- }\n- }\n+ evaluate: function(context) {\n+ return true;\n },\n \n /**\n- * Method: getTile\n- * Get the tile's markup.\n- *\n+ * APIMethod: clone\n+ * Clones this filter. Should be implemented by subclasses.\n+ * \n * Returns:\n- * {DOMElement} The tile's markup\n+ * {<OpenLayers.Filter>} Clone of this filter.\n */\n- getTile: function() {\n- return this.frame ? this.frame : this.getImage();\n+ clone: function() {\n+ return null;\n },\n \n /**\n- * Method: createBackBuffer\n- * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n- * of the tile's markup, because we want to avoid the reloading of the\n- * image. So we clone the frame, and steal the image from the tile.\n+ * APIMethod: toString\n *\n * Returns:\n- * {DOMElement} The markup, or undefined if the tile has no image\n- * or if it's currently loading.\n+ * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n+ * representation of the filter returned. Otherwise \"[Object object]\"\n+ * will be returned.\n */\n- createBackBuffer: function() {\n- if (!this.imgDiv || this.isLoading) {\n- return;\n- }\n- var backBuffer;\n- if (this.frame) {\n- backBuffer = this.frame.cloneNode(false);\n- backBuffer.appendChild(this.imgDiv);\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this);\n } else {\n- backBuffer = this.imgDiv;\n+ string = Object.prototype.toString.call(this);\n }\n- this.imgDiv = null;\n- return backBuffer;\n+ return string;\n },\n \n+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n+/* ======================================================================\n+ OpenLayers/Filter/Comparison.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Filter.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter.Comparison\n+ * This class represents a comparison filter.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Filter>\n+ */\n+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\n+\n /**\n- * Method: onImageLoad\n- * Handler for the image onload event\n+ * APIProperty: type\n+ * {String} type: type of the comparison. This is one of\n+ * - OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n+ * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n+ * - OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n+ * - OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n+ * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n+ * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n+ * - OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n+ * - OpenLayers.Filter.Comparison.LIKE = \"~\";\n+ * - OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n */\n- onImageLoad: function() {\n- var img = this.imgDiv;\n- this.stopLoading();\n- img.style.visibility = 'inherit';\n- img.style.opacity = this.layer.opacity;\n- this.isLoading = false;\n- this.canvasContext = null;\n- this.events.triggerEvent(\"loadend\");\n+ type: null,\n \n- if (this.layerAlphaHack === true) {\n- img.style.filter =\n- \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n- img.src + \"', sizingMethod='scale')\";\n+ /**\n+ * APIProperty: property\n+ * {String}\n+ * name of the context property to compare\n+ */\n+ property: null,\n+\n+ /**\n+ * APIProperty: value\n+ * {Number} or {String}\n+ * comparison value for binary comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n+ */\n+ value: null,\n+\n+ /**\n+ * Property: matchCase\n+ * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO\n+ * comparisons. The Filter Encoding 1.1 specification added a matchCase\n+ * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo\n+ * elements. This property will be serialized with those elements only\n+ * if using the v1.1.0 filter format. However, when evaluating filters\n+ * here, the matchCase property will always be respected (for EQUAL_TO\n+ * and NOT_EQUAL_TO). Default is true. \n+ */\n+ matchCase: true,\n+\n+ /**\n+ * APIProperty: lowerBoundary\n+ * {Number} or {String}\n+ * lower boundary for between comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n+ */\n+ lowerBoundary: null,\n+\n+ /**\n+ * APIProperty: upperBoundary\n+ * {Number} or {String}\n+ * upper boundary for between comparisons. In the case of a String, this\n+ * can be a combination of text and propertyNames in the form\n+ * \"literal ${propertyName}\"\n+ */\n+ upperBoundary: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Filter.Comparison\n+ * Creates a comparison rule.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter.Comparison>}\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n+ // since matchCase on PropertyIsLike is not schema compliant, we only\n+ // want to use this if explicitly asked for\n+ if (this.type === OpenLayers.Filter.Comparison.LIKE &&\n+ options.matchCase === undefined) {\n+ this.matchCase = null;\n }\n },\n \n /**\n- * Method: onImageError\n- * Handler for the image onerror event\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context.\n+ * \n+ * Parameters:\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n */\n- onImageError: function() {\n- var img = this.imgDiv;\n- if (img.src != null) {\n- this.imageReloadAttempts++;\n- if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n- this.setImgSrc(this.layer.getURL(this.bounds));\n- } else {\n- OpenLayers.Element.addClass(img, \"olImageLoadError\");\n- this.events.triggerEvent(\"loaderror\");\n- this.onImageLoad();\n- }\n+ evaluate: function(context) {\n+ if (context instanceof OpenLayers.Feature.Vector) {\n+ context = context.attributes;\n+ }\n+ var result = false;\n+ var got = context[this.property];\n+ var exp;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Comparison.EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase &&\n+ typeof got == \"string\" && typeof exp == \"string\") {\n+ result = (got.toUpperCase() == exp.toUpperCase());\n+ } else {\n+ result = (got == exp);\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase &&\n+ typeof got == \"string\" && typeof exp == \"string\") {\n+ result = (got.toUpperCase() != exp.toUpperCase());\n+ } else {\n+ result = (got != exp);\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN:\n+ result = got < this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN:\n+ result = got > this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n+ result = got <= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n+ result = got >= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.BETWEEN:\n+ result = (got >= this.lowerBoundary) &&\n+ (got <= this.upperBoundary);\n+ break;\n+ case OpenLayers.Filter.Comparison.LIKE:\n+ var regexp = new RegExp(this.value, \"gi\");\n+ result = regexp.test(got);\n+ break;\n+ case OpenLayers.Filter.Comparison.IS_NULL:\n+ result = (got === null);\n+ break;\n }\n+ return result;\n },\n \n /**\n- * Method: stopLoading\n- * Stops a loading sequence so <onImageLoad> won't be executed.\n+ * APIMethod: value2regex\n+ * Converts the value of this rule into a regular expression string,\n+ * according to the wildcard characters specified. This method has to\n+ * be called after instantiation of this class, if the value is not a\n+ * regular expression already.\n+ * \n+ * Parameters:\n+ * wildCard - {Char} wildcard character in the above value, default\n+ * is \"*\"\n+ * singleChar - {Char} single-character wildcard in the above value\n+ * default is \".\"\n+ * escapeChar - {Char} escape character in the above value, default is\n+ * \"!\"\n+ * \n+ * Returns:\n+ * {String} regular expression string\n */\n- stopLoading: function() {\n- OpenLayers.Event.stopObservingElement(this.imgDiv);\n- window.clearTimeout(this._loadTimeout);\n- delete this._loadTimeout;\n+ value2regex: function(wildCard, singleChar, escapeChar) {\n+ if (wildCard == \".\") {\n+ throw new Error(\"'.' is an unsupported wildCard character for \" +\n+ \"OpenLayers.Filter.Comparison\");\n+ }\n+\n+\n+ // set UMN MapServer defaults for unspecified parameters\n+ wildCard = wildCard ? wildCard : \"*\";\n+ singleChar = singleChar ? singleChar : \".\";\n+ escapeChar = escapeChar ? escapeChar : \"!\";\n+\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n+ this.value = this.value.replace(\n+ new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n+\n+ return this.value;\n },\n \n /**\n- * APIMethod: getCanvasContext\n- * Returns a canvas context associated with the tile image (with\n- * the image drawn on it).\n- * Returns undefined if the browser does not support canvas, if\n- * the tile has no image or if it's currently loading.\n- *\n- * The function returns a canvas context instance but the\n- * underlying canvas is still available in the 'canvas' property:\n- * (code)\n- * var context = tile.getCanvasContext();\n- * if (context) {\n- * var data = context.canvas.toDataURL('image/jpeg');\n- * }\n- * (end)\n- *\n+ * Method: regex2value\n+ * Convert the value of this rule from a regular expression string into an\n+ * ogc literal string using a wildCard of *, a singleChar of ., and an\n+ * escape of !. Leaves the <value> property unmodified.\n+ * \n * Returns:\n- * {Boolean}\n+ * {String} A string value.\n */\n- getCanvasContext: function() {\n- if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n- if (!this.canvasContext) {\n- var canvas = document.createElement(\"canvas\");\n- canvas.width = this.size.w;\n- canvas.height = this.size.h;\n- this.canvasContext = canvas.getContext(\"2d\");\n- this.canvasContext.drawImage(this.imgDiv, 0, 0);\n- }\n- return this.canvasContext;\n- }\n+ regex2value: function() {\n+\n+ var value = this.value;\n+\n+ // replace ! with !!\n+ value = value.replace(/!/g, \"!!\");\n+\n+ // replace \\. with !. (watching out for \\\\.)\n+ value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n+ return $1 ? $0 : \"!.\";\n+ });\n+\n+ // replace \\* with #* (watching out for \\\\*)\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"!*\";\n+ });\n+\n+ // replace \\\\ with \\\n+ value = value.replace(/\\\\\\\\/g, \"\\\\\");\n+\n+ // convert .* to * (the sequence #.* is not allowed)\n+ value = value.replace(/\\.\\*/g, \"*\");\n+\n+ return value;\n },\n \n- CLASS_NAME: \"OpenLayers.Tile.Image\"\n+ /**\n+ * APIMethod: clone\n+ * Clones this filter.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter.Comparison>} Clone of this filter.\n+ */\n+ clone: function() {\n+ return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n });\n \n-/** \n- * Constant: OpenLayers.Tile.Image.IMAGE\n- * {HTMLImageElement} The image for a tile.\n- */\n-OpenLayers.Tile.Image.IMAGE = (function() {\n- var img = new Image();\n- img.className = \"olTileImage\";\n- // avoid image gallery menu in IE6\n- img.galleryImg = \"no\";\n- return img;\n-}());\n \n+OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n+OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n+OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n+OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n+OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n+OpenLayers.Filter.Comparison.LIKE = \"~\";\n+OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n /* ======================================================================\n- OpenLayers/Layer/Grid.js\n+ OpenLayers/Filter/Logical.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Layer/HTTPRequest.js\n- * @requires OpenLayers/Tile/Image.js\n+ * @requires OpenLayers/Filter.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Grid\n- * Base class for layers that use a lattice of tiles. Create a new grid\n- * layer with the <OpenLayers.Layer.Grid> constructor.\n- *\n+ * Class: OpenLayers.Filter.Logical\n+ * This class represents ogc:And, ogc:Or and ogc:Not rules.\n+ * \n * Inherits from:\n- * - <OpenLayers.Layer.HTTPRequest>\n+ * - <OpenLayers.Filter>\n */\n-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\n \n /**\n- * APIProperty: tileSize\n- * {<OpenLayers.Size>}\n+ * APIProperty: filters\n+ * {Array(<OpenLayers.Filter>)} Child filters for this filter.\n */\n- tileSize: null,\n+ filters: null,\n \n /**\n- * Property: tileOriginCorner\n- * {String} If the <tileOrigin> property is not provided, the tile origin \n- * will be derived from the layer's <maxExtent>. The corner of the \n- * <maxExtent> used is determined by this property. Acceptable values\n- * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n- * (bottom right). Default is \"bl\".\n+ * APIProperty: type\n+ * {String} type of logical operator. Available types are:\n+ * - OpenLayers.Filter.Logical.AND = \"&&\";\n+ * - OpenLayers.Filter.Logical.OR = \"||\";\n+ * - OpenLayers.Filter.Logical.NOT = \"!\";\n */\n- tileOriginCorner: \"bl\",\n+ type: null,\n \n- /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n- * If provided, requests for tiles at all resolutions will be aligned\n- * with this location (no tiles shall overlap this location). If\n- * not provided, the grid of tiles will be aligned with the layer's\n- * <maxExtent>. Default is ``null``.\n+ /** \n+ * Constructor: OpenLayers.Filter.Logical\n+ * Creates a logical filter (And, Or, Not).\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object with properties to set on the\n+ * filter.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter.Logical>}\n */\n- tileOrigin: null,\n+ initialize: function(options) {\n+ this.filters = [];\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n+ },\n \n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer, if supported by the tile class.\n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to child filters.\n */\n- tileOptions: null,\n+ destroy: function() {\n+ this.filters = null;\n+ OpenLayers.Filter.prototype.destroy.apply(this);\n+ },\n \n /**\n- * APIProperty: tileClass\n- * {<OpenLayers.Tile>} The tile class to use for this layer.\n- * Defaults is OpenLayers.Tile.Image.\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context.\n+ * \n+ * Parameters:\n+ * context - {Object} Context to use in evaluating the filter. A vector\n+ * feature may also be provided to evaluate feature attributes in \n+ * comparison filters or geometries in spatial filters.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n */\n- tileClass: OpenLayers.Tile.Image,\n+ evaluate: function(context) {\n+ var i, len;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Logical.AND:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == false) {\n+ return false;\n+ }\n+ }\n+ return true;\n \n- /**\n- * Property: grid\n- * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is \n- * an array of tiles.\n- */\n- grid: null,\n+ case OpenLayers.Filter.Logical.OR:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == true) {\n+ return true;\n+ }\n+ }\n+ return false;\n+\n+ case OpenLayers.Filter.Logical.NOT:\n+ return (!this.filters[0].evaluate(context));\n+ }\n+ return undefined;\n+ },\n \n /**\n- * APIProperty: singleTile\n- * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n- * will be loaded. The tile's size will be determined by the 'ratio'\n- * property. When the tile is dragged such that it does not cover the \n- * entire viewport, it is reloaded.\n+ * APIMethod: clone\n+ * Clones this filter.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter.Logical>} Clone of this filter.\n */\n- singleTile: false,\n+ clone: function() {\n+ var filters = [];\n+ for (var i = 0, len = this.filters.length; i < len; ++i) {\n+ filters.push(this.filters[i].clone());\n+ }\n+ return new OpenLayers.Filter.Logical({\n+ type: this.type,\n+ filters: filters\n+ });\n+ },\n \n- /** APIProperty: ratio\n- * {Float} Used only when in single-tile mode, this specifies the \n- * ratio of the size of the single tile to the size of the map.\n- * Default value is 1.5.\n- */\n- ratio: 1.5,\n+ CLASS_NAME: \"OpenLayers.Filter.Logical\"\n+});\n \n- /**\n- * APIProperty: buffer\n- * {Integer} Used only when in gridded mode, this specifies the number of \n- * extra rows and colums of tiles on each side which will\n- * surround the minimum grid tiles to cover the map.\n- * For very slow loading layers, a larger value may increase\n- * performance somewhat when dragging, but will increase bandwidth\n- * use significantly. \n+\n+OpenLayers.Filter.Logical.AND = \"&&\";\n+OpenLayers.Filter.Logical.OR = \"||\";\n+OpenLayers.Filter.Logical.NOT = \"!\";\n+/* ======================================================================\n+ OpenLayers/Control.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control\n+ * Controls affect the display or behavior of the map. They allow everything\n+ * from panning and zooming to displaying a scale indicator. Controls by \n+ * default are added to the map they are contained within however it is\n+ * possible to add a control to an external div by passing the div in the\n+ * options parameter.\n+ * \n+ * Example:\n+ * The following example shows how to add many of the common controls\n+ * to a map.\n+ * \n+ * > var map = new OpenLayers.Map('map', { controls: [] });\n+ * >\n+ * > map.addControl(new OpenLayers.Control.PanZoomBar());\n+ * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));\n+ * > map.addControl(new OpenLayers.Control.Permalink());\n+ * > map.addControl(new OpenLayers.Control.Permalink('permalink'));\n+ * > map.addControl(new OpenLayers.Control.MousePosition());\n+ * > map.addControl(new OpenLayers.Control.OverviewMap());\n+ * > map.addControl(new OpenLayers.Control.KeyboardDefaults());\n+ *\n+ * The next code fragment is a quick example of how to intercept \n+ * shift-mouse click to display the extent of the bounding box\n+ * dragged out by the user. Usually controls are not created\n+ * in exactly this manner. See the source for a more complete \n+ * example:\n+ *\n+ * > var control = new OpenLayers.Control();\n+ * > OpenLayers.Util.extend(control, {\n+ * > draw: function () {\n+ * > // this Handler.Box will intercept the shift-mousedown\n+ * > // before Control.MouseDefault gets to see it\n+ * > this.box = new OpenLayers.Handler.Box( control, \n+ * > {\"done\": this.notice},\n+ * > {keyMask: OpenLayers.Handler.MOD_SHIFT});\n+ * > this.box.activate();\n+ * > },\n+ * >\n+ * > notice: function (bounds) {\n+ * > OpenLayers.Console.userError(bounds);\n+ * > }\n+ * > }); \n+ * > map.addControl(control);\n+ * \n+ */\n+OpenLayers.Control = OpenLayers.Class({\n+\n+ /** \n+ * Property: id \n+ * {String} \n */\n- buffer: 0,\n+ id: null,\n \n- /**\n- * APIProperty: transitionEffect\n- * {String} The transition effect to use when the map is zoomed.\n- * Two posible values:\n- *\n- * \"resize\" - Existing tiles are resized on zoom to provide a visual\n- * effect of the zoom having taken place immediately. As the\n- * new tiles become available, they are drawn on top of the\n- * resized tiles (this is the default setting).\n- * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n- * base layer. New tiles for the base layer will cover existing tiles.\n- * This setting is recommended when having an overlay duplicated during\n- * the transition is undesirable (e.g. street labels or big transparent\n- * fills). \n- * null - No transition effect.\n- *\n- * Using \"resize\" on non-opaque layers can cause undesired visual\n- * effects. Set transitionEffect to null in this case.\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} this gets set in the addControl() function in\n+ * OpenLayers.Map \n */\n- transitionEffect: \"resize\",\n+ map: null,\n \n- /**\n- * APIProperty: numLoadingTiles\n- * {Integer} How many tiles are still loading?\n+ /** \n+ * APIProperty: div \n+ * {DOMElement} The element that contains the control, if not present the \n+ * control is placed inside the map.\n */\n- numLoadingTiles: 0,\n+ div: null,\n \n- /**\n- * Property: serverResolutions\n- * {Array(Number}} This property is documented in subclasses as\n- * an API property.\n+ /** \n+ * APIProperty: type \n+ * {Number} Controls can have a 'type'. The type determines the type of\n+ * interactions which are possible with them when they are placed in an\n+ * <OpenLayers.Control.Panel>. \n */\n- serverResolutions: null,\n+ type: null,\n \n- /**\n- * Property: loading\n- * {Boolean} Indicates if tiles are being loaded.\n+ /** \n+ * Property: allowSelection\n+ * {Boolean} By default, controls do not allow selection, because\n+ * it may interfere with map dragging. If this is true, OpenLayers\n+ * will not prevent selection of the control.\n+ * Default is false.\n */\n- loading: false,\n+ allowSelection: false,\n \n- /**\n- * Property: backBuffer\n- * {DOMElement} The back buffer.\n+ /** \n+ * Property: displayClass \n+ * {string} This property is used for CSS related to the drawing of the\n+ * Control. \n */\n- backBuffer: null,\n+ displayClass: \"\",\n \n /**\n- * Property: gridResolution\n- * {Number} The resolution of the current grid. Used for backbuffer and\n- * client zoom. This property is updated every time the grid is\n- * initialized.\n+ * APIProperty: title \n+ * {string} This property is used for showing a tooltip over the \n+ * Control. \n */\n- gridResolution: null,\n+ title: \"\",\n \n /**\n- * Property: backBufferResolution\n- * {Number} The resolution of the current back buffer. This property is\n- * updated each time a back buffer is created.\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * false.\n */\n- backBufferResolution: null,\n+ autoActivate: false,\n \n- /**\n- * Property: backBufferLonLat\n- * {Object} The top-left corner of the current back buffer. Includes lon\n- * and lat properties. This object is updated each time a back buffer\n- * is created.\n+ /** \n+ * APIProperty: active \n+ * {Boolean} The control is active (read-only). Use <activate> and \n+ * <deactivate> to change control state.\n */\n- backBufferLonLat: null,\n+ active: null,\n \n /**\n- * Property: backBufferTimerId\n- * {Number} The id of the back buffer timer. This timer is used to\n- * delay the removal of the back buffer, thereby preventing\n- * flash effects caused by tile animation.\n+ * Property: handlerOptions\n+ * {Object} Used to set non-default properties on the control's handler\n */\n- backBufferTimerId: null,\n+ handlerOptions: null,\n \n- /**\n- * APIProperty: removeBackBufferDelay\n- * {Number} Delay for removing the backbuffer when all tiles have finished\n- * loading. Can be set to 0 when no css opacity transitions for the\n- * olTileImage class are used. Default is 0 for <singleTile> layers,\n- * 2500 for tiled layers. See <className> for more information on\n- * tile animation.\n+ /** \n+ * Property: handler \n+ * {<OpenLayers.Handler>} null\n */\n- removeBackBufferDelay: null,\n+ handler: null,\n \n /**\n- * APIProperty: className\n- * {String} Name of the class added to the layer div. If not set in the\n- * options passed to the constructor then className defaults to\n- * \"olLayerGridSingleTile\" for single tile layers (see <singleTile>),\n- * and \"olLayerGrid\" for non single tile layers.\n- *\n- * Note:\n- *\n- * The displaying of tiles is not animated by default for single tile\n- * layers - OpenLayers' default theme (style.css) includes this:\n- * (code)\n- * .olLayerGrid .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * To animate tile displaying for any grid layer the following\n- * CSS rule can be used:\n- * (code)\n- * .olTileImage {\n- * -webkit-transition: opacity 0.2s linear;\n- * -moz-transition: opacity 0.2s linear;\n- * -o-transition: opacity 0.2s linear;\n- * transition: opacity 0.2s linear;\n- * }\n- * (end)\n- * In that case, to avoid flash effects, <removeBackBufferDelay>\n- * should not be zero.\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n */\n- className: null,\n+ eventListeners: null,\n \n- /**\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n * Register a listener for a particular event with the following syntax:\n * (code)\n- * layer.events.register(type, obj, listener);\n+ * control.events.register(type, obj, listener);\n * (end)\n *\n * Listeners will be called with a reference to an event object. The\n * properties of this event depends on exactly what happened.\n *\n * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n+ * object - {Object} A reference to control.events.object (a reference\n+ * to the control).\n+ * element - {DOMElement} A reference to control.events.element (which\n+ * will be null unless documented otherwise).\n *\n- * Supported event types:\n- * addtile - Triggered when a tile is added to this layer. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that has been added.\n- * tileloadstart - Triggered when a tile starts loading. Listeners receive\n- * an object as first argument, which has a tile property that\n- * references the tile that starts loading.\n- * tileloaded - Triggered when each new tile is\n- * loaded, as a means of progress update to listeners.\n- * listeners can access 'numLoadingTiles' if they wish to keep\n- * track of the loading progress. Listeners are called with an object\n- * with a 'tile' property as first argument, making the loaded tile\n- * available to the listener, and an 'aborted' property, which will be\n- * true when loading was aborted and no tile data is available.\n- * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n- * still hidden) if a tile failed to load. Listeners receive an object\n- * as first argument, which has a tile property that references the\n- * tile that could not be loaded.\n- * retile - Triggered when the layer recreates its tile grid.\n- */\n-\n- /**\n- * Property: gridLayout\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n- */\n- gridLayout: null,\n-\n- /**\n- * Property: rowSign\n- * {Number} 1 for grids starting at the top, -1 for grids starting at the\n- * bottom. This is used for several grid index and offset calculations.\n- */\n- rowSign: null,\n-\n- /**\n- * Property: transitionendEvents\n- * {Array} Event names for transitionend\n+ * Supported map event types:\n+ * activate - Triggered when activated.\n+ * deactivate - Triggered when deactivated.\n */\n- transitionendEvents: [\n- 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n- 'oTransitionEnd'\n- ],\n+ events: null,\n \n /**\n- * Constructor: OpenLayers.Layer.Grid\n- * Create a new grid layer\n+ * Constructor: OpenLayers.Control\n+ * Create an OpenLayers Control. The options passed as a parameter\n+ * directly extend the control. For example passing the following:\n+ * \n+ * > var control = new OpenLayers.Control({div: myDiv});\n *\n+ * Overrides the default div attribute value of null.\n+ * \n * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * options - {Object} \n */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n- arguments);\n- this.grid = [];\n- this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n+ initialize: function(options) {\n+ // We do this before the extend so that instances can override\n+ // className in options.\n+ this.displayClass =\n+ this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n \n- this.initProperties();\n+ OpenLayers.Util.extend(this, options);\n \n- this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ }\n },\n \n /**\n- * Method: initProperties\n- * Set any properties that depend on the value of singleTile.\n- * Currently sets removeBackBufferDelay and className\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n */\n- initProperties: function() {\n- if (this.options.removeBackBufferDelay === undefined) {\n- this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n+ destroy: function() {\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ }\n+ this.events.destroy();\n+ this.events = null;\n }\n+ this.eventListeners = null;\n \n- if (this.options.className === undefined) {\n- this.className = this.singleTile ? 'olLayerGridSingleTile' :\n- 'olLayerGrid';\n+ // eliminate circular references\n+ if (this.handler) {\n+ this.handler.destroy();\n+ this.handler = null;\n+ }\n+ if (this.handlers) {\n+ for (var key in this.handlers) {\n+ if (this.handlers.hasOwnProperty(key) &&\n+ typeof this.handlers[key].destroy == \"function\") {\n+ this.handlers[key].destroy();\n+ }\n+ }\n+ this.handlers = null;\n+ }\n+ if (this.map) {\n+ this.map.removeControl(this);\n+ this.map = null;\n }\n+ this.div = null;\n },\n \n- /**\n+ /** \n * Method: setMap\n+ * Set the map property for the control. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n *\n * Parameters:\n- * map - {<OpenLayers.Map>} The map.\n+ * map - {<OpenLayers.Map>} \n */\n setMap: function(map) {\n- OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n- OpenLayers.Element.addClass(this.div, this.className);\n+ this.map = map;\n+ if (this.handler) {\n+ this.handler.setMap(map);\n+ }\n },\n \n /**\n- * Method: removeMap\n- * Called when the layer is removed from the map.\n+ * Method: draw\n+ * The draw method is called when the control is ready to be displayed\n+ * on the page. If a div has not been created one is created. Controls\n+ * with a visual component will almost always want to override this method \n+ * to customize the look of control. \n *\n * Parameters:\n- * map - {<OpenLayers.Map>} The map.\n- */\n- removeMap: function(map) {\n- this.removeBackBuffer();\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Deconstruct the layer and clear the grid.\n- */\n- destroy: function() {\n- this.removeBackBuffer();\n- this.clearGrid();\n-\n- this.grid = null;\n- this.tileSize = null;\n- OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * Refetches tiles with new params merged, keeping a backbuffer. Each\n- * loading new tile will have a css class of '.olTileReplacing'. If a\n- * stylesheet applies a 'display: none' style to that class, any fade-in\n- * transition will not apply, and backbuffers for each tile will be removed\n- * as soon as the tile is loaded.\n- * \n- * Parameters:\n- * newParams - {Object}\n+ * px - {<OpenLayers.Pixel>} The top-left pixel position of the control\n+ * or null.\n *\n * Returns:\n- * redrawn: {Boolean} whether the layer was actually redrawn.\n- */\n-\n- /**\n- * Method: clearGrid\n- * Go through and remove all tiles from the grid, calling\n- * destroy() on each of them to kill circular references\n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n */\n- clearGrid: function() {\n- if (this.grid) {\n- for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n- var row = this.grid[iRow];\n- for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n- var tile = row[iCol];\n- this.destroyTile(tile);\n- }\n+ draw: function(px) {\n+ if (this.div == null) {\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.className = this.displayClass;\n+ if (!this.allowSelection) {\n+ this.div.className += \" olControlNoSelect\";\n+ this.div.setAttribute(\"unselectable\", \"on\", 0);\n+ this.div.onselectstart = OpenLayers.Function.False;\n }\n- this.grid = [];\n- this.gridResolution = null;\n- this.gridLayout = null;\n+ if (this.title != \"\") {\n+ this.div.title = this.title;\n+ }\n+ }\n+ if (px != null) {\n+ this.position = px.clone();\n }\n+ this.moveTo(this.position);\n+ return this.div;\n },\n \n /**\n- * APIMethod: addOptions\n- * \n+ * Method: moveTo\n+ * Sets the left and top style attributes to the passed in pixel \n+ * coordinates.\n+ *\n * Parameters:\n- * newOptions - {Object}\n- * reinitialize - {Boolean} If set to true, and if resolution options of the\n- * current baseLayer were changed, the map will be recentered to make\n- * sure that it is displayed with a valid resolution, and a\n- * changebaselayer event will be triggered.\n+ * px - {<OpenLayers.Pixel>}\n */\n- addOptions: function(newOptions, reinitialize) {\n- var singleTileChanged = newOptions.singleTile !== undefined &&\n- newOptions.singleTile !== this.singleTile;\n- OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n- if (this.map && singleTileChanged) {\n- this.initProperties();\n- this.clearGrid();\n- this.tileSize = this.options.tileSize;\n- this.setTileSize();\n- this.moveTo(null, true);\n+ moveTo: function(px) {\n+ if ((px != null) && (this.div != null)) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\";\n }\n },\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Is this ever used?\n+ * APIMethod: activate\n+ * Explicitly activates a control and it's associated\n+ * handler if one has been set. Controls can be\n+ * deactivated by calling the deactivate() method.\n * \n * Returns:\n- * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid\n+ * {Boolean} True if the control was successfully activated or\n+ * false if the control was already active.\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Grid(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n+ activate: function() {\n+ if (this.active) {\n+ return false;\n }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone();\n+ if (this.handler) {\n+ this.handler.activate();\n }\n-\n- // we do not want to copy reference to grid, so we make a new array\n- obj.grid = [];\n- obj.gridResolution = null;\n- // same for backbuffer\n- obj.backBuffer = null;\n- obj.backBufferTimerId = null;\n- obj.loading = false;\n- obj.numLoadingTiles = 0;\n-\n- return obj;\n+ this.active = true;\n+ if (this.map) {\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv,\n+ this.displayClass.replace(/ /g, \"\") + \"Active\"\n+ );\n+ }\n+ this.events.triggerEvent(\"activate\");\n+ return true;\n },\n \n /**\n- * Method: moveTo\n- * This function is called whenever the map is moved. All the moving\n- * of actual 'tiles' is done by the map, but moveTo's role is to accept\n- * a bounds and make sure the data that that bounds requires is pre-loaded.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n+ * APIMethod: deactivate\n+ * Deactivates a control and it's associated handler if any. The exact\n+ * effect of this depends on the control itself.\n+ * \n+ * Returns:\n+ * {Boolean} True if the control was effectively deactivated or false\n+ * if the control was already inactive.\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n-\n- OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n+ deactivate: function() {\n+ if (this.active) {\n+ if (this.handler) {\n+ this.handler.deactivate();\n+ }\n+ this.active = false;\n+ if (this.map) {\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv,\n+ this.displayClass.replace(/ /g, \"\") + \"Active\"\n+ );\n+ }\n+ this.events.triggerEvent(\"deactivate\");\n+ return true;\n+ }\n+ return false;\n+ },\n \n- bounds = bounds || this.map.getExtent();\n+ CLASS_NAME: \"OpenLayers.Control\"\n+});\n \n- if (bounds != null) {\n+/**\n+ * Constant: OpenLayers.Control.TYPE_BUTTON\n+ */\n+OpenLayers.Control.TYPE_BUTTON = 1;\n \n- // if grid is empty or zoom has changed, we *must* re-tile\n- var forceReTile = !this.grid.length || zoomChanged;\n+/**\n+ * Constant: OpenLayers.Control.TYPE_TOGGLE\n+ */\n+OpenLayers.Control.TYPE_TOGGLE = 2;\n \n- // total bounds of the tiles\n- var tilesBounds = this.getTilesBounds();\n+/**\n+ * Constant: OpenLayers.Control.TYPE_TOOL\n+ */\n+OpenLayers.Control.TYPE_TOOL = 3;\n+/* ======================================================================\n+ OpenLayers/Events/buttonclick.js\n+ ====================================================================== */\n \n- // the new map resolution\n- var resolution = this.map.getResolution();\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- // the server-supported resolution for the new map resolution\n- var serverResolution = this.getServerResolution(resolution);\n+/**\n+ * @requires OpenLayers/Events.js\n+ */\n \n- if (this.singleTile) {\n+/**\n+ * Class: OpenLayers.Events.buttonclick\n+ * Extension event type for handling buttons on top of a dom element. This\n+ * event type fires \"buttonclick\" on its <target> when a button was\n+ * clicked. Buttons are detected by the \"olButton\" class.\n+ *\n+ * This event type makes sure that button clicks do not interfere with other\n+ * events that are registered on the same <element>.\n+ *\n+ * Event types provided by this extension:\n+ * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n+ * object with a *buttonElement* property referencing the dom element of\n+ * the clicked button, and an *buttonXY* property with the click position\n+ * relative to the button.\n+ */\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n \n- // We want to redraw whenever even the slightest part of the \n- // current bounds is not contained by our tile.\n- // (thus, we do not specify partial -- its default is false)\n+ /**\n+ * Property: target\n+ * {<OpenLayers.Events>} The events instance that the buttonclick event will\n+ * be triggered on.\n+ */\n+ target: null,\n \n- if (forceReTile ||\n- (!dragging && !tilesBounds.containsBounds(bounds))) {\n+ /**\n+ * Property: events\n+ * {Array} Events to observe and conditionally stop from propagating when\n+ * an element with the olButton class (or its olAlphaImg child) is\n+ * clicked.\n+ */\n+ events: [\n+ 'mousedown', 'mouseup', 'click', 'dblclick',\n+ 'touchstart', 'touchmove', 'touchend', 'keydown'\n+ ],\n \n- // In single tile mode with no transition effect, we insert\n- // a non-scaled backbuffer when the layer is moved. But if\n- // a zoom occurs right after a move, i.e. before the new\n- // image is received, we need to remove the backbuffer, or\n- // an ill-positioned image will be visible during the zoom\n- // transition.\n+ /**\n+ * Property: startRegEx\n+ * {RegExp} Regular expression to test Event.type for events that start\n+ * a buttonclick sequence.\n+ */\n+ startRegEx: /^mousedown|touchstart$/,\n \n- if (zoomChanged && this.transitionEffect !== 'resize') {\n- this.removeBackBuffer();\n- }\n+ /**\n+ * Property: cancelRegEx\n+ * {RegExp} Regular expression to test Event.type for events that cancel\n+ * a buttonclick sequence.\n+ */\n+ cancelRegEx: /^touchmove$/,\n \n- if (!zoomChanged || this.transitionEffect === 'resize') {\n- this.applyBackBuffer(resolution);\n- }\n+ /**\n+ * Property: completeRegEx\n+ * {RegExp} Regular expression to test Event.type for events that complete\n+ * a buttonclick sequence.\n+ */\n+ completeRegEx: /^mouseup|touchend$/,\n \n- this.initSingleTile(bounds);\n- }\n- } else {\n+ /**\n+ * Property: startEvt\n+ * {Event} The event that started the click sequence\n+ */\n \n- // if the bounds have changed such that they are not even \n- // *partially* contained by our tiles (e.g. when user has \n- // programmatically panned to the other side of the earth on\n- // zoom level 18), then moveGriddedTiles could potentially have\n- // to run through thousands of cycles, so we want to reTile\n- // instead (thus, partial true). \n- forceReTile = forceReTile ||\n- !tilesBounds.intersectsBounds(bounds, {\n- worldBounds: this.map.baseLayer.wrapDateLine &&\n- this.map.getMaxExtent()\n- });\n+ /**\n+ * Constructor: OpenLayers.Events.buttonclick\n+ * Construct a buttonclick event type. Applications are not supposed to\n+ * create instances of this class - they are created on demand by\n+ * <OpenLayers.Events> instances.\n+ *\n+ * Parameters:\n+ * target - {<OpenLayers.Events>} The events instance that the buttonclick\n+ * event will be triggered on.\n+ */\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ });\n+ }\n+ },\n \n- if (forceReTile) {\n- if (zoomChanged && (this.transitionEffect === 'resize' ||\n- this.gridResolution === resolution)) {\n- this.applyBackBuffer(resolution);\n- }\n- this.initGriddedTiles(bounds);\n- } else {\n- this.moveGriddedTiles();\n- }\n- }\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick);\n }\n+ delete this.target;\n },\n \n /**\n- * Method: getTileData\n- * Given a map location, retrieve a tile and the pixel offset within that\n- * tile corresponding to the location. If there is not an existing \n- * tile in the grid that covers the given location, null will be \n- * returned.\n+ * Method: getPressedButton\n+ * Get the pressed button, if any. Returns undefined if no button\n+ * was pressed.\n *\n- * Parameters:\n- * loc - {<OpenLayers.LonLat>} map location\n+ * Arguments:\n+ * element - {DOMElement} The event target.\n *\n * Returns:\n- * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),\n- * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n- * offset from top left).\n+ * {DOMElement} The button element, or undefined.\n */\n- getTileData: function(loc) {\n- var data = null,\n- x = loc.lon,\n- y = loc.lat,\n- numRows = this.grid.length;\n-\n- if (this.map && numRows) {\n- var res = this.map.getResolution(),\n- tileWidth = this.tileSize.w,\n- tileHeight = this.tileSize.h,\n- bounds = this.grid[0][0].bounds,\n- left = bounds.left,\n- top = bounds.top;\n-\n- if (x < left) {\n- // deal with multiple worlds\n- if (this.map.baseLayer.wrapDateLine) {\n- var worldWidth = this.map.getMaxExtent().getWidth();\n- var worldsAway = Math.ceil((left - x) / worldWidth);\n- x += worldWidth * worldsAway;\n- }\n- }\n- // tile distance to location (fractional number of tiles);\n- var dtx = (x - left) / (res * tileWidth);\n- var dty = (top - y) / (res * tileHeight);\n- // index of tile in grid\n- var col = Math.floor(dtx);\n- var row = Math.floor(dty);\n- if (row >= 0 && row < numRows) {\n- var tile = this.grid[row][col];\n- if (tile) {\n- data = {\n- tile: tile,\n- // pixel index within tile\n- i: Math.floor((dtx - col) * tileWidth),\n- j: Math.floor((dty - row) * tileHeight)\n- };\n- }\n+ getPressedButton: function(element) {\n+ var depth = 3, // limit the search depth\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ // hit!\n+ button = element;\n+ break;\n }\n- }\n- return data;\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return button;\n },\n \n /**\n- * Method: destroyTile\n+ * Method: ignore\n+ * Check for event target elements that should be ignored by OpenLayers.\n *\n * Parameters:\n- * tile - {<OpenLayers.Tile>}\n+ * element - {DOMElement} The event target.\n */\n- destroyTile: function(tile) {\n- this.removeTileMonitoringHooks(tile);\n- tile.destroy();\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === 'a') {\n+ ignore = true;\n+ break;\n+ }\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return ignore;\n },\n \n /**\n- * Method: getServerResolution\n- * Return the closest server-supported resolution.\n+ * Method: buttonClick\n+ * Check if a button was clicked, and fire the buttonclick event\n *\n * Parameters:\n- * resolution - {Number} The base resolution. If undefined the\n- * map resolution is used.\n- *\n- * Returns:\n- * {Number} The closest server resolution value.\n+ * evt - {Event}\n */\n- getServerResolution: function(resolution) {\n- var distance = Number.POSITIVE_INFINITY;\n- resolution = resolution || this.map.getResolution();\n- if (this.serverResolutions &&\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n- var i, newDistance, newResolution, serverResolution;\n- for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n- newResolution = this.serverResolutions[i];\n- newDistance = Math.abs(newResolution - resolution);\n- if (newDistance > distance) {\n- break;\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ // was a button pressed?\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break;\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ });\n+ }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt;\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n }\n- distance = newDistance;\n- serverResolution = newResolution;\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt;\n }\n- resolution = serverResolution;\n }\n- return resolution;\n- },\n+ return propagate;\n+ }\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Panel.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Panel\n+ * The Panel control is a container for other controls. With it toolbars\n+ * may be composed.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n /**\n- * Method: getServerZoom\n- * Return the zoom value corresponding to the best matching server\n- * resolution, taking into account <serverResolutions> and <zoomOffset>.\n- *\n- * Returns:\n- * {Number} The closest server supported zoom. This is not the map zoom\n- * level, but an index of the server's resolutions array.\n+ * Property: controls\n+ * {Array(<OpenLayers.Control>)}\n */\n- getServerZoom: function() {\n- var resolution = this.getServerResolution();\n- return this.serverResolutions ?\n- OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n- this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n- },\n+ controls: null,\n \n /**\n- * Method: applyBackBuffer\n- * Create, insert, scale and position a back buffer for the layer.\n- *\n- * Parameters:\n- * resolution - {Number} The resolution to transition to.\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n */\n- applyBackBuffer: function(resolution) {\n- if (this.backBufferTimerId !== null) {\n- this.removeBackBuffer();\n- }\n- var backBuffer = this.backBuffer;\n- if (!backBuffer) {\n- backBuffer = this.createBackBuffer();\n- if (!backBuffer) {\n- return;\n- }\n- if (resolution === this.gridResolution) {\n- this.div.insertBefore(backBuffer, this.div.firstChild);\n- } else {\n- this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n- }\n- this.backBuffer = backBuffer;\n+ autoActivate: true,\n \n- // set some information in the instance for subsequent\n- // calls to applyBackBuffer where the same back buffer\n- // is reused\n- var topLeftTileBounds = this.grid[0][0].bounds;\n- this.backBufferLonLat = {\n- lon: topLeftTileBounds.left,\n- lat: topLeftTileBounds.top\n- };\n- this.backBufferResolution = this.gridResolution;\n- }\n+ /** \n+ * APIProperty: defaultControl\n+ * {<OpenLayers.Control>} The control which is activated when the control is\n+ * activated (turned on), which also happens at instantiation.\n+ * If <saveState> is true, <defaultControl> will be nullified after the\n+ * first activation of the panel.\n+ */\n+ defaultControl: null,\n \n- var ratio = this.backBufferResolution / resolution;\n+ /**\n+ * APIProperty: saveState\n+ * {Boolean} If set to true, the active state of this panel's controls will\n+ * be stored on panel deactivation, and restored on reactivation. Default\n+ * is false.\n+ */\n+ saveState: false,\n \n- // scale the tiles inside the back buffer\n- var tiles = backBuffer.childNodes,\n- tile;\n- for (var i = tiles.length - 1; i >= 0; --i) {\n- tile = tiles[i];\n- tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n- tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n- tile.style.width = Math.round(ratio * tile._w) + 'px';\n- tile.style.height = Math.round(ratio * tile._h) + 'px';\n- }\n+ /**\n+ * APIProperty: allowDepress\n+ * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n+ * be deactivated by clicking the icon that represents them. Default \n+ * is false.\n+ */\n+ allowDepress: false,\n \n- // and position it (based on the grid's top-left corner)\n- var position = this.getViewPortPxFromLonLat(\n- this.backBufferLonLat, resolution);\n- var leftOffset = this.map.layerContainerOriginPx.x;\n- var topOffset = this.map.layerContainerOriginPx.y;\n- backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n- backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n- },\n+ /**\n+ * Property: activeState\n+ * {Object} stores the active state of this panel's controls.\n+ */\n+ activeState: null,\n \n /**\n- * Method: createBackBuffer\n- * Create a back buffer.\n+ * Constructor: OpenLayers.Control.Panel\n+ * Create a new control panel.\n *\n- * Returns:\n- * {DOMElement} The DOM element for the back buffer, undefined if the\n- * grid isn't initialized yet.\n+ * Each control in the panel is represented by an icon. When clicking \n+ * on an icon, the <activateControl> method is called.\n+ *\n+ * Specific properties for controls on a panel:\n+ * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n+ * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n+ * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n+ * title - {string} Text displayed when mouse is over the icon that \n+ * represents the control. \n+ *\n+ * The <OpenLayers.Control.type> of a control determines the behavior when\n+ * clicking its icon:\n+ * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n+ * controls of this type in the same panel are deactivated. This is\n+ * the default type.\n+ * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n+ * toggled.\n+ * <OpenLayers.Control.TYPE_BUTTON> - The\n+ * <OpenLayers.Control.Button.trigger> method of the control is called,\n+ * but its active state is not changed.\n+ *\n+ * If a control is <OpenLayers.Control.active>, it will be drawn with the\n+ * olControl[Name]ItemActive class, otherwise with the\n+ * olControl[Name]ItemInactive class.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n */\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.grid.length > 0) {\n- backBuffer = document.createElement('div');\n- backBuffer.id = this.div.id + '_bb';\n- backBuffer.className = 'olBackBuffer';\n- backBuffer.style.position = 'absolute';\n- var map = this.map;\n- backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n- this.getZIndex() - 1 :\n- // 'map-resize':\n- map.Z_INDEX_BASE.BaseLayer -\n- (map.getNumLayers() - map.getLayerIndex(this));\n- for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n- for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n- var tile = this.grid[i][j],\n- markup = this.grid[i][j].createBackBuffer();\n- if (markup) {\n- markup._i = i;\n- markup._j = j;\n- markup._w = tile.size.w;\n- markup._h = tile.size.h;\n- markup.id = tile.id + '_bb';\n- backBuffer.appendChild(markup);\n- }\n- }\n- }\n- }\n- return backBuffer;\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.controls = [];\n+ this.activeState = {};\n },\n \n /**\n- * Method: removeBackBuffer\n- * Remove back buffer from DOM.\n+ * APIMethod: destroy\n */\n- removeBackBuffer: function() {\n- if (this._transitionElement) {\n- for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.stopObserving(this._transitionElement,\n- this.transitionendEvents[i], this._removeBackBuffer);\n- }\n- delete this._transitionElement;\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n }\n- if (this.backBuffer) {\n- if (this.backBuffer.parentNode) {\n- this.backBuffer.parentNode.removeChild(this.backBuffer);\n- }\n- this.backBuffer = null;\n- this.backBufferResolution = null;\n- if (this.backBufferTimerId !== null) {\n- window.clearTimeout(this.backBufferTimerId);\n- this.backBufferTimerId = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n+ ctl = this.controls[i];\n+ if (ctl.events) {\n+ ctl.events.un({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n }\n+ ctl.panel_div = null;\n }\n+ this.activeState = null;\n },\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n+ * APIMethod: activate\n */\n- moveByPx: function(dx, dy) {\n- if (!this.singleTile) {\n- this.moveGriddedTiles();\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ if (control === this.defaultControl ||\n+ (this.saveState && this.activeState[control.id])) {\n+ control.activate();\n+ }\n+ }\n+ if (this.saveState === true) {\n+ this.defaultControl = null;\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n /**\n- * APIMethod: setTileSize\n- * Check if we are in singleTile mode and if so, set the size as a ratio\n- * of the map size (as specified by the layer's 'ratio' property).\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>}\n+ * APIMethod: deactivate\n */\n- setTileSize: function(size) {\n- if (this.singleTile) {\n- size = this.map.getSize();\n- size.h = parseInt(size.h * this.ratio, 10);\n- size.w = parseInt(size.w * this.ratio, 10);\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ this.activeState[control.id] = control.deactivate();\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n }\n- OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n },\n \n /**\n- * APIMethod: getTilesBounds\n- * Return the bounds of the tile grid.\n+ * Method: draw\n *\n * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the\n- * currently loaded tiles (including those partially or not at all seen \n- * onscreen).\n+ * {DOMElement}\n */\n- getTilesBounds: function() {\n- var bounds = null;\n-\n- var length = this.grid.length;\n- if (length) {\n- var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n- width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n- height = this.grid.length * bottomLeftTileBounds.getHeight();\n-\n- bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n- bottomLeftTileBounds.bottom,\n- bottomLeftTileBounds.left + width,\n- bottomLeftTileBounds.bottom + height);\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n }\n- return bounds;\n+ this.addControlsToMap(this.controls);\n+ return this.div;\n },\n \n /**\n- * Method: initSingleTile\n- * \n- * Parameters: \n- * bounds - {<OpenLayers.Bounds>}\n+ * Method: redraw\n */\n- initSingleTile: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- //determine new tile bounds\n- var center = bounds.getCenterLonLat();\n- var tileWidth = bounds.getWidth() * this.ratio;\n- var tileHeight = bounds.getHeight() * this.ratio;\n-\n- var tileBounds =\n- new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n- center.lat - (tileHeight / 2),\n- center.lon + (tileWidth / 2),\n- center.lat + (tileHeight / 2));\n-\n- var px = this.map.getLayerPxFromLonLat({\n- lon: tileBounds.left,\n- lat: tileBounds.top\n- });\n-\n- if (!this.grid.length) {\n- this.grid[0] = [];\n+ redraw: function() {\n+ for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n+ this.div.removeChild(this.div.childNodes[i]);\n }\n-\n- var tile = this.grid[0][0];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n-\n- this.addTileMonitoringHooks(tile);\n- tile.draw();\n- this.grid[0][0] = tile;\n- } else {\n- tile.moveTo(tileBounds, px);\n+ this.div.innerHTML = \"\";\n+ if (this.active) {\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.div.appendChild(this.controls[i].panel_div);\n+ }\n }\n-\n- //remove all but our single tile\n- this.removeExcessTiles(1, 1);\n-\n- // store the resolution of the grid\n- this.gridResolution = this.getServerResolution();\n },\n \n- /** \n- * Method: calculateGridLayout\n- * Generate parameters for the grid layout.\n+ /**\n+ * APIMethod: activateControl\n+ * This method is called when the user click on the icon representing a \n+ * control in the panel.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an\n- * object with a 'left' and 'top' properties.\n- * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * resolution - {Number}\n- *\n- * Returns:\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n+ * control - {<OpenLayers.Control>}\n */\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n-\n- var offsetlon = bounds.left - origin.lon;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n-\n- var rowSign = this.rowSign;\n-\n- var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n- var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\n-\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n- };\n-\n+ activateControl: function(control) {\n+ if (!this.active) {\n+ return false;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n+ control.trigger();\n+ return;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n+ if (control.active) {\n+ control.deactivate();\n+ } else {\n+ control.activate();\n+ }\n+ return;\n+ }\n+ if (this.allowDepress && control.active) {\n+ control.deactivate();\n+ } else {\n+ var c;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ c = this.controls[i];\n+ if (c != control &&\n+ (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n+ c.deactivate();\n+ }\n+ }\n+ control.activate();\n+ }\n },\n \n /**\n- * Method: getTileOrigin\n- * Determine the origin for aligning the grid of tiles. If a <tileOrigin>\n- * property is supplied, that will be returned. Otherwise, the origin\n- * will be derived from the layer's <maxExtent> property. In this case,\n- * the tile origin will be the corner of the <maxExtent> given by the \n- * <tileOriginCorner> property.\n+ * APIMethod: addControls\n+ * To build a toolbar, you add a set of controls to it. addControls\n+ * lets you add a single control or a list of controls to the \n+ * Control Panel.\n *\n- * Returns:\n- * {<OpenLayers.LonLat>} The tile origin.\n+ * Parameters:\n+ * controls - {<OpenLayers.Control>} Controls to add in the panel.\n */\n- getTileOrigin: function() {\n- var origin = this.tileOrigin;\n- if (!origin) {\n- var extent = this.getMaxExtent();\n- var edges = ({\n- \"tl\": [\"left\", \"top\"],\n- \"tr\": [\"right\", \"top\"],\n- \"bl\": [\"left\", \"bottom\"],\n- \"br\": [\"right\", \"bottom\"]\n- })[this.tileOriginCorner];\n- origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n+ addControls: function(controls) {\n+ if (!(OpenLayers.Util.isArray(controls))) {\n+ controls = [controls];\n+ }\n+ this.controls = this.controls.concat(controls);\n+\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var control = controls[i],\n+ element = this.createControlMarkup(control);\n+ OpenLayers.Element.addClass(element,\n+ control.displayClass + \"ItemInactive\");\n+ OpenLayers.Element.addClass(element, \"olButton\");\n+ if (control.title != \"\" && !element.title) {\n+ element.title = control.title;\n+ }\n+ control.panel_div = element;\n+ }\n+\n+ if (this.map) { // map.addControl() has already been called on the panel\n+ this.addControlsToMap(controls);\n+ this.redraw();\n }\n- return origin;\n },\n \n /**\n- * Method: getTileBoundsForGridIndex\n+ * APIMethod: createControlMarkup\n+ * This function just creates a div for the control. If specific HTML\n+ * markup is needed this function can be overridden in specific classes,\n+ * or at panel instantiation time:\n+ *\n+ * Example:\n+ * (code)\n+ * var panel = new OpenLayers.Control.Panel({\n+ * defaultControl: control,\n+ * // ovverride createControlMarkup to create actual buttons\n+ * // including texts wrapped into span elements.\n+ * createControlMarkup: function(control) {\n+ * var button = document.createElement('button'),\n+ * span = document.createElement('span');\n+ * if (control.text) {\n+ * span.innerHTML = control.text;\n+ * }\n+ * return button;\n+ * }\n+ * });\n+ * (end)\n *\n * Parameters:\n- * row - {Number} The row of the grid\n- * col - {Number} The column of the grid\n+ * control - {<OpenLayers.Control>} The control to create the HTML\n+ * markup for.\n *\n * Returns:\n- * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n+ * {DOMElement} The markup.\n */\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var startcol = tileLayout.startcol;\n- var startrow = tileLayout.startrow;\n- var rowSign = this.rowSign;\n- return new OpenLayers.Bounds(\n- origin.lon + (startcol + col) * tilelon,\n- origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n- origin.lon + (startcol + col + 1) * tilelon,\n- origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n- );\n+ createControlMarkup: function(control) {\n+ return document.createElement(\"div\");\n },\n \n /**\n- * Method: initGriddedTiles\n- * \n+ * Method: addControlsToMap\n+ * Only for internal use in draw() and addControls() methods.\n+ *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n */\n- initGriddedTiles: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n-\n- // work out mininum number of rows and columns; this is the number of\n- // tiles required to cover the viewport plus at least one for panning\n-\n- var viewSize = this.map.getSize();\n-\n- var origin = this.getTileOrigin();\n- var resolution = this.map.getResolution(),\n- serverResolution = this.getServerResolution(),\n- ratio = resolution / serverResolution,\n- tileSize = {\n- w: this.tileSize.w / ratio,\n- h: this.tileSize.h / ratio\n- };\n-\n- var minRows = Math.ceil(viewSize.h / tileSize.h) +\n- 2 * this.buffer + 1;\n- var minCols = Math.ceil(viewSize.w / tileSize.w) +\n- 2 * this.buffer + 1;\n-\n- var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n- this.gridLayout = tileLayout;\n-\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n-\n- var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n- var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n-\n- var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n- var startPx = this.map.getViewPortPxFromLonLat(\n- new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n- );\n- startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n- startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n-\n- var tileData = [],\n- center = this.map.getCenter();\n-\n- var rowidx = 0;\n- do {\n- var row = this.grid[rowidx];\n- if (!row) {\n- row = [];\n- this.grid.push(row);\n+ addControlsToMap: function(controls) {\n+ var control;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ control = controls[i];\n+ if (control.autoActivate === true) {\n+ control.autoActivate = false;\n+ this.map.addControl(control);\n+ control.autoActivate = true;\n+ } else {\n+ this.map.addControl(control);\n+ control.deactivate();\n }\n+ control.events.on({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n+ }\n+ },\n \n- var colidx = 0;\n- do {\n- tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n- var px = startPx.clone();\n- px.x = px.x + colidx * Math.round(tileSize.w);\n- px.y = px.y + rowidx * Math.round(tileSize.h);\n- var tile = row[colidx];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- row.push(tile);\n- } else {\n- tile.moveTo(tileBounds, px, false);\n- }\n- var tileCenter = tileBounds.getCenterLonLat();\n- tileData.push({\n- tile: tile,\n- distance: Math.pow(tileCenter.lon - center.lon, 2) +\n- Math.pow(tileCenter.lat - center.lat, 2)\n- });\n-\n- colidx += 1;\n- } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n- colidx < minCols);\n-\n- rowidx += 1;\n- } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n- rowidx < minRows);\n-\n- //shave off exceess rows and colums\n- this.removeExcessTiles(rowidx, colidx);\n+ /**\n+ * Method: iconOn\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOn: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n+ d.className = d.className.replace(re, \"$1Active\");\n+ },\n \n- var resolution = this.getServerResolution();\n- // store the resolution of the grid\n- this.gridResolution = resolution;\n+ /**\n+ * Method: iconOff\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOff: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n+ d.className = d.className.replace(re, \"$1Inactive\");\n+ },\n \n- //now actually draw the tiles\n- tileData.sort(function(a, b) {\n- return a.distance - b.distance;\n- });\n- for (var i = 0, ii = tileData.length; i < ii; ++i) {\n- tileData[i].tile.draw();\n+ /**\n+ * Method: onButtonClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ onButtonClick: function(evt) {\n+ var controls = this.controls,\n+ button = evt.buttonElement;\n+ for (var i = controls.length - 1; i >= 0; --i) {\n+ if (controls[i].panel_div === button) {\n+ this.activateControl(controls[i]);\n+ break;\n+ }\n }\n },\n \n /**\n- * Method: getMaxExtent\n- * Get this layer's maximum extent. (Implemented as a getter for\n- * potential specific implementations in sub-classes.)\n+ * APIMethod: getControlsBy\n+ * Get a list of controls with properties matching the given criteria.\n+ *\n+ * Parameters:\n+ * property - {String} A control property to be matched.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(control[property]) evaluates to true, the control will be\n+ * included in the array returned. If no controls are found, an empty\n+ * array is returned.\n *\n * Returns:\n- * {<OpenLayers.Bounds>}\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n+ * An empty array is returned if no matches are found.\n */\n- getMaxExtent: function() {\n- return this.maxExtent;\n+ getControlsBy: function(property, match) {\n+ var test = (typeof match.test == \"function\");\n+ var found = OpenLayers.Array.filter(this.controls, function(item) {\n+ return item[property] == match || (test && match.test(item[property]));\n+ });\n+ return found;\n },\n \n /**\n- * APIMethod: addTile\n- * Create a tile, initialize it, and add it to the layer div. \n+ * APIMethod: getControlsByName\n+ * Get a list of contorls with names matching the given name.\n *\n- * Parameters\n- * bounds - {<OpenLayers.Bounds>}\n- * position - {<OpenLayers.Pixel>}\n+ * Parameters:\n+ * match - {String | Object} A control name. The name can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * name.test(control.name) evaluates to true, the control will be included\n+ * in the list of controls returned. If no controls are found, an empty\n+ * array is returned.\n *\n * Returns:\n- * {<OpenLayers.Tile>} The added OpenLayers.Tile\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n+ * An empty array is returned if no matches are found.\n */\n- addTile: function(bounds, position) {\n- var tile = new this.tileClass(\n- this, position, bounds, null, this.tileSize, this.tileOptions\n- );\n- this.events.triggerEvent(\"addtile\", {\n- tile: tile\n- });\n- return tile;\n+ getControlsByName: function(match) {\n+ return this.getControlsBy(\"name\", match);\n },\n \n- /** \n- * Method: addTileMonitoringHooks\n- * This function takes a tile as input and adds the appropriate hooks to \n- * the tile so that the layer can keep track of the loading tiles.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n+ /**\n+ * APIMethod: getControlsByClass\n+ * Get a list of controls of a given type (CLASS_NAME).\n+ *\n+ * Parameters:\n+ * match - {String | Object} A control class name. The type can also be a\n+ * regular expression literal or object. In addition, it can be any\n+ * object with a method named test. For reqular expressions or other,\n+ * if type.test(control.CLASS_NAME) evaluates to true, the control will\n+ * be included in the list of controls returned. If no controls are\n+ * found, an empty array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n+ * An empty array is returned if no matches are found.\n */\n- addTileMonitoringHooks: function(tile) {\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match);\n+ },\n \n- var replacingCls = 'olTileReplacing';\n+ CLASS_NAME: \"OpenLayers.Control.Panel\"\n+});\n \n- tile.onLoadStart = function() {\n- //if that was first tile then trigger a 'loadstart' on the layer\n- if (this.loading === false) {\n- this.loading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.events.triggerEvent(\"tileloadstart\", {\n- tile: tile\n- });\n- this.numLoadingTiles++;\n- if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n- }\n- };\n+/* ======================================================================\n+ OpenLayers/Handler.js\n+ ====================================================================== */\n \n- tile.onLoadEnd = function(evt) {\n- this.numLoadingTiles--;\n- var aborted = evt.type === 'unload';\n- this.events.triggerEvent(\"tileloaded\", {\n- tile: tile,\n- aborted: aborted\n- });\n- if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- var tileDiv = tile.getTile();\n- if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n- var bufferTile = document.getElementById(tile.id + '_bb');\n- if (bufferTile) {\n- bufferTile.parentNode.removeChild(bufferTile);\n- }\n- }\n- OpenLayers.Element.removeClass(tileDiv, replacingCls);\n- }\n- //if that was the last tile, then trigger a 'loadend' on the layer\n- if (this.numLoadingTiles === 0) {\n- if (this.backBuffer) {\n- if (this.backBuffer.childNodes.length === 0) {\n- // no tiles transitioning, remove immediately\n- this.removeBackBuffer();\n- } else {\n- // wait until transition has ended or delay has passed\n- this._transitionElement = aborted ?\n- this.div.lastChild : tile.imgDiv;\n- var transitionendEvents = this.transitionendEvents;\n- for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.observe(this._transitionElement,\n- transitionendEvents[i],\n- this._removeBackBuffer);\n- }\n- // the removal of the back buffer is delayed to prevent\n- // flash effects due to the animation of tile displaying\n- this.backBufferTimerId = window.setTimeout(\n- this._removeBackBuffer, this.removeBackBufferDelay\n- );\n- }\n- }\n- this.loading = false;\n- this.events.triggerEvent(\"loadend\");\n- }\n- };\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- tile.onLoadError = function() {\n- this.events.triggerEvent(\"tileerror\", {\n- tile: tile\n- });\n- };\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n+ */\n \n- tile.events.on({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n+/**\n+ * Class: OpenLayers.Handler\n+ * Base class to construct a higher-level handler for event sequences. All\n+ * handlers have activate and deactivate methods. In addition, they have\n+ * methods named like browser events. When a handler is activated, any\n+ * additional methods named like a browser event is registered as a\n+ * listener for the corresponding event. When a handler is deactivated,\n+ * those same methods are unregistered as event listeners.\n+ *\n+ * Handlers also typically have a callbacks object with keys named like\n+ * the abstracted events or event sequences that they are in charge of\n+ * handling. The controls that wrap handlers define the methods that\n+ * correspond to these abstract events - so instead of listening for\n+ * individual browser events, they only listen for the abstract events\n+ * defined by the handler.\n+ * \n+ * Handlers are created by controls, which ultimately have the responsibility\n+ * of making changes to the the state of the application. Handlers\n+ * themselves may make temporary changes, but in general are expected to\n+ * return the application in the same state that they found it.\n+ */\n+OpenLayers.Handler = OpenLayers.Class({\n \n- /** \n- * Method: removeTileMonitoringHooks\n- * This function takes a tile as input and removes the tile hooks \n- * that were added in addTileMonitoringHooks()\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n+ /**\n+ * Property: id\n+ * {String}\n */\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- \"loaderror\": tile.onLoadError,\n- scope: this\n- });\n- },\n+ id: null,\n \n /**\n- * Method: moveGriddedTiles\n+ * APIProperty: control\n+ * {<OpenLayers.Control>}. The control that initialized this handler. The\n+ * control is assumed to have a valid map property - that map is used\n+ * in the handler's own setMap method.\n */\n- moveGriddedTiles: function() {\n- var buffer = this.buffer + 1;\n- while (true) {\n- var tlTile = this.grid[0][0];\n- var tlViewPort = {\n- x: tlTile.position.x +\n- this.map.layerContainerOriginPx.x,\n- y: tlTile.position.y +\n- this.map.layerContainerOriginPx.y\n- };\n- var ratio = this.getServerResolution() / this.map.getResolution();\n- var tileSize = {\n- w: Math.round(this.tileSize.w * ratio),\n- h: Math.round(this.tileSize.h * ratio)\n- };\n- if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n- this.shiftColumn(true, tileSize);\n- } else if (tlViewPort.x < -tileSize.w * buffer) {\n- this.shiftColumn(false, tileSize);\n- } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n- this.shiftRow(true, tileSize);\n- } else if (tlViewPort.y < -tileSize.h * buffer) {\n- this.shiftRow(false, tileSize);\n- } else {\n- break;\n- }\n- }\n- },\n+ control: null,\n \n /**\n- * Method: shiftRow\n- * Shifty grid work\n+ * Property: map\n+ * {<OpenLayers.Map>}\n+ */\n+ map: null,\n+\n+ /**\n+ * APIProperty: keyMask\n+ * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n+ * constants to construct a keyMask. The keyMask is used by\n+ * <checkModifiers>. If the keyMask matches the combination of keys\n+ * down on an event, checkModifiers returns true.\n *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n+ * Example:\n+ * (code)\n+ * // handler only responds if the Shift key is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n+ *\n+ * // handler only responds if Ctrl-Shift is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n+ * OpenLayers.Handler.MOD_CTRL;\n+ * (end)\n */\n- shiftRow: function(prepend, tileSize) {\n- var grid = this.grid;\n- var rowIndex = prepend ? 0 : (grid.length - 1);\n- var sign = prepend ? -1 : 1;\n- var rowSign = this.rowSign;\n- var tileLayout = this.gridLayout;\n- tileLayout.startrow += sign * rowSign;\n+ keyMask: null,\n \n- var modelRow = grid[rowIndex];\n- var row = grid[prepend ? 'pop' : 'shift']();\n- for (var i = 0, len = row.length; i < len; i++) {\n- var tile = row[i];\n- var position = modelRow[i].position.clone();\n- position.y += tileSize.h * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n- }\n- grid[prepend ? 'unshift' : 'push'](row);\n- },\n+ /**\n+ * Property: active\n+ * {Boolean}\n+ */\n+ active: false,\n \n /**\n- * Method: shiftColumn\n- * Shift grid work in the other dimension\n- *\n- * Parameters:\n- * prepend - {Boolean} if true, prepend to beginning.\n- * if false, then append to end\n- * tileSize - {Object} rendered tile size; object with w and h properties\n+ * Property: evt\n+ * {Event} This property references the last event handled by the handler.\n+ * Note that this property is not part of the stable API. Use of the\n+ * evt property should be restricted to controls in the library\n+ * or other applications that are willing to update with changes to\n+ * the OpenLayers code.\n */\n- shiftColumn: function(prepend, tileSize) {\n- var grid = this.grid;\n- var colIndex = prepend ? 0 : (grid[0].length - 1);\n- var sign = prepend ? -1 : 1;\n- var tileLayout = this.gridLayout;\n- tileLayout.startcol += sign;\n+ evt: null,\n \n- for (var i = 0, len = grid.length; i < len; i++) {\n- var row = grid[i];\n- var position = row[colIndex].position.clone();\n- var tile = row[prepend ? 'pop' : 'shift']();\n- position.x += tileSize.w * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n- row[prepend ? 'unshift' : 'push'](tile);\n- }\n- },\n+ /**\n+ * Property: touch\n+ * {Boolean} Indicates the support of touch events. When touch events are \n+ * started touch will be true and all mouse related listeners will do \n+ * nothing.\n+ */\n+ touch: false,\n \n /**\n- * Method: removeExcessTiles\n- * When the size of the map or the buffer changes, we may need to\n- * remove some excess rows and columns.\n- * \n+ * Constructor: OpenLayers.Handler\n+ * Construct a handler.\n+ *\n * Parameters:\n- * rows - {Integer} Maximum number of rows we want our grid to have.\n- * columns - {Integer} Maximum number of columns we want our grid to have.\n+ * control - {<OpenLayers.Control>} The control that initialized this\n+ * handler. The control is assumed to have a valid map property; that\n+ * map is used in the handler's own setMap method. If a map property\n+ * is present in the options argument it will be used instead.\n+ * callbacks - {Object} An object whose properties correspond to abstracted\n+ * events or sequences of browser events. The values for these\n+ * properties are functions defined by the control that get called by\n+ * the handler.\n+ * options - {Object} An optional object whose properties will be set on\n+ * the handler.\n */\n- removeExcessTiles: function(rows, columns) {\n- var i, l;\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.control = control;\n+ this.callbacks = callbacks;\n \n- // remove extra rows\n- while (this.grid.length > rows) {\n- var row = this.grid.pop();\n- for (i = 0, l = row.length; i < l; i++) {\n- var tile = row[i];\n- this.destroyTile(tile);\n- }\n+ var map = this.map || control.map;\n+ if (map) {\n+ this.setMap(map);\n }\n \n- // remove extra columns\n- for (i = 0, l = this.grid.length; i < l; i++) {\n- while (this.grid[i].length > columns) {\n- var row = this.grid[i];\n- var tile = row.pop();\n- this.destroyTile(tile);\n- }\n- }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n /**\n- * Method: onMapResize\n- * For singleTile layers, this will set a new tile size according to the\n- * dimensions of the map pane.\n+ * Method: setMap\n */\n- onMapResize: function() {\n- if (this.singleTile) {\n- this.clearGrid();\n- this.setTileSize();\n- }\n+ setMap: function(map) {\n+ this.map = map;\n },\n \n /**\n- * APIMethod: getTileBounds\n- * Returns The tile bounds for a layer given a pixel location.\n- *\n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n+ * Method: checkModifiers\n+ * Check the keyMask on the handler. If no <keyMask> is set, this always\n+ * returns true. If a <keyMask> is set and it matches the combination\n+ * of keys down on an event, this returns true.\n *\n * Returns:\n- * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n+ * {Boolean} The keyMask matches the keys down on an event.\n */\n- getTileBounds: function(viewPortPx) {\n- var maxExtent = this.maxExtent;\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = maxExtent.left + (tileMapWidth *\n- Math.floor((mapPoint.lon -\n- maxExtent.left) /\n- tileMapWidth));\n- var tileBottom = maxExtent.bottom + (tileMapHeight *\n- Math.floor((mapPoint.lat -\n- maxExtent.bottom) /\n- tileMapHeight));\n- return new OpenLayers.Bounds(tileLeft, tileBottom,\n- tileLeft + tileMapWidth,\n- tileBottom + tileMapHeight);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Grid\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/XYZ.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/** \n- * Class: OpenLayers.Layer.XYZ\n- * The XYZ class is designed to make it easier for people who have tiles\n- * arranged by a standard XYZ grid. \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true;\n+ }\n+ /* calculate the keyboard modifier mask for this event */\n+ var keyModifiers =\n+ (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n+ (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n+ (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n+ (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n \n- /**\n- * APIProperty: isBaseLayer\n- * Default is true, as this is designed to be a base tile source. \n- */\n- isBaseLayer: true,\n+ /* if it differs from the handler object's key mask,\n+ bail out of the event handler */\n+ return (keyModifiers == this.keyMask);\n+ },\n \n /**\n- * APIProperty: sphericalMercator\n- * Whether the tile extents should be set to the defaults for \n- * spherical mercator. Useful for things like OpenStreetMap.\n- * Default is false, except for the OSM subclass.\n+ * APIMethod: activate\n+ * Turn on the handler. Returns false if the handler was already active.\n+ * \n+ * Returns: \n+ * {Boolean} The handler was activated.\n */\n- sphericalMercator: false,\n+ activate: function() {\n+ if (this.active) {\n+ return false;\n+ }\n+ // register for event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.register(events[i], this[events[i]]);\n+ }\n+ }\n+ this.active = true;\n+ return true;\n+ },\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * APIMethod: deactivate\n+ * Turn off the handler. Returns false if the handler was already inactive.\n+ * \n+ * Returns:\n+ * {Boolean} The handler was deactivated.\n */\n- zoomOffset: 0,\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false;\n+ }\n+ // unregister event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n+ }\n+ }\n+ this.touch = false;\n+ this.active = false;\n+ return true;\n+ },\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * Method: startTouch\n+ * Start touch events, this method must be called by subclasses in \n+ * \"touchstart\" method. When touch events are started <touch> will be\n+ * true and all mouse related listeners will do nothing.\n */\n- serverResolutions: null,\n+ startTouch: function() {\n+ if (!this.touch) {\n+ this.touch = true;\n+ var events = [\n+ \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n+ \"mouseout\"\n+ ];\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.XYZ\n+ * Method: callback\n+ * Trigger the control's named callback with the given arguments\n *\n * Parameters:\n- * name - {String}\n- * url - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * name - {String} The key for the callback that is one of the properties\n+ * of the handler's callbacks object.\n+ * args - {Array(*)} An array of arguments (any type) with which to call \n+ * the callback (defined by the control).\n */\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options);\n+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args);\n }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name || this.name, url || this.url, {},\n- options\n- ]);\n },\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n- * Returns:\n- * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n+ * Method: register\n+ * register an event on the map\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name,\n- this.url,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n+ register: function(name, method) {\n+ // TODO: deal with registerPriority in 3.0\n+ this.map.events.registerPriority(name, this, method);\n+ this.map.events.registerPriority(name, this, this.setEvent);\n },\n \n /**\n- * Method: getURL\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n+ * Method: unregister\n+ * unregister an event from the map\n */\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = '' + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url);\n- }\n-\n- return OpenLayers.String.format(url, xyz);\n+ unregister: function(name, method) {\n+ this.map.events.unregister(name, this, method);\n+ this.map.events.unregister(name, this, this.setEvent);\n },\n \n /**\n- * Method: getXYZ\n- * Calculates x, y and z for the given bounds.\n+ * Method: setEvent\n+ * With each registered browser event, the handler sets its own evt\n+ * property. This property can be accessed by controls if needed\n+ * to get more information about the event that the handler is\n+ * processing.\n *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n+ * and meta cannot be checked with the keyboard handler). For a\n+ * control to determine which modifier keys are associated with the\n+ * event that a handler is currently processing, it should access\n+ * (code)handler.evt.altKey || handler.evt.shiftKey ||\n+ * handler.evt.ctrlKey || handler.evt.metaKey(end).\n *\n- * Returns:\n- * {Object} - an object with x, y and z properties.\n+ * Parameters:\n+ * evt - {Event} The browser event.\n */\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) /\n- (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) /\n- (res * this.tileSize.h));\n- var z = this.getServerZoom();\n-\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = ((x % limit) + limit) % limit;\n- }\n-\n- return {\n- 'x': x,\n- 'y': y,\n- 'z': z\n- };\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true;\n },\n \n- /* APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ /**\n+ * Method: destroy\n+ * Deconstruct the handler.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n- this.maxExtent.bottom);\n- }\n+ destroy: function() {\n+ // unregister event listeners\n+ this.deactivate();\n+ // eliminate circular references\n+ this.control = this.map = null;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+ CLASS_NAME: \"OpenLayers.Handler\"\n });\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_NONE\n+ * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n+ */\n+OpenLayers.Handler.MOD_NONE = 0;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_SHIFT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n+ */\n+OpenLayers.Handler.MOD_SHIFT = 1;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_CTRL\n+ * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n+ */\n+OpenLayers.Handler.MOD_CTRL = 2;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_ALT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n+ */\n+OpenLayers.Handler.MOD_ALT = 4;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_META\n+ * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n+ */\n+OpenLayers.Handler.MOD_META = 8;\n+\n+\n /* ======================================================================\n- OpenLayers/Layer/OSM.js\n+ OpenLayers/Handler/Drag.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/XYZ.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Layer.OSM\n- * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n- * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n- * a different layer instead, you need to provide a different\n- * URL to the constructor. Here's an example for using OpenCycleMap:\n- * \n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n+ * Class: OpenLayers.Handler.Drag\n+ * The drag handler is used to deal with sequences of browser events related\n+ * to dragging. The handler is used by controls that want to know when\n+ * a drag sequence begins, when a drag is happening, and when it has\n+ * finished.\n+ *\n+ * Controls that use the drag handler typically construct it with callbacks\n+ * for 'down', 'move', and 'done'. Callbacks for these keys are called\n+ * when the drag begins, with each move, and when the drag is done. In\n+ * addition, controls can have callbacks keyed to 'up' and 'out' if they\n+ * care to differentiate between the types of events that correspond with\n+ * the end of a drag sequence. If no drag actually occurs (no mouse move)\n+ * the 'down' and 'up' callbacks will be called, but not the 'done'\n+ * callback.\n+ *\n+ * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n+ * - <OpenLayers.Handler>\n */\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+\n+ /** \n+ * Property: started\n+ * {Boolean} When a mousedown or touchstart event is received, we want to\n+ * record it, but not set 'dragging' until the mouse moves after starting.\n+ */\n+ started: false,\n \n /**\n- * APIProperty: name\n- * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n- * argument to the constructor is null or undefined.\n+ * Property: stopDown\n+ * {Boolean} Stop propagation of mousedown events from getting to listeners\n+ * on the same element. Default is true.\n */\n- name: \"OpenStreetMap\",\n+ stopDown: true,\n+\n+ /** \n+ * Property: dragging \n+ * {Boolean} \n+ */\n+ dragging: false,\n+\n+ /** \n+ * Property: last\n+ * {<OpenLayers.Pixel>} The last pixel location of the drag.\n+ */\n+ last: null,\n+\n+ /** \n+ * Property: start\n+ * {<OpenLayers.Pixel>} The first pixel location of the drag.\n+ */\n+ start: null,\n \n /**\n- * APIProperty: url\n- * {String} The tileset URL scheme. Defaults to\n- * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n- * (the official OSM tileset) if the second argument to the constructor\n- * is null or undefined. To use another tileset you can have something\n- * like this:\n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n+ * Property: lastMoveEvt\n+ * {Object} The last mousemove event that occurred. Used to\n+ * position the map correctly when our \"delay drag\"\n+ * timeout expired.\n */\n- url: [\n- 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n- ],\n+ lastMoveEvt: null,\n \n /**\n- * Property: attribution\n- * {String} The layer attribution.\n+ * Property: oldOnselectstart\n+ * {Function}\n */\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+ oldOnselectstart: null,\n \n /**\n- * Property: sphericalMercator\n- * {Boolean}\n+ * Property: interval\n+ * {Integer} In order to increase performance, an interval (in \n+ * milliseconds) can be set to reduce the number of drag events \n+ * called. If set, a new drag event will not be set until the \n+ * interval has passed. \n+ * Defaults to 0, meaning no interval. \n */\n- sphericalMercator: true,\n+ interval: 0,\n \n /**\n- * Property: wrapDateLine\n- * {Boolean}\n+ * Property: timeoutId\n+ * {String} The id of the timeout used for the mousedown interval.\n+ * This is \"private\", and should be left alone.\n */\n- wrapDateLine: true,\n+ timeoutId: null,\n \n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- *\n- * When using OSM tilesets other than the default ones, it may be\n- * necessary to set this to\n- *\n- * (code)\n- * {crossOriginKeyword: null}\n- * (end)\n- *\n- * if the server does not send Access-Control-Allow-Origin headers.\n+ /**\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, the handler will also handle mouse moves when\n+ * the cursor has moved out of the map viewport. Default is false.\n */\n- tileOptions: null,\n+ documentDrag: false,\n \n /**\n- * Constructor: OpenLayers.Layer.OSM\n- *\n- * Parameters:\n- * name - {String} The layer name.\n- * url - {String} The tileset URL scheme.\n- * options - {Object} Configuration options for the layer. Any inherited\n- * layer option can be set in this object (e.g.\n- * <OpenLayers.Layer.Grid.buffer>).\n+ * Property: documentEvents\n+ * {Boolean} Are we currently observing document events?\n */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options && this.options.tileOptions);\n- },\n+ documentEvents: null,\n \n /**\n- * Method: clone\n+ * Constructor: OpenLayers.Handler.Drag\n+ * Returns OpenLayers.Handler.Drag\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handlers setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object containing a single function to be\n+ * called when the drag operation is finished. The callback should\n+ * expect to recieve a single argument, the pixel location of the event.\n+ * Callbacks for 'move' and 'done' are supported. You can also speficy\n+ * callbacks for 'down', 'up', and 'out' to respond to those events.\n+ * options - {Object} \n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(\n- this.name, this.url, this.getOptions());\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+\n+ if (this.documentDrag === true) {\n+ var me = this;\n+ this._docMove = function(evt) {\n+ me.mousemove({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ },\n+ element: document\n+ });\n+ };\n+ this._docUp = function(evt) {\n+ me.mouseup({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ }\n+ });\n+ };\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Bing.js\n- ====================================================================== */\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Method: dragstart\n+ * This private method is factorized from mousedown and touchstart methods\n+ *\n+ * Parameters:\n+ * evt - {Event} The event\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n+ */\n+ dragstart: function(evt) {\n+ var propagate = true;\n+ this.dragging = false;\n+ if (this.checkModifiers(evt) &&\n+ (OpenLayers.Event.isLeftClick(evt) ||\n+ OpenLayers.Event.isSingleTouch(evt))) {\n+ this.started = true;\n+ this.start = evt.xy;\n+ this.last = evt.xy;\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.down(evt);\n+ this.callback(\"down\", [evt.xy]);\n \n-/**\n- * @requires OpenLayers/Layer/XYZ.js\n- */\n+ // prevent document dragging\n+ OpenLayers.Event.preventDefault(evt);\n \n-/** \n- * Class: OpenLayers.Layer.Bing\n- * Bing layer using direct tile access as provided by Bing Maps REST Services.\n- * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n- * information. Note: Terms of Service compliant use requires the map to be\n- * configured with an <OpenLayers.Control.Attribution> control and the\n- * attribution placed on or near the map.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n- */\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart ?\n+ document.onselectstart : OpenLayers.Function.True;\n+ }\n+ document.onselectstart = OpenLayers.Function.False;\n+\n+ propagate = !this.stopDown;\n+ } else {\n+ this.started = false;\n+ this.start = null;\n+ this.last = null;\n+ }\n+ return propagate;\n+ },\n \n /**\n- * Property: key\n- * {String} API key for Bing maps, get your own key \n- * at http://bingmapsportal.com/ .\n+ * Method: dragmove\n+ * This private method is factorized from mousemove and touchmove methods\n+ *\n+ * Parameters:\n+ * evt - {Event} The event\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- key: null,\n+ dragmove: function(evt) {\n+ this.lastMoveEvt = evt;\n+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||\n+ evt.xy.y != this.last.y)) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ if (evt.element === document) {\n+ this.adjustXY(evt);\n+ // do setEvent manually because the documentEvents are not\n+ // registered with the map\n+ this.setEvent(evt);\n+ } else {\n+ this.removeDocumentEvents();\n+ }\n+ }\n+ if (this.interval > 0) {\n+ this.timeoutId = setTimeout(\n+ OpenLayers.Function.bind(this.removeTimeout, this),\n+ this.interval);\n+ }\n+ this.dragging = true;\n+\n+ this.move(evt);\n+ this.callback(\"move\", [evt.xy]);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart;\n+ document.onselectstart = OpenLayers.Function.False;\n+ }\n+ this.last = evt.xy;\n+ }\n+ return true;\n+ },\n \n /**\n- * Property: serverResolutions\n- * {Array} the resolutions provided by the Bing servers.\n+ * Method: dragend\n+ * This private method is factorized from mouseup and touchend methods\n+ *\n+ * Parameters:\n+ * evt - {Event} The event\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- serverResolutions: [\n- 156543.03390625, 78271.516953125, 39135.7584765625,\n- 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n- 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n- 305.74811309814453, 152.87405654907226, 76.43702827453613,\n- 38.218514137268066, 19.109257068634033, 9.554628534317017,\n- 4.777314267158508, 2.388657133579254, 1.194328566789627,\n- 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n- 0.07464553542435169\n- ],\n+ dragend: function(evt) {\n+ if (this.started) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ this.adjustXY(evt);\n+ this.removeDocumentEvents();\n+ }\n+ var dragged = (this.start != this.last);\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.up(evt);\n+ this.callback(\"up\", [evt.xy]);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy]);\n+ }\n+ document.onselectstart = this.oldOnselectstart;\n+ }\n+ return true;\n+ },\n \n /**\n- * Property: attributionTemplate\n- * {String}\n+ * The four methods below (down, move, up, and out) are used by subclasses\n+ * to do their own processing related to these mouse events.\n */\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n- '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n- '<img src=\"${logo}\" /></a></div>${copyrights}' +\n- '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n- 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n- 'Terms of Use</a></span>',\n \n /**\n- * Property: metadata\n- * {Object} Metadata for this layer, as returned by the callback script\n+ * Method: down\n+ * This method is called during the handling of the mouse down event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse down event\n */\n- metadata: null,\n+ down: function(evt) {},\n \n /**\n- * Property: protocolRegex\n- * {RegExp} Regular expression to match and replace http: in bing urls\n+ * Method: move\n+ * This method is called during the handling of the mouse move event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse move event\n+ *\n */\n- protocolRegex: /^http:/i,\n+ move: function(evt) {},\n \n /**\n- * APIProperty: type\n- * {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used. Default is \"Road\".\n+ * Method: up\n+ * This method is called during the handling of the mouse up event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse up event\n */\n- type: \"Road\",\n+ up: function(evt) {},\n \n /**\n- * APIProperty: culture\n- * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n- * for the definition and the possible values. Default is \"en-US\".\n+ * Method: out\n+ * This method is called during the handling of the mouse out event.\n+ * Subclasses can do their own processing here.\n+ *\n+ * Parameters:\n+ * evt - {Event} The mouse out event\n */\n- culture: \"en-US\",\n+ out: function(evt) {},\n \n /**\n- * APIProperty: metadataParams\n- * {Object} Optional url parameters for the Get Imagery Metadata request\n- * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n+ * The methods below are part of the magic of event handling. Because\n+ * they are named like browser events, they are registered as listeners\n+ * for the events they represent.\n */\n- metadataParams: null,\n \n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n+ /**\n+ * Method: mousedown\n+ * Handle mousedown events\n *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- tileOptions: null,\n+ mousedown: function(evt) {\n+ return this.dragstart(evt);\n+ },\n \n- /** APIProperty: protocol\n- * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n- * Can be 'http:' 'https:' or ''\n+ /**\n+ * Method: touchstart\n+ * Handle touchstart events\n *\n- * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n- * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n- * this is undocumented and the Imagery Metadata API always returns HTTP\n- * urls.\n+ * Parameters:\n+ * evt - {Event}\n *\n- * Default is '', unless when executed from a file:/// uri, in which case\n- * it is 'http:'.\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return this.dragstart(evt);\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.Bing\n- * Create a new Bing layer.\n- *\n- * Example:\n- * (code)\n- * var road = new OpenLayers.Layer.Bing({\n- * name: \"My Bing Aerial Layer\",\n- * type: \"Aerial\",\n- * key: \"my-api-key-here\",\n- * });\n- * (end)\n+ * Method: mousemove\n+ * Handle mousemove events\n *\n * Parameters:\n- * options - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * key - {String} Bing Maps API key for your application. Get one at\n- * http://bingmapsportal.com/.\n- * type - {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used.\n+ * evt - {Event}\n *\n- * Any other documented layer properties can be provided in the config object.\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n-\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options.tileOptions);\n- this.loadMetadata();\n+ mousemove: function(evt) {\n+ return this.dragmove(evt);\n },\n \n /**\n- * Method: loadMetadata\n+ * Method: touchmove\n+ * Handle touchmove events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- // link the processMetadata method to the global scope and bind it\n- // to this instance\n- window[this._callbackId] = OpenLayers.Function.bind(\n- OpenLayers.Layer.Bing.processMetadata, this\n- );\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n- this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script);\n+ touchmove: function(evt) {\n+ return this.dragmove(evt);\n },\n \n /**\n- * Method: initLayer\n- *\n- * Sets layer properties according to the metadata provided by the API\n+ * Method: removeTimeout\n+ * Private. Called by mousemove() to remove the drag timeout.\n */\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n- }\n- this.addOptions({\n- maxResolution: Math.min(\n- this.serverResolutions[res.zoomMin],\n- this.maxResolution || Number.POSITIVE_INFINITY\n- ),\n- numZoomLevels: Math.min(\n- res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n- )\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw();\n+ removeTimeout: function() {\n+ this.timeoutId = null;\n+ // if timeout expires while we're still dragging (mouseup\n+ // hasn't occurred) then call mousemove to move to the\n+ // correct position\n+ if (this.dragging) {\n+ this.mousemove(this.lastMoveEvt);\n }\n- this.updateAttribution();\n },\n \n /**\n- * Method: getURL\n+ * Method: mouseup\n+ * Handle mouseup events\n *\n- * Paramters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- getURL: function(bounds) {\n- if (!this.url) {\n- return;\n- }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = '0';\n- var mask = 1 << (i - 1);\n- if ((x & mask) != 0) {\n- digit++;\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++;\n- }\n- quadDigits.push(digit);\n- }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl('' + x + y + z, this.url);\n+ mouseup: function(evt) {\n+ return this.dragend(evt);\n+ },\n \n- return OpenLayers.String.format(url, {\n- 'quadkey': quadKey\n- });\n+ /**\n+ * Method: touchend\n+ * Handle touchend events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n+ */\n+ touchend: function(evt) {\n+ // override evt.xy with last position since touchend does not have\n+ // any touch position\n+ evt.xy = this.last;\n+ return this.dragend(evt);\n },\n \n /**\n- * Method: updateAttribution\n- * Updates the attribution according to the requirements outlined in\n- * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n+ * Method: mouseout\n+ * Handle mouseout events\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n- return;\n- }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(\n- this.map.getProjectionObject(),\n- new OpenLayers.Projection(\"EPSG:4326\")\n- );\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n- this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- // axis order provided is Y,X\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) &&\n- zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \";\n+ mouseout: function(evt) {\n+ if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ if (this.documentDrag === true) {\n+ this.addDocumentEvents();\n+ } else {\n+ var dragged = (this.start != this.last);\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n+ this.out(evt);\n+ this.callback(\"out\", []);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy]);\n+ }\n+ if (document.onselectstart) {\n+ document.onselectstart = this.oldOnselectstart;\n }\n }\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- });\n+ return true;\n },\n \n /**\n- * Method: setMap\n+ * Method: click\n+ * The drag handler captures the click event. If something else registers\n+ * for clicks on the same element, its listener will not be called \n+ * after a drag.\n+ * \n+ * Parameters: \n+ * evt - {Event} \n+ * \n+ * Returns:\n+ * {Boolean} Let the event propagate.\n */\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution);\n+ click: function(evt) {\n+ // let the click event propagate only if the mouse moved\n+ return (this.start == this.last);\n },\n \n /**\n- * APIMethod: clone\n+ * Method: activate\n+ * Activate the handler.\n * \n- * Parameters:\n- * obj - {Object}\n+ * Returns:\n+ * {Boolean} The handler was successfully activated.\n+ */\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragging = false;\n+ activated = true;\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * Method: deactivate \n+ * Deactivate the handler.\n * \n * Returns:\n- * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n+ * {Boolean} The handler was successfully deactivated.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options);\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.started = false;\n+ this.dragging = false;\n+ this.start = null;\n+ this.last = null;\n+ deactivated = true;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDragDown\"\n+ );\n }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n+ return deactivated;\n },\n \n /**\n- * Method: destroy\n+ * Method: adjustXY\n+ * Converts event coordinates that are relative to the document body to\n+ * ones that are relative to the map viewport. The latter is the default in\n+ * OpenLayers.\n+ * \n+ * Parameters:\n+ * evt - {Object}\n */\n- destroy: function() {\n- this.map &&\n- this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n+ adjustXY: function(evt) {\n+ var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n+ evt.xy.x -= pos[0];\n+ evt.xy.y -= pos[1];\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n-});\n+ /**\n+ * Method: addDocumentEvents\n+ * Start observing document events when documentDrag is true and the mouse\n+ * cursor leaves the map viewport while dragging.\n+ */\n+ addDocumentEvents: function() {\n+ OpenLayers.Element.addClass(document.body, \"olDragDown\");\n+ this.documentEvents = true;\n+ OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.observe(document, \"mouseup\", this._docUp);\n+ },\n \n-/**\n- * Function: OpenLayers.Layer.Bing.processMetadata\n- * This function will be bound to an instance, linked to the global scope with\n- * an id, and called by the JSONP script returned by the API.\n- *\n- * Parameters:\n- * metadata - {Object} metadata as returned by the API\n- */\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined; // cannot delete from window in IE\n- delete this._callbackId;\n-};\n+ /**\n+ * Method: removeDocumentEvents\n+ * Stops observing document events when documentDrag is true and the mouse\n+ * cursor re-enters the map viewport while dragging.\n+ */\n+ removeDocumentEvents: function() {\n+ OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n+ this.documentEvents = false;\n+ OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+});\n /* ======================================================================\n- OpenLayers/Renderer.js\n+ OpenLayers/Handler/Box.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Handler/Drag.js\n */\n \n /**\n- * Class: OpenLayers.Renderer \n- * This is the base class for all renderers.\n- *\n- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n- * It is largely composed of virtual functions that are to be implemented\n- * in technology-specific subclasses, but there is some generic code too.\n- * \n- * The functions that *are* implemented here merely deal with the maintenance\n- * of the size and extent variables, as well as the cached 'resolution' \n- * value. \n- * \n- * A note to the user that all subclasses should use getResolution() instead\n- * of directly accessing this.resolution in order to correctly use the \n- * cacheing system.\n+ * Class: OpenLayers.Handler.Box\n+ * Handler for dragging a rectangle across the map. Box is displayed \n+ * on mouse down, moves on mouse move, and is finished on mouse up.\n *\n+ * Inherits from:\n+ * - <OpenLayers.Handler> \n */\n-OpenLayers.Renderer = OpenLayers.Class({\n+OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n \n /** \n- * Property: container\n- * {DOMElement} \n+ * Property: dragHandler \n+ * {<OpenLayers.Handler.Drag>} \n */\n- container: null,\n+ dragHandler: null,\n \n /**\n- * Property: root\n- * {DOMElement}\n- */\n- root: null,\n-\n- /** \n- * Property: extent\n- * {<OpenLayers.Bounds>}\n+ * APIProperty: boxDivClassName\n+ * {String} The CSS class to use for drawing the box. Default is\n+ * olHandlerBoxZoomBox\n */\n- extent: null,\n+ boxDivClassName: 'olHandlerBoxZoomBox',\n \n /**\n- * Property: locked\n- * {Boolean} If the renderer is currently in a state where many things\n- * are changing, the 'locked' property is set to true. This means \n- * that renderers can expect at least one more drawFeature event to be\n- * called with the 'locked' property set to 'true': In some renderers,\n- * this might make sense to use as a 'only update local information'\n- * flag. \n- */\n- locked: false,\n-\n- /** \n- * Property: size\n- * {<OpenLayers.Size>} \n+ * Property: boxOffsets\n+ * {Object} Caches box offsets from css. This is used by the getBoxOffsets\n+ * method.\n */\n- size: null,\n+ boxOffsets: null,\n \n /**\n- * Property: resolution\n- * {Float} cache of current map resolution\n+ * Constructor: OpenLayers.Handler.Box\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} \n+ * callbacks - {Object} An object with a properties whose values are\n+ * functions. Various callbacks described below.\n+ * options - {Object} \n+ *\n+ * Named callbacks:\n+ * start - Called when the box drag operation starts.\n+ * done - Called when the box drag operation is finished.\n+ * The callback should expect to receive a single argument, the box \n+ * bounds or a pixel. If the box dragging didn't span more than a 5 \n+ * pixel distance, a pixel will be returned instead of a bounds object.\n */\n- resolution: null,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.dragHandler = new OpenLayers.Handler.Drag(\n+ this, {\n+ down: this.startBox,\n+ move: this.moveBox,\n+ out: this.removeBox,\n+ up: this.endBox\n+ }, {\n+ keyMask: this.keyMask\n+ }\n+ );\n+ },\n \n /**\n- * Property: map \n- * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n+ * Method: destroy\n */\n- map: null,\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.destroy();\n+ this.dragHandler = null;\n+ }\n+ },\n \n /**\n- * Property: featureDx\n- * {Number} Feature offset in x direction. Will be calculated for and\n- * applied to the current feature while rendering (see\n- * <calculateFeatureDx>).\n+ * Method: setMap\n */\n- featureDx: 0,\n+ setMap: function(map) {\n+ OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.setMap(map);\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Renderer \n+ * Method: startBox\n *\n * Parameters:\n- * containerID - {<String>} \n- * options - {Object} options for this renderer. See sublcasses for\n- * supported options.\n+ * xy - {<OpenLayers.Pixel>}\n */\n- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n- OpenLayers.Util.extend(this, options);\n- },\n+ startBox: function(xy) {\n+ this.callback(\"start\", []);\n+ this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {\n+ x: -9999,\n+ y: -9999\n+ });\n+ this.zoomBox.className = this.boxDivClassName;\n+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.container = null;\n- this.extent = null;\n- this.size = null;\n- this.resolution = null;\n- this.map = null;\n+ this.map.viewPortDiv.appendChild(this.zoomBox);\n+\n+ OpenLayers.Element.addClass(\n+ this.map.viewPortDiv, \"olDrawBox\"\n+ );\n },\n \n /**\n- * APIMethod: supported\n- * This should be overridden by specific subclasses\n- * \n- * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n+ * Method: moveBox\n */\n- supported: function() {\n- return false;\n+ moveBox: function(xy) {\n+ var startX = this.dragHandler.start.x;\n+ var startY = this.dragHandler.start.y;\n+ var deltaX = Math.abs(startX - xy.x);\n+ var deltaY = Math.abs(startY - xy.y);\n+\n+ var offset = this.getBoxOffsets();\n+ this.zoomBox.style.width = (deltaX + offset.width + 1) + \"px\";\n+ this.zoomBox.style.height = (deltaY + offset.height + 1) + \"px\";\n+ this.zoomBox.style.left = (xy.x < startX ?\n+ startX - deltaX - offset.left : startX - offset.left) + \"px\";\n+ this.zoomBox.style.top = (xy.y < startY ?\n+ startY - deltaY - offset.top : startY - offset.top) + \"px\";\n },\n \n /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n- *\n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n- * We nullify the resolution cache (this.resolution) if resolutionChanged\n- * is set to true - this way it will be re-computed on the next\n- * getResolution() request.\n- *\n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n- *\n- * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * Method: endBox\n */\n- setExtent: function(extent, resolutionChanged) {\n- this.extent = extent.clone();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio);\n- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n- }\n- if (resolutionChanged) {\n- this.resolution = null;\n+ endBox: function(end) {\n+ var result;\n+ if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||\n+ Math.abs(this.dragHandler.start.y - end.y) > 5) {\n+ var start = this.dragHandler.start;\n+ var top = Math.min(start.y, end.y);\n+ var bottom = Math.max(start.y, end.y);\n+ var left = Math.min(start.x, end.x);\n+ var right = Math.max(start.x, end.x);\n+ result = new OpenLayers.Bounds(left, bottom, right, top);\n+ } else {\n+ result = this.dragHandler.start.clone(); // i.e. OL.Pixel\n }\n- return true;\n+ this.removeBox();\n+\n+ this.callback(\"done\", [result]);\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n- * \n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n- *\n- * Parameters:\n- * size - {<OpenLayers.Size>} \n+ * Method: removeBox\n+ * Remove the zoombox from the screen and nullify our reference to it.\n */\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null;\n- },\n+ removeBox: function() {\n+ this.map.viewPortDiv.removeChild(this.zoomBox);\n+ this.zoomBox = null;\n+ this.boxOffsets = null;\n+ OpenLayers.Element.removeClass(\n+ this.map.viewPortDiv, \"olDrawBox\"\n+ );\n \n- /** \n- * Method: getResolution\n- * Uses cached copy of resolution if available to minimize computing\n- * \n- * Returns:\n- * {Float} The current map's resolution\n- */\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution;\n },\n \n /**\n- * Method: drawFeature\n- * Draw the feature. The optional style argument can be used\n- * to override the feature's own style. This method should only\n- * be called from layer.drawFeature().\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {<Object>}\n- * \n- * Returns:\n- * {Boolean} true if the feature has been drawn completely, false if not,\n- * undefined if the feature had no geometry\n+ * Method: activate\n */\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style;\n+ activate: function() {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragHandler.activate();\n+ return true;\n+ } else {\n+ return false;\n }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n- };\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds);\n- }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n+ },\n \n- var location = feature.geometry.getCentroid();\n- if (style.labelXOffset || style.labelYOffset) {\n- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n- var res = this.getResolution();\n- location.move(xOffset * res, yOffset * res);\n- }\n- this.drawText(feature.id, style, location);\n- } else {\n- this.removeText(feature.id);\n+ /**\n+ * Method: deactivate\n+ */\n+ deactivate: function() {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ if (this.dragHandler.deactivate()) {\n+ if (this.zoomBox) {\n+ this.removeBox();\n }\n- return rendered;\n }\n+ return true;\n+ } else {\n+ return false;\n }\n },\n \n /**\n- * Method: calculateFeatureDx\n- * {Number} Calculates the feature offset in x direction. Looking at the\n- * center of the feature bounds and the renderer extent, we calculate how\n- * many world widths the two are away from each other. This distance is\n- * used to shift the feature as close as possible to the center of the\n- * current enderer extent, which ensures that the feature is visible in the\n- * current viewport.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n- * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n+ * Method: getBoxOffsets\n+ * Determines border offsets for a box, according to the box model.\n+ * \n+ * Returns:\n+ * {Object} an object with the following offsets:\n+ * - left\n+ * - right\n+ * - top\n+ * - bottom\n+ * - width\n+ * - height\n */\n- calculateFeatureDx: function(bounds, worldBounds) {\n- this.featureDx = 0;\n- if (worldBounds) {\n- var worldWidth = worldBounds.getWidth(),\n- rendererCenterX = (this.extent.left + this.extent.right) / 2,\n- featureCenterX = (bounds.left + bounds.right) / 2,\n- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n- this.featureDx = worldsAway * worldWidth;\n+ getBoxOffsets: function() {\n+ if (!this.boxOffsets) {\n+ // Determine the box model. If the testDiv's clientWidth is 3, then\n+ // the borders are outside and we are dealing with the w3c box\n+ // model. Otherwise, the browser uses the traditional box model and\n+ // the borders are inside the box bounds, leaving us with a\n+ // clientWidth of 1.\n+ var testDiv = document.createElement(\"div\");\n+ //testDiv.style.visibility = \"hidden\";\n+ testDiv.style.position = \"absolute\";\n+ testDiv.style.border = \"1px solid black\";\n+ testDiv.style.width = \"3px\";\n+ document.body.appendChild(testDiv);\n+ var w3cBoxModel = testDiv.clientWidth == 3;\n+ document.body.removeChild(testDiv);\n+\n+ var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n+ \"border-left-width\"));\n+ var right = parseInt(OpenLayers.Element.getStyle(\n+ this.zoomBox, \"border-right-width\"));\n+ var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n+ \"border-top-width\"));\n+ var bottom = parseInt(OpenLayers.Element.getStyle(\n+ this.zoomBox, \"border-bottom-width\"));\n+ this.boxOffsets = {\n+ left: left,\n+ right: right,\n+ top: top,\n+ bottom: bottom,\n+ width: w3cBoxModel === false ? left + right : 0,\n+ height: w3cBoxModel === false ? top + bottom : 0\n+ };\n }\n+ return this.boxOffsets;\n },\n \n- /** \n- * Method: drawGeometry\n- * \n- * Draw a geometry. This should only be called from the renderer itself.\n- * Use layer.drawFeature() from outside the renderer.\n- * virtual function\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * style - {Object} \n- * featureId - {<String>} \n- */\n- drawGeometry: function(geometry, style, featureId) {},\n+ CLASS_NAME: \"OpenLayers.Handler.Box\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/ZoomBox.js\n+ ====================================================================== */\n \n- /**\n- * Method: drawText\n- * Function for drawing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- * style -\n- * location - {<OpenLayers.Geometry.Point>}\n- */\n- drawText: function(featureId, style, location) {},\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * Method: removeText\n- * Function for removing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- */\n- removeText: function(featureId) {},\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Box.js\n+ */\n \n+/**\n+ * Class: OpenLayers.Control.ZoomBox\n+ * The ZoomBox control enables zooming directly to a given extent, by drawing \n+ * a box on the map. The box is drawn by holding down shift, whilst dragging \n+ * the mouse.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n /**\n- * Method: clear\n- * Clear all vectors from the renderer.\n- * virtual function.\n+ * Property: type\n+ * {OpenLayers.Control.TYPE}\n */\n- clear: function() {},\n+ type: OpenLayers.Control.TYPE_TOOL,\n \n /**\n- * Method: getFeatureIdFromEvent\n- * Returns a feature id from an event on the renderer. \n- * How this happens is specific to the renderer. This should be\n- * called from layer.getFeatureFromEvent().\n- * Virtual function.\n- * \n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n- *\n- * Returns:\n- * {String} A feature id or undefined.\n+ * Property: out\n+ * {Boolean} Should the control be used for zooming out?\n */\n- getFeatureIdFromEvent: function(evt) {},\n+ out: false,\n \n /**\n- * Method: eraseFeatures \n- * This is called by the layer to erase features\n- * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * APIProperty: keyMask\n+ * {Integer} Zoom only occurs if the keyMask matches the combination of \n+ * keys down. Use bitwise operators and one or more of the\n+ * <OpenLayers.Handler> constants to construct a keyMask. Leave null if \n+ * not used mask. Default is null.\n */\n- eraseFeatures: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- for (var i = 0, len = features.length; i < len; ++i) {\n- var feature = features[i];\n- this.eraseGeometry(feature.geometry, feature.id);\n- this.removeText(feature.id);\n- }\n- },\n+ keyMask: null,\n \n /**\n- * Method: eraseGeometry\n- * Remove a geometry from the renderer (by id).\n- * virtual function.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * featureId - {String}\n+ * APIProperty: alwaysZoom\n+ * {Boolean} Always zoom in/out when box drawn, even if the zoom level does\n+ * not change.\n */\n- eraseGeometry: function(geometry, featureId) {},\n+ alwaysZoom: false,\n \n /**\n- * Method: moveRoot\n- * moves this renderer's root to a (different) renderer.\n- * To be implemented by subclasses that require a common renderer root for\n- * feature selection.\n- * \n- * Parameters:\n- * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ * APIProperty: zoomOnClick\n+ * {Boolean} Should we zoom when no box was dragged, i.e. the user only\n+ * clicked? Default is true.\n */\n- moveRoot: function(renderer) {},\n+ zoomOnClick: true,\n \n /**\n- * Method: getRenderLayerId\n- * Gets the layer that this renderer's output appears on. If moveRoot was\n- * used, this will be different from the id of the layer containing the\n- * features rendered by this renderer.\n- * \n- * Returns:\n- * {String} the id of the output layer.\n+ * Method: draw\n */\n- getRenderLayerId: function() {\n- return this.container.id;\n+ draw: function() {\n+ this.handler = new OpenLayers.Handler.Box(this, {\n+ done: this.zoomBox\n+ }, {\n+ keyMask: this.keyMask\n+ });\n },\n \n /**\n- * Method: applyDefaultSymbolizer\n- * \n+ * Method: zoomBox\n+ *\n * Parameters:\n- * symbolizer - {Object}\n- * \n- * Returns:\n- * {Object}\n+ * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}\n */\n- applyDefaultSymbolizer: function(symbolizer) {\n- var result = OpenLayers.Util.extend({},\n- OpenLayers.Renderer.defaultSymbolizer);\n- if (symbolizer.stroke === false) {\n- delete result.strokeWidth;\n- delete result.strokeColor;\n- }\n- if (symbolizer.fill === false) {\n- delete result.fillColor;\n+ zoomBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var bounds,\n+ targetCenterPx = position.getCenterPixel();\n+ if (!this.out) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,\n+ maxXY.lon, maxXY.lat);\n+ } else {\n+ var pixWidth = position.right - position.left;\n+ var pixHeight = position.bottom - position.top;\n+ var zoomFactor = Math.min((this.map.size.h / pixHeight),\n+ (this.map.size.w / pixWidth));\n+ var extent = this.map.getExtent();\n+ var center = this.map.getLonLatFromPixel(targetCenterPx);\n+ var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor;\n+ var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor;\n+ var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor;\n+ var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor;\n+ bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);\n+ }\n+ // always zoom in/out \n+ var lastZoom = this.map.getZoom(),\n+ size = this.map.getSize(),\n+ centerPx = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ },\n+ zoom = this.map.getZoomForExtent(bounds),\n+ oldRes = this.map.getResolution(),\n+ newRes = this.map.getResolutionForZoom(zoom);\n+ if (oldRes == newRes) {\n+ this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));\n+ } else {\n+ var zoomOriginPx = {\n+ x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /\n+ (oldRes - newRes),\n+ y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /\n+ (oldRes - newRes)\n+ };\n+ this.map.zoomTo(zoom, zoomOriginPx);\n+ }\n+ if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n+ this.map.zoomTo(lastZoom + (this.out ? -1 : 1));\n+ }\n+ } else if (this.zoomOnClick) { // it's a pixel\n+ if (!this.out) {\n+ this.map.zoomTo(this.map.getZoom() + 1, position);\n+ } else {\n+ this.map.zoomTo(this.map.getZoom() - 1, position);\n+ }\n }\n- OpenLayers.Util.extend(result, symbolizer);\n- return result;\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n });\n-\n-/**\n- * Constant: OpenLayers.Renderer.defaultSymbolizer\n- * {Object} Properties from this symbolizer will be applied to symbolizers\n- * with missing properties. This can also be used to set a global\n- * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n- * following code before rendering any vector features:\n- * (code)\n- * OpenLayers.Renderer.defaultSymbolizer = {\n- * fillColor: \"#808080\",\n- * fillOpacity: 1,\n- * strokeColor: \"#000000\",\n- * strokeOpacity: 1,\n- * strokeWidth: 1,\n- * pointRadius: 3,\n- * graphicName: \"square\"\n- * };\n- * (end)\n- */\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: 'cm'\n-};\n-\n-\n-\n-/**\n- * Constant: OpenLayers.Renderer.symbol\n- * Coordinate arrays for well known (named) symbols.\n- */\n-OpenLayers.Renderer.symbol = {\n- \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n- 303, 215, 231, 161, 321, 161, 350, 75\n- ],\n- \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n- 4, 0\n- ],\n- \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n- \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n- \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n-};\n /* ======================================================================\n- OpenLayers/Layer/Vector.js\n+ OpenLayers/Control/DragPan.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Renderer.js\n- * @requires OpenLayers/StyleMap.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Handler/Drag.js\n */\n \n /**\n- * Class: OpenLayers.Layer.Vector\n- * Instances of OpenLayers.Layer.Vector are used to render vector data from\n- * a variety of sources. Create a new vector layer with the\n- * <OpenLayers.Layer.Vector> constructor.\n+ * Class: OpenLayers.Control.DragPan\n+ * The DragPan control pans the map with a drag of the mouse.\n *\n * Inherits from:\n- * - <OpenLayers.Layer>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>}\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n- *\n- * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n- * beforefeatureadded - Triggered before a feature is added. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be added. To stop the feature from being added, a\n- * listener should return false.\n- * beforefeaturesadded - Triggered before an array of features is added.\n- * Listeners will receive an object with a *features* property\n- * referencing the feature to be added. To stop the features from\n- * being added, a listener should return false.\n- * featureadded - Triggered after a feature is added. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the added feature.\n- * featuresadded - Triggered after features are added. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of added features.\n- * beforefeatureremoved - Triggered before a feature is removed. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be removed.\n- * beforefeaturesremoved - Triggered before multiple features are removed. \n- * Listeners will receive an object with a *features* property\n- * referencing the features to be removed.\n- * featureremoved - Triggerd after a feature is removed. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the removed feature.\n- * featuresremoved - Triggered after features are removed. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of removed features.\n- * beforefeatureselected - Triggered before a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be selected. To stop the feature from being selectd, a\n- * listener should return false.\n- * featureselected - Triggered after a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * selected feature.\n- * featureunselected - Triggered after a feature is unselected.\n- * Listeners will receive an object with a *feature* property\n- * referencing the unselected feature.\n- * beforefeaturemodified - Triggered when a feature is selected to \n- * be modified. Listeners will receive an object with a *feature* \n- * property referencing the selected feature.\n- * featuremodified - Triggered when a feature has been modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * afterfeaturemodified - Triggered when a feature is finished being modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * vertexmodified - Triggered when a vertex within any feature geometry\n- * has been modified. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * modification.\n- * vertexremoved - Triggered when a vertex within any feature geometry\n- * has been deleted. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * removal.\n- * sketchstarted - Triggered when a feature sketch bound for this layer\n- * is started. Listeners will receive an object with a *feature*\n- * property referencing the new sketch feature and a *vertex* property\n- * referencing the creation point.\n- * sketchmodified - Triggered when a feature sketch bound for this layer\n- * is modified. Listeners will receive an object with a *vertex*\n- * property referencing the modified vertex and a *feature* property\n- * referencing the sketch feature.\n- * sketchcomplete - Triggered when a feature sketch bound for this layer\n- * is complete. Listeners will receive an object with a *feature*\n- * property referencing the sketch feature. By returning false, a\n- * listener can stop the sketch feature from being added to the layer.\n- * refresh - Triggered when something wants a strategy to ask the protocol\n- * for a new set of features.\n- */\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is false. Set this property\n- * in the layer options.\n- */\n- isBaseLayer: false,\n-\n- /** \n- * APIProperty: isFixed\n- * {Boolean} Whether the layer remains in one place while dragging the\n- * map. Note that setting this to true will move the layer to the bottom\n- * of the layer stack.\n- */\n- isFixed: false,\n-\n- /** \n- * APIProperty: features\n- * {Array(<OpenLayers.Feature.Vector>)} \n- */\n- features: null,\n-\n- /** \n- * Property: filter\n- * {<OpenLayers.Filter>} The filter set in this layer,\n- * a strategy launching read requests can combined\n- * this filter with its own filter.\n- */\n- filter: null,\n+OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n- * Property: selectedFeatures\n- * {Array(<OpenLayers.Feature.Vector>)} \n- */\n- selectedFeatures: null,\n-\n- /**\n- * Property: unrenderedFeatures\n- * {Object} hash of features, keyed by feature.id, that the renderer\n- * failed to draw\n+ * Property: type\n+ * {OpenLayers.Control.TYPES}\n */\n- unrenderedFeatures: null,\n+ type: OpenLayers.Control.TYPE_TOOL,\n \n /**\n- * APIProperty: reportError\n- * {Boolean} report friendly error message when loading of renderer\n- * fails.\n- */\n- reportError: true,\n-\n- /** \n- * APIProperty: style\n- * {Object} Default style for the layer\n+ * Property: panned\n+ * {Boolean} The map moved.\n */\n- style: null,\n+ panned: false,\n \n /**\n- * Property: styleMap\n- * {<OpenLayers.StyleMap>}\n+ * Property: interval\n+ * {Integer} The number of milliseconds that should ellapse before\n+ * panning the map again. Defaults to 0 milliseconds, which means that\n+ * no separate cycle is used for panning. In most cases you won't want\n+ * to change this value. For slow machines/devices larger values can be\n+ * tried out.\n */\n- styleMap: null,\n+ interval: 0,\n \n /**\n- * Property: strategies\n- * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n+ * APIProperty: documentDrag\n+ * {Boolean} If set to true, mouse dragging will continue even if the\n+ * mouse cursor leaves the map viewport. Default is false.\n */\n- strategies: null,\n+ documentDrag: false,\n \n /**\n- * Property: protocol\n- * {<OpenLayers.Protocol>} Optional protocol for the layer.\n+ * Property: kinetic\n+ * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.\n */\n- protocol: null,\n+ kinetic: null,\n \n /**\n- * Property: renderers\n- * {Array(String)} List of supported Renderer classes. Add to this list to\n- * add support for additional renderers. This list is ordered:\n- * the first renderer which returns true for the 'supported()'\n- * method will be used, if not defined in the 'renderer' option.\n- */\n- renderers: ['SVG', 'VML', 'Canvas'],\n-\n- /** \n- * Property: renderer\n- * {<OpenLayers.Renderer>}\n+ * APIProperty: enableKinetic\n+ * {Boolean} Set this option to enable \"kinetic dragging\". Can be\n+ * set to true or to an object. If set to an object this\n+ * object will be passed to the {<OpenLayers.Kinetic>}\n+ * constructor. Defaults to true.\n+ * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is\n+ * included in your build config.\n */\n- renderer: null,\n+ enableKinetic: true,\n \n /**\n- * APIProperty: rendererOptions\n- * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n- * supported options.\n- */\n- rendererOptions: null,\n-\n- /** \n- * APIProperty: geometryType\n- * {String} geometryType allows you to limit the types of geometries this\n- * layer supports. This should be set to something like\n- * \"OpenLayers.Geometry.Point\" to limit types.\n- */\n- geometryType: null,\n-\n- /** \n- * Property: drawn\n- * {Boolean} Whether the Vector Layer features have been drawn yet.\n+ * APIProperty: kineticInterval\n+ * {Integer} Interval in milliseconds between 2 steps in the \"kinetic\n+ * scrolling\". Applies only if enableKinetic is set. Defaults\n+ * to 10 milliseconds.\n */\n- drawn: false,\n+ kineticInterval: 10,\n \n- /** \n- * APIProperty: ratio\n- * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n- */\n- ratio: 1,\n \n /**\n- * Constructor: OpenLayers.Layer.Vector\n- * Create a new vector layer\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector>} A new vector layer\n+ * Method: draw\n+ * Creates a Drag handler, using <panMap> and\n+ * <panMapDone> as callbacks.\n */\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n-\n- // allow user-set renderer, otherwise assign one\n- if (!this.renderer || !this.renderer.supported()) {\n- this.assignRenderer();\n- }\n-\n- // if no valid renderer found, display error\n- if (!this.renderer || !this.renderer.supported()) {\n- this.renderer = null;\n- this.displayError();\n- }\n-\n- if (!this.styleMap) {\n- this.styleMap = new OpenLayers.StyleMap();\n- }\n-\n- this.features = [];\n- this.selectedFeatures = [];\n- this.unrenderedFeatures = {};\n-\n- // Allow for custom layer behavior\n- if (this.strategies) {\n- for (var i = 0, len = this.strategies.length; i < len; i++) {\n- this.strategies[i].setLayer(this);\n+ draw: function() {\n+ if (this.enableKinetic && OpenLayers.Kinetic) {\n+ var config = {\n+ interval: this.kineticInterval\n+ };\n+ if (typeof this.enableKinetic === \"object\") {\n+ config = OpenLayers.Util.extend(config, this.enableKinetic);\n }\n+ this.kinetic = new OpenLayers.Kinetic(config);\n }\n-\n+ this.handler = new OpenLayers.Handler.Drag(this, {\n+ \"move\": this.panMap,\n+ \"done\": this.panMapDone,\n+ \"down\": this.panMapStart\n+ }, {\n+ interval: this.interval,\n+ documentDrag: this.documentDrag\n+ });\n },\n \n /**\n- * APIMethod: destroy\n- * Destroy this layer\n+ * Method: panMapStart\n */\n- destroy: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoDestroy) {\n- strategy.destroy();\n- }\n- }\n- this.strategies = null;\n- }\n- if (this.protocol) {\n- if (this.protocol.autoDestroy) {\n- this.protocol.destroy();\n- }\n- this.protocol = null;\n- }\n- this.destroyFeatures();\n- this.features = null;\n- this.selectedFeatures = null;\n- this.unrenderedFeatures = null;\n- if (this.renderer) {\n- this.renderer.destroy();\n+ panMapStart: function() {\n+ if (this.kinetic) {\n+ this.kinetic.begin();\n }\n- this.renderer = null;\n- this.geometryType = null;\n- this.drawn = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer.\n- * \n- * Note: Features of the layer are also cloned.\n+ * Method: panMap\n *\n- * Returns:\n- * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- var features = this.features;\n- var len = features.length;\n- var clonedFeatures = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- clonedFeatures[i] = features[i].clone();\n+ panMap: function(xy) {\n+ if (this.kinetic) {\n+ this.kinetic.update(xy);\n }\n- obj.features = clonedFeatures;\n-\n- return obj;\n+ this.panned = true;\n+ this.map.pan(\n+ this.handler.last.x - xy.x,\n+ this.handler.last.y - xy.y, {\n+ dragging: true,\n+ animate: false\n+ }\n+ );\n },\n \n /**\n- * Method: refresh\n- * Ask the layer to request features again and redraw them. Triggers\n- * the refresh event if the layer is in range and visible.\n+ * Method: panMapDone\n+ * Finish the panning operation. Only call setCenter (through <panMap>)\n+ * if the map has actually been moved.\n *\n * Parameters:\n- * obj - {Object} Optional object with properties for any listener of\n- * the refresh event.\n- */\n- refresh: function(obj) {\n- if (this.calculateInRange() && this.visibility) {\n- this.events.triggerEvent(\"refresh\", obj);\n- }\n- },\n-\n- /** \n- * Method: assignRenderer\n- * Iterates through the available renderer implementations and selects \n- * and assigns the first one whose \"supported()\" function returns true.\n+ * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n */\n- assignRenderer: function() {\n- for (var i = 0, len = this.renderers.length; i < len; i++) {\n- var rendererClass = this.renderers[i];\n- var renderer = (typeof rendererClass == \"function\") ?\n- rendererClass :\n- OpenLayers.Renderer[rendererClass];\n- if (renderer && renderer.prototype.supported()) {\n- this.renderer = new renderer(this.div, this.rendererOptions);\n- break;\n+ panMapDone: function(xy) {\n+ if (this.panned) {\n+ var res = null;\n+ if (this.kinetic) {\n+ res = this.kinetic.end(xy);\n+ }\n+ this.map.pan(\n+ this.handler.last.x - xy.x,\n+ this.handler.last.y - xy.y, {\n+ dragging: !!res,\n+ animate: false\n+ }\n+ );\n+ if (res) {\n+ var self = this;\n+ this.kinetic.move(res, function(x, y, end) {\n+ self.map.pan(x, y, {\n+ dragging: !end,\n+ animate: false\n+ });\n+ });\n }\n+ this.panned = false;\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Control.DragPan\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/MouseWheel.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.MouseWheel\n+ * Handler for wheel up/down events.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n+ */\n+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n /** \n- * Method: displayError \n- * Let the user know their browser isn't supported.\n+ * Property: wheelListener \n+ * {function} \n */\n- displayError: function() {\n- if (this.reportError) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n- renderers: this.renderers.join('\\n')\n- }));\n- }\n- },\n+ wheelListener: null,\n \n- /** \n- * Method: setMap\n- * The layer has been added to the map. \n- * \n- * If there is no renderer set, the layer can't be used. Remove it.\n- * Otherwise, give the renderer a reference to the map and set its size.\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n+ /**\n+ * Property: interval\n+ * {Integer} In order to increase server performance, an interval (in \n+ * milliseconds) can be set to reduce the number of up/down events \n+ * called. If set, a new up/down event will not be set until the \n+ * interval has passed. \n+ * Defaults to 0, meaning no interval. \n */\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ interval: 0,\n \n- if (!this.renderer) {\n- this.map.removeLayer(this);\n- } else {\n- this.renderer.map = this.map;\n+ /**\n+ * Property: maxDelta\n+ * {Integer} Maximum delta to collect before breaking from the current\n+ * interval. In cumulative mode, this also limits the maximum delta\n+ * returned from the handler. Default is Number.POSITIVE_INFINITY.\n+ */\n+ maxDelta: Number.POSITIVE_INFINITY,\n \n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n- }\n- },\n+ /**\n+ * Property: delta\n+ * {Integer} When interval is set, delta collects the mousewheel z-deltas\n+ * of the events that occur within the interval.\n+ * See also the cumulative option\n+ */\n+ delta: 0,\n \n /**\n- * Method: afterAdd\n- * Called at the end of the map.addLayer sequence. At this point, the map\n- * will have a base layer. Any autoActivate strategies will be\n- * activated here.\n+ * Property: cumulative\n+ * {Boolean} When interval is set: true to collect all the mousewheel \n+ * z-deltas, false to only record the delta direction (positive or\n+ * negative)\n */\n- afterAdd: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.activate();\n- }\n- }\n- }\n- },\n+ cumulative: true,\n \n /**\n- * Method: removeMap\n- * The layer has been removed from the map.\n+ * Constructor: OpenLayers.Handler.MouseWheel\n *\n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * control - {<OpenLayers.Control>} \n+ * callbacks - {Object} An object containing a single function to be\n+ * called when the drag operation is finished.\n+ * The callback should expect to recieve a single\n+ * argument, the point geometry.\n+ * options - {Object} \n */\n- removeMap: function(map) {\n- this.drawn = false;\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.deactivate();\n- }\n- }\n- }\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.wheelListener = OpenLayers.Function.bindAsEventListener(\n+ this.onWheelEvent, this\n+ );\n },\n \n /**\n- * Method: onMapResize\n- * Notify the renderer of the change in size. \n- * \n+ * Method: destroy\n */\n- onMapResize: function() {\n- OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n-\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ this.wheelListener = null;\n },\n \n /**\n- * Method: moveTo\n- * Reset the vector layer's div so that it once again is lined up with \n- * the map. Notify the renderer of the change of extent, and in the\n- * case of a change of zoom level (resolution), have the \n- * renderer redraw features.\n- * \n- * If the layer has not yet been drawn, cycle through the layer's \n- * features and draw each one.\n+ * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/\n+ */\n+\n+ /** \n+ * Method: onWheelEvent\n+ * Catch the wheel event and handle it xbrowserly\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * zoomChanged - {Boolean} \n- * dragging - {Boolean} \n+ * e - {Event} \n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- var coordSysUnchanged = true;\n- if (!dragging) {\n- this.renderer.root.style.visibility = 'hidden';\n-\n- var viewSize = this.map.getSize(),\n- viewWidth = viewSize.w,\n- viewHeight = viewSize.h,\n- offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n- offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n- offsetLeft += this.map.layerContainerOriginPx.x;\n- offsetLeft = -Math.round(offsetLeft);\n- offsetTop += this.map.layerContainerOriginPx.y;\n- offsetTop = -Math.round(offsetTop);\n+ onWheelEvent: function(e) {\n \n- this.div.style.left = offsetLeft + 'px';\n- this.div.style.top = offsetTop + 'px';\n+ // make sure we have a map and check keyboard modifiers\n+ if (!this.map || !this.checkModifiers(e)) {\n+ return;\n+ }\n \n- var extent = this.map.getExtent().scale(this.ratio);\n- coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+ // Ride up the element's DOM hierarchy to determine if it or any of \n+ // its ancestors was: \n+ // * specifically marked as scrollable (CSS overflow property)\n+ // * one of our layer divs or a div marked as scrollable\n+ // ('olScrollable' CSS class)\n+ // * the map div\n+ //\n+ var overScrollableDiv = false;\n+ var allowScroll = false;\n+ var overMapDiv = false;\n \n- this.renderer.root.style.visibility = 'visible';\n+ var elem = OpenLayers.Event.element(e);\n+ while ((elem != null) && !overMapDiv && !overScrollableDiv) {\n \n- // Force a reflow on gecko based browsers to prevent jump/flicker.\n- // This seems to happen on only certain configurations; it was originally\n- // noticed in FF 2.0 and Linux.\n- if (OpenLayers.IS_GECKO === true) {\n- this.div.scrollLeft = this.div.scrollLeft;\n+ if (!overScrollableDiv) {\n+ try {\n+ var overflow;\n+ if (elem.currentStyle) {\n+ overflow = elem.currentStyle[\"overflow\"];\n+ } else {\n+ var style =\n+ document.defaultView.getComputedStyle(elem, null);\n+ overflow = style.getPropertyValue(\"overflow\");\n+ }\n+ overScrollableDiv = (overflow &&\n+ (overflow == \"auto\") || (overflow == \"scroll\"));\n+ } catch (err) {\n+ //sometimes when scrolling in a popup, this causes \n+ // obscure browser error\n+ }\n }\n \n- if (!zoomChanged && coordSysUnchanged) {\n- for (var i in this.unrenderedFeatures) {\n- var feature = this.unrenderedFeatures[i];\n- this.drawFeature(feature);\n+ if (!allowScroll) {\n+ allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');\n+ if (!allowScroll) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ // Are we in the layer div? Note that we have two cases\n+ // here: one is to catch EventPane layers, which have a\n+ // pane above the layer (layer.pane)\n+ var layer = this.map.layers[i];\n+ if (elem == layer.div || elem == layer.pane) {\n+ allowScroll = true;\n+ break;\n+ }\n+ }\n }\n }\n+ overMapDiv = (elem == this.map.div);\n+\n+ elem = elem.parentNode;\n }\n- if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n- this.drawn = true;\n- var feature;\n- for (var i = 0, len = this.features.length; i < len; i++) {\n- this.renderer.locked = (i !== (len - 1));\n- feature = this.features[i];\n- this.drawFeature(feature);\n+\n+ // Logic below is the following:\n+ //\n+ // If we are over a scrollable div or not over the map div:\n+ // * do nothing (let the browser handle scrolling)\n+ //\n+ // otherwise \n+ // \n+ // If we are over the layer div or a 'olScrollable' div:\n+ // * zoom/in out\n+ // then\n+ // * kill event (so as not to also scroll the page after zooming)\n+ //\n+ // otherwise\n+ //\n+ // Kill the event (dont scroll the page if we wheel over the \n+ // layerswitcher or the pan/zoom control)\n+ //\n+ if (!overScrollableDiv && overMapDiv) {\n+ if (allowScroll) {\n+ var delta = 0;\n+\n+ if (e.wheelDelta) {\n+ delta = e.wheelDelta;\n+ if (delta % 160 === 0) {\n+ // opera have steps of 160 instead of 120\n+ delta = delta * 0.75;\n+ }\n+ delta = delta / 120;\n+ } else if (e.detail) {\n+ // detail in Firefox on OS X is 1/3 of Windows\n+ // so force delta 1 / -1\n+ delta = -(e.detail / Math.abs(e.detail));\n+ }\n+ this.delta += delta;\n+\n+ window.clearTimeout(this._timeoutId);\n+ if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n+ // store e because window.event might change during delay\n+ var evt = OpenLayers.Util.extend({}, e);\n+ this._timeoutId = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.wheelZoom(evt);\n+ }, this),\n+ this.interval\n+ );\n+ } else {\n+ this.wheelZoom(e);\n+ }\n }\n+ OpenLayers.Event.stop(e);\n }\n },\n \n- /** \n- * APIMethod: display\n- * Hide or show the Layer\n+ /**\n+ * Method: wheelZoom\n+ * Given the wheel event, we carry out the appropriate zooming in or out,\n+ * based on the 'wheelDelta' or 'detail' property of the event.\n * \n * Parameters:\n- * display - {Boolean}\n+ * e - {Event}\n */\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- // we need to set the display style of the root in case it is attached\n- // to a foreign layer\n- var currentDisplay = this.div.style.display;\n- if (currentDisplay != this.renderer.root.style.display) {\n- this.renderer.root.style.display = currentDisplay;\n+ wheelZoom: function(e) {\n+ var delta = this.delta;\n+ this.delta = 0;\n+\n+ if (delta) {\n+ e.xy = this.map.events.getMousePosition(e);\n+ if (delta < 0) {\n+ this.callback(\"down\",\n+ [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);\n+ } else {\n+ this.callback(\"up\",\n+ [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);\n+ }\n }\n },\n \n /**\n- * APIMethod: addFeatures\n- * Add Features to the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- * options - {Object}\n+ * Method: activate \n */\n- addFeatures: function(features, options) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n+ activate: function(evt) {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ //register mousewheel events specifically on the window and document\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n+ return true;\n+ } else {\n+ return false;\n }\n+ },\n \n- var notify = !options || !options.silent;\n- if (notify) {\n- var event = {\n- features: features\n- };\n- var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n- if (ret === false) {\n- return;\n- }\n- features = event.features;\n+ /**\n+ * Method: deactivate \n+ */\n+ deactivate: function(evt) {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ // unregister mousewheel events specifically on the window and document\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n+ return true;\n+ } else {\n+ return false;\n }\n+ },\n \n- // Track successfully added features for featuresadded event, since\n- // beforefeatureadded can veto single features.\n- var featuresAdded = [];\n- for (var i = 0, len = features.length; i < len; i++) {\n- if (i != (features.length - 1)) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n- var feature = features[i];\n+ CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+});\n+/* ======================================================================\n+ OpenLayers/Handler/Click.js\n+ ====================================================================== */\n \n- if (this.geometryType &&\n- !(feature.geometry instanceof this.geometryType)) {\n- throw new TypeError('addFeatures: component should be an ' +\n- this.geometryType.prototype.CLASS_NAME);\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- //give feature reference to its layer\n- feature.layer = this;\n+/**\n+ * @requires OpenLayers/Handler.js\n+ */\n \n- if (!feature.style && this.style) {\n- feature.style = OpenLayers.Util.extend({}, this.style);\n- }\n+/**\n+ * Class: OpenLayers.Handler.Click\n+ * A handler for mouse clicks. The intention of this handler is to give\n+ * controls more flexibility with handling clicks. Browsers trigger\n+ * click events twice for a double-click. In addition, the mousedown,\n+ * mousemove, mouseup sequence fires a click event. With this handler,\n+ * controls can decide whether to ignore clicks associated with a double\n+ * click. By setting a <pixelTolerance>, controls can also ignore clicks\n+ * that include a drag. Create a new instance with the\n+ * <OpenLayers.Handler.Click> constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Handler> \n+ */\n+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n+ /**\n+ * APIProperty: delay\n+ * {Number} Number of milliseconds between clicks before the event is\n+ * considered a double-click.\n+ */\n+ delay: 300,\n \n- if (notify) {\n- if (this.events.triggerEvent(\"beforefeatureadded\", {\n- feature: feature\n- }) === false) {\n- continue;\n- }\n- this.preFeatureInsert(feature);\n- }\n+ /**\n+ * APIProperty: single\n+ * {Boolean} Handle single clicks. Default is true. If false, clicks\n+ * will not be reported. If true, single-clicks will be reported.\n+ */\n+ single: true,\n \n- featuresAdded.push(feature);\n- this.features.push(feature);\n- this.drawFeature(feature);\n+ /**\n+ * APIProperty: double\n+ * {Boolean} Handle double-clicks. Default is false.\n+ */\n+ 'double': false,\n \n- if (notify) {\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- this.onFeatureInsert(feature);\n- }\n- }\n+ /**\n+ * APIProperty: pixelTolerance\n+ * {Number} Maximum number of pixels between mouseup and mousedown for an\n+ * event to be considered a click. Default is 0. If set to an\n+ * integer value, clicks with a drag greater than the value will be\n+ * ignored. This property can only be set when the handler is\n+ * constructed.\n+ */\n+ pixelTolerance: 0,\n \n- if (notify) {\n- this.events.triggerEvent(\"featuresadded\", {\n- features: featuresAdded\n- });\n- }\n- },\n+ /**\n+ * APIProperty: dblclickTolerance\n+ * {Number} Maximum distance in pixels between clicks for a sequence of \n+ * events to be considered a double click. Default is 13. If the\n+ * distance between two clicks is greater than this value, a double-\n+ * click will not be fired.\n+ */\n+ dblclickTolerance: 13,\n \n+ /**\n+ * APIProperty: stopSingle\n+ * {Boolean} Stop other listeners from being notified of clicks. Default\n+ * is false. If true, any listeners registered before this one for \n+ * click or rightclick events will not be notified.\n+ */\n+ stopSingle: false,\n \n /**\n- * APIMethod: removeFeatures\n- * Remove features from the layer. This erases any drawn features and\n- * removes them from the layer's control. The beforefeatureremoved\n- * and featureremoved events will be triggered for each feature. The\n- * featuresremoved event will be triggered after all features have\n- * been removed. To supress event triggering, use the silent option.\n+ * APIProperty: stopDouble\n+ * {Boolean} Stop other listeners from being notified of double-clicks.\n+ * Default is false. If true, any click listeners registered before\n+ * this one will not be notified of *any* double-click events.\n * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n- * removed.\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n- *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n+ * The one caveat with stopDouble is that given a map with two click\n+ * handlers, one with stopDouble true and the other with stopSingle\n+ * true, the stopSingle handler should be activated last to get\n+ * uniform cross-browser performance. Since IE triggers one click\n+ * with a dblclick and FF triggers two, if a stopSingle handler is\n+ * activated first, all it gets in IE is a single click when the\n+ * second handler stops propagation on the dblclick.\n */\n- removeFeatures: function(features, options) {\n- if (!features || features.length === 0) {\n- return;\n- }\n- if (features === this.features) {\n- return this.removeAllFeatures(options);\n- }\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- if (features === this.selectedFeatures) {\n- features = features.slice();\n- }\n-\n- var notify = !options || !options.silent;\n-\n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n- }\n-\n- for (var i = features.length - 1; i >= 0; i--) {\n- // We remain locked so long as we're not at 0\n- // and the 'next' feature has a geometry. We do the geometry check\n- // because if all the features after the current one are 'null', we\n- // won't call eraseGeometry, so we break the 'renderer functions\n- // will always be called with locked=false *last*' rule. The end result\n- // is a possible gratiutious unlocking to save a loop through the rest \n- // of the list checking the remaining features every time. So long as\n- // null geoms are rare, this is probably okay. \n- if (i != 0 && features[i - 1].geometry) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n+ stopDouble: false,\n \n- var feature = features[i];\n- delete this.unrenderedFeatures[feature.id];\n+ /**\n+ * Property: timerId\n+ * {Number} The id of the timeout waiting to clear the <delayedCall>.\n+ */\n+ timerId: null,\n \n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n+ /**\n+ * Property: down\n+ * {Object} Object that store relevant information about the last\n+ * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives\n+ * the average location of the mouse/touch event. Its 'touches'\n+ * property records clientX/clientY of each touches.\n+ */\n+ down: null,\n \n- this.features = OpenLayers.Util.removeItem(this.features, feature);\n- // feature has no layer at this point\n- feature.layer = null;\n+ /**\n+ * Property: last\n+ * {Object} Object that store relevant information about the last\n+ * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives\n+ * the average location of the mouse/touch event. Its 'touches'\n+ * property records clientX/clientY of each touches.\n+ */\n+ last: null,\n \n- if (feature.geometry) {\n- this.renderer.eraseFeatures(feature);\n- }\n+ /** \n+ * Property: first\n+ * {Object} When waiting for double clicks, this object will store \n+ * information about the first click in a two click sequence.\n+ */\n+ first: null,\n \n- //in the case that this feature is one of the selected features, \n- // remove it from that array as well.\n- if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n- OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n- }\n+ /**\n+ * Property: rightclickTimerId\n+ * {Number} The id of the right mouse timeout waiting to clear the \n+ * <delayedEvent>.\n+ */\n+ rightclickTimerId: null,\n \n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n- }\n- }\n+ /**\n+ * Constructor: OpenLayers.Handler.Click\n+ * Create a new click handler.\n+ * \n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that is making use of\n+ * this handler. If a handler is being used without a control, the\n+ * handler's setMap method must be overridden to deal properly with\n+ * the map.\n+ * callbacks - {Object} An object with keys corresponding to callbacks\n+ * that will be called by the handler. The callbacks should\n+ * expect to recieve a single argument, the click event.\n+ * Callbacks for 'click' and 'dblclick' are supported.\n+ * options - {Object} Optional object whose properties will be set on the\n+ * handler.\n+ */\n \n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n- }\n+ /**\n+ * Method: touchstart\n+ * Handle touchstart.\n+ *\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true;\n },\n \n- /** \n- * APIMethod: removeAllFeatures\n- * Remove all features from the layer.\n+ /**\n+ * Method: touchmove\n+ * Store position of last move, because touchend event can have\n+ * an empty \"touches\" property.\n *\n- * Parameters:\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ touchmove: function(evt) {\n+ this.last = this.getEventInfo(evt);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: touchend\n+ * Correctly set event xy property, and add lastTouches to have\n+ * touches property from last touchstart or touchmove\n *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- removeAllFeatures: function(options) {\n- var notify = !options || !options.silent;\n- var features = this.features;\n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n- }\n- var feature;\n- for (var i = features.length - 1; i >= 0; i--) {\n- feature = features[i];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n- feature.layer = null;\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n- }\n- }\n- this.renderer.clear();\n- this.features = [];\n- this.unrenderedFeatures = {};\n- this.selectedFeatures = [];\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n+ touchend: function(evt) {\n+ // touchstart may not have been allowed to propagate\n+ if (this.down) {\n+ evt.xy = this.last.xy;\n+ evt.lastTouches = this.last.touches;\n+ this.handleSingle(evt);\n+ this.down = null;\n }\n+ return true;\n },\n \n /**\n- * APIMethod: destroyFeatures\n- * Erase and destroy features on the layer.\n+ * Method: mousedown\n+ * Handle mousedown.\n *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n- * features to destroy. If not supplied, all features on the layer\n- * will be destroyed.\n- * options - {Object}\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- destroyFeatures: function(features, options) {\n- var all = (features == undefined); // evaluates to true if\n- // features is null\n- if (all) {\n- features = this.features;\n- }\n- if (features) {\n- this.removeFeatures(features, options);\n- for (var i = features.length - 1; i >= 0; i--) {\n- features[i].destroy();\n- }\n- }\n+ mousedown: function(evt) {\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true;\n },\n \n /**\n- * APIMethod: drawFeature\n- * Draw (or redraw) a feature on the layer. If the optional style argument\n- * is included, this style will be used. If no style is included, the\n- * feature's style will be used. If the feature doesn't have a style,\n- * the layer's style will be used.\n+ * Method: mouseup\n+ * Handle mouseup. Installed to support collection of right mouse events.\n * \n- * This function is not designed to be used when adding features to \n- * the layer (use addFeatures instead). It is meant to be used when\n- * the style of a feature has changed, or in some other way needs to \n- * visually updated *after* it has already been added to a layer. You\n- * must add the feature to the layer for most layer-related events to \n- * happen.\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {String | Object} Named render intent or full symbolizer object.\n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- drawFeature: function(feature, style) {\n- // don't try to draw the feature with the renderer if the layer is not \n- // drawn itself\n- if (!this.drawn) {\n- return;\n+ mouseup: function(evt) {\n+ var propagate = true;\n+\n+ // Collect right mouse clicks from the mouseup\n+ // IE - ignores the second right click in mousedown so using\n+ // mouseup instead\n+ if (this.checkModifiers(evt) && this.control.handleRightClicks &&\n+ OpenLayers.Event.isRightClick(evt)) {\n+ propagate = this.rightclick(evt);\n }\n- if (typeof style != \"object\") {\n- if (!style && feature.state === OpenLayers.State.DELETE) {\n- style = \"delete\";\n- }\n- var renderIntent = style || feature.renderIntent;\n- style = feature.style || this.style;\n- if (!style) {\n- style = this.styleMap.createSymbolizer(feature, renderIntent);\n+\n+ return propagate;\n+ },\n+\n+ /**\n+ * Method: rightclick\n+ * Handle rightclick. For a dblrightclick, we get two clicks so we need \n+ * to always register for dblrightclick to properly handle single \n+ * clicks.\n+ * \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n+ */\n+ rightclick: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.rightclickTimerId != null) {\n+ //Second click received before timeout this must be \n+ // a double click\n+ this.clearTimer();\n+ this.callback('dblrightclick', [evt]);\n+ return !this.stopDouble;\n+ } else {\n+ //Set the rightclickTimerId, send evt only if double is \n+ // true else trigger single\n+ var clickEvent = this['double'] ?\n+ OpenLayers.Util.extend({}, evt) :\n+ this.callback('rightclick', [evt]);\n+\n+ var delayedRightCall = OpenLayers.Function.bind(\n+ this.delayedRightCall,\n+ this,\n+ clickEvent\n+ );\n+ this.rightclickTimerId = window.setTimeout(\n+ delayedRightCall, this.delay\n+ );\n }\n }\n+ return !this.stopSingle;\n+ },\n \n- var drawn = this.renderer.drawFeature(feature, style);\n- //TODO remove the check for null when we get rid of Renderer.SVG\n- if (drawn === false || drawn === null) {\n- this.unrenderedFeatures[feature.id] = feature;\n- } else {\n- delete this.unrenderedFeatures[feature.id];\n+ /**\n+ * Method: delayedRightCall\n+ * Sets <rightclickTimerId> to null. And optionally triggers the \n+ * rightclick callback if evt is set.\n+ */\n+ delayedRightCall: function(evt) {\n+ this.rightclickTimerId = null;\n+ if (evt) {\n+ this.callback('rightclick', [evt]);\n }\n },\n \n /**\n- * Method: eraseFeatures\n- * Erase features from the layer.\n+ * Method: click\n+ * Handle click events from the browser. This is registered as a listener\n+ * for click events and should not be called from other events in this\n+ * handler.\n *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * Returns:\n+ * {Boolean} Continue propagating this event.\n */\n- eraseFeatures: function(features) {\n- this.renderer.eraseFeatures(features);\n+ click: function(evt) {\n+ if (!this.last) {\n+ this.last = this.getEventInfo(evt);\n+ }\n+ this.handleSingle(evt);\n+ return !this.stopSingle;\n },\n \n /**\n- * Method: getFeatureFromEvent\n- * Given an event, return a feature if the event occurred over one.\n- * Otherwise, return null.\n- *\n- * Parameters:\n- * evt - {Event} \n- *\n+ * Method: dblclick\n+ * Handle dblclick. For a dblclick, we get two clicks in some browsers\n+ * (FF) and one in others (IE). So we need to always register for\n+ * dblclick to properly handle single clicks. This method is registered\n+ * as a listener for the dblclick browser event. It should *not* be\n+ * called by other methods in this handler.\n+ * \n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n+ * {Boolean} Continue propagating this event.\n */\n- getFeatureFromEvent: function(evt) {\n- if (!this.renderer) {\n- throw new Error('getFeatureFromEvent called on layer with no ' +\n- 'renderer. This usually means you destroyed a ' +\n- 'layer, but not some handler which is associated ' +\n- 'with it.');\n+ dblclick: function(evt) {\n+ this.handleDouble(evt);\n+ return !this.stopDouble;\n+ },\n+\n+ /** \n+ * Method: handleDouble\n+ * Handle double-click sequence.\n+ */\n+ handleDouble: function(evt) {\n+ if (this.passesDblclickTolerance(evt)) {\n+ if (this[\"double\"]) {\n+ this.callback(\"dblclick\", [evt]);\n+ }\n+ // to prevent a dblclick from firing the click callback in IE\n+ this.clearTimer();\n }\n- var feature = null;\n- var featureId = this.renderer.getFeatureIdFromEvent(evt);\n- if (featureId) {\n- if (typeof featureId === \"string\") {\n- feature = this.getFeatureById(featureId);\n+ },\n+\n+ /** \n+ * Method: handleSingle\n+ * Handle single click sequence.\n+ */\n+ handleSingle: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.timerId != null) {\n+ // already received a click\n+ if (this.last.touches && this.last.touches.length === 1) {\n+ // touch device, no dblclick event - this may be a double\n+ if (this[\"double\"]) {\n+ // on Android don't let the browser zoom on the page\n+ OpenLayers.Event.preventDefault(evt);\n+ }\n+ this.handleDouble(evt);\n+ }\n+ // if we're not in a touch environment we clear the click timer\n+ // if we've got a second touch, we'll get two touchend events\n+ if (!this.last.touches || this.last.touches.length !== 2) {\n+ this.clearTimer();\n+ }\n } else {\n- feature = featureId;\n+ // remember the first click info so we can compare to the second\n+ this.first = this.getEventInfo(evt);\n+ // set the timer, send evt only if single is true\n+ //use a clone of the event object because it will no longer \n+ //be a valid event object in IE in the timer callback\n+ var clickEvent = this.single ?\n+ OpenLayers.Util.extend({}, evt) : null;\n+ this.queuePotentialClick(clickEvent);\n }\n }\n- return feature;\n+ },\n+\n+ /** \n+ * Method: queuePotentialClick\n+ * This method is separated out largely to make testing easier (so we\n+ * don't have to override window.setTimeout)\n+ */\n+ queuePotentialClick: function(evt) {\n+ this.timerId = window.setTimeout(\n+ OpenLayers.Function.bind(this.delayedCall, this, evt),\n+ this.delay\n+ );\n },\n \n /**\n- * APIMethod: getFeatureBy\n- * Given a property value, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * property - {String}\n- * value - {String}\n+ * Method: passesTolerance\n+ * Determine whether the event is within the optional pixel tolerance. Note\n+ * that the pixel tolerance check only works if mousedown events get to\n+ * the listeners registered here. If they are stopped by other elements,\n+ * the <pixelTolerance> will have no effect here (this method will always\n+ * return true).\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * property value or null if there is no such feature.\n+ * {Boolean} The click is within the pixel tolerance (if specified).\n */\n- getFeatureBy: function(property, value) {\n- //TBD - would it be more efficient to use a hash for this.features?\n- var feature = null;\n- for (var i = 0, len = this.features.length; i < len; ++i) {\n- if (this.features[i][property] == value) {\n- feature = this.features[i];\n- break;\n+ passesTolerance: function(evt) {\n+ var passes = true;\n+ if (this.pixelTolerance != null && this.down && this.down.xy) {\n+ passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n+ // for touch environments, we also enforce that all touches\n+ // start and end within the given tolerance to be considered a click\n+ if (passes && this.touch &&\n+ this.down.touches.length === this.last.touches.length) {\n+ // the touchend event doesn't come with touches, so we check\n+ // down and last\n+ for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n+ if (this.getTouchDistance(\n+ this.down.touches[i],\n+ this.last.touches[i]\n+ ) > this.pixelTolerance) {\n+ passes = false;\n+ break;\n+ }\n+ }\n }\n }\n- return feature;\n+ return passes;\n },\n \n- /**\n- * APIMethod: getFeatureById\n- * Given a feature id, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * featureId - {String}\n+ /** \n+ * Method: getTouchDistance\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureId or null if there is no such feature.\n+ * {Boolean} The pixel displacement between two touches.\n */\n- getFeatureById: function(featureId) {\n- return this.getFeatureBy('id', featureId);\n+ getTouchDistance: function(from, to) {\n+ return Math.sqrt(\n+ Math.pow(from.clientX - to.clientX, 2) +\n+ Math.pow(from.clientY - to.clientY, 2)\n+ );\n },\n \n /**\n- * APIMethod: getFeatureByFid\n- * Given a feature fid, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * featureFid - {String}\n+ * Method: passesDblclickTolerance\n+ * Determine whether the event is within the optional double-cick pixel \n+ * tolerance.\n *\n * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureFid or null if there is no such feature.\n+ * {Boolean} The click is within the double-click pixel tolerance.\n */\n- getFeatureByFid: function(featureFid) {\n- return this.getFeatureBy('fid', featureFid);\n+ passesDblclickTolerance: function(evt) {\n+ var passes = true;\n+ if (this.down && this.first) {\n+ passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;\n+ }\n+ return passes;\n },\n \n /**\n- * APIMethod: getFeaturesByAttribute\n- * Returns an array of features that have the given attribute key set to the\n- * given value. Comparison of attribute values takes care of datatypes, e.g.\n- * the string '1234' is not equal to the number 1234.\n- *\n- * Parameters:\n- * attrName - {String}\n- * attrValue - {Mixed}\n- *\n- * Returns:\n- * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n- * passed named attribute set to the given value.\n+ * Method: clearTimer\n+ * Clear the timer and set <timerId> to null.\n */\n- getFeaturesByAttribute: function(attrName, attrValue) {\n- var i,\n- feature,\n- len = this.features.length,\n- foundFeatures = [];\n- for (i = 0; i < len; i++) {\n- feature = this.features[i];\n- if (feature && feature.attributes) {\n- if (feature.attributes[attrName] === attrValue) {\n- foundFeatures.push(feature);\n- }\n- }\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null;\n+ }\n+ if (this.rightclickTimerId != null) {\n+ window.clearTimeout(this.rightclickTimerId);\n+ this.rightclickTimerId = null;\n }\n- return foundFeatures;\n },\n \n /**\n- * Unselect the selected features\n- * i.e. clears the featureSelection array\n- * change the style back\n- clearSelection: function() {\n-\n- var vectorLayer = this.map.vectorLayer;\n- for (var i = 0; i < this.map.featureSelection.length; i++) {\n- var featureSelection = this.map.featureSelection[i];\n- vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n+ * Method: delayedCall\n+ * Sets <timerId> to null. And optionally triggers the click callback if\n+ * evt is set.\n+ */\n+ delayedCall: function(evt) {\n+ this.timerId = null;\n+ if (evt) {\n+ this.callback(\"click\", [evt]);\n }\n- this.map.featureSelection = [];\n },\n- */\n-\n \n /**\n- * APIMethod: onFeatureInsert\n- * method called after a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something on feature updates.\n+ * Method: getEventInfo\n+ * This method allows us to store event information without storing the\n+ * actual event. In touch devices (at least), the same event is \n+ * modified between touchstart, touchmove, and touchend.\n *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n+ * Returns:\n+ * {Object} An object with event related info.\n */\n- onFeatureInsert: function(feature) {},\n+ getEventInfo: function(evt) {\n+ var touches;\n+ if (evt.touches) {\n+ var len = evt.touches.length;\n+ touches = new Array(len);\n+ var touch;\n+ for (var i = 0; i < len; i++) {\n+ touch = evt.touches[i];\n+ touches[i] = {\n+ clientX: touch.olClientX,\n+ clientY: touch.olClientY\n+ };\n+ }\n+ }\n+ return {\n+ xy: evt.xy,\n+ touches: touches\n+ };\n+ },\n \n /**\n- * APIMethod: preFeatureInsert\n- * method called before a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something when features are first added to the\n- * layer, but before they are drawn, such as adjust the style.\n+ * APIMethod: deactivate\n+ * Deactivate the handler.\n *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- preFeatureInsert: function(feature) {},\n-\n- /** \n- * APIMethod: getDataExtent\n- * Calculates the max extent which includes all of the features.\n- * \n * Returns:\n- * {<OpenLayers.Bounds>} or null if the layer has no features with\n- * geometries.\n+ * {Boolean} The handler was successfully deactivated.\n */\n- getDataExtent: function() {\n- var maxExtent = null;\n- var features = this.features;\n- if (features && (features.length > 0)) {\n- var geometry = null;\n- for (var i = 0, len = features.length; i < len; i++) {\n- geometry = features[i].geometry;\n- if (geometry) {\n- if (maxExtent === null) {\n- maxExtent = new OpenLayers.Bounds();\n- }\n- maxExtent.extend(geometry.getBounds());\n- }\n- }\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ this.down = null;\n+ this.first = null;\n+ this.last = null;\n+ deactivated = true;\n }\n- return maxExtent;\n+ return deactivated;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Vector\"\n+ CLASS_NAME: \"OpenLayers.Handler.Click\"\n });\n /* ======================================================================\n- OpenLayers/Layer/WMS.js\n+ OpenLayers/Control/Navigation.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Control/ZoomBox.js\n+ * @requires OpenLayers/Control/DragPan.js\n+ * @requires OpenLayers/Handler/MouseWheel.js\n+ * @requires OpenLayers/Handler/Click.js\n */\n \n /**\n- * Class: OpenLayers.Layer.WMS\n- * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n- * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n- * constructor.\n+ * Class: OpenLayers.Control.Navigation\n+ * The navigation control handles map browsing with mouse events (dragging,\n+ * double-clicking, and scrolling the wheel). Create a new navigation \n+ * control with the <OpenLayers.Control.Navigation> control. \n * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Note that this control is added to the map by default (if no controls \n+ * array is sent in the options object to the <OpenLayers.Map> \n+ * constructor).\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * Property: dragPan\n+ * {<OpenLayers.Control.DragPan>} \n+ */\n+ dragPan: null,\n \n /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n+ * APIProperty: dragPanOptions\n+ * {Object} Options passed to the DragPan control.\n */\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n- },\n+ dragPanOptions: null,\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Default is true for WMS layer\n+ * Property: pinchZoom\n+ * {<OpenLayers.Control.PinchZoom>}\n */\n- isBaseLayer: true,\n+ pinchZoom: null,\n \n /**\n- * APIProperty: encodeBBOX\n- * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n- * but some services want it that way. Default false.\n+ * APIProperty: pinchZoomOptions\n+ * {Object} Options passed to the PinchZoom control.\n */\n- encodeBBOX: false,\n+ pinchZoomOptions: null,\n+\n+ /**\n+ * APIProperty: documentDrag\n+ * {Boolean} Allow panning of the map by dragging outside map viewport.\n+ * Default is false.\n+ */\n+ documentDrag: false,\n \n /** \n- * APIProperty: noMagic \n- * {Boolean} If true, the image format will not be automagicaly switched \n- * from image/jpeg to image/png or image/gif when using \n- * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n- * constructor. Default false. \n+ * Property: zoomBox\n+ * {<OpenLayers.Control.ZoomBox>}\n */\n- noMagic: false,\n+ zoomBox: null,\n \n /**\n- * Property: yx\n- * {Object} Keys in this object are EPSG codes for which the axis order\n- * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n- * true as value. This is only relevant for WMS versions >= 1.3.0, and\n- * only if yx is not set in <OpenLayers.Projection.defaults> for the\n- * used projection.\n+ * APIProperty: zoomBoxEnabled\n+ * {Boolean} Whether the user can draw a box to zoom\n */\n- yx: {},\n+ zoomBoxEnabled: true,\n \n /**\n- * Constructor: OpenLayers.Layer.WMS\n- * Create a new WMS layer object\n- *\n- * Examples:\n- *\n- * The code below creates a simple WMS layer using the image/jpeg format.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {layers: \"modis,global_mosaic\"});\n- * (end)\n- * Note the 3rd argument (params). Properties added to this object will be\n- * added to the WMS GetMap requests used for this layer's tiles. The only\n- * mandatory parameter is \"layers\". Other common WMS params include\n- * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n- * always be ignored. Instead, it will be derived from the baseLayer's or\n- * map's projection.\n- *\n- * The code below creates a transparent WMS layer with additional options.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {\n- * layers: \"modis,global_mosaic\",\n- * transparent: true\n- * }, {\n- * opacity: 0.5,\n- * singleTile: true\n- * });\n- * (end)\n- * Note that by default, a WMS layer is configured as baseLayer. Setting\n- * the \"transparent\" param to true will apply some magic (see <noMagic>).\n- * The default image format changes from image/jpeg to image/png, and the\n- * layer is not configured as baseLayer.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the WMS\n- * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n- * params - {Object} An object with key/value pairs representing the\n- * GetMap query string parameters and parameter values.\n- * options - {Object} Hashtable of extra options to tag onto the layer.\n- * These options include all properties listed above, plus the ones\n- * inherited from superclasses.\n+ * APIProperty: zoomWheelEnabled\n+ * {Boolean} Whether the mousewheel should zoom the map\n */\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- //uppercase params\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\";\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n- );\n+ zoomWheelEnabled: true,\n \n+ /**\n+ * Property: mouseWheelOptions\n+ * {Object} Options passed to the MouseWheel control (only useful if\n+ * <zoomWheelEnabled> is set to true). Default is no options for maps\n+ * with fractionalZoom set to true, otherwise\n+ * {cumulative: false, interval: 50, maxDelta: 6} \n+ */\n+ mouseWheelOptions: null,\n \n- //layer is transparent \n- if (!this.noMagic && this.params.TRANSPARENT &&\n- this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ /**\n+ * APIProperty: handleRightClicks\n+ * {Boolean} Whether or not to handle right clicks. Default is false.\n+ */\n+ handleRightClicks: false,\n \n- // unless explicitly set in options, make layer an overlay\n- if ((options == null) || (!options.isBaseLayer)) {\n- this.isBaseLayer = false;\n- }\n+ /**\n+ * APIProperty: zoomBoxKeyMask\n+ * {Integer} <OpenLayers.Handler> key code of the key, which has to be\n+ * pressed, while drawing the zoom box with the mouse on the screen. \n+ * You should probably set handleRightClicks to true if you use this\n+ * with MOD_CTRL, to disable the context menu for machines which use\n+ * CTRL-Click as a right click.\n+ * Default: <OpenLayers.Handler.MOD_SHIFT>\n+ */\n+ zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n \n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n- \"image/png\";\n- }\n- }\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n \n+ /**\n+ * Constructor: OpenLayers.Control.Navigation\n+ * Create a new navigation control\n+ * \n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * the control\n+ */\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n },\n \n /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n+ * Method: destroy\n+ * The destroy method is used to perform any clean up before the control\n+ * is dereferenced. Typically this is where event listeners are removed\n+ * to prevent memory leaks.\n */\n- clone: function(obj) {\n+ destroy: function() {\n+ this.deactivate();\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n+ if (this.dragPan) {\n+ this.dragPan.destroy();\n }\n+ this.dragPan = null;\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ if (this.zoomBox) {\n+ this.zoomBox.destroy();\n+ }\n+ this.zoomBox = null;\n \n- // copy/set any non-init, non-simple values here\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy();\n+ }\n+ this.pinchZoom = null;\n \n- return obj;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * APIMethod: reverseAxisOrder\n- * Returns true if the axis order is reversed for the WMS version and\n- * projection of the layer.\n- * \n- * Returns:\n- * {Boolean} true if the axis order is reversed, false otherwise.\n+ * Method: activate\n */\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 &&\n- !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n- OpenLayers.Projection.defaults[projCode].yx));\n+ activate: function() {\n+ this.dragPan.activate();\n+ if (this.zoomWheelEnabled) {\n+ this.handlers.wheel.activate();\n+ }\n+ this.handlers.click.activate();\n+ if (this.zoomBoxEnabled) {\n+ this.zoomBox.activate();\n+ }\n+ if (this.pinchZoom) {\n+ this.pinchZoom.activate();\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments);\n },\n \n /**\n- * Method: getURL\n- * Return a GetMap query string for this layer\n+ * Method: deactivate\n+ */\n+ deactivate: function() {\n+ if (this.pinchZoom) {\n+ this.pinchZoom.deactivate();\n+ }\n+ this.zoomBox.deactivate();\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.handlers.wheel.deactivate();\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: draw\n+ */\n+ draw: function() {\n+ // disable right mouse context menu for support of right click events\n+ if (this.handleRightClicks) {\n+ this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;\n+ }\n+\n+ var clickCallbacks = {\n+ 'click': this.defaultClick,\n+ 'dblclick': this.defaultDblClick,\n+ 'dblrightclick': this.defaultDblRightClick\n+ };\n+ var clickOptions = {\n+ 'double': true,\n+ 'stopDouble': true\n+ };\n+ this.handlers.click = new OpenLayers.Handler.Click(\n+ this, clickCallbacks, clickOptions\n+ );\n+ this.dragPan = new OpenLayers.Control.DragPan(\n+ OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions)\n+ );\n+ this.zoomBox = new OpenLayers.Control.ZoomBox({\n+ map: this.map,\n+ keyMask: this.zoomBoxKeyMask\n+ });\n+ this.dragPan.draw();\n+ this.zoomBox.draw();\n+ var wheelOptions = this.map.fractionalZoom ? {} : {\n+ cumulative: false,\n+ interval: 50,\n+ maxDelta: 6\n+ };\n+ this.handlers.wheel = new OpenLayers.Handler.MouseWheel(\n+ this, {\n+ up: this.wheelUp,\n+ down: this.wheelDown\n+ },\n+ OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)\n+ );\n+ if (OpenLayers.Control.PinchZoom) {\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(\n+ OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions));\n+ }\n+ },\n+\n+ /**\n+ * Method: defaultClick\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as \n- * parameters.\n+ * evt - {Event}\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n-\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- // WMS 1.3 introduced axis order\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ?\n- bounds.toBBOX(null, reverseAxisOrder) :\n- bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString;\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut();\n+ }\n },\n \n /**\n- * APIMethod: mergeNewParams\n- * Catch changeParams and uppercase the new params to be merged in\n- * before calling changeParams on the super class.\n+ * Method: defaultDblClick \n * \n- * Once params have been changed, the tiles will be reloaded with\n- * the new parameters.\n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy);\n+ },\n+\n+ /**\n+ * Method: defaultDblRightClick \n * \n * Parameters:\n- * newParams - {Object} Hashtable of new params to use\n+ * evt - {Event} \n */\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n- newArguments);\n+ defaultDblRightClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom - 1, evt.xy);\n },\n \n- /** \n- * APIMethod: getFullRequestString\n- * Combine the layer's url with its params and these newParams. \n- * \n- * Add the SRS parameter from projection -- this is probably\n- * more eloquently done via a setProjection() method, but this \n- * works for now and always.\n+ /**\n+ * Method: wheelChange \n *\n * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n- * Returns:\n- * {String} \n+ * evt - {Event}\n+ * deltaZ - {Integer}\n */\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n- this.projection.getCode() :\n- mapProjection.getCode();\n- var value = (projectionCode == \"none\") ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value;\n- } else {\n- this.params.SRS = value;\n+ wheelChange: function(evt, deltaZ) {\n+ if (!this.map.fractionalZoom) {\n+ deltaZ = Math.round(deltaZ);\n+ }\n+ var currentZoom = this.map.getZoom(),\n+ newZoom = currentZoom + deltaZ;\n+ newZoom = Math.max(newZoom, 0);\n+ newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n+ if (newZoom === currentZoom) {\n+ return;\n }\n+ this.map.zoomTo(newZoom, evt.xy);\n+ },\n \n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n+ /** \n+ * Method: wheelUp\n+ * User spun scroll wheel up\n+ * \n+ * Parameters:\n+ * evt - {Event}\n+ * delta - {Integer}\n+ */\n+ wheelUp: function(evt, delta) {\n+ this.wheelChange(evt, delta || 1);\n+ },\n+\n+ /** \n+ * Method: wheelDown\n+ * User spun scroll wheel down\n+ * \n+ * Parameters:\n+ * evt - {Event}\n+ * delta - {Integer}\n+ */\n+ wheelDown: function(evt, delta) {\n+ this.wheelChange(evt, delta || -1);\n+ },\n+\n+ /**\n+ * Method: disableZoomBox\n+ */\n+ disableZoomBox: function() {\n+ this.zoomBoxEnabled = false;\n+ this.zoomBox.deactivate();\n+ },\n+\n+ /**\n+ * Method: enableZoomBox\n+ */\n+ enableZoomBox: function() {\n+ this.zoomBoxEnabled = true;\n+ if (this.active) {\n+ this.zoomBox.activate();\n }\n+ },\n \n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n- this, arguments);\n+ /**\n+ * Method: disableZoomWheel\n+ */\n+\n+ disableZoomWheel: function() {\n+ this.zoomWheelEnabled = false;\n+ this.handlers.wheel.deactivate();\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+ /**\n+ * Method: enableZoomWheel\n+ */\n+\n+ enableZoomWheel: function() {\n+ this.zoomWheelEnabled = true;\n+ if (this.active) {\n+ this.handlers.wheel.activate();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Navigation\"\n });\n /* ======================================================================\n- OpenLayers/Layer/SphericalMercator.js\n+ OpenLayers/Control/Attribution.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Projection.js\n+ * @requires OpenLayers/Control.js\n */\n \n /**\n- * Class: OpenLayers.Layer.SphericalMercator\n- * A mixin for layers that wraps up the pieces neccesary to have a coordinate\n- * conversion for working with commercial APIs which use a spherical\n- * mercator projection. Using this layer as a base layer, additional\n- * layers can be used as overlays if they are in the same projection.\n- *\n- * A layer is given properties of this object by setting the sphericalMercator\n- * property to true.\n- *\n- * More projection information:\n- * - http://spatialreference.org/ref/user/google-projection/\n- *\n- * Proj4 Text:\n- * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0\n- * +k=1.0 +units=m +nadgrids=@null +no_defs\n+ * Class: OpenLayers.Control.Attribution\n+ * The attribution control adds attribution from layers to the map display. \n+ * It uses 'attribution' property of each layer.\n *\n- * WKT:\n- * 900913=PROJCS[\"WGS84 / Simple Mercator\", GEOGCS[\"WGS 84\",\n- * DATUM[\"WGS_1984\", SPHEROID[\"WGS_1984\", 6378137.0, 298.257223563]], \n- * PRIMEM[\"Greenwich\", 0.0], UNIT[\"degree\", 0.017453292519943295], \n- * AXIS[\"Longitude\", EAST], AXIS[\"Latitude\", NORTH]],\n- * PROJECTION[\"Mercator_1SP_Google\"], \n- * PARAMETER[\"latitude_of_origin\", 0.0], PARAMETER[\"central_meridian\", 0.0], \n- * PARAMETER[\"scale_factor\", 1.0], PARAMETER[\"false_easting\", 0.0], \n- * PARAMETER[\"false_northing\", 0.0], UNIT[\"m\", 1.0], AXIS[\"x\", EAST],\n- * AXIS[\"y\", NORTH], AUTHORITY[\"EPSG\",\"900913\"]]\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Layer.SphericalMercator = {\n+OpenLayers.Control.Attribution =\n+ OpenLayers.Class(OpenLayers.Control, {\n \n- /**\n- * Method: getExtent\n- * Get the map's extent.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} The map extent.\n- */\n- getExtent: function() {\n- var extent = null;\n- if (this.sphericalMercator) {\n- extent = this.map.calculateBounds();\n- } else {\n- extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);\n- }\n- return extent;\n- },\n+ /**\n+ * APIProperty: separator\n+ * {String} String used to separate layers.\n+ */\n+ separator: \", \",\n \n- /**\n- * Method: getLonLatFromViewPortPx\n- * Get a map location from a pixel location\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>}\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n- * port OpenLayers.Pixel, translated into lon/lat by map lib\n- * If the map lib is not loaded or not centered, returns null\n- */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);\n- },\n+ /**\n+ * APIProperty: template\n+ * {String} Template for the attribution. This has to include the substring\n+ * \"${layers}\", which will be replaced by the layer specific\n+ * attributions, separated by <separator>. The default is \"${layers}\".\n+ */\n+ template: \"${layers}\",\n \n- /**\n- * Method: getViewPortPxFromLonLat\n- * Get a pixel location from a map location\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n- *\n- * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n- * OpenLayers.LonLat, translated into view port pixels by map lib\n- * If map lib is not loaded or not centered, returns null\n- */\n- getViewPortPxFromLonLat: function(lonlat) {\n- return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);\n- },\n+ /**\n+ * Constructor: OpenLayers.Control.Attribution \n+ * \n+ * Parameters:\n+ * options - {Object} Options for control.\n+ */\n \n- /** \n- * Method: initMercatorParameters \n- * Set up the mercator parameters on the layer: resolutions,\n- * projection, units.\n- */\n- initMercatorParameters: function() {\n- // set up properties for Mercator - assume EPSG:900913\n- this.RESOLUTIONS = [];\n- var maxResolution = 156543.03390625;\n- for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n- this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);\n- }\n- this.units = \"m\";\n- this.projection = this.projection || \"EPSG:900913\";\n- },\n+ /** \n+ * Method: destroy\n+ * Destroy control.\n+ */\n+ destroy: function() {\n+ this.map.events.un({\n+ \"removelayer\": this.updateAttribution,\n+ \"addlayer\": this.updateAttribution,\n+ \"changelayer\": this.updateAttribution,\n+ \"changebaselayer\": this.updateAttribution,\n+ scope: this\n+ });\n \n- /**\n- * APIMethod: forwardMercator\n- * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.\n- *\n- * Parameters:\n- * lon - {float} \n- * lat - {float}\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.\n- */\n- forwardMercator: (function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(lon, lat) {\n- var point = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- }, gg, sm);\n- return new OpenLayers.LonLat(point.x, point.y);\n- };\n- })(),\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n \n- /**\n- * APIMethod: inverseMercator\n- * Given a x,y in Spherical Mercator, return a point in EPSG:4326.\n- *\n- * Parameters:\n- * x - {float} A map x in Spherical Mercator.\n- * y - {float} A map y in Spherical Mercator.\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.\n- */\n- inverseMercator: (function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(x, y) {\n- var point = OpenLayers.Projection.transform({\n- x: x,\n- y: y\n- }, sm, gg);\n- return new OpenLayers.LonLat(point.x, point.y);\n- };\n- })()\n+ /**\n+ * Method: draw\n+ * Initialize control.\n+ * \n+ * Returns: \n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n \n-};\n+ this.map.events.on({\n+ 'changebaselayer': this.updateAttribution,\n+ 'changelayer': this.updateAttribution,\n+ 'addlayer': this.updateAttribution,\n+ 'removelayer': this.updateAttribution,\n+ scope: this\n+ });\n+ this.updateAttribution();\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: updateAttribution\n+ * Update attribution string.\n+ */\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ // add attribution only if attribution text is unique\n+ if (OpenLayers.Util.indexOf(\n+ attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution);\n+ }\n+ }\n+ }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ });\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+ });\n /* ======================================================================\n- OpenLayers/Layer/EventPane.js\n+ OpenLayers/Control/LayerSwitcher.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Lang.js\n * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Events/buttonclick.js\n */\n \n /**\n- * Class: OpenLayers.Layer.EventPane\n- * Base class for 3rd party layers, providing a DOM element which isolates\n- * the 3rd-party layer from mouse events.\n- * Only used by Google layers.\n+ * Class: OpenLayers.Control.LayerSwitcher\n+ * The LayerSwitcher control displays a table of contents for the map. This\n+ * allows the user interface to switch between BaseLasyers and to show or hide\n+ * Overlays. By default the switcher is shown minimized on the right edge of\n+ * the map, the user may expand it by clicking on the handle.\n *\n- * Automatically instantiated by the Google constructor, and not usually instantiated directly.\n+ * To create the LayerSwitcher outside of the map, pass the Id of a html div\n+ * as the first argument to the constructor.\n *\n- * Create a new event pane layer with the\n- * <OpenLayers.Layer.EventPane> constructor.\n- * \n * Inherits from:\n- * - <OpenLayers.Layer>\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n+OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n \n- /**\n- * APIProperty: smoothDragPan\n- * {Boolean} smoothDragPan determines whether non-public/internal API\n- * methods are used for better performance while dragging EventPane \n- * layers. When not in sphericalMercator mode, the smoother dragging \n- * doesn't actually move north/south directly with the number of \n- * pixels moved, resulting in a slight offset when you drag your mouse \n- * north south with this option on. If this visual disparity bothers \n- * you, you should turn this option off, or use spherical mercator. \n- * Default is on.\n+ /** \n+ * Property: layerStates \n+ * {Array(Object)} Basically a copy of the \"state\" of the map's layers \n+ * the last time the control was drawn. We have this in order to avoid\n+ * unnecessarily redrawing the control.\n */\n- smoothDragPan: true,\n+ layerStates: null,\n \n- /**\n- * Property: isBaseLayer\n- * {Boolean} EventPaned layers are always base layers, by necessity.\n- */\n- isBaseLayer: true,\n+ // DOM Elements\n \n /**\n- * APIProperty: isFixed\n- * {Boolean} EventPaned layers are fixed by default.\n+ * Property: layersDiv\n+ * {DOMElement}\n */\n- isFixed: true,\n+ layersDiv: null,\n \n /**\n- * Property: pane\n- * {DOMElement} A reference to the element that controls the events.\n+ * Property: baseLayersDiv\n+ * {DOMElement}\n */\n- pane: null,\n-\n+ baseLayersDiv: null,\n \n /**\n- * Property: mapObject\n- * {Object} This is the object which will be used to load the 3rd party library\n- * in the case of the google layer, this will be of type GMap, \n- * in the case of the ve layer, this will be of type VEMap\n+ * Property: baseLayers\n+ * {Array(Object)}\n */\n- mapObject: null,\n+ baseLayers: null,\n \n \n /**\n- * Constructor: OpenLayers.Layer.EventPane\n- * Create a new event pane layer\n- *\n- * Parameters:\n- * name - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * Property: dataLbl\n+ * {DOMElement}\n */\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (this.pane == null) {\n- this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\");\n- }\n- },\n+ dataLbl: null,\n \n /**\n- * APIMethod: destroy\n- * Deconstruct this layer.\n+ * Property: dataLayersDiv\n+ * {DOMElement}\n */\n- destroy: function() {\n- this.mapObject = null;\n- this.pane = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n- },\n-\n+ dataLayersDiv: null,\n \n /**\n- * Method: setMap\n- * Set the map property for the layer. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * Property: dataLayers\n+ * {Array(Object)}\n */\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n-\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n- this.pane.style.display = this.div.style.display;\n- this.pane.style.width = \"100%\";\n- this.pane.style.height = \"100%\";\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.pane.style.background =\n- \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\";\n- }\n-\n- if (this.isFixed) {\n- this.map.viewPortDiv.appendChild(this.pane);\n- } else {\n- this.map.layerContainerDiv.appendChild(this.pane);\n- }\n-\n- // once our layer has been added to the map, we can load it\n- this.loadMapObject();\n-\n- // if map didn't load, display warning\n- if (this.mapObject == null) {\n- this.loadWarningMessage();\n- }\n- },\n+ dataLayers: null,\n \n- /**\n- * APIMethod: removeMap\n- * On being removed from the map, we'll like to remove the invisible 'pane'\n- * div that we added to it on creation. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- if (this.pane && this.pane.parentNode) {\n- this.pane.parentNode.removeChild(this.pane);\n- }\n- OpenLayers.Layer.prototype.removeMap.apply(this, arguments);\n- },\n \n /**\n- * Method: loadWarningMessage\n- * If we can't load the map lib, then display an error message to the \n- * user and tell them where to go for help.\n- * \n- * This function sets up the layout for the warning message. Each 3rd\n- * party layer must implement its own getWarningHTML() function to \n- * provide the actual warning message.\n- */\n- loadWarningMessage: function() {\n-\n- this.div.style.backgroundColor = \"darkblue\";\n-\n- var viewSize = this.map.getSize();\n-\n- var msgW = Math.min(viewSize.w, 300);\n- var msgH = Math.min(viewSize.h, 200);\n- var size = new OpenLayers.Size(msgW, msgH);\n-\n- var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n-\n- var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n-\n- var div = OpenLayers.Util.createDiv(this.name + \"_warning\",\n- topLeft,\n- size,\n- null,\n- null,\n- null,\n- \"auto\");\n-\n- div.style.padding = \"7px\";\n- div.style.backgroundColor = \"yellow\";\n-\n- div.innerHTML = this.getWarningHTML();\n- this.div.appendChild(div);\n- },\n-\n- /** \n- * Method: getWarningHTML\n- * To be implemented by subclasses.\n- * \n- * Returns:\n- * {String} String with information on why layer is broken, how to get\n- * it working.\n+ * Property: minimizeDiv\n+ * {DOMElement}\n */\n- getWarningHTML: function() {\n- //should be implemented by subclasses\n- return \"\";\n- },\n+ minimizeDiv: null,\n \n /**\n- * Method: display\n- * Set the display on the pane\n- *\n- * Parameters:\n- * display - {Boolean}\n+ * Property: maximizeDiv\n+ * {DOMElement}\n */\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- this.pane.style.display = this.div.style.display;\n- },\n+ maximizeDiv: null,\n \n /**\n- * Method: setZIndex\n- * Set the z-index order for the pane.\n- * \n- * Parameters:\n- * zIndex - {int}\n+ * APIProperty: ascending\n+ * {Boolean}\n */\n- setZIndex: function(zIndex) {\n- OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n- },\n+ ascending: true,\n \n /**\n- * Method: moveByPx\n- * Move the layer based on pixel vector. To be implemented by subclasses.\n+ * Constructor: OpenLayers.Control.LayerSwitcher\n *\n * Parameters:\n- * dx - {Number} The x coord of the displacement vector.\n- * dy - {Number} The y coord of the displacement vector.\n+ * options - {Object}\n */\n- moveByPx: function(dx, dy) {\n- OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n-\n- if (this.dragPanMapObject) {\n- this.dragPanMapObject(dx, -dy);\n- } else {\n- this.moveTo(this.map.getCachedCenter());\n- }\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ this.layerStates = [];\n },\n \n /**\n- * Method: moveTo\n- * Handle calls to move the layer.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n+ * APIMethod: destroy\n */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- if (this.mapObject != null) {\n-\n- var newCenter = this.map.getCenter();\n- var newZoom = this.map.getZoom();\n-\n- if (newCenter != null) {\n-\n- var moOldCenter = this.getMapObjectCenter();\n- var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n+ destroy: function() {\n \n- var moOldZoom = this.getMapObjectZoom();\n- var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n+ //clear out layers info and unregister their events\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n \n- if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n \n- if (!zoomChanged && oldCenter && this.dragPanMapObject &&\n- this.smoothDragPan) {\n- var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n- var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n- this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y);\n- } else {\n- var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n- var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n- this.setMapObjectCenter(center, zoom, dragging);\n- }\n- }\n- }\n- }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n },\n \n-\n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /********************************************************/\n-\n /**\n- * Method: getLonLatFromViewPortPx\n- * Get a map location from a pixel location\n- * \n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>}\n+ * Method: setMap\n *\n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n- * port OpenLayers.Pixel, translated into lon/lat by map lib\n- * If the map lib is not loaded or not centered, returns null\n+ * Properties:\n+ * map - {<OpenLayers.Map>}\n */\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if ((this.mapObject != null) &&\n- (this.getMapObjectCenter() != null)) {\n- var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n- var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n- lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+\n+ this.map.events.on({\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n }\n- return lonlat;\n },\n \n-\n /**\n- * Method: getViewPortPxFromLonLat\n- * Get a pixel location from a map location\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>}\n+ * Method: draw\n *\n * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n- * OpenLayers.LonLat, translated into view port pixels by map lib\n- * If map lib is not loaded or not centered, returns null\n+ * {DOMElement} A reference to the DIV DOMElement containing the\n+ * switcher tabs.\n */\n- getViewPortPxFromLonLat: function(lonlat) {\n- var viewPortPx = null;\n- if ((this.mapObject != null) &&\n- (this.getMapObjectCenter() != null)) {\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this);\n \n- var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n- var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n+ // create layout divs\n+ this.loadContents();\n \n- viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);\n+ // set mode to minimize\n+ if (!this.outsideViewport) {\n+ this.minimizeControl();\n }\n- return viewPortPx;\n- },\n \n- /********************************************************/\n- /* */\n- /* Translation Functions */\n- /* */\n- /* The following functions translate Map Object and */\n- /* OL formats for Pixel, LonLat */\n- /* */\n- /********************************************************/\n+ // populate div with current info\n+ this.redraw();\n \n- //\n- // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat\n- //\n+ return this.div;\n+ },\n \n /**\n- * Method: getOLLonLatFromMapObjectLonLat\n- * Get an OL style map location from a 3rd party style map location\n+ * Method: onButtonClick\n *\n- * Parameters\n- * moLonLat - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in \n- * MapObject LonLat\n- * Returns null if null value is passed in\n+ * Parameters:\n+ * evt - {Event}\n */\n- getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n- var olLonLat = null;\n- if (moLonLat != null) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- olLonLat = new OpenLayers.LonLat(lon, lat);\n+ onButtonClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.minimizeDiv) {\n+ this.minimizeControl();\n+ } else if (button === this.maximizeDiv) {\n+ this.maximizeControl();\n+ } else if (button._layerSwitcher === this.id) {\n+ if (button[\"for\"]) {\n+ button = document.getElementById(button[\"for\"]);\n+ }\n+ if (!button.disabled) {\n+ if (button.type == \"radio\") {\n+ button.checked = true;\n+ this.map.setBaseLayer(this.map.getLayer(button._layer));\n+ } else {\n+ button.checked = !button.checked;\n+ this.updateMap();\n+ }\n+ }\n }\n- return olLonLat;\n },\n \n /**\n- * Method: getMapObjectLonLatFromOLLonLat\n- * Get a 3rd party map location from an OL map location.\n+ * Method: clearLayersArray\n+ * User specifies either \"base\" or \"data\". we then clear all the\n+ * corresponding listeners, the div, and reinitialize a new array.\n *\n * Parameters:\n- * olLonLat - {<OpenLayers.LonLat>}\n- * \n- * Returns:\n- * {Object} A MapObject LonLat, translated from the passed in \n- * OpenLayers.LonLat\n- * Returns null if null value is passed in\n+ * layersType - {String}\n */\n- getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n- var moLatLng = null;\n- if (olLonLat != null) {\n- moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,\n- olLonLat.lat);\n- }\n- return moLatLng;\n+ clearLayersArray: function(layersType) {\n+ this[layersType + \"LayersDiv\"].innerHTML = \"\";\n+ this[layersType + \"Layers\"] = [];\n },\n \n \n- //\n- // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel\n- //\n-\n /**\n- * Method: getOLPixelFromMapObjectPixel\n- * Get an OL pixel location from a 3rd party pixel location.\n+ * Method: checkRedraw\n+ * Checks if the layer state has changed since the last redraw() call.\n *\n- * Parameters:\n- * moPixel - {Object}\n- * \n * Returns:\n- * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in \n- * MapObject Pixel\n- * Returns null if null value is passed in\n+ * {Boolean} The layer state changed since the last redraw() call.\n */\n- getOLPixelFromMapObjectPixel: function(moPixel) {\n- var olPixel = null;\n- if (moPixel != null) {\n- var x = this.getXFromMapObjectPixel(moPixel);\n- var y = this.getYFromMapObjectPixel(moPixel);\n- olPixel = new OpenLayers.Pixel(x, y);\n+ checkRedraw: function() {\n+ if (!this.layerStates.length ||\n+ (this.map.layers.length != this.layerStates.length)) {\n+ return true;\n }\n- return olPixel;\n+\n+ for (var i = 0, len = this.layerStates.length; i < len; i++) {\n+ var layerState = this.layerStates[i];\n+ var layer = this.map.layers[i];\n+ if ((layerState.name != layer.name) ||\n+ (layerState.inRange != layer.inRange) ||\n+ (layerState.id != layer.id) ||\n+ (layerState.visibility != layer.visibility)) {\n+ return true;\n+ }\n+ }\n+\n+ return false;\n },\n \n /**\n- * Method: getMapObjectPixelFromOLPixel\n- * Get a 3rd party pixel location from an OL pixel location\n+ * Method: redraw\n+ * Goes through and takes the current state of the Map and rebuilds the\n+ * control to display that state. Groups base layers into a\n+ * radio-button group and lists each data layer with a checkbox.\n *\n- * Parameters:\n- * olPixel - {<OpenLayers.Pixel>}\n- * \n * Returns:\n- * {Object} A MapObject Pixel, translated from the passed in \n- * OpenLayers.Pixel\n- * Returns null if null value is passed in\n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n */\n- getMapObjectPixelFromOLPixel: function(olPixel) {\n- var moPixel = null;\n- if (olPixel != null) {\n- moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);\n+ redraw: function() {\n+ //if the state hasn't changed since last redraw, no need\n+ // to do anything. Just return the existing div.\n+ if (!this.checkRedraw()) {\n+ return this.div;\n }\n- return moPixel;\n- },\n \n- CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/FixedZoomLevels.js\n- ====================================================================== */\n+ //clear out previous layers\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ var containsOverlays = false;\n+ var containsBaseLayers = false;\n \n-/**\n- * @requires OpenLayers/Layer.js\n- */\n+ // Save state -- for checking layer if the map state changed.\n+ // We save this before redrawing, because in the process of redrawing\n+ // we will trigger more visibility changes, and we want to not redraw\n+ // and enter an infinite loop.\n+ var len = this.map.layers.length;\n+ this.layerStates = new Array(len);\n+ for (var i = 0; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ this.layerStates[i] = {\n+ 'name': layer.name,\n+ 'visibility': layer.visibility,\n+ 'inRange': layer.inRange,\n+ 'id': layer.id\n+ };\n+ }\n \n-/**\n- * Class: OpenLayers.Layer.FixedZoomLevels\n- * Some Layers will already have established zoom levels (like google \n- * or ve). Instead of trying to determine them and populate a resolutions[]\n- * Array with those values, we will hijack the resolution functionality\n- * here.\n- * \n- * When you subclass FixedZoomLevels: \n- * \n- * The initResolutions() call gets nullified, meaning no resolutions[] array \n- * is set up. Which would be a big problem getResolution() in Layer, since \n- * it merely takes map.zoom and indexes into resolutions[]... but....\n- * \n- * The getResolution() call is also overridden. Instead of using the \n- * resolutions[] array, we simply calculate the current resolution based\n- * on the current extent and the current map size. But how will we be able\n- * to calculate the current extent without knowing the resolution...?\n- * \n- * The getExtent() function is also overridden. Instead of calculating extent\n- * based on the center point and the current resolution, we instead \n- * calculate the extent by getting the lonlats at the top-left and \n- * bottom-right by using the getLonLatFromViewPortPx() translation function,\n- * taken from the pixel locations (0,0) and the size of the map. But how \n- * will we be able to do lonlat-px translation without resolution....?\n- * \n- * The getZoomForResolution() method is overridden. Instead of indexing into\n- * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in\n- * the desired resolution. With this extent, we then call getZoomForExtent() \n- * \n- * \n- * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, \n- * it is your responsibility to provide the following three functions:\n- * \n- * - getLonLatFromViewPortPx\n- * - getViewPortPxFromLonLat\n- * - getZoomForExtent\n- * \n- * ...those three functions should generally be provided by any reasonable \n- * API that you might be working from.\n- *\n- */\n-OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n+ var layers = this.map.layers.slice();\n+ if (!this.ascending) {\n+ layers.reverse();\n+ }\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ var baseLayer = layer.isBaseLayer;\n \n- /********************************************************/\n- /* */\n- /* Baselayer Functions */\n- /* */\n- /* The following functions must all be implemented */\n- /* by all base layers */\n- /* */\n- /********************************************************/\n+ if (layer.displayInLayerSwitcher) {\n \n- /**\n- * Constructor: OpenLayers.Layer.FixedZoomLevels\n- * Create a new fixed zoom levels layer.\n- */\n- initialize: function() {\n- //this class is only just to add the following functions... \n- // nothing to actually do here... but it is probably a good\n- // idea to have layers that use these functions call this \n- // inititalize() anyways, in case at some point we decide we \n- // do want to put some functionality or state in here. \n- },\n+ if (baseLayer) {\n+ containsBaseLayers = true;\n+ } else {\n+ containsOverlays = true;\n+ }\n \n- /**\n- * Method: initResolutions\n- * Populate the resolutions array\n- */\n- initResolutions: function() {\n+ // only check a baselayer if it is *the* baselayer, check data\n+ // layers if they are visible\n+ var checked = (baseLayer) ? (layer == this.map.baseLayer) :\n+ layer.getVisibility();\n \n- var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];\n+ // create input element\n+ var inputElem = document.createElement(\"input\"),\n+ // The input shall have an id attribute so we can use\n+ // labels to interact with them.\n+ inputId = OpenLayers.Util.createUniqueID(\n+ this.id + \"_input_\"\n+ );\n \n- for (var i = 0, len = props.length; i < len; i++) {\n- var property = props[i];\n- this[property] = (this.options[property] != null) ?\n- this.options[property] :\n- this.map[property];\n- }\n+ inputElem.id = inputId;\n+ inputElem.name = (baseLayer) ? this.id + \"_baseLayers\" : layer.name;\n+ inputElem.type = (baseLayer) ? \"radio\" : \"checkbox\";\n+ inputElem.value = layer.name;\n+ inputElem.checked = checked;\n+ inputElem.defaultChecked = checked;\n+ inputElem.className = \"olButton\";\n+ inputElem._layer = layer.id;\n+ inputElem._layerSwitcher = this.id;\n \n- if ((this.minZoomLevel == null) ||\n- (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) {\n- this.minZoomLevel = this.MIN_ZOOM_LEVEL;\n- }\n+ if (!baseLayer && !layer.inRange) {\n+ inputElem.disabled = true;\n+ }\n \n- //\n- // At this point, we know what the minimum desired zoom level is, and\n- // we must calculate the total number of zoom levels. \n- // \n- // Because we allow for the setting of either the 'numZoomLevels'\n- // or the 'maxZoomLevel' properties... on either the layer or the \n- // map, we have to define some rules to see which we take into\n- // account first in this calculation. \n- //\n- // The following is the precedence list for these properties:\n- // \n- // (1) numZoomLevels set on layer\n- // (2) maxZoomLevel set on layer\n- // (3) numZoomLevels set on map\n- // (4) maxZoomLevel set on map*\n- // (5) none of the above*\n- //\n- // *Note that options (4) and (5) are only possible if the user \n- // _explicitly_ sets the 'numZoomLevels' property on the map to \n- // null, since it is set by default to 16. \n- //\n+ // create span\n+ var labelSpan = document.createElement(\"label\");\n+ // this isn't the DOM attribute 'for', but an arbitrary name we\n+ // use to find the appropriate input element in <onButtonClick>\n+ labelSpan[\"for\"] = inputElem.id;\n+ OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n+ labelSpan._layer = layer.id;\n+ labelSpan._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ labelSpan.style.color = \"gray\";\n+ }\n+ labelSpan.innerHTML = layer.name;\n+ labelSpan.style.verticalAlign = (baseLayer) ? \"bottom\" :\n+ \"baseline\";\n+ // create line break\n+ var br = document.createElement(\"br\");\n \n- //\n- // Note to future: In 3.0, I think we should remove the default \n- // value of 16 for map.numZoomLevels. Rather, I think that value \n- // should be set as a default on the Layer.WMS class. If someone\n- // creates a 3rd party layer and does not specify any 'minZoomLevel', \n- // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly \n- // specified any of those on the map object either.. then I think\n- // it is fair to say that s/he wants all the zoom levels available.\n- // \n- // By making map.numZoomLevels *null* by default, that will be the \n- // case. As it is, I don't feel comfortable changing that right now\n- // as it would be a glaring API change and actually would probably\n- // break many peoples' codes. \n- //\n \n- //the number of zoom levels we'd like to have.\n- var desiredZoomLevels;\n+ var groupArray = (baseLayer) ? this.baseLayers :\n+ this.dataLayers;\n+ groupArray.push({\n+ 'layer': layer,\n+ 'inputElem': inputElem,\n+ 'labelSpan': labelSpan\n+ });\n \n- //this is the maximum number of zoom levels the layer will allow, \n- // given the specified starting minimum zoom level.\n- var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n \n- if (((this.options.numZoomLevels == null) &&\n- (this.options.maxZoomLevel != null)) // (2)\n- ||\n- ((this.numZoomLevels == null) &&\n- (this.maxZoomLevel != null)) // (4)\n- ) {\n- //calculate based on specified maxZoomLevel (on layer or map)\n- desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;\n- } else {\n- //calculate based on specified numZoomLevels (on layer or map)\n- // this covers cases (1) and (3)\n- desiredZoomLevels = this.numZoomLevels;\n+ var groupDiv = (baseLayer) ? this.baseLayersDiv :\n+ this.dataLayersDiv;\n+ groupDiv.appendChild(inputElem);\n+ groupDiv.appendChild(labelSpan);\n+ groupDiv.appendChild(br);\n+ }\n }\n \n- if (desiredZoomLevels != null) {\n- //Now that we know what we would *like* the number of zoom levels\n- // to be, based on layer or map options, we have to make sure that\n- // it does not conflict with the actual limit, as specified by \n- // the constants on the layer itself (and calculated into the\n- // 'limitZoomLevels' variable). \n- this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);\n- } else {\n- // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was \n- // set on either the layer or the map. So we just use the \n- // maximum limit as calculated by the layer's constants.\n- this.numZoomLevels = limitZoomLevels;\n- }\n+ // if no overlays, dont display the overlay label\n+ this.dataLbl.style.display = (containsOverlays) ? \"\" : \"none\";\n \n- //now that the 'numZoomLevels' is appropriately, safely set, \n- // we go back and re-calculate the 'maxZoomLevel'.\n- this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n+ // if no baselayers, dont display the baselayer label\n+ this.baseLbl.style.display = (containsBaseLayers) ? \"\" : \"none\";\n \n- if (this.RESOLUTIONS != null) {\n- var resolutionsIndex = 0;\n- this.resolutions = [];\n- for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n- this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];\n- }\n- this.maxResolution = this.resolutions[0];\n- this.minResolution = this.resolutions[this.resolutions.length - 1];\n- }\n+ return this.div;\n },\n \n /**\n- * APIMethod: getResolution\n- * Get the current map resolution\n- * \n- * Returns:\n- * {Float} Map units per Pixel\n+ * Method: updateMap\n+ * Cycles through the loaded data and base layer input arrays and makes\n+ * the necessary calls to the Map object such that that the map's\n+ * visual state corresponds to what the user has selected in\n+ * the control.\n */\n- getResolution: function() {\n-\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);\n- } else {\n- var resolution = null;\n-\n- var viewSize = this.map.getSize();\n- var extent = this.getExtent();\n+ updateMap: function() {\n \n- if ((viewSize != null) && (extent != null)) {\n- resolution = Math.max(extent.getWidth() / viewSize.w,\n- extent.getHeight() / viewSize.h);\n+ // set the newly selected base layer\n+ for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n+ var layerEntry = this.baseLayers[i];\n+ if (layerEntry.inputElem.checked) {\n+ this.map.setBaseLayer(layerEntry.layer, false);\n }\n- return resolution;\n }\n- },\n-\n- /**\n- * APIMethod: getExtent\n- * Calculates using px-> lonlat translation functions on tl and br \n- * corners of viewport\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n- * bounds of the current viewPort.\n- */\n- getExtent: function() {\n- var size = this.map.getSize();\n- var tl = this.getLonLatFromViewPortPx({\n- x: 0,\n- y: 0\n- });\n- var br = this.getLonLatFromViewPortPx({\n- x: size.w,\n- y: size.h\n- });\n \n- if ((tl != null) && (br != null)) {\n- return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);\n- } else {\n- return null;\n+ // set the correct visibilities for the overlays\n+ for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n+ var layerEntry = this.dataLayers[i];\n+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked);\n }\n+\n },\n \n /**\n- * Method: getZoomForResolution\n- * Get the zoom level for a given resolution\n+ * Method: maximizeControl\n+ * Set up the labels and divs for the control\n *\n * Parameters:\n- * resolution - {Float}\n- *\n- * Returns:\n- * {Integer} A suitable zoom level for the specified resolution.\n- * If no baselayer is set, returns null.\n+ * e - {Event}\n */\n- getZoomForResolution: function(resolution) {\n-\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);\n- } else {\n- var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n- return this.getZoomForExtent(extent);\n- }\n- },\n-\n-\n-\n+ maximizeControl: function(e) {\n \n- /********************************************************/\n- /* */\n- /* Translation Functions */\n- /* */\n- /* The following functions translate GMaps and OL */\n- /* formats for Pixel, LonLat, Bounds, and Zoom */\n- /* */\n- /********************************************************/\n+ // set the div's width and height to empty values, so\n+ // the div dimensions can be controlled by CSS\n+ this.div.style.width = \"\";\n+ this.div.style.height = \"\";\n \n+ this.showControls(false);\n \n- //\n- // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom\n- //\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n+ }\n+ },\n \n /**\n- * Method: getOLZoomFromMapObjectZoom\n- * Get the OL zoom index from the map object zoom level\n+ * Method: minimizeControl\n+ * Hide all the contents of the control, shrink the size,\n+ * add the maximize icon\n *\n * Parameters:\n- * moZoom - {Integer}\n- * \n- * Returns:\n- * {Integer} An OpenLayers Zoom level, translated from the passed in zoom\n- * Returns null if null value is passed in\n+ * e - {Event}\n */\n- getOLZoomFromMapObjectZoom: function(moZoom) {\n- var zoom = null;\n- if (moZoom != null) {\n- zoom = moZoom - this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.map.baseLayer.getZoomForResolution(\n- this.getResolutionForZoom(zoom)\n- );\n- }\n+ minimizeControl: function(e) {\n+\n+ // to minimize the control we set its div's width\n+ // and height to 0px, we cannot just set \"display\"\n+ // to \"none\" because it would hide the maximize\n+ // div\n+ this.div.style.width = \"0px\";\n+ this.div.style.height = \"0px\";\n+\n+ this.showControls(true);\n+\n+ if (e != null) {\n+ OpenLayers.Event.stop(e);\n }\n- return zoom;\n },\n \n /**\n- * Method: getMapObjectZoomFromOLZoom\n- * Get the map object zoom level from the OL zoom level\n+ * Method: showControls\n+ * Hide/Show all LayerSwitcher controls depending on whether we are\n+ * minimized or not\n *\n * Parameters:\n- * olZoom - {Integer}\n- * \n- * Returns:\n- * {Integer} A MapObject level, translated from the passed in olZoom\n- * Returns null if null value is passed in\n+ * minimize - {Boolean}\n */\n- getMapObjectZoomFromOLZoom: function(olZoom) {\n- var zoom = null;\n- if (olZoom != null) {\n- zoom = olZoom + this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.getZoomForResolution(\n- this.map.baseLayer.getResolutionForZoom(zoom)\n- );\n- }\n- }\n- return zoom;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n-});\n-\n-/* ======================================================================\n- OpenLayers/Layer/Google.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/SphericalMercator.js\n- * @requires OpenLayers/Layer/EventPane.js\n- * @requires OpenLayers/Layer/FixedZoomLevels.js\n- * @requires OpenLayers/Lang.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Google\n- * \n- * Provides a wrapper for Google's Maps API\n- * Normally the Terms of Use for this API do not allow wrapping, but Google\n- * have provided written consent to OpenLayers for this - see email in \n- * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.SphericalMercator>\n- * - <OpenLayers.Layer.EventPane>\n- * - <OpenLayers.Layer.FixedZoomLevels>\n- */\n-OpenLayers.Layer.Google = OpenLayers.Class(\n- OpenLayers.Layer.EventPane,\n- OpenLayers.Layer.FixedZoomLevels, {\n-\n- /** \n- * Constant: MIN_ZOOM_LEVEL\n- * {Integer} 0 \n- */\n- MIN_ZOOM_LEVEL: 0,\n-\n- /** \n- * Constant: MAX_ZOOM_LEVEL\n- * {Integer} 21\n- */\n- MAX_ZOOM_LEVEL: 21,\n-\n- /** \n- * Constant: RESOLUTIONS\n- * {Array(Float)} Hardcode these resolutions so that they are more closely\n- * tied with the standard wms projection\n- */\n- RESOLUTIONS: [\n- 1.40625,\n- 0.703125,\n- 0.3515625,\n- 0.17578125,\n- 0.087890625,\n- 0.0439453125,\n- 0.02197265625,\n- 0.010986328125,\n- 0.0054931640625,\n- 0.00274658203125,\n- 0.001373291015625,\n- 0.0006866455078125,\n- 0.00034332275390625,\n- 0.000171661376953125,\n- 0.0000858306884765625,\n- 0.00004291534423828125,\n- 0.00002145767211914062,\n- 0.00001072883605957031,\n- 0.00000536441802978515,\n- 0.00000268220901489257,\n- 0.0000013411045074462891,\n- 0.00000067055225372314453\n- ],\n-\n- /**\n- * APIProperty: type\n- * {GMapType}\n- */\n- type: null,\n-\n- /**\n- * APIProperty: wrapDateLine\n- * {Boolean} Allow user to pan forever east/west. Default is true. \n- * Setting this to false only restricts panning if \n- * <sphericalMercator> is true. \n- */\n- wrapDateLine: true,\n-\n- /**\n- * APIProperty: sphericalMercator\n- * {Boolean} Should the map act as a mercator-projected map? This will\n- * cause all interactions with the map to be in the actual map \n- * projection, which allows support for vector drawing, overlaying \n- * other maps, etc. \n- */\n- sphericalMercator: false,\n-\n- /**\n- * Property: version\n- * {Number} The version of the Google Maps API\n- */\n- version: null,\n-\n- /** \n- * Constructor: OpenLayers.Layer.Google\n- * \n- * Parameters:\n- * name - {String} A name for the layer.\n- * options - {Object} An optional object whose properties will be set\n- * on the layer.\n- */\n- initialize: function(name, options) {\n- options = options || {};\n- if (!options.version) {\n- options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\";\n- }\n- var mixin = OpenLayers.Layer.Google[\"v\" +\n- options.version.replace(/\\./g, \"_\")];\n- if (mixin) {\n- OpenLayers.Util.applyDefaults(options, mixin);\n- } else {\n- throw \"Unsupported Google Maps API version: \" + options.version;\n- }\n-\n- OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n- if (options.maxExtent) {\n- options.maxExtent = options.maxExtent.clone();\n- }\n-\n- OpenLayers.Layer.EventPane.prototype.initialize.apply(this,\n- [name, options]);\n- OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,\n- [name, options]);\n-\n- if (this.sphericalMercator) {\n- OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n- this.initMercatorParameters();\n- }\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Google>} An exact clone of this layer\n- */\n- clone: function() {\n- /**\n- * This method isn't intended to be called by a subclass and it\n- * doesn't call the same method on the superclass. We don't call\n- * the super's clone because we don't want properties that are set\n- * on this layer after initialize (i.e. this.mapObject etc.).\n- */\n- return new OpenLayers.Layer.Google(\n- this.name, this.getOptions()\n- );\n- },\n-\n- /**\n- * APIMethod: setVisibility\n- * Set the visibility flag for the layer and hide/show & redraw \n- * accordingly. Fire event unless otherwise specified\n- * \n- * Note that visibility is no longer simply whether or not the layer's\n- * style.display is set to \"block\". Now we store a 'visibility' state \n- * property on the layer class, this allows us to remember whether or \n- * not we *desire* for a layer to be visible. In the case where the \n- * map's resolution is out of the layer's range, this desire may be \n- * subverted.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the layer (if in range)\n- */\n- setVisibility: function(visible) {\n- // sharing a map container, opacity has to be set per layer\n- var opacity = this.opacity == null ? 1 : this.opacity;\n- OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n- this.setOpacity(opacity);\n- },\n-\n- /** \n- * APIMethod: display\n- * Hide or show the Layer\n- * \n- * Parameters:\n- * visible - {Boolean}\n- */\n- display: function(visible) {\n- if (!this._dragging) {\n- this.setGMapVisibility(visible);\n- }\n- OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);\n- },\n-\n- /**\n- * Method: moveTo\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- this._dragging = dragging;\n- OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n- delete this._dragging;\n- },\n-\n- /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n- * \n- * Parameters:\n- * opacity - {Float}\n- */\n- setOpacity: function(opacity) {\n- if (opacity !== this.opacity) {\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- });\n- }\n- this.opacity = opacity;\n- }\n- // Though this layer's opacity may not change, we're sharing a container\n- // and need to update the opacity for the entire container.\n- if (this.getVisibility()) {\n- var container = this.getMapContainer();\n- OpenLayers.Util.modifyDOMElement(\n- container, null, null, null, null, null, null, opacity\n- );\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up this layer.\n- */\n- destroy: function() {\n- /**\n- * We have to override this method because the event pane destroy\n- * deletes the mapObject reference before removing this layer from\n- * the map.\n- */\n- if (this.map) {\n- this.setGMapVisibility(false);\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache && cache.count <= 1) {\n- this.removeGMapElements();\n- }\n- }\n- OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: removeGMapElements\n- * Remove all elements added to the dom. This should only be called if\n- * this is the last of the Google layers for the given map.\n- */\n- removeGMapElements: function() {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // remove shared elements from dom\n- var container = this.mapObject && this.getMapContainer();\n- if (container && container.parentNode) {\n- container.parentNode.removeChild(container);\n- }\n- var termsOfUse = cache.termsOfUse;\n- if (termsOfUse && termsOfUse.parentNode) {\n- termsOfUse.parentNode.removeChild(termsOfUse);\n- }\n- var poweredBy = cache.poweredBy;\n- if (poweredBy && poweredBy.parentNode) {\n- poweredBy.parentNode.removeChild(poweredBy);\n- }\n- if (this.mapObject && window.google && google.maps &&\n- google.maps.event && google.maps.event.clearListeners) {\n- google.maps.event.clearListeners(this.mapObject, 'tilesloaded');\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: removeMap\n- * On being removed from the map, also remove termsOfUse and poweredBy divs\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- // hide layer before removing\n- if (this.visibility && this.mapObject) {\n- this.setGMapVisibility(false);\n- }\n- // check to see if last Google layer in this map\n- var cache = OpenLayers.Layer.Google.cache[map.id];\n- if (cache) {\n- if (cache.count <= 1) {\n- this.removeGMapElements();\n- delete OpenLayers.Layer.Google.cache[map.id];\n- } else {\n- // decrement the layer count\n- --cache.count;\n- }\n- }\n- // remove references to gmap elements\n- delete this.termsOfUse;\n- delete this.poweredBy;\n- delete this.mapObject;\n- delete this.dragObject;\n- OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n- /**\n- * APIMethod: getOLBoundsFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the \n- * passed-in MapObject Bounds.\n- * Returns null if null value is passed in.\n- */\n- getOLBoundsFromMapObjectBounds: function(moBounds) {\n- var olBounds = null;\n- if (moBounds != null) {\n- var sw = moBounds.getSouthWest();\n- var ne = moBounds.getNorthEast();\n- if (this.sphericalMercator) {\n- sw = this.forwardMercator(sw.lng(), sw.lat());\n- ne = this.forwardMercator(ne.lng(), ne.lat());\n- } else {\n- sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n- ne = new OpenLayers.LonLat(ne.lng(), ne.lat());\n- }\n- olBounds = new OpenLayers.Bounds(sw.lon,\n- sw.lat,\n- ne.lon,\n- ne.lat);\n- }\n- return olBounds;\n- },\n-\n- /** \n- * APIMethod: getWarningHTML\n- * \n- * Returns: \n- * {String} String with information on why layer is broken, how to get\n- * it working.\n- */\n- getWarningHTML: function() {\n- return OpenLayers.i18n(\"googleWarning\");\n- },\n+ showControls: function(minimize) {\n \n+ this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n+ this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n \n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n+ this.layersDiv.style.display = minimize ? \"none\" : \"\";\n+ },\n \n+ /**\n+ * Method: loadContents\n+ * Set up the labels and divs for the control\n+ */\n+ loadContents: function() {\n \n- // Get&Set Center, Zoom\n+ // layers list div\n+ this.layersDiv = document.createElement(\"div\");\n+ this.layersDiv.id = this.id + \"_layersDiv\";\n+ OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n \n- /**\n- * APIMethod: getMapObjectCenter\n- * \n- * Returns: \n- * {Object} The mapObject's current center in Map Object format\n- */\n- getMapObjectCenter: function() {\n- return this.mapObject.getCenter();\n- },\n+ this.baseLbl = document.createElement(\"div\");\n+ this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n+ OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n \n- /** \n- * APIMethod: getMapObjectZoom\n- * \n- * Returns:\n- * {Integer} The mapObject's current zoom, in Map Object format\n- */\n- getMapObjectZoom: function() {\n- return this.mapObject.getZoom();\n- },\n+ this.baseLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n \n+ this.dataLbl = document.createElement(\"div\");\n+ this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n+ OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n \n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n+ this.dataLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n \n+ if (this.ascending) {\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ } else {\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ }\n \n- // LonLat\n+ this.div.appendChild(this.layersDiv);\n \n- /**\n- * APIMethod: getLongitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Longitude of the given MapObject LonLat\n- */\n- getLongitudeFromMapObjectLonLat: function(moLonLat) {\n- return this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :\n- moLonLat.lng();\n- },\n+ // maximize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ \"OpenLayers_Control_MaximizeDiv\",\n+ null,\n+ null,\n+ img,\n+ \"absolute\");\n+ OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n+ this.maximizeDiv.style.display = \"none\";\n \n- /**\n- * APIMethod: getLatitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Latitude of the given MapObject LonLat\n- */\n- getLatitudeFromMapObjectLonLat: function(moLonLat) {\n- var lat = this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :\n- moLonLat.lat();\n- return lat;\n- },\n+ this.div.appendChild(this.maximizeDiv);\n \n- // Pixel\n+ // minimize button div\n+ var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n+ \"OpenLayers_Control_MinimizeDiv\",\n+ null,\n+ null,\n+ img,\n+ \"absolute\");\n+ OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n+ this.minimizeDiv.style.display = \"none\";\n \n- /**\n- * APIMethod: getXFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} X value of the MapObject Pixel\n- */\n- getXFromMapObjectPixel: function(moPixel) {\n- return moPixel.x;\n- },\n+ this.div.appendChild(this.minimizeDiv);\n+ },\n \n- /**\n- * APIMethod: getYFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} Y value of the MapObject Pixel\n- */\n- getYFromMapObjectPixel: function(moPixel) {\n- return moPixel.y;\n- },\n+ CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Zoom.js\n+ ====================================================================== */\n \n- CLASS_NAME: \"OpenLayers.Layer.Google\"\n- });\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n /**\n- * Property: OpenLayers.Layer.Google.cache\n- * {Object} Cache for elements that should only be created once per map.\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n */\n-OpenLayers.Layer.Google.cache = {};\n-\n \n /**\n- * Constant: OpenLayers.Layer.Google.v2\n- * \n- * Mixin providing functionality specific to the Google Maps API v2.\n- * \n- * This API has been deprecated by Google.\n- * Developers are encouraged to migrate to v3 of the API; support for this\n- * is provided by <OpenLayers.Layer.Google.v3>\n+ * Class: OpenLayers.Control.Zoom\n+ * The Zoom control is a pair of +/- links for zooming in and out.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Layer.Google.v2 = {\n-\n- /**\n- * Property: termsOfUse\n- * {DOMElement} Div for Google's copyright and terms of use link\n- */\n- termsOfUse: null,\n+OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n \n /**\n- * Property: poweredBy\n- * {DOMElement} Div for Google's powered by logo and link\n+ * APIProperty: zoomInText\n+ * {String}\n+ * Text for zoom-in link. Default is \"+\".\n */\n- poweredBy: null,\n+ zoomInText: \"+\",\n \n /**\n- * Property: dragObject\n- * {GDraggableObject} Since 2.93, Google has exposed the ability to get\n- * the maps GDraggableObject. We can now use this for smooth panning\n- */\n- dragObject: null,\n-\n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners. If we can't \n- * load GMap2, then display a warning message.\n+ * APIProperty: zoomInId\n+ * {String}\n+ * Instead of having the control create a zoom in link, you can provide \n+ * the identifier for an anchor element already added to the document.\n+ * By default, an element with id \"olZoomInLink\" will be searched for\n+ * and used if it exists.\n */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = G_NORMAL_MAP;\n- }\n- var mapObject, termsOfUse, poweredBy;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- termsOfUse = cache.termsOfUse;\n- poweredBy = cache.poweredBy;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n-\n- var container = this.map.viewPortDiv;\n- var div = document.createElement(\"div\");\n- div.id = this.map.id + \"_GMap2Container\";\n- div.style.position = \"absolute\";\n- div.style.width = \"100%\";\n- div.style.height = \"100%\";\n- container.appendChild(div);\n-\n- // create GMap and shuffle elements\n- try {\n- mapObject = new GMap2(div);\n-\n- // move the ToS and branding stuff up to the container div\n- termsOfUse = div.lastChild;\n- container.appendChild(termsOfUse);\n- termsOfUse.style.zIndex = \"1100\";\n- termsOfUse.style.right = \"\";\n- termsOfUse.style.bottom = \"\";\n- termsOfUse.className = \"olLayerGoogleCopyright\";\n-\n- poweredBy = div.lastChild;\n- container.appendChild(poweredBy);\n- poweredBy.style.zIndex = \"1100\";\n- poweredBy.style.right = \"\";\n- poweredBy.style.bottom = \"\";\n- poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\";\n-\n- } catch (e) {\n- throw (e);\n- }\n- // cache elements for use by any other google layers added to\n- // this same map\n- OpenLayers.Layer.Google.cache[this.map.id] = {\n- mapObject: mapObject,\n- termsOfUse: termsOfUse,\n- poweredBy: poweredBy,\n- count: 1\n- };\n- }\n-\n- this.mapObject = mapObject;\n- this.termsOfUse = termsOfUse;\n- this.poweredBy = poweredBy;\n-\n- // ensure this layer type is one of the mapObject types\n- if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),\n- this.type) === -1) {\n- this.mapObject.addMapType(this.type);\n- }\n-\n- //since v 2.93 getDragObject is now available.\n- if (typeof mapObject.getDragObject == \"function\") {\n- this.dragObject = mapObject.getDragObject();\n- } else {\n- this.dragPanMapObject = null;\n- }\n-\n- if (this.isBaseLayer === false) {\n- this.setGMapVisibility(this.div.style.display !== \"none\");\n- }\n-\n- },\n+ zoomInId: \"olZoomInLink\",\n \n /**\n- * APIMethod: onMapResize\n+ * APIProperty: zoomOutText\n+ * {String}\n+ * Text for zoom-out link. Default is \"\\u2212\".\n */\n- onMapResize: function() {\n- // workaround for resizing of invisible or not yet fully loaded layers\n- // where GMap2.checkResize() does not work. We need to load the GMap\n- // for the old div size, then checkResize(), and then call\n- // layer.moveTo() to trigger GMap.setCenter() (which will finish\n- // the GMap initialization).\n- if (this.visibility && this.mapObject.isLoaded()) {\n- this.mapObject.checkResize();\n- } else {\n- if (!this._resized) {\n- var layer = this;\n- var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n- GEvent.removeListener(handle);\n- delete layer._resized;\n- layer.mapObject.checkResize();\n- layer.moveTo(layer.map.getCenter(), layer.map.getZoom());\n- });\n- }\n- this._resized = true;\n- }\n- },\n+ zoomOutText: \"\\u2212\",\n \n /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n+ * APIProperty: zoomOutId\n+ * {String}\n+ * Instead of having the control create a zoom out link, you can provide \n+ * the identifier for an anchor element already added to the document.\n+ * By default, an element with id \"olZoomOutLink\" will be searched for\n+ * and used if it exists.\n */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject.getContainer();\n- if (visible === true) {\n- this.mapObject.setMapType(this.type);\n- container.style.display = \"\";\n- this.termsOfUse.style.left = \"\";\n- this.termsOfUse.style.display = \"\";\n- this.poweredBy.style.display = \"\";\n- cache.displayed = this.id;\n- } else {\n- if (cache.displayed === this.id) {\n- delete cache.displayed;\n- }\n- if (!cache.displayed) {\n- container.style.display = \"none\";\n- this.termsOfUse.style.display = \"none\";\n- // move ToU far to the left in addition to setting display\n- // to \"none\", because at the end of the GMap2 load\n- // sequence, display: none will be unset and ToU would be\n- // visible after loading a map with a google layer that is\n- // initially hidden. \n- this.termsOfUse.style.left = \"-9999px\";\n- this.poweredBy.style.display = \"none\";\n- }\n- }\n- }\n- },\n+ zoomOutId: \"olZoomOutLink\",\n \n /**\n- * Method: getMapContainer\n- * \n+ * Method: draw\n+ *\n * Returns:\n- * {DOMElement} the GMap container's div\n+ * {DOMElement} A reference to the DOMElement containing the zoom links.\n */\n- getMapContainer: function() {\n- return this.mapObject.getContainer();\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n+ draw: function() {\n+ var div = OpenLayers.Control.prototype.draw.apply(this),\n+ links = this.getOrCreateLinks(div),\n+ zoomIn = links.zoomIn,\n+ zoomOut = links.zoomOut,\n+ eventsInstance = this.map.events;\n \n- /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n- * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n- */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),\n- new GLatLng(ne.lat, ne.lon));\n+ if (zoomOut.parentNode !== div) {\n+ eventsInstance = this.events;\n+ eventsInstance.attachToElement(zoomOut.parentNode);\n }\n- return moBounds;\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n-\n-\n- // Get&Set Center, Zoom\n-\n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n- * \n- * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n- */\n- setMapObjectCenter: function(center, zoom) {\n- this.mapObject.setCenter(center, zoom);\n- },\n+ eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n \n- /**\n- * APIMethod: dragPanMapObject\n- * \n- * Parameters:\n- * dX - {Integer}\n- * dY - {Integer}\n- */\n- dragPanMapObject: function(dX, dY) {\n- this.dragObject.moveBy(new GSize(-dX, dY));\n+ this.zoomInLink = zoomIn;\n+ this.zoomOutLink = zoomOut;\n+ return div;\n },\n \n-\n- // LonLat - Pixel Translation\n-\n /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n+ * Method: getOrCreateLinks\n * \n * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n+ * el - {DOMElement}\n+ *\n+ * Return: \n+ * {Object} Object with zoomIn and zoomOut properties referencing links.\n */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- return this.mapObject.fromContainerPixelToLatLng(moPixel);\n+ getOrCreateLinks: function(el) {\n+ var zoomIn = document.getElementById(this.zoomInId),\n+ zoomOut = document.getElementById(this.zoomOutId);\n+ if (!zoomIn) {\n+ zoomIn = document.createElement(\"a\");\n+ zoomIn.href = \"#zoomIn\";\n+ zoomIn.appendChild(document.createTextNode(this.zoomInText));\n+ zoomIn.className = \"olControlZoomIn\";\n+ el.appendChild(zoomIn);\n+ }\n+ OpenLayers.Element.addClass(zoomIn, \"olButton\");\n+ if (!zoomOut) {\n+ zoomOut = document.createElement(\"a\");\n+ zoomOut.href = \"#zoomOut\";\n+ zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n+ zoomOut.className = \"olControlZoomOut\";\n+ el.appendChild(zoomOut);\n+ }\n+ OpenLayers.Element.addClass(zoomOut, \"olButton\");\n+ return {\n+ zoomIn: zoomIn,\n+ zoomOut: zoomOut\n+ };\n },\n \n /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n+ * Method: onZoomClick\n+ * Called when zoomin/out link is clicked.\n */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- return this.mapObject.fromLatLngToContainerPixel(moLonLat);\n+ onZoomClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.zoomInLink) {\n+ this.map.zoomIn();\n+ } else if (button === this.zoomOutLink) {\n+ this.map.zoomOut();\n+ }\n },\n \n-\n- // Bounds\n-\n /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n- */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n- },\n-\n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n- /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n- * \n- * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n- * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n+ * Method: destroy\n+ * Clean up.\n */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new GLatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new GLatLng(lat, lon);\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onZoomClick);\n }\n- return gLatLng;\n+ delete this.zoomInLink;\n+ delete this.zoomOutLink;\n+ OpenLayers.Control.prototype.destroy.apply(this);\n },\n \n- // Pixel\n-\n- /**\n- * APIMethod: getMapObjectPixelFromXY\n- * \n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n- * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n- */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new GPoint(x, y);\n- }\n-\n-};\n+ CLASS_NAME: \"OpenLayers.Control.Zoom\"\n+});\n /* ======================================================================\n- OpenLayers/Layer/Google/v3.js\n+ OpenLayers/Handler/Feature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n- * @requires OpenLayers/Layer/Google.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Constant: OpenLayers.Layer.Google.v3\n- * \n- * Mixin providing functionality specific to the Google Maps API v3.\n- * \n- * To use this layer, you must include the GMaps v3 API in your html.\n- * \n- * Note that this layer configures the google.maps.map object with the\n- * \"disableDefaultUI\" option set to true. Using UI controls that the Google\n- * Maps API provides is not supported by the OpenLayers API.\n+ * Class: OpenLayers.Handler.Feature \n+ * Handler to respond to mouse events related to a drawn feature. Callbacks\n+ * with the following keys will be notified of the following events\n+ * associated with features: click, clickout, over, out, and dblclick.\n+ *\n+ * This handler stops event propagation for mousedown and mouseup if those\n+ * browser events target features that can be selected.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n */\n-OpenLayers.Layer.Google.v3 = {\n+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n \n /**\n- * Constant: DEFAULTS\n- * {Object} It is not recommended to change the properties set here. Note\n- * that Google.v3 layers only work when sphericalMercator is set to true.\n- * \n- * (code)\n- * {\n- * sphericalMercator: true,\n- * projection: \"EPSG:900913\"\n- * }\n- * (end)\n+ * Property: EVENTMAP\n+ * {Object} A object mapping the browser events to objects with callback\n+ * keys for in and out.\n */\n- DEFAULTS: {\n- sphericalMercator: true,\n- projection: \"EPSG:900913\"\n+ EVENTMAP: {\n+ 'click': {\n+ 'in': 'click',\n+ 'out': 'clickout'\n+ },\n+ 'mousemove': {\n+ 'in': 'over',\n+ 'out': 'out'\n+ },\n+ 'dblclick': {\n+ 'in': 'dblclick',\n+ 'out': null\n+ },\n+ 'mousedown': {\n+ 'in': null,\n+ 'out': null\n+ },\n+ 'mouseup': {\n+ 'in': null,\n+ 'out': null\n+ },\n+ 'touchstart': {\n+ 'in': 'click',\n+ 'out': 'clickout'\n+ }\n },\n \n /**\n- * APIProperty: animationEnabled\n- * {Boolean} If set to true, the transition between zoom levels will be\n- * animated (if supported by the GMaps API for the device used). Set to\n- * false to match the zooming experience of other layer types. Default\n- * is true. Note that the GMaps API does not give us control over zoom\n- * animation, so if set to false, when zooming, this will make the\n- * layer temporarily invisible, wait until GMaps reports the map being\n- * idle, and make it visible again. The result will be a blank layer\n- * for a few moments while zooming.\n+ * Property: feature\n+ * {<OpenLayers.Feature.Vector>} The last feature that was hovered.\n */\n- animationEnabled: true,\n+ feature: null,\n \n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners.\n+ /**\n+ * Property: lastFeature\n+ * {<OpenLayers.Feature.Vector>} The last feature that was handled.\n */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = google.maps.MapTypeId.ROADMAP;\n- }\n- var mapObject;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n- // create GMap\n- var center = this.map.getCenter();\n- var container = document.createElement('div');\n- container.className = \"olForeignContainer\";\n- container.style.width = '100%';\n- container.style.height = '100%';\n- mapObject = new google.maps.Map(container, {\n- center: center ?\n- new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n- zoom: this.map.getZoom() || 0,\n- mapTypeId: this.type,\n- disableDefaultUI: true,\n- keyboardShortcuts: false,\n- draggable: false,\n- disableDoubleClickZoom: true,\n- scrollwheel: false,\n- streetViewControl: false\n- });\n- var googleControl = document.createElement('div');\n- googleControl.style.width = '100%';\n- googleControl.style.height = '100%';\n- mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n-\n- // cache elements for use by any other google layers added to\n- // this same map\n- cache = {\n- googleControl: googleControl,\n- mapObject: mapObject,\n- count: 1\n- };\n- OpenLayers.Layer.Google.cache[this.map.id] = cache;\n- }\n- this.mapObject = mapObject;\n- this.setGMapVisibility(this.visibility);\n- },\n+ lastFeature: null,\n \n /**\n- * APIMethod: onMapResize\n+ * Property: down\n+ * {<OpenLayers.Pixel>} The location of the last mousedown.\n */\n- onMapResize: function() {\n- if (this.visibility) {\n- google.maps.event.trigger(this.mapObject, \"resize\");\n- }\n- },\n+ down: null,\n \n /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n+ * Property: up\n+ * {<OpenLayers.Pixel>} The location of the last mouseup.\n */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- var map = this.map;\n- if (cache) {\n- var type = this.type;\n- var layers = map.layers;\n- var layer;\n- for (var i = layers.length - 1; i >= 0; --i) {\n- layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.Google &&\n- layer.visibility === true && layer.inRange === true) {\n- type = layer.type;\n- visible = true;\n- break;\n- }\n- }\n- var container = this.mapObject.getDiv();\n- if (visible === true) {\n- if (container.parentNode !== map.div) {\n- if (!cache.rendered) {\n- var me = this;\n- google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {\n- cache.rendered = true;\n- me.setGMapVisibility(me.getVisibility());\n- me.moveTo(me.map.getCenter());\n- });\n- } else {\n- map.div.appendChild(container);\n- cache.googleControl.appendChild(map.viewPortDiv);\n- google.maps.event.trigger(this.mapObject, 'resize');\n- }\n- }\n- this.mapObject.setMapTypeId(type);\n- } else if (cache.googleControl.hasChildNodes()) {\n- map.div.appendChild(map.viewPortDiv);\n- map.div.removeChild(container);\n- }\n- }\n- },\n+ up: null,\n \n /**\n- * Method: getMapContainer\n- * \n- * Returns:\n- * {DOMElement} the GMap container's div\n+ * Property: clickTolerance\n+ * {Number} The number of pixels the mouse can move between mousedown\n+ * and mouseup for the event to still be considered a click.\n+ * Dragging the map should not trigger the click and clickout callbacks\n+ * unless the map is moved by less than this tolerance. Defaults to 4.\n */\n- getMapContainer: function() {\n- return this.mapObject.getDiv();\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n+ clickTolerance: 4,\n \n /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n- * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n+ * Property: geometryTypes\n+ * To restrict dragging to a limited set of geometry types, send a list\n+ * of strings corresponding to the geometry class names.\n * \n- * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n+ * @type Array(String)\n */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new google.maps.LatLngBounds(\n- new google.maps.LatLng(sw.lat, sw.lon),\n- new google.maps.LatLng(ne.lat, ne.lon)\n- );\n- }\n- return moBounds;\n- },\n-\n+ geometryTypes: null,\n \n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n+ /**\n+ * Property: stopClick\n+ * {Boolean} If stopClick is set to true, handled clicks do not\n+ * propagate to other click listeners. Otherwise, handled clicks\n+ * do propagate. Unhandled clicks always propagate, whatever the\n+ * value of stopClick. Defaults to true.\n+ */\n+ stopClick: true,\n \n+ /**\n+ * Property: stopDown\n+ * {Boolean} If stopDown is set to true, handled mousedowns do not\n+ * propagate to other mousedown listeners. Otherwise, handled\n+ * mousedowns do propagate. Unhandled mousedowns always propagate,\n+ * whatever the value of stopDown. Defaults to true.\n+ */\n+ stopDown: true,\n \n- // LonLat - Pixel Translation\n+ /**\n+ * Property: stopUp\n+ * {Boolean} If stopUp is set to true, handled mouseups do not\n+ * propagate to other mouseup listeners. Otherwise, handled mouseups\n+ * do propagate. Unhandled mouseups always propagate, whatever the\n+ * value of stopUp. Defaults to false.\n+ */\n+ stopUp: false,\n \n /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n- * \n+ * Constructor: OpenLayers.Handler.Feature\n+ *\n * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n+ * control - {<OpenLayers.Control>} \n+ * layer - {<OpenLayers.Layer.Vector>}\n+ * callbacks - {Object} An object with a 'over' property whos value is\n+ * a function to be called when the mouse is over a feature. The \n+ * callback should expect to recieve a single argument, the feature.\n+ * options - {Object} \n */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- var size = this.map.getSize();\n- var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n- var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n- var res = this.map.getResolution();\n-\n- var delta_x = moPixel.x - (size.w / 2);\n- var delta_y = moPixel.y - (size.h / 2);\n-\n- var lonlat = new OpenLayers.LonLat(\n- lon + delta_x * res,\n- lat - delta_y * res\n- );\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n- }\n- return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);\n+ initialize: function(control, layer, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.layer = layer;\n },\n \n /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n+ * Method: touchstart\n+ * Handle touchstart events\n+ *\n * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n+ * evt - {Event}\n+ *\n * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n+ * {Boolean} Let the event propagate.\n */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- var res = this.map.getResolution();\n- var extent = this.map.getExtent();\n- return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)),\n- (1 / res * (extent.top - lat)));\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return OpenLayers.Event.isMultiTouch(evt) ?\n+ true : this.mousedown(evt);\n },\n \n+ /**\n+ * Method: touchmove\n+ * Handle touchmove events. We just prevent the browser default behavior,\n+ * for Android Webkit not to select text when moving the finger after\n+ * selecting a feature.\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ touchmove: function(evt) {\n+ OpenLayers.Event.preventDefault(evt);\n+ },\n \n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n+ /**\n+ * Method: mousedown\n+ * Handle mouse down. Stop propagation if a feature is targeted by this\n+ * event (stops map dragging during feature selection).\n * \n * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n+ * evt - {Event} \n */\n- setMapObjectCenter: function(center, zoom) {\n- if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n- var mapContainer = this.getMapContainer();\n- google.maps.event.addListenerOnce(\n- this.mapObject,\n- \"idle\",\n- function() {\n- mapContainer.style.visibility = \"\";\n- }\n- );\n- mapContainer.style.visibility = \"hidden\";\n+ mousedown: function(evt) {\n+ // Feature selection is only done with a left click. Other handlers may stop the\n+ // propagation of left-click mousedown events but not right-click mousedown events.\n+ // This mismatch causes problems when comparing the location of the down and up\n+ // events in the click function so it is important ignore right-clicks.\n+ if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n+ this.down = evt.xy;\n }\n- this.mapObject.setOptions({\n- center: center,\n- zoom: zoom\n- });\n+ return this.handle(evt) ? !this.stopDown : true;\n },\n \n-\n- // Bounds\n-\n- /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n+ /**\n+ * Method: mouseup\n+ * Handle mouse up. Stop propagation if a feature is targeted by this\n+ * event.\n * \n * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n+ * evt - {Event} \n */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n+ mouseup: function(evt) {\n+ this.up = evt.xy;\n+ return this.handle(evt) ? !this.stopUp : true;\n },\n \n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n+ * Method: click\n+ * Handle click. Call the \"click\" callback if click on a feature,\n+ * or the \"clickout\" callback if click outside any feature.\n * \n * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n+ * evt - {Event} \n+ *\n * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n+ * {Boolean}\n */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new google.maps.LatLng(lat, lon);\n- }\n- return gLatLng;\n+ click: function(evt) {\n+ return this.handle(evt) ? !this.stopClick : true;\n },\n \n- // Pixel\n-\n /**\n- * APIMethod: getMapObjectPixelFromXY\n+ * Method: mousemove\n+ * Handle mouse moves. Call the \"over\" callback if moving in to a feature,\n+ * or the \"out\" callback if moving out of a feature.\n * \n * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n- * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n- */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new google.maps.Point(x, y);\n- }\n-\n-};\n-/* ======================================================================\n- OpenLayers/Filter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Filter\n- * This class represents an OGC Filter.\n- */\n-OpenLayers.Filter = OpenLayers.Class({\n-\n- /** \n- * Constructor: OpenLayers.Filter\n- * This class represents a generic filter.\n+ * evt - {Event} \n *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- * \n * Returns:\n- * {<OpenLayers.Filter>}\n+ * {Boolean}\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n+ mousemove: function(evt) {\n+ if (!this.callbacks['over'] && !this.callbacks['out']) {\n+ return true;\n+ }\n+ this.handle(evt);\n+ return true;\n },\n \n- /** \n- * APIMethod: destroy\n- * Remove reference to anything added.\n- */\n- destroy: function() {},\n-\n /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context. Instances or subclasses\n- * are supposed to override this method.\n- * \n+ * Method: dblclick\n+ * Handle dblclick. Call the \"dblclick\" callback if dblclick on a feature.\n+ *\n * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n- * \n+ * evt - {Event} \n+ *\n * Returns:\n- * {Boolean} The filter applies.\n+ * {Boolean}\n */\n- evaluate: function(context) {\n- return true;\n+ dblclick: function(evt) {\n+ return !this.handle(evt);\n },\n \n /**\n- * APIMethod: clone\n- * Clones this filter. Should be implemented by subclasses.\n- * \n+ * Method: geometryTypeMatches\n+ * Return true if the geometry type of the passed feature matches\n+ * one of the geometry types in the geometryTypes array.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Vector.Feature>}\n+ *\n * Returns:\n- * {<OpenLayers.Filter>} Clone of this filter.\n+ * {Boolean}\n */\n- clone: function() {\n- return null;\n+ geometryTypeMatches: function(feature) {\n+ return this.geometryTypes == null ||\n+ OpenLayers.Util.indexOf(this.geometryTypes,\n+ feature.geometry.CLASS_NAME) > -1;\n },\n \n /**\n- * APIMethod: toString\n+ * Method: handle\n+ *\n+ * Parameters:\n+ * evt - {Event}\n *\n * Returns:\n- * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n- * representation of the filter returned. Otherwise \"[Object object]\"\n- * will be returned.\n+ * {Boolean} The event occurred over a relevant feature.\n */\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this);\n- } else {\n- string = Object.prototype.toString.call(this);\n+ handle: function(evt) {\n+ if (this.feature && !this.feature.layer) {\n+ // feature has been destroyed\n+ this.feature = null;\n }\n- return string;\n+ var type = evt.type;\n+ var handled = false;\n+ var previouslyIn = !!(this.feature); // previously in a feature\n+ var click = (type == \"click\" || type == \"dblclick\" || type == \"touchstart\");\n+ this.feature = this.layer.getFeatureFromEvent(evt);\n+ if (this.feature && !this.feature.layer) {\n+ // feature has been destroyed\n+ this.feature = null;\n+ }\n+ if (this.lastFeature && !this.lastFeature.layer) {\n+ // last feature has been destroyed\n+ this.lastFeature = null;\n+ }\n+ if (this.feature) {\n+ if (type === \"touchstart\") {\n+ // stop the event to prevent Android Webkit from\n+ // \"flashing\" the map div\n+ OpenLayers.Event.preventDefault(evt);\n+ }\n+ var inNew = (this.feature != this.lastFeature);\n+ if (this.geometryTypeMatches(this.feature)) {\n+ // in to a feature\n+ if (previouslyIn && inNew) {\n+ // out of last feature and in to another\n+ if (this.lastFeature) {\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n+ }\n+ this.triggerCallback(type, 'in', [this.feature]);\n+ } else if (!previouslyIn || click) {\n+ // in feature for the first time\n+ this.triggerCallback(type, 'in', [this.feature]);\n+ }\n+ this.lastFeature = this.feature;\n+ handled = true;\n+ } else {\n+ // not in to a feature\n+ if (this.lastFeature && (previouslyIn && inNew || click)) {\n+ // out of last feature for the first time\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n+ }\n+ // next time the mouse goes in a feature whose geometry type\n+ // doesn't match we don't want to call the 'out' callback\n+ // again, so let's set this.feature to null so that\n+ // previouslyIn will evaluate to false the next time\n+ // we enter handle. Yes, a bit hackish...\n+ this.feature = null;\n+ }\n+ } else if (this.lastFeature && (previouslyIn || click)) {\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n+ }\n+ return handled;\n },\n \n- CLASS_NAME: \"OpenLayers.Filter\"\n-});\n-/* ======================================================================\n- OpenLayers/Filter/Logical.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Filter.js\n- */\n-\n-/**\n- * Class: OpenLayers.Filter.Logical\n- * This class represents ogc:And, ogc:Or and ogc:Not rules.\n- * \n- * Inherits from:\n- * - <OpenLayers.Filter>\n- */\n-OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\n-\n /**\n- * APIProperty: filters\n- * {Array(<OpenLayers.Filter>)} Child filters for this filter.\n+ * Method: triggerCallback\n+ * Call the callback keyed in the event map with the supplied arguments.\n+ * For click and clickout, the <clickTolerance> is checked first.\n+ *\n+ * Parameters:\n+ * type - {String}\n */\n- filters: null,\n+ triggerCallback: function(type, mode, args) {\n+ var key = this.EVENTMAP[type][mode];\n+ if (key) {\n+ if (type == 'click' && this.up && this.down) {\n+ // for click/clickout, only trigger callback if tolerance is met\n+ var dpx = Math.sqrt(\n+ Math.pow(this.up.x - this.down.x, 2) +\n+ Math.pow(this.up.y - this.down.y, 2)\n+ );\n+ if (dpx <= this.clickTolerance) {\n+ this.callback(key, args);\n+ }\n+ // we're done with this set of events now: clear the cached\n+ // positions so we can't trip over them later (this can occur\n+ // if one of the up/down events gets eaten before it gets to us\n+ // but we still get the click)\n+ this.up = this.down = null;\n+ } else {\n+ this.callback(key, args);\n+ }\n+ }\n+ },\n \n /**\n- * APIProperty: type\n- * {String} type of logical operator. Available types are:\n- * - OpenLayers.Filter.Logical.AND = \"&&\";\n- * - OpenLayers.Filter.Logical.OR = \"||\";\n- * - OpenLayers.Filter.Logical.NOT = \"!\";\n- */\n- type: null,\n-\n- /** \n- * Constructor: OpenLayers.Filter.Logical\n- * Creates a logical filter (And, Or, Not).\n+ * Method: activate \n+ * Turn on the handler. Returns false if the handler was already active.\n *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * filter.\n- * \n * Returns:\n- * {<OpenLayers.Filter.Logical>}\n+ * {Boolean}\n */\n- initialize: function(options) {\n- this.filters = [];\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ activated = true;\n+ }\n+ return activated;\n },\n \n- /** \n- * APIMethod: destroy\n- * Remove reference to child filters.\n+ /**\n+ * Method: deactivate \n+ * Turn off the handler. Returns false if the handler was already active.\n+ *\n+ * Returns: \n+ * {Boolean}\n */\n- destroy: function() {\n- this.filters = null;\n- OpenLayers.Filter.prototype.destroy.apply(this);\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.feature = null;\n+ this.lastFeature = null;\n+ this.down = null;\n+ this.up = null;\n+ this.map.events.un({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ deactivated = true;\n+ }\n+ return deactivated;\n },\n \n /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context.\n+ * Method: handleMapEvents\n * \n * Parameters:\n- * context - {Object} Context to use in evaluating the filter. A vector\n- * feature may also be provided to evaluate feature attributes in \n- * comparison filters or geometries in spatial filters.\n- * \n- * Returns:\n- * {Boolean} The filter applies.\n+ * evt - {Object}\n */\n- evaluate: function(context) {\n- var i, len;\n- switch (this.type) {\n- case OpenLayers.Filter.Logical.AND:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == false) {\n- return false;\n- }\n- }\n- return true;\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop();\n+ }\n+ },\n \n- case OpenLayers.Filter.Logical.OR:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == true) {\n- return true;\n- }\n- }\n- return false;\n+ /**\n+ * Method: moveLayerToTop\n+ * Moves the layer for this handler to the top, so mouse events can reach\n+ * it.\n+ */\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n+ this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index);\n \n- case OpenLayers.Filter.Logical.NOT:\n- return (!this.filters[0].evaluate(context));\n- }\n- return undefined;\n },\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\n- * \n- * Returns:\n- * {<OpenLayers.Filter.Logical>} Clone of this filter.\n+ * Method: moveLayerBack\n+ * Moves the layer back to the position determined by the map's layers\n+ * array.\n */\n- clone: function() {\n- var filters = [];\n- for (var i = 0, len = this.filters.length; i < len; ++i) {\n- filters.push(this.filters[i].clone());\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE['Feature']) {\n+ this.layer.setZIndex(index);\n+ } else {\n+ this.map.setLayerZIndex(this.layer,\n+ this.map.getLayerIndex(this.layer));\n }\n- return new OpenLayers.Filter.Logical({\n- type: this.type,\n- filters: filters\n- });\n },\n \n- CLASS_NAME: \"OpenLayers.Filter.Logical\"\n+ CLASS_NAME: \"OpenLayers.Handler.Feature\"\n });\n-\n-\n-OpenLayers.Filter.Logical.AND = \"&&\";\n-OpenLayers.Filter.Logical.OR = \"||\";\n-OpenLayers.Filter.Logical.NOT = \"!\";\n /* ======================================================================\n- OpenLayers/Filter/Comparison.js\n+ OpenLayers/Layer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Filter.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Map.js\n+ * @requires OpenLayers/Projection.js\n */\n \n /**\n- * Class: OpenLayers.Filter.Comparison\n- * This class represents a comparison filter.\n- * \n- * Inherits from:\n- * - <OpenLayers.Filter>\n+ * Class: OpenLayers.Layer\n */\n-OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\n+OpenLayers.Layer = OpenLayers.Class({\n \n /**\n- * APIProperty: type\n- * {String} type: type of the comparison. This is one of\n- * - OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n- * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n- * - OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n- * - OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n- * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n- * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n- * - OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n- * - OpenLayers.Filter.Comparison.LIKE = \"~\";\n- * - OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n+ * APIProperty: id\n+ * {String}\n */\n- type: null,\n+ id: null,\n \n- /**\n- * APIProperty: property\n+ /** \n+ * APIProperty: name\n * {String}\n- * name of the context property to compare\n */\n- property: null,\n+ name: null,\n \n- /**\n- * APIProperty: value\n- * {Number} or {String}\n- * comparison value for binary comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n+ /** \n+ * APIProperty: div\n+ * {DOMElement}\n */\n- value: null,\n+ div: null,\n \n /**\n- * Property: matchCase\n- * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO\n- * comparisons. The Filter Encoding 1.1 specification added a matchCase\n- * attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo\n- * elements. This property will be serialized with those elements only\n- * if using the v1.1.0 filter format. However, when evaluating filters\n- * here, the matchCase property will always be respected (for EQUAL_TO\n- * and NOT_EQUAL_TO). Default is true. \n+ * APIProperty: opacity\n+ * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default\n+ * is 1.\n */\n- matchCase: true,\n+ opacity: 1,\n \n /**\n- * APIProperty: lowerBoundary\n- * {Number} or {String}\n- * lower boundary for between comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n+ * APIProperty: alwaysInRange\n+ * {Boolean} If a layer's display should not be scale-based, this should \n+ * be set to true. This will cause the layer, as an overlay, to always \n+ * be 'active', by always returning true from the calculateInRange() \n+ * function. \n+ * \n+ * If not explicitly specified for a layer, its value will be \n+ * determined on startup in initResolutions() based on whether or not \n+ * any scale-specific properties have been set as options on the \n+ * layer. If no scale-specific options have been set on the layer, we \n+ * assume that it should always be in range.\n+ * \n+ * See #987 for more info.\n */\n- lowerBoundary: null,\n+ alwaysInRange: null,\n \n /**\n- * APIProperty: upperBoundary\n- * {Number} or {String}\n- * upper boundary for between comparisons. In the case of a String, this\n- * can be a combination of text and propertyNames in the form\n- * \"literal ${propertyName}\"\n- */\n- upperBoundary: null,\n-\n- /** \n- * Constructor: OpenLayers.Filter.Comparison\n- * Creates a comparison rule.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\n- * {<OpenLayers.Filter.Comparison>}\n+ * Constant: RESOLUTION_PROPERTIES\n+ * {Array} The properties that are used for calculating resolutions\n+ * information.\n */\n- initialize: function(options) {\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n- // since matchCase on PropertyIsLike is not schema compliant, we only\n- // want to use this if explicitly asked for\n- if (this.type === OpenLayers.Filter.Comparison.LIKE &&\n- options.matchCase === undefined) {\n- this.matchCase = null;\n- }\n- },\n+ RESOLUTION_PROPERTIES: [\n+ 'scales', 'resolutions',\n+ 'maxScale', 'minScale',\n+ 'maxResolution', 'minResolution',\n+ 'numZoomLevels', 'maxZoomLevel'\n+ ],\n \n /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context.\n- * \n- * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n- * \n- * Returns:\n- * {Boolean} The filter applies.\n+ * APIProperty: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported map event types:\n+ * loadstart - Triggered when layer loading starts. When using a Vector \n+ * layer with a Fixed or BBOX strategy, the event object includes \n+ * a *filter* property holding the OpenLayers.Filter used when \n+ * calling read on the protocol.\n+ * loadend - Triggered when layer loading ends. When using a Vector layer\n+ * with a Fixed or BBOX strategy, the event object includes a \n+ * *response* property holding an OpenLayers.Protocol.Response object.\n+ * visibilitychanged - Triggered when the layer's visibility property is\n+ * changed, e.g. by turning the layer on or off in the layer switcher.\n+ * Note that the actual visibility of the layer can also change if it\n+ * gets out of range (see <calculateInRange>). If you also want to catch\n+ * these cases, register for the map's 'changelayer' event instead.\n+ * move - Triggered when layer moves (triggered with every mousemove\n+ * during a drag).\n+ * moveend - Triggered when layer is done moving, object passed as\n+ * argument has a zoomChanged boolean property which tells that the\n+ * zoom has changed.\n+ * added - Triggered after the layer is added to a map. Listeners will\n+ * receive an object with a *map* property referencing the map and a\n+ * *layer* property referencing the layer.\n+ * removed - Triggered after the layer is removed from the map. Listeners\n+ * will receive an object with a *map* property referencing the map and\n+ * a *layer* property referencing the layer.\n */\n- evaluate: function(context) {\n- if (context instanceof OpenLayers.Feature.Vector) {\n- context = context.attributes;\n- }\n- var result = false;\n- var got = context[this.property];\n- var exp;\n- switch (this.type) {\n- case OpenLayers.Filter.Comparison.EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase &&\n- typeof got == \"string\" && typeof exp == \"string\") {\n- result = (got.toUpperCase() == exp.toUpperCase());\n- } else {\n- result = (got == exp);\n- }\n- break;\n- case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase &&\n- typeof got == \"string\" && typeof exp == \"string\") {\n- result = (got.toUpperCase() != exp.toUpperCase());\n- } else {\n- result = (got != exp);\n- }\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN:\n- result = got < this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN:\n- result = got > this.value;\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n- result = got <= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n- result = got >= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.BETWEEN:\n- result = (got >= this.lowerBoundary) &&\n- (got <= this.upperBoundary);\n- break;\n- case OpenLayers.Filter.Comparison.LIKE:\n- var regexp = new RegExp(this.value, \"gi\");\n- result = regexp.test(got);\n- break;\n- case OpenLayers.Filter.Comparison.IS_NULL:\n- result = (got === null);\n- break;\n- }\n- return result;\n- },\n+ events: null,\n \n /**\n- * APIMethod: value2regex\n- * Converts the value of this rule into a regular expression string,\n- * according to the wildcard characters specified. This method has to\n- * be called after instantiation of this class, if the value is not a\n- * regular expression already.\n- * \n- * Parameters:\n- * wildCard - {Char} wildcard character in the above value, default\n- * is \"*\"\n- * singleChar - {Char} single-character wildcard in the above value\n- * default is \".\"\n- * escapeChar - {Char} escape character in the above value, default is\n- * \"!\"\n- * \n- * Returns:\n- * {String} regular expression string\n+ * APIProperty: map\n+ * {<OpenLayers.Map>} This variable is set when the layer is added to \n+ * the map, via the accessor function setMap().\n */\n- value2regex: function(wildCard, singleChar, escapeChar) {\n- if (wildCard == \".\") {\n- throw new Error(\"'.' is an unsupported wildCard character for \" +\n- \"OpenLayers.Filter.Comparison\");\n- }\n-\n-\n- // set UMN MapServer defaults for unspecified parameters\n- wildCard = wildCard ? wildCard : \"*\";\n- singleChar = singleChar ? singleChar : \".\";\n- escapeChar = escapeChar ? escapeChar : \"!\";\n-\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n- this.value = this.value.replace(\n- new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n-\n- return this.value;\n- },\n+ map: null,\n \n /**\n- * Method: regex2value\n- * Convert the value of this rule from a regular expression string into an\n- * ogc literal string using a wildCard of *, a singleChar of ., and an\n- * escape of !. Leaves the <value> property unmodified.\n- * \n- * Returns:\n- * {String} A string value.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Whether or not the layer is a base layer. This should be set \n+ * individually by all subclasses. Default is false\n */\n- regex2value: function() {\n-\n- var value = this.value;\n-\n- // replace ! with !!\n- value = value.replace(/!/g, \"!!\");\n-\n- // replace \\. with !. (watching out for \\\\.)\n- value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n- return $1 ? $0 : \"!.\";\n- });\n-\n- // replace \\* with #* (watching out for \\\\*)\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"!*\";\n- });\n-\n- // replace \\\\ with \\\n- value = value.replace(/\\\\\\\\/g, \"\\\\\");\n-\n- // convert .* to * (the sequence #.* is not allowed)\n- value = value.replace(/\\.\\*/g, \"*\");\n-\n- return value;\n- },\n+ isBaseLayer: false,\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\n- * \n- * Returns:\n- * {<OpenLayers.Filter.Comparison>} Clone of this filter.\n+ * Property: alpha\n+ * {Boolean} The layer's images have an alpha channel. Default is false.\n */\n- clone: function() {\n- return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n-});\n-\n-\n-OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n-OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n-OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n-OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n-OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n-OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n-OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n-OpenLayers.Filter.Comparison.LIKE = \"~\";\n-OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n-/* ======================================================================\n- OpenLayers/Popup.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-\n-/**\n- * Class: OpenLayers.Popup\n- * A popup is a small div that can opened and closed on the map.\n- * Typically opened in response to clicking on a marker. \n- * See <OpenLayers.Marker>. Popup's don't require their own\n- * layer and are added the the map using the <OpenLayers.Map.addPopup>\n- * method.\n- *\n- * Example:\n- * (code)\n- * popup = new OpenLayers.Popup(\"chicken\", \n- * new OpenLayers.LonLat(5,40),\n- * new OpenLayers.Size(200,200),\n- * \"example popup\",\n- * true);\n- * \n- * map.addPopup(popup);\n- * (end)\n- */\n-OpenLayers.Popup = OpenLayers.Class({\n+ alpha: false,\n \n /** \n- * Property: events \n- * {<OpenLayers.Events>} custom event manager \n+ * APIProperty: displayInLayerSwitcher\n+ * {Boolean} Display the layer's name in the layer switcher. Default is\n+ * true.\n */\n- events: null,\n+ displayInLayerSwitcher: true,\n \n- /** Property: id\n- * {String} the unique identifier assigned to this popup.\n+ /**\n+ * APIProperty: visibility\n+ * {Boolean} The layer should be displayed in the map. Default is true.\n */\n- id: \"\",\n+ visibility: true,\n \n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} the position of this popup on the map\n+ /**\n+ * APIProperty: attribution\n+ * {String} Attribution string, displayed when an \n+ * <OpenLayers.Control.Attribution> has been added to the map.\n */\n- lonlat: null,\n+ attribution: null,\n \n /** \n- * Property: div \n- * {DOMElement} the div that contains this popup.\n+ * Property: inRange\n+ * {Boolean} The current map resolution is within the layer's min/max \n+ * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom \n+ * changes.\n */\n- div: null,\n+ inRange: false,\n \n- /** \n- * Property: contentSize \n- * {<OpenLayers.Size>} the width and height of the content.\n+ /**\n+ * Propery: imageSize\n+ * {<OpenLayers.Size>} For layers with a gutter, the image is larger than \n+ * the tile by twice the gutter in each dimension.\n */\n- contentSize: null,\n+ imageSize: null,\n \n- /** \n- * Property: size \n- * {<OpenLayers.Size>} the width and height of the popup.\n- */\n- size: null,\n+ // OPTIONS\n \n /** \n- * Property: contentHTML \n- * {String} An HTML string for this popup to display.\n+ * Property: options\n+ * {Object} An optional object whose properties will be set on the layer.\n+ * Any of the layer properties can be set as a property of the options\n+ * object and sent to the constructor when the layer is created.\n */\n- contentHTML: null,\n+ options: null,\n \n- /** \n- * Property: backgroundColor \n- * {String} the background color used by the popup.\n+ /**\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n */\n- backgroundColor: \"\",\n+ eventListeners: null,\n \n- /** \n- * Property: opacity \n- * {float} the opacity of this popup (between 0.0 and 1.0)\n+ /**\n+ * APIProperty: gutter\n+ * {Integer} Determines the width (in pixels) of the gutter around image\n+ * tiles to ignore. By setting this property to a non-zero value,\n+ * images will be requested that are wider and taller than the tile\n+ * size by a value of 2 x gutter. This allows artifacts of rendering\n+ * at tile edges to be ignored. Set a gutter value that is equal to\n+ * half the size of the widest symbol that needs to be displayed.\n+ * Defaults to zero. Non-tiled layers always have zero gutter.\n */\n- opacity: \"\",\n+ gutter: 0,\n \n- /** \n- * Property: border \n- * {String} the border size of the popup. (eg 2px)\n+ /**\n+ * APIProperty: projection\n+ * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.\n+ * Can be set in the layer options. If not specified in the layer options,\n+ * it is set to the default projection specified in the map,\n+ * when the layer is added to the map.\n+ * Projection along with default maxExtent and resolutions\n+ * are set automatically with commercial baselayers in EPSG:3857,\n+ * such as Google, Bing and OpenStreetMap, and do not need to be specified.\n+ * Otherwise, if specifying projection, also set maxExtent,\n+ * maxResolution or resolutions as appropriate.\n+ * When using vector layers with strategies, layer projection should be set\n+ * to the projection of the source data if that is different from the map default.\n+ * \n+ * Can be either a string or an <OpenLayers.Projection> object;\n+ * if a string is passed, will be converted to an object when\n+ * the layer is added to the map.\n+ * \n */\n- border: \"\",\n+ projection: null,\n \n- /** \n- * Property: contentDiv \n- * {DOMElement} a reference to the element that holds the content of\n- * the div.\n+ /**\n+ * APIProperty: units\n+ * {String} The layer map units. Defaults to null. Possible values\n+ * are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.\n+ * Normally taken from the projection.\n+ * Only required if both map and layers do not define a projection,\n+ * or if they define a projection which does not define units.\n */\n- contentDiv: null,\n+ units: null,\n \n- /** \n- * Property: groupDiv \n- * {DOMElement} First and only child of 'div'. The group Div contains the\n- * 'contentDiv' and the 'closeDiv'.\n+ /**\n+ * APIProperty: scales\n+ * {Array} An array of map scales in descending order. The values in the\n+ * array correspond to the map scale denominator. Note that these\n+ * values only make sense if the display (monitor) resolution of the\n+ * client is correctly guessed by whomever is configuring the\n+ * application. In addition, the units property must also be set.\n+ * Use <resolutions> instead wherever possible.\n */\n- groupDiv: null,\n+ scales: null,\n \n- /** \n- * Property: closeDiv\n- * {DOMElement} the optional closer image\n+ /**\n+ * APIProperty: resolutions\n+ * {Array} A list of map resolutions (map units per pixel) in descending\n+ * order. If this is not set in the layer constructor, it will be set\n+ * based on other resolution related properties (maxExtent,\n+ * maxResolution, maxScale, etc.).\n */\n- closeDiv: null,\n+ resolutions: null,\n \n- /** \n- * APIProperty: autoSize\n- * {Boolean} Resize the popup to auto-fit the contents.\n- * Default is false.\n+ /**\n+ * APIProperty: maxExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The maximum extent for the layer. Defaults to null.\n+ * \n+ * The center of these bounds will not stray outside\n+ * of the viewport extent during panning. In addition, if\n+ * <displayOutsideMaxExtent> is set to false, data will not be\n+ * requested that falls completely outside of these bounds.\n */\n- autoSize: false,\n+ maxExtent: null,\n \n /**\n- * APIProperty: minSize\n- * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.\n+ * APIProperty: minExtent\n+ * {<OpenLayers.Bounds>|Array} If provided as an array, the array\n+ * should consist of four values (left, bottom, right, top).\n+ * The minimum extent for the layer. Defaults to null.\n */\n- minSize: null,\n+ minExtent: null,\n \n /**\n- * APIProperty: maxSize\n- * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.\n+ * APIProperty: maxResolution\n+ * {Float} Default max is 360 deg / 256 px, which corresponds to\n+ * zoom level 0 on gmaps. Specify a different value in the layer \n+ * options if you are not using the default <OpenLayers.Map.tileSize>\n+ * and displaying the whole world.\n */\n- maxSize: null,\n+ maxResolution: null,\n \n- /** \n- * Property: displayClass\n- * {String} The CSS class of the popup.\n+ /**\n+ * APIProperty: minResolution\n+ * {Float}\n */\n- displayClass: \"olPopup\",\n+ minResolution: null,\n \n- /** \n- * Property: contentDisplayClass\n- * {String} The CSS class of the popup content div.\n+ /**\n+ * APIProperty: numZoomLevels\n+ * {Integer}\n */\n- contentDisplayClass: \"olPopupContent\",\n+ numZoomLevels: null,\n \n- /** \n- * Property: padding \n- * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal \n- * padding of the content div inside the popup. This was originally\n- * confused with the css padding as specified in style.css's \n- * 'olPopupContent' class. We would like to get rid of this altogether,\n- * except that it does come in handy for the framed and anchoredbubble\n- * popups, who need to maintain yet another barrier between their \n- * content and the outer border of the popup itself. \n- * \n- * Note that in order to not break API, we must continue to support \n- * this property being set as an integer. Really, though, we'd like to \n- * have this specified as a Bounds object so that user can specify\n- * distinct left, top, right, bottom paddings. With the 3.0 release\n- * we can make this only a bounds.\n+ /**\n+ * APIProperty: minScale\n+ * {Float}\n */\n- padding: 0,\n+ minScale: null,\n \n- /** \n- * Property: disableFirefoxOverflowHack\n- * {Boolean} The hack for overflow in Firefox causes all elements \n- * to be re-drawn, which causes Flash elements to be \n- * re-initialized, which is troublesome.\n- * With this property the hack can be disabled.\n+ /**\n+ * APIProperty: maxScale\n+ * {Float}\n */\n- disableFirefoxOverflowHack: false,\n+ maxScale: null,\n \n /**\n- * Method: fixPadding\n- * To be removed in 3.0, this function merely helps us to deal with the \n- * case where the user may have set an integer value for padding, \n- * instead of an <OpenLayers.Bounds> object.\n+ * APIProperty: displayOutsideMaxExtent\n+ * {Boolean} Request map tiles that are completely outside of the max \n+ * extent for this layer. Defaults to false.\n */\n- fixPadding: function() {\n- if (typeof this.padding == \"number\") {\n- this.padding = new OpenLayers.Bounds(\n- this.padding, this.padding, this.padding, this.padding\n- );\n- }\n- },\n+ displayOutsideMaxExtent: false,\n \n /**\n- * APIProperty: panMapIfOutOfView\n- * {Boolean} When drawn, pan map such that the entire popup is visible in\n- * the current viewport (if necessary).\n- * Default is false.\n+ * APIProperty: wrapDateLine\n+ * {Boolean} Wraps the world at the international dateline, so the map can\n+ * be panned infinitely in longitudinal direction. Only use this on the\n+ * base layer, and only if the layer's maxExtent equals the world bounds.\n+ * #487 for more info. \n */\n- panMapIfOutOfView: false,\n+ wrapDateLine: false,\n \n /**\n- * APIProperty: keepInMap \n- * {Boolean} If panMapIfOutOfView is false, and this property is true, \n- * contrain the popup such that it always fits in the available map\n- * space. By default, this is not set on the base class. If you are\n- * creating popups that are near map edges and not allowing pannning,\n- * and especially if you have a popup which has a\n- * fixedRelativePosition, setting this to false may be a smart thing to\n- * do. Subclasses may want to override this setting.\n- * \n- * Default is false.\n+ * Property: metadata\n+ * {Object} This object can be used to store additional information on a\n+ * layer object.\n */\n- keepInMap: false,\n+ metadata: null,\n \n /**\n- * APIProperty: closeOnMove\n- * {Boolean} When map pans, close the popup.\n- * Default is false.\n+ * Constructor: OpenLayers.Layer\n+ *\n+ * Parameters:\n+ * name - {String} The layer name\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- closeOnMove: false,\n+ initialize: function(name, options) {\n \n- /** \n- * Property: map \n- * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map\n- */\n- map: null,\n+ this.metadata = {};\n \n- /** \n- * Constructor: OpenLayers.Popup\n- * Create a popup.\n- * \n- * Parameters: \n- * id - {String} a unqiue identifier for this popup. If null is passed\n- * an identifier will be automatically generated. \n- * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will\n- * be shown.\n- * contentSize - {<OpenLayers.Size>} The size of the content.\n- * contentHTML - {String} An HTML string to display inside the \n- * popup.\n- * closeBox - {Boolean} Whether to display a close box inside\n- * the popup.\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n- if (id == null) {\n- id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ options = OpenLayers.Util.extend({}, options);\n+ // make sure we respect alwaysInRange if set on the prototype\n+ if (this.alwaysInRange != null) {\n+ options.alwaysInRange = this.alwaysInRange;\n }\n+ this.addOptions(options);\n \n- this.id = id;\n- this.lonlat = lonlat;\n+ this.name = name;\n \n- this.contentSize = (contentSize != null) ? contentSize :\n- new OpenLayers.Size(\n- OpenLayers.Popup.WIDTH,\n- OpenLayers.Popup.HEIGHT);\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n- }\n- this.backgroundColor = OpenLayers.Popup.COLOR;\n- this.opacity = OpenLayers.Popup.OPACITY;\n- this.border = OpenLayers.Popup.BORDER;\n+ if (this.id == null) {\n \n- this.div = OpenLayers.Util.createDiv(this.id, null, null,\n- null, null, null, \"hidden\");\n- this.div.className = this.displayClass;\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n \n- var groupDivId = this.id + \"_GroupDiv\";\n- this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n- null, \"relative\", null,\n- \"hidden\");\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.style.width = \"100%\";\n+ this.div.style.height = \"100%\";\n+ this.div.dir = \"ltr\";\n \n- var id = this.div.id + \"_contentDiv\";\n- this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n- null, \"relative\");\n- this.contentDiv.className = this.contentDisplayClass;\n- this.groupDiv.appendChild(this.contentDiv);\n- this.div.appendChild(this.groupDiv);\n+ this.events = new OpenLayers.Events(this, this.div);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners);\n+ }\n \n- if (closeBox) {\n- this.addCloseBox(closeBoxCallback);\n }\n-\n- this.registerEvents();\n },\n \n- /** \n+ /**\n * Method: destroy\n- * nullify references to prevent circular references and memory leaks\n+ * Destroy is a destructor: this is to alleviate cyclic references which\n+ * the Javascript garbage cleaner can not take care of on its own.\n+ *\n+ * Parameters:\n+ * setNewBaseLayer - {Boolean} Set a new base layer when this layer has\n+ * been destroyed. Default is true.\n */\n- destroy: function() {\n-\n- this.id = null;\n- this.lonlat = null;\n- this.size = null;\n- this.contentHTML = null;\n-\n- this.backgroundColor = null;\n- this.opacity = null;\n- this.border = null;\n-\n- if (this.closeOnMove && this.map) {\n- this.map.events.unregister(\"movestart\", this, this.hide);\n- }\n-\n- this.events.destroy();\n- this.events = null;\n-\n- if (this.closeDiv) {\n- OpenLayers.Event.stopObservingElement(this.closeDiv);\n- this.groupDiv.removeChild(this.closeDiv);\n+ destroy: function(setNewBaseLayer) {\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true;\n }\n- this.closeDiv = null;\n-\n- this.div.removeChild(this.groupDiv);\n- this.groupDiv = null;\n-\n if (this.map != null) {\n- this.map.removePopup(this);\n+ this.map.removeLayer(this, setNewBaseLayer);\n }\n+ this.projection = null;\n this.map = null;\n+ this.name = null;\n this.div = null;\n+ this.options = null;\n \n- this.autoSize = null;\n- this.minSize = null;\n- this.maxSize = null;\n- this.padding = null;\n- this.panMapIfOutOfView = null;\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ }\n+ this.events.destroy();\n+ }\n+ this.eventListeners = null;\n+ this.events = null;\n },\n \n- /** \n- * Method: draw\n- * Constructs the elements that make up the popup.\n+ /**\n+ * Method: clone\n *\n * Parameters:\n- * px - {<OpenLayers.Pixel>} the position the popup in pixels.\n- * \n+ * obj - {<OpenLayers.Layer>} The layer to be cloned\n+ *\n * Returns:\n- * {DOMElement} Reference to a div that contains the drawn popup\n+ * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>\n */\n- draw: function(px) {\n- if (px == null) {\n- if ((this.lonlat != null) && (this.map != null)) {\n- px = this.map.getLayerPxFromLonLat(this.lonlat);\n- }\n- }\n+ clone: function(obj) {\n \n- // this assumes that this.map already exists, which is okay because \n- // this.draw is only called once the popup has been added to the map.\n- if (this.closeOnMove) {\n- this.map.events.register(\"movestart\", this, this.hide);\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer(this.name, this.getOptions());\n }\n \n- //listen to movestart, moveend to disable overflow (FF bug)\n- if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n- this.map.events.register(\"movestart\", this, function() {\n- var style = document.defaultView.getComputedStyle(\n- this.contentDiv, null\n- );\n- var currentOverflow = style.getPropertyValue(\"overflow\");\n- if (currentOverflow != \"hidden\") {\n- this.contentDiv._oldOverflow = currentOverflow;\n- this.contentDiv.style.overflow = \"hidden\";\n- }\n- });\n- this.map.events.register(\"moveend\", this, function() {\n- var oldOverflow = this.contentDiv._oldOverflow;\n- if (oldOverflow) {\n- this.contentDiv.style.overflow = oldOverflow;\n- this.contentDiv._oldOverflow = null;\n- }\n- });\n- }\n+ // catch any randomly tagged-on properties\n+ OpenLayers.Util.applyDefaults(obj, this);\n \n- this.moveTo(px);\n- if (!this.autoSize && !this.size) {\n- this.setSize(this.contentSize);\n- }\n- this.setBackgroundColor();\n- this.setOpacity();\n- this.setBorder();\n- this.setContentHTML();\n+ // a cloned layer should never have its map property set\n+ // because it has not been added to a map yet. \n+ obj.map = null;\n \n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n- }\n+ return obj;\n+ },\n \n- return this.div;\n+ /**\n+ * Method: getOptions\n+ * Extracts an object from the layer with the properties that were set as\n+ * options, but updates them with the values currently set on the\n+ * instance.\n+ * \n+ * Returns:\n+ * {Object} the <options> of the layer, representing the current state.\n+ */\n+ getOptions: function() {\n+ var options = {};\n+ for (var o in this.options) {\n+ options[o] = this[o];\n+ }\n+ return options;\n },\n \n /** \n- * Method: updatePosition\n- * if the popup has a lonlat and its map members set, \n- * then have it move itself to its proper position\n+ * APIMethod: setName\n+ * Sets the new layer name for this layer. Can trigger a changelayer event\n+ * on the map.\n+ *\n+ * Parameters:\n+ * newName - {String} The new name.\n */\n- updatePosition: function() {\n- if ((this.lonlat) && (this.map)) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- if (px) {\n- this.moveTo(px);\n+ setName: function(newName) {\n+ if (newName != this.name) {\n+ this.name = newName;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"name\"\n+ });\n }\n }\n },\n \n /**\n- * Method: moveTo\n+ * APIMethod: addOptions\n * \n * Parameters:\n- * px - {<OpenLayers.Pixel>} the top and left position of the popup div. \n+ * newOptions - {Object}\n+ * reinitialize - {Boolean} If set to true, and if resolution options of the\n+ * current baseLayer were changed, the map will be recentered to make\n+ * sure that it is displayed with a valid resolution, and a\n+ * changebaselayer event will be triggered.\n */\n- moveTo: function(px) {\n- if ((px != null) && (this.div != null)) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\";\n+ addOptions: function(newOptions, reinitialize) {\n+\n+ if (this.options == null) {\n+ this.options = {};\n }\n- },\n \n- /**\n- * Method: visible\n- *\n- * Returns: \n- * {Boolean} Boolean indicating whether or not the popup is visible\n- */\n- visible: function() {\n- return OpenLayers.Element.visible(this.div);\n- },\n+ if (newOptions) {\n+ // make sure this.projection references a projection object\n+ if (typeof newOptions.projection == \"string\") {\n+ newOptions.projection = new OpenLayers.Projection(newOptions.projection);\n+ }\n+ if (newOptions.projection) {\n+ // get maxResolution, units and maxExtent from projection defaults if\n+ // they are not defined already\n+ OpenLayers.Util.applyDefaults(newOptions,\n+ OpenLayers.Projection.defaults[newOptions.projection.getCode()]);\n+ }\n+ // allow array for extents\n+ if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);\n+ }\n+ if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);\n+ }\n+ }\n \n- /**\n- * Method: toggle\n- * Toggles visibility of the popup.\n- */\n- toggle: function() {\n- if (this.visible()) {\n- this.hide();\n- } else {\n- this.show();\n+ // update our copy for clone\n+ OpenLayers.Util.extend(this.options, newOptions);\n+\n+ // add new options to this\n+ OpenLayers.Util.extend(this, newOptions);\n+\n+ // get the units from the projection, if we have a projection\n+ // and it it has units\n+ if (this.projection && this.projection.getUnits()) {\n+ this.units = this.projection.getUnits();\n }\n- },\n \n- /**\n- * Method: show\n- * Makes the popup visible.\n- */\n- show: function() {\n- this.div.style.display = '';\n+ // re-initialize resolutions if necessary, i.e. if any of the\n+ // properties of the \"properties\" array defined below is set\n+ // in the new options\n+ if (this.map) {\n+ // store current resolution so we can try to restore it later\n+ var resolution = this.map.getResolution();\n+ var properties = this.RESOLUTION_PROPERTIES.concat(\n+ [\"projection\", \"units\", \"minExtent\", \"maxExtent\"]\n+ );\n+ for (var o in newOptions) {\n+ if (newOptions.hasOwnProperty(o) &&\n+ OpenLayers.Util.indexOf(properties, o) >= 0) {\n \n- if (this.panMapIfOutOfView) {\n- this.panIntoView();\n+ this.initResolutions();\n+ if (reinitialize && this.map.baseLayer === this) {\n+ // update map position, and restore previous resolution\n+ this.map.setCenter(this.map.getCenter(),\n+ this.map.getZoomForResolution(resolution),\n+ false, true\n+ );\n+ // trigger a changebaselayer event to make sure that\n+ // all controls (especially\n+ // OpenLayers.Control.PanZoomBar) get notified of the\n+ // new options\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ });\n+ }\n+ break;\n+ }\n+ }\n }\n },\n \n /**\n- * Method: hide\n- * Makes the popup invisible.\n+ * APIMethod: onMapResize\n+ * This function can be implemented by subclasses\n */\n- hide: function() {\n- this.div.style.display = 'none';\n+ onMapResize: function() {\n+ //this function can be implemented by subclasses \n },\n \n /**\n- * Method: setSize\n- * Used to adjust the size of the popup. \n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n *\n- * Parameters:\n- * contentSize - {<OpenLayers.Size>} the new size for the popup's \n- * contents div (in pixels).\n+ * Returns:\n+ * {Boolean} The layer was redrawn.\n */\n- setSize: function(contentSize) {\n- this.size = contentSize.clone();\n-\n- // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n- // must add that to the desired \"size\". \n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n-\n- // take into account the popup's 'padding' property\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n+ redraw: function() {\n+ var redrawn = false;\n+ if (this.map) {\n \n- // make extra space for the close div\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right;\n- }\n+ // min/max Range may have changed\n+ this.inRange = this.calculateInRange();\n \n- //increase size of the main popup div to take into account the \n- // users's desired padding and close div. \n- this.size.w += wPadding;\n- this.size.h += hPadding;\n+ // map's center might not yet be set\n+ var extent = this.getExtent();\n \n- //now if our browser is IE, we need to actually make the contents \n- // div itself bigger to take its own padding into effect. this makes \n- // me want to shoot someone, but so it goes.\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.contentSize.w +=\n- contentDivPadding.left + contentDivPadding.right;\n- this.contentSize.h +=\n- contentDivPadding.bottom + contentDivPadding.top;\n+ if (extent && this.inRange && this.visibility) {\n+ var zoomChanged = true;\n+ this.moveTo(extent, zoomChanged, false);\n+ this.events.triggerEvent(\"moveend\", {\n+ \"zoomChanged\": zoomChanged\n+ });\n+ redrawn = true;\n+ }\n }\n+ return redrawn;\n+ },\n \n- if (this.div != null) {\n- this.div.style.width = this.size.w + \"px\";\n- this.div.style.height = this.size.h + \"px\";\n- }\n- if (this.contentDiv != null) {\n- this.contentDiv.style.width = contentSize.w + \"px\";\n- this.contentDiv.style.height = contentSize.h + \"px\";\n+ /**\n+ * Method: moveTo\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n+ * do some init work in that case.\n+ * dragging - {Boolean}\n+ */\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ var display = this.visibility;\n+ if (!this.isBaseLayer) {\n+ display = display && this.inRange;\n }\n+ this.display(display);\n },\n \n /**\n- * APIMethod: updateSize\n- * Auto size the popup so that it precisely fits its contents (as \n- * determined by this.contentDiv.innerHTML). Popup size will, of\n- * course, be limited by the available space on the current map\n+ * Method: moveByPx\n+ * Move the layer based on pixel vector. To be implemented by subclasses.\n+ *\n+ * Parameters:\n+ * dx - {Number} The x coord of the displacement vector.\n+ * dy - {Number} The y coord of the displacement vector.\n */\n- updateSize: function() {\n-\n- // determine actual render dimensions of the contents by putting its\n- // contents into a fake contentDiv (for the CSS) and then measuring it\n- var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" +\n- this.contentDiv.innerHTML +\n- \"</div>\";\n-\n- var containerElement = (this.map) ? this.map.div : document.body;\n- var realSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, null, {\n- displayClass: this.displayClass,\n- containerElement: containerElement\n- }\n- );\n+ moveByPx: function(dx, dy) {},\n \n- // is the \"real\" size of the div is safe to display in our map?\n- var safeSize = this.getSafeContentSize(realSize);\n+ /**\n+ * Method: setMap\n+ * Set the map property for the layer. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n+ * \n+ * Here we take care to bring over any of the necessary default \n+ * properties from the map. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ if (this.map == null) {\n \n- var newSize = null;\n- if (safeSize.equals(realSize)) {\n- //real size of content is small enough to fit on the map, \n- // so we use real size.\n- newSize = realSize;\n+ this.map = map;\n \n- } else {\n+ // grab some essential layer data from the map if it hasn't already\n+ // been set\n+ this.maxExtent = this.maxExtent || this.map.maxExtent;\n+ this.minExtent = this.minExtent || this.map.minExtent;\n \n- // make a new 'size' object with the clipped dimensions \n- // set or null if not clipped.\n- var fixedSize = {\n- w: (safeSize.w < realSize.w) ? safeSize.w : null,\n- h: (safeSize.h < realSize.h) ? safeSize.h : null\n- };\n+ this.projection = this.projection || this.map.projection;\n+ if (typeof this.projection == \"string\") {\n+ this.projection = new OpenLayers.Projection(this.projection);\n+ }\n \n- if (fixedSize.w && fixedSize.h) {\n- //content is too big in both directions, so we will use \n- // max popup size (safeSize), knowing well that it will \n- // overflow both ways. \n- newSize = safeSize;\n- } else {\n- //content is clipped in only one direction, so we need to \n- // run getRenderedDimensions() again with a fixed dimension\n- var clippedSize = OpenLayers.Util.getRenderedDimensions(\n- preparedHTML, fixedSize, {\n- displayClass: this.contentDisplayClass,\n- containerElement: containerElement\n- }\n- );\n+ // Check the projection to see if we can get units -- if not, refer\n+ // to properties.\n+ this.units = this.projection.getUnits() ||\n+ this.units || this.map.units;\n \n- //if the clipped size is still the same as the safeSize, \n- // that means that our content must be fixed in the \n- // offending direction. If overflow is 'auto', this means \n- // we are going to have a scrollbar for sure, so we must \n- // adjust for that.\n- //\n- var currentOverflow = OpenLayers.Element.getStyle(\n- this.contentDiv, \"overflow\"\n- );\n- if ((currentOverflow != \"hidden\") &&\n- (clippedSize.equals(safeSize))) {\n- var scrollBar = OpenLayers.Util.getScrollbarWidth();\n- if (fixedSize.w) {\n- clippedSize.h += scrollBar;\n- } else {\n- clippedSize.w += scrollBar;\n- }\n- }\n+ this.initResolutions();\n \n- newSize = this.getSafeContentSize(clippedSize);\n+ if (!this.isBaseLayer) {\n+ this.inRange = this.calculateInRange();\n+ var show = ((this.visibility) && (this.inRange));\n+ this.div.style.display = show ? \"\" : \"none\";\n }\n+\n+ // deal with gutters\n+ this.setTileSize();\n }\n- this.setSize(newSize);\n },\n \n /**\n- * Method: setBackgroundColor\n- * Sets the background color of the popup.\n- *\n- * Parameters:\n- * color - {String} the background color. eg \"#FFBBBB\"\n+ * Method: afterAdd\n+ * Called at the end of the map.addLayer sequence. At this point, the map\n+ * will have a base layer. To be overridden by subclasses.\n */\n- setBackgroundColor: function(color) {\n- if (color != undefined) {\n- this.backgroundColor = color;\n- }\n-\n- if (this.div != null) {\n- this.div.style.backgroundColor = this.backgroundColor;\n- }\n- },\n+ afterAdd: function() {},\n \n /**\n- * Method: setOpacity\n- * Sets the opacity of the popup.\n+ * APIMethod: removeMap\n+ * Just as setMap() allows each layer the possibility to take a \n+ * personalized action on being added to the map, removeMap() allows\n+ * each layer to take a personalized action on being removed from it. \n+ * For now, this will be mostly unused, except for the EventPane layer,\n+ * which needs this hook so that it can remove the special invisible\n+ * pane. \n * \n * Parameters:\n- * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ * map - {<OpenLayers.Map>}\n */\n- setOpacity: function(opacity) {\n- if (opacity != undefined) {\n- this.opacity = opacity;\n- }\n-\n- if (this.div != null) {\n- // for Mozilla and Safari\n- this.div.style.opacity = this.opacity;\n-\n- // for IE\n- this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n- }\n+ removeMap: function(map) {\n+ //to be overridden by subclasses\n },\n \n /**\n- * Method: setBorder\n- * Sets the border style of the popup.\n+ * APIMethod: getImageSize\n *\n * Parameters:\n- * border - {String} The border style value. eg 2px \n+ * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used\n+ * by subclasses that have to deal with different tile sizes at the\n+ * layer extent edges (e.g. Zoomify)\n+ * \n+ * Returns:\n+ * {<OpenLayers.Size>} The size that the image should be, taking into \n+ * account gutters.\n */\n- setBorder: function(border) {\n- if (border != undefined) {\n- this.border = border;\n- }\n-\n- if (this.div != null) {\n- this.div.style.border = this.border;\n- }\n+ getImageSize: function(bounds) {\n+ return (this.imageSize || this.tileSize);\n },\n \n /**\n- * Method: setContentHTML\n- * Allows the user to set the HTML content of the popup.\n- *\n+ * APIMethod: setTileSize\n+ * Set the tile size based on the map size. This also sets layer.imageSize\n+ * or use by Tile.Image.\n+ * \n * Parameters:\n- * contentHTML - {String} HTML for the div.\n+ * size - {<OpenLayers.Size>}\n */\n- setContentHTML: function(contentHTML) {\n-\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML;\n+ setTileSize: function(size) {\n+ var tileSize = (size) ? size :\n+ ((this.tileSize) ? this.tileSize :\n+ this.map.getTileSize());\n+ this.tileSize = tileSize;\n+ if (this.gutter) {\n+ // layers with gutters need non-null tile sizes\n+ //if(tileSize == null) {\n+ // OpenLayers.console.error(\"Error in layer.setMap() for \" +\n+ // this.name + \": layers with \" +\n+ // \"gutters need non-null tile sizes\");\n+ //}\n+ this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter),\n+ tileSize.h + (2 * this.gutter));\n }\n+ },\n \n- if ((this.contentDiv != null) &&\n- (this.contentHTML != null) &&\n- (this.contentHTML != this.contentDiv.innerHTML)) {\n-\n- this.contentDiv.innerHTML = this.contentHTML;\n-\n- if (this.autoSize) {\n-\n- //if popup has images, listen for when they finish\n- // loading and resize accordingly\n- this.registerImageListeners();\n+ /**\n+ * APIMethod: getVisibility\n+ * \n+ * Returns:\n+ * {Boolean} The layer should be displayed (if in range).\n+ */\n+ getVisibility: function() {\n+ return this.visibility;\n+ },\n \n- //auto size the popup to its current contents\n- this.updateSize();\n+ /** \n+ * APIMethod: setVisibility\n+ * Set the visibility flag for the layer and hide/show & redraw \n+ * accordingly. Fire event unless otherwise specified\n+ * \n+ * Note that visibility is no longer simply whether or not the layer's\n+ * style.display is set to \"block\". Now we store a 'visibility' state \n+ * property on the layer class, this allows us to remember whether or \n+ * not we *desire* for a layer to be visible. In the case where the \n+ * map's resolution is out of the layer's range, this desire may be \n+ * subverted.\n+ * \n+ * Parameters:\n+ * visibility - {Boolean} Whether or not to display the layer (if in range)\n+ */\n+ setVisibility: function(visibility) {\n+ if (visibility != this.visibility) {\n+ this.visibility = visibility;\n+ this.display(visibility);\n+ this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"visibility\"\n+ });\n }\n+ this.events.triggerEvent(\"visibilitychanged\");\n }\n+ },\n \n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer. This is designed to be used internally, and \n+ * is not generally the way to enable or disable the layer. For that,\n+ * use the setVisibility function instead..\n+ * \n+ * Parameters:\n+ * display - {Boolean}\n+ */\n+ display: function(display) {\n+ if (display != (this.div.style.display != \"none\")) {\n+ this.div.style.display = (display && this.calculateInRange()) ? \"block\" : \"none\";\n+ }\n },\n \n /**\n- * Method: registerImageListeners\n- * Called when an image contained by the popup loaded. this function\n- * updates the popup size, then unregisters the image load listener.\n- */\n+ * APIMethod: calculateInRange\n+ * \n+ * Returns:\n+ * {Boolean} The layer is displayable at the current map's current\n+ * resolution. Note that if 'alwaysInRange' is true for the layer, \n+ * this function will always return true.\n+ */\n+ calculateInRange: function() {\n+ var inRange = false;\n+\n+ if (this.alwaysInRange) {\n+ inRange = true;\n+ } else {\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ inRange = ((resolution >= this.minResolution) &&\n+ (resolution <= this.maxResolution));\n+ }\n+ }\n+ return inRange;\n+ },\n+\n+ /** \n+ * APIMethod: setIsBaseLayer\n+ * \n+ * Parameters:\n+ * isBaseLayer - {Boolean}\n+ */\n+ setIsBaseLayer: function(isBaseLayer) {\n+ if (isBaseLayer != this.isBaseLayer) {\n+ this.isBaseLayer = isBaseLayer;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ });\n+ }\n+ }\n+ },\n+\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /********************************************************/\n+\n+ /** \n+ * Method: initResolutions\n+ * This method's responsibility is to set up the 'resolutions' array \n+ * for the layer -- this array is what the layer will use to interface\n+ * between the zoom levels of the map and the resolution display \n+ * of the layer.\n+ * \n+ * The user has several options that determine how the array is set up.\n+ * \n+ * For a detailed explanation, see the following wiki from the \n+ * openlayers.org homepage:\n+ * http://trac.openlayers.org/wiki/SettingZoomLevels\n+ */\n+ initResolutions: function() {\n+\n+ // ok we want resolutions, here's our strategy:\n+ //\n+ // 1. if resolutions are defined in the layer config, use them\n+ // 2. else, if scales are defined in the layer config then derive\n+ // resolutions from these scales\n+ // 3. else, attempt to calculate resolutions from maxResolution,\n+ // minResolution, numZoomLevels, maxZoomLevel set in the\n+ // layer config\n+ // 4. if we still don't have resolutions, and if resolutions\n+ // are defined in the same, use them\n+ // 5. else, if scales are defined in the map then derive\n+ // resolutions from these scales\n+ // 6. else, attempt to calculate resolutions from maxResolution,\n+ // minResolution, numZoomLevels, maxZoomLevel set in the\n+ // map\n+ // 7. hope for the best!\n+\n+ var i, len, p;\n+ var props = {},\n+ alwaysInRange = true;\n+\n+ // get resolution data from layer config\n+ // (we also set alwaysInRange in the layer as appropriate)\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p];\n+ if (alwaysInRange && this.options[p]) {\n+ alwaysInRange = false;\n+ }\n+ }\n+ if (this.options.alwaysInRange == null) {\n+ this.alwaysInRange = alwaysInRange;\n+ }\n+\n+ // if we don't have resolutions then attempt to derive them from scales\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales);\n+ }\n+\n+ // if we still don't have resolutions then attempt to calculate them\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props);\n+ }\n+\n+ // if we couldn't calculate resolutions then we look at we have\n+ // in the map\n+ if (props.resolutions == null) {\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p] != null ?\n+ this.options[p] : this.map[p];\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales);\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props);\n+ }\n+ }\n+\n+ // ok, we new need to set properties in the instance\n+\n+ // get maxResolution from the config if it's defined there\n+ var maxResolution;\n+ if (this.options.maxResolution &&\n+ this.options.maxResolution !== \"auto\") {\n+ maxResolution = this.options.maxResolution;\n+ }\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.minScale, this.units);\n+ }\n+\n+ // get minResolution from the config if it's defined there\n+ var minResolution;\n+ if (this.options.minResolution &&\n+ this.options.minResolution !== \"auto\") {\n+ minResolution = this.options.minResolution;\n+ }\n+ if (this.options.maxScale) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(\n+ this.options.maxScale, this.units);\n+ }\n+\n+ if (props.resolutions) {\n+\n+ //sort resolutions array descendingly\n+ props.resolutions.sort(function(a, b) {\n+ return (b - a);\n+ });\n+\n+ // if we still don't have a maxResolution get it from the\n+ // resolutions array\n+ if (!maxResolution) {\n+ maxResolution = props.resolutions[0];\n+ }\n+\n+ // if we still don't have a minResolution get it from the\n+ // resolutions array\n+ if (!minResolution) {\n+ var lastIdx = props.resolutions.length - 1;\n+ minResolution = props.resolutions[lastIdx];\n+ }\n+ }\n+\n+ this.resolutions = props.resolutions;\n+ if (this.resolutions) {\n+ len = this.resolutions.length;\n+ this.scales = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ this.scales[i] = OpenLayers.Util.getScaleFromResolution(\n+ this.resolutions[i], this.units);\n+ }\n+ this.numZoomLevels = len;\n+ }\n+ this.minResolution = minResolution;\n+ if (minResolution) {\n+ this.maxScale = OpenLayers.Util.getScaleFromResolution(\n+ minResolution, this.units);\n+ }\n+ this.maxResolution = maxResolution;\n+ if (maxResolution) {\n+ this.minScale = OpenLayers.Util.getScaleFromResolution(\n+ maxResolution, this.units);\n+ }\n+ },\n+\n+ /**\n+ * Method: resolutionsFromScales\n+ * Derive resolutions from scales.\n+ *\n+ * Parameters:\n+ * scales - {Array(Number)} Scales\n+ *\n+ * Returns\n+ * {Array(Number)} Resolutions\n+ */\n+ resolutionsFromScales: function(scales) {\n+ if (scales == null) {\n+ return;\n+ }\n+ var resolutions, i, len;\n+ len = scales.length;\n+ resolutions = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ resolutions[i] = OpenLayers.Util.getResolutionFromScale(\n+ scales[i], this.units);\n+ }\n+ return resolutions;\n+ },\n+\n+ /**\n+ * Method: calculateResolutions\n+ * Calculate resolutions based on the provided properties.\n+ *\n+ * Parameters:\n+ * props - {Object} Properties\n+ *\n+ * Returns:\n+ * {Array({Number})} Array of resolutions.\n+ */\n+ calculateResolutions: function(props) {\n+\n+ var viewSize, wRes, hRes;\n+\n+ // determine maxResolution\n+ var maxResolution = props.maxResolution;\n+ if (props.minScale != null) {\n+ maxResolution =\n+ OpenLayers.Util.getResolutionFromScale(props.minScale,\n+ this.units);\n+ } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.maxExtent.getWidth() / viewSize.w;\n+ hRes = this.maxExtent.getHeight() / viewSize.h;\n+ maxResolution = Math.max(wRes, hRes);\n+ }\n+\n+ // determine minResolution\n+ var minResolution = props.minResolution;\n+ if (props.maxScale != null) {\n+ minResolution =\n+ OpenLayers.Util.getResolutionFromScale(props.maxScale,\n+ this.units);\n+ } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.minExtent.getWidth() / viewSize.w;\n+ hRes = this.minExtent.getHeight() / viewSize.h;\n+ minResolution = Math.max(wRes, hRes);\n+ }\n+\n+ if (typeof maxResolution !== \"number\" &&\n+ typeof minResolution !== \"number\" &&\n+ this.maxExtent != null) {\n+ // maxResolution for default grid sets assumes that at zoom\n+ // level zero, the whole world fits on one tile.\n+ var tileSize = this.map.getTileSize();\n+ maxResolution = Math.max(\n+ this.maxExtent.getWidth() / tileSize.w,\n+ this.maxExtent.getHeight() / tileSize.h\n+ );\n+ }\n+\n+ // determine numZoomLevels\n+ var maxZoomLevel = props.maxZoomLevel;\n+ var numZoomLevels = props.numZoomLevels;\n+ if (typeof minResolution === \"number\" &&\n+ typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n+ var ratio = maxResolution / minResolution;\n+ numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;\n+ } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n+ numZoomLevels = maxZoomLevel + 1;\n+ }\n+\n+ // are we able to calculate resolutions?\n+ if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 ||\n+ (typeof maxResolution !== \"number\" &&\n+ typeof minResolution !== \"number\")) {\n+ return;\n+ }\n+\n+ // now we have numZoomLevels and at least one of maxResolution\n+ // or minResolution, we can populate the resolutions array\n+\n+ var resolutions = new Array(numZoomLevels);\n+ var base = 2;\n+ if (typeof minResolution == \"number\" &&\n+ typeof maxResolution == \"number\") {\n+ // if maxResolution and minResolution are set, we calculate\n+ // the base for exponential scaling that starts at\n+ // maxResolution and ends at minResolution in numZoomLevels\n+ // steps.\n+ base = Math.pow(\n+ (maxResolution / minResolution),\n+ (1 / (numZoomLevels - 1))\n+ );\n+ }\n+\n+ var i;\n+ if (typeof maxResolution === \"number\") {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[i] = maxResolution / Math.pow(base, i);\n+ }\n+ } else {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[numZoomLevels - 1 - i] =\n+ minResolution * Math.pow(base, i);\n+ }\n+ }\n+\n+ return resolutions;\n+ },\n+\n+ /**\n+ * APIMethod: getResolution\n+ * \n+ * Returns:\n+ * {Float} The currently selected resolution of the map, taken from the\n+ * resolutions array, indexed by current zoom level.\n+ */\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom);\n+ },\n+\n+ /** \n+ * APIMethod: getExtent\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort.\n+ */\n+ getExtent: function() {\n+ // just use stock map calculateBounds function -- passing no arguments\n+ // means it will user map's current center & resolution\n+ //\n+ return this.map.calculateBounds();\n+ },\n+\n+ /**\n+ * APIMethod: getZoomForExtent\n+ * \n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * closest - {Boolean} Find the zoom level that most closely fits the \n+ * specified bounds. Note that this may result in a zoom that does \n+ * not exactly contain the entire extent.\n+ * Default is false.\n+ *\n+ * Returns:\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * for the passed-in extent. We do this by calculating the ideal \n+ * resolution for the given extent (based on the map size) and then \n+ * calling getZoomForResolution(), passing along the 'closest'\n+ * parameter.\n+ */\n+ getZoomForExtent: function(extent, closest) {\n+ var viewSize = this.map.getSize();\n+ var idealResolution = Math.max(extent.getWidth() / viewSize.w,\n+ extent.getHeight() / viewSize.h);\n+\n+ return this.getZoomForResolution(idealResolution, closest);\n+ },\n+\n+ /** \n+ * Method: getDataExtent\n+ * Calculates the max extent which includes all of the data for the layer.\n+ * This function is to be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>}\n+ */\n+ getDataExtent: function() {\n+ //to be implemented by subclasses\n+ },\n+\n+ /**\n+ * APIMethod: getResolutionForZoom\n+ * \n+ * Parameters:\n+ * zoom - {Float}\n+ * \n+ * Returns:\n+ * {Float} A suitable resolution for the specified zoom.\n+ */\n+ getResolutionForZoom: function(zoom) {\n+ zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n+ var resolution;\n+ if (this.map.fractionalZoom) {\n+ var low = Math.floor(zoom);\n+ var high = Math.ceil(zoom);\n+ resolution = this.resolutions[low] -\n+ ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));\n+ } else {\n+ resolution = this.resolutions[Math.round(zoom)];\n+ }\n+ return resolution;\n+ },\n+\n+ /**\n+ * APIMethod: getZoomForResolution\n+ * \n+ * Parameters:\n+ * resolution - {Float}\n+ * closest - {Boolean} Find the zoom level that corresponds to the absolute \n+ * closest resolution, which may result in a zoom whose corresponding\n+ * resolution is actually smaller than we would have desired (if this\n+ * is being called from a getZoomForExtent() call, then this means that\n+ * the returned zoom index might not actually contain the entire \n+ * extent specified... but it'll be close).\n+ * Default is false.\n+ * \n+ * Returns:\n+ * {Integer} The index of the zoomLevel (entry in the resolutions array) \n+ * that corresponds to the best fit resolution given the passed in \n+ * value and the 'closest' specification.\n+ */\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom, i, len;\n+ if (this.map.fractionalZoom) {\n+ var lowZoom = 0;\n+ var highZoom = this.resolutions.length - 1;\n+ var highRes = this.resolutions[lowZoom];\n+ var lowRes = this.resolutions[highZoom];\n+ var res;\n+ for (i = 0, len = this.resolutions.length; i < len; ++i) {\n+ res = this.resolutions[i];\n+ if (res >= resolution) {\n+ highRes = res;\n+ lowZoom = i;\n+ }\n+ if (res <= resolution) {\n+ lowRes = res;\n+ highZoom = i;\n+ break;\n+ }\n+ }\n+ var dRes = highRes - lowRes;\n+ if (dRes > 0) {\n+ zoom = lowZoom + ((highRes - resolution) / dRes);\n+ } else {\n+ zoom = lowZoom;\n+ }\n+ } else {\n+ var diff;\n+ var minDiff = Number.POSITIVE_INFINITY;\n+ for (i = 0, len = this.resolutions.length; i < len; i++) {\n+ if (closest) {\n+ diff = Math.abs(this.resolutions[i] - resolution);\n+ if (diff > minDiff) {\n+ break;\n+ }\n+ minDiff = diff;\n+ } else {\n+ if (this.resolutions[i] < resolution) {\n+ break;\n+ }\n+ }\n+ }\n+ zoom = Math.max(0, i - 1);\n+ }\n+ return zoom;\n+ },\n+\n+ /**\n+ * APIMethod: getLonLatFromViewPortPx\n+ * \n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or\n+ * an object with a 'x'\n+ * and 'y' properties.\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in \n+ * view port <OpenLayers.Pixel>, translated into lon/lat by the layer.\n+ */\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ var map = this.map;\n+ if (viewPortPx != null && map.minPx) {\n+ var res = map.getResolution();\n+ var maxExtent = map.getMaxExtent({\n+ restricted: true\n+ });\n+ var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n+ var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n+ lonlat = new OpenLayers.LonLat(lon, lat);\n+\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent);\n+ }\n+ }\n+ return lonlat;\n+ },\n+\n+ /**\n+ * APIMethod: getViewPortPxFromLonLat\n+ * Returns a pixel location given a map location. This method will return\n+ * fractional pixel values.\n+ * \n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or\n+ * an object with a 'lon'\n+ * and 'lat' properties.\n+ *\n+ * Returns: \n+ * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in \n+ * lonlat translated into view port pixels.\n+ */\n+ getViewPortPxFromLonLat: function(lonlat, resolution) {\n+ var px = null;\n+ if (lonlat != null) {\n+ resolution = resolution || this.map.getResolution();\n+ var extent = this.map.calculateBounds(null, resolution);\n+ px = new OpenLayers.Pixel(\n+ (1 / resolution * (lonlat.lon - extent.left)),\n+ (1 / resolution * (extent.top - lonlat.lat))\n+ );\n+ }\n+ return px;\n+ },\n+\n+ /**\n+ * APIMethod: setOpacity\n+ * Sets the opacity for the entire layer (all images)\n+ * \n+ * Parameters:\n+ * opacity - {Float}\n+ */\n+ setOpacity: function(opacity) {\n+ if (opacity != this.opacity) {\n+ this.opacity = opacity;\n+ var childNodes = this.div.childNodes;\n+ for (var i = 0, len = childNodes.length; i < len; ++i) {\n+ var element = childNodes[i].firstChild || childNodes[i];\n+ var lastChild = childNodes[i].lastChild;\n+ //TODO de-uglify this\n+ if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n+ element = lastChild.parentNode;\n+ }\n+ OpenLayers.Util.modifyDOMElement(element, null, null, null,\n+ null, null, null, opacity);\n+ }\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ });\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: getZIndex\n+ * \n+ * Returns: \n+ * {Integer} the z-index of this layer\n+ */\n+ getZIndex: function() {\n+ return this.div.style.zIndex;\n+ },\n+\n+ /**\n+ * Method: setZIndex\n+ * \n+ * Parameters: \n+ * zIndex - {Integer}\n+ */\n+ setZIndex: function(zIndex) {\n+ this.div.style.zIndex = zIndex;\n+ },\n+\n+ /**\n+ * Method: adjustBounds\n+ * This function will take a bounds, and if wrapDateLine option is set\n+ * on the layer, it will return a bounds which is wrapped around the \n+ * world. We do not wrap for bounds which *cross* the \n+ * maxExtent.left/right, only bounds which are entirely to the left \n+ * or entirely to the right.\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ */\n+ adjustBounds: function(bounds) {\n+\n+ if (this.gutter) {\n+ // Adjust the extent of a bounds in map units by the \n+ // layer's gutter in pixels.\n+ var mapGutter = this.gutter * this.map.getResolution();\n+ bounds = new OpenLayers.Bounds(bounds.left - mapGutter,\n+ bounds.bottom - mapGutter,\n+ bounds.right + mapGutter,\n+ bounds.top + mapGutter);\n+ }\n+\n+ if (this.wrapDateLine) {\n+ // wrap around the date line, within the limits of rounding error\n+ var wrappingOptions = {\n+ 'rightTolerance': this.getResolution(),\n+ 'leftTolerance': this.getResolution()\n+ };\n+ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);\n+\n+ }\n+ return bounds;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer\"\n+});\n+/* ======================================================================\n+ OpenLayers/Renderer.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Renderer \n+ * This is the base class for all renderers.\n+ *\n+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n+ * It is largely composed of virtual functions that are to be implemented\n+ * in technology-specific subclasses, but there is some generic code too.\n+ * \n+ * The functions that *are* implemented here merely deal with the maintenance\n+ * of the size and extent variables, as well as the cached 'resolution' \n+ * value. \n+ * \n+ * A note to the user that all subclasses should use getResolution() instead\n+ * of directly accessing this.resolution in order to correctly use the \n+ * cacheing system.\n+ *\n+ */\n+OpenLayers.Renderer = OpenLayers.Class({\n+\n+ /** \n+ * Property: container\n+ * {DOMElement} \n+ */\n+ container: null,\n+\n+ /**\n+ * Property: root\n+ * {DOMElement}\n+ */\n+ root: null,\n+\n+ /** \n+ * Property: extent\n+ * {<OpenLayers.Bounds>}\n+ */\n+ extent: null,\n+\n+ /**\n+ * Property: locked\n+ * {Boolean} If the renderer is currently in a state where many things\n+ * are changing, the 'locked' property is set to true. This means \n+ * that renderers can expect at least one more drawFeature event to be\n+ * called with the 'locked' property set to 'true': In some renderers,\n+ * this might make sense to use as a 'only update local information'\n+ * flag. \n+ */\n+ locked: false,\n+\n+ /** \n+ * Property: size\n+ * {<OpenLayers.Size>} \n+ */\n+ size: null,\n+\n+ /**\n+ * Property: resolution\n+ * {Float} cache of current map resolution\n+ */\n+ resolution: null,\n+\n+ /**\n+ * Property: map \n+ * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n+ */\n+ map: null,\n+\n+ /**\n+ * Property: featureDx\n+ * {Number} Feature offset in x direction. Will be calculated for and\n+ * applied to the current feature while rendering (see\n+ * <calculateFeatureDx>).\n+ */\n+ featureDx: 0,\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer \n+ *\n+ * Parameters:\n+ * containerID - {<String>} \n+ * options - {Object} options for this renderer. See sublcasses for\n+ * supported options.\n+ */\n+ initialize: function(containerID, options) {\n+ this.container = OpenLayers.Util.getElement(containerID);\n+ OpenLayers.Util.extend(this, options);\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.container = null;\n+ this.extent = null;\n+ this.size = null;\n+ this.resolution = null;\n+ this.map = null;\n+ },\n+\n+ /**\n+ * APIMethod: supported\n+ * This should be overridden by specific subclasses\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the renderer class\n+ */\n+ supported: function() {\n+ return false;\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ * We nullify the resolution cache (this.resolution) if resolutionChanged\n+ * is set to true - this way it will be re-computed on the next\n+ * getResolution() request.\n+ *\n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n+ *\n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n+ */\n+ setExtent: function(extent, resolutionChanged) {\n+ this.extent = extent.clone();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio);\n+ this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n+ }\n+ if (resolutionChanged) {\n+ this.resolution = null;\n+ }\n+ return true;\n+ },\n+\n+ /**\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ * \n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ *\n+ * Parameters:\n+ * size - {<OpenLayers.Size>} \n+ */\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ this.resolution = null;\n+ },\n+\n+ /** \n+ * Method: getResolution\n+ * Uses cached copy of resolution if available to minimize computing\n+ * \n+ * Returns:\n+ * {Float} The current map's resolution\n+ */\n+ getResolution: function() {\n+ this.resolution = this.resolution || this.map.getResolution();\n+ return this.resolution;\n+ },\n+\n+ /**\n+ * Method: drawFeature\n+ * Draw the feature. The optional style argument can be used\n+ * to override the feature's own style. This method should only\n+ * be called from layer.drawFeature().\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {<Object>}\n+ * \n+ * Returns:\n+ * {Boolean} true if the feature has been drawn completely, false if not,\n+ * undefined if the feature had no geometry\n+ */\n+ drawFeature: function(feature, style) {\n+ if (style == null) {\n+ style = feature.style;\n+ }\n+ if (feature.geometry) {\n+ var bounds = feature.geometry.getBounds();\n+ if (bounds) {\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n+ if (!bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ })) {\n+ style = {\n+ display: \"none\"\n+ };\n+ } else {\n+ this.calculateFeatureDx(bounds, worldBounds);\n+ }\n+ var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n+ if (style.display != \"none\" && style.label && rendered !== false) {\n+\n+ var location = feature.geometry.getCentroid();\n+ if (style.labelXOffset || style.labelYOffset) {\n+ var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n+ var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n+ var res = this.getResolution();\n+ location.move(xOffset * res, yOffset * res);\n+ }\n+ this.drawText(feature.id, style, location);\n+ } else {\n+ this.removeText(feature.id);\n+ }\n+ return rendered;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: calculateFeatureDx\n+ * {Number} Calculates the feature offset in x direction. Looking at the\n+ * center of the feature bounds and the renderer extent, we calculate how\n+ * many world widths the two are away from each other. This distance is\n+ * used to shift the feature as close as possible to the center of the\n+ * current enderer extent, which ensures that the feature is visible in the\n+ * current viewport.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n+ * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n+ */\n+ calculateFeatureDx: function(bounds, worldBounds) {\n+ this.featureDx = 0;\n+ if (worldBounds) {\n+ var worldWidth = worldBounds.getWidth(),\n+ rendererCenterX = (this.extent.left + this.extent.right) / 2,\n+ featureCenterX = (bounds.left + bounds.right) / 2,\n+ worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n+ this.featureDx = worldsAway * worldWidth;\n+ }\n+ },\n+\n+ /** \n+ * Method: drawGeometry\n+ * \n+ * Draw a geometry. This should only be called from the renderer itself.\n+ * Use layer.drawFeature() from outside the renderer.\n+ * virtual function\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * style - {Object} \n+ * featureId - {<String>} \n+ */\n+ drawGeometry: function(geometry, style, featureId) {},\n+\n+ /**\n+ * Method: drawText\n+ * Function for drawing text labels.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ * style -\n+ * location - {<OpenLayers.Geometry.Point>}\n+ */\n+ drawText: function(featureId, style, location) {},\n+\n+ /**\n+ * Method: removeText\n+ * Function for removing text labels.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ */\n+ removeText: function(featureId) {},\n+\n+ /**\n+ * Method: clear\n+ * Clear all vectors from the renderer.\n+ * virtual function.\n+ */\n+ clear: function() {},\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * Returns a feature id from an event on the renderer. \n+ * How this happens is specific to the renderer. This should be\n+ * called from layer.getFeatureFromEvent().\n+ * Virtual function.\n+ * \n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n+ *\n+ * Returns:\n+ * {String} A feature id or undefined.\n+ */\n+ getFeatureIdFromEvent: function(evt) {},\n+\n+ /**\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features\n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ eraseFeatures: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ var feature = features[i];\n+ this.eraseGeometry(feature.geometry, feature.id);\n+ this.removeText(feature.id);\n+ }\n+ },\n+\n+ /**\n+ * Method: eraseGeometry\n+ * Remove a geometry from the renderer (by id).\n+ * virtual function.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * featureId - {String}\n+ */\n+ eraseGeometry: function(geometry, featureId) {},\n+\n+ /**\n+ * Method: moveRoot\n+ * moves this renderer's root to a (different) renderer.\n+ * To be implemented by subclasses that require a common renderer root for\n+ * feature selection.\n+ * \n+ * Parameters:\n+ * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ */\n+ moveRoot: function(renderer) {},\n+\n+ /**\n+ * Method: getRenderLayerId\n+ * Gets the layer that this renderer's output appears on. If moveRoot was\n+ * used, this will be different from the id of the layer containing the\n+ * features rendered by this renderer.\n+ * \n+ * Returns:\n+ * {String} the id of the output layer.\n+ */\n+ getRenderLayerId: function() {\n+ return this.container.id;\n+ },\n+\n+ /**\n+ * Method: applyDefaultSymbolizer\n+ * \n+ * Parameters:\n+ * symbolizer - {Object}\n+ * \n+ * Returns:\n+ * {Object}\n+ */\n+ applyDefaultSymbolizer: function(symbolizer) {\n+ var result = OpenLayers.Util.extend({},\n+ OpenLayers.Renderer.defaultSymbolizer);\n+ if (symbolizer.stroke === false) {\n+ delete result.strokeWidth;\n+ delete result.strokeColor;\n+ }\n+ if (symbolizer.fill === false) {\n+ delete result.fillColor;\n+ }\n+ OpenLayers.Util.extend(result, symbolizer);\n+ return result;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Renderer\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Renderer.defaultSymbolizer\n+ * {Object} Properties from this symbolizer will be applied to symbolizers\n+ * with missing properties. This can also be used to set a global\n+ * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n+ * following code before rendering any vector features:\n+ * (code)\n+ * OpenLayers.Renderer.defaultSymbolizer = {\n+ * fillColor: \"#808080\",\n+ * fillOpacity: 1,\n+ * strokeColor: \"#000000\",\n+ * strokeOpacity: 1,\n+ * strokeWidth: 1,\n+ * pointRadius: 3,\n+ * graphicName: \"square\"\n+ * };\n+ * (end)\n+ */\n+OpenLayers.Renderer.defaultSymbolizer = {\n+ fillColor: \"#000000\",\n+ strokeColor: \"#000000\",\n+ strokeWidth: 2,\n+ fillOpacity: 1,\n+ strokeOpacity: 1,\n+ pointRadius: 0,\n+ labelAlign: 'cm'\n+};\n+\n+\n+\n+/**\n+ * Constant: OpenLayers.Renderer.symbol\n+ * Coordinate arrays for well known (named) symbols.\n+ */\n+OpenLayers.Renderer.symbol = {\n+ \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n+ 303, 215, 231, 161, 321, 161, 350, 75\n+ ],\n+ \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n+ 4, 0\n+ ],\n+ \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n+ \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n+ \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n+};\n+/* ======================================================================\n+ OpenLayers/Layer/Vector.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/StyleMap.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Lang.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector\n+ * Instances of OpenLayers.Layer.Vector are used to render vector data from\n+ * a variety of sources. Create a new vector layer with the\n+ * <OpenLayers.Layer.Vector> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer>\n+ */\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n+ * beforefeatureadded - Triggered before a feature is added. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be added. To stop the feature from being added, a\n+ * listener should return false.\n+ * beforefeaturesadded - Triggered before an array of features is added.\n+ * Listeners will receive an object with a *features* property\n+ * referencing the feature to be added. To stop the features from\n+ * being added, a listener should return false.\n+ * featureadded - Triggered after a feature is added. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the added feature.\n+ * featuresadded - Triggered after features are added. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of added features.\n+ * beforefeatureremoved - Triggered before a feature is removed. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be removed.\n+ * beforefeaturesremoved - Triggered before multiple features are removed. \n+ * Listeners will receive an object with a *features* property\n+ * referencing the features to be removed.\n+ * featureremoved - Triggerd after a feature is removed. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the removed feature.\n+ * featuresremoved - Triggered after features are removed. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of removed features.\n+ * beforefeatureselected - Triggered before a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be selected. To stop the feature from being selectd, a\n+ * listener should return false.\n+ * featureselected - Triggered after a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * selected feature.\n+ * featureunselected - Triggered after a feature is unselected.\n+ * Listeners will receive an object with a *feature* property\n+ * referencing the unselected feature.\n+ * beforefeaturemodified - Triggered when a feature is selected to \n+ * be modified. Listeners will receive an object with a *feature* \n+ * property referencing the selected feature.\n+ * featuremodified - Triggered when a feature has been modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * afterfeaturemodified - Triggered when a feature is finished being modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * vertexmodified - Triggered when a vertex within any feature geometry\n+ * has been modified. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * modification.\n+ * vertexremoved - Triggered when a vertex within any feature geometry\n+ * has been deleted. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * removal.\n+ * sketchstarted - Triggered when a feature sketch bound for this layer\n+ * is started. Listeners will receive an object with a *feature*\n+ * property referencing the new sketch feature and a *vertex* property\n+ * referencing the creation point.\n+ * sketchmodified - Triggered when a feature sketch bound for this layer\n+ * is modified. Listeners will receive an object with a *vertex*\n+ * property referencing the modified vertex and a *feature* property\n+ * referencing the sketch feature.\n+ * sketchcomplete - Triggered when a feature sketch bound for this layer\n+ * is complete. Listeners will receive an object with a *feature*\n+ * property referencing the sketch feature. By returning false, a\n+ * listener can stop the sketch feature from being added to the layer.\n+ * refresh - Triggered when something wants a strategy to ask the protocol\n+ * for a new set of features.\n+ */\n+\n+ /**\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is false. Set this property\n+ * in the layer options.\n+ */\n+ isBaseLayer: false,\n+\n+ /** \n+ * APIProperty: isFixed\n+ * {Boolean} Whether the layer remains in one place while dragging the\n+ * map. Note that setting this to true will move the layer to the bottom\n+ * of the layer stack.\n+ */\n+ isFixed: false,\n+\n+ /** \n+ * APIProperty: features\n+ * {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ features: null,\n+\n+ /** \n+ * Property: filter\n+ * {<OpenLayers.Filter>} The filter set in this layer,\n+ * a strategy launching read requests can combined\n+ * this filter with its own filter.\n+ */\n+ filter: null,\n+\n+ /** \n+ * Property: selectedFeatures\n+ * {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ selectedFeatures: null,\n+\n+ /**\n+ * Property: unrenderedFeatures\n+ * {Object} hash of features, keyed by feature.id, that the renderer\n+ * failed to draw\n+ */\n+ unrenderedFeatures: null,\n+\n+ /**\n+ * APIProperty: reportError\n+ * {Boolean} report friendly error message when loading of renderer\n+ * fails.\n+ */\n+ reportError: true,\n+\n+ /** \n+ * APIProperty: style\n+ * {Object} Default style for the layer\n+ */\n+ style: null,\n+\n+ /**\n+ * Property: styleMap\n+ * {<OpenLayers.StyleMap>}\n+ */\n+ styleMap: null,\n+\n+ /**\n+ * Property: strategies\n+ * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n+ */\n+ strategies: null,\n+\n+ /**\n+ * Property: protocol\n+ * {<OpenLayers.Protocol>} Optional protocol for the layer.\n+ */\n+ protocol: null,\n+\n+ /**\n+ * Property: renderers\n+ * {Array(String)} List of supported Renderer classes. Add to this list to\n+ * add support for additional renderers. This list is ordered:\n+ * the first renderer which returns true for the 'supported()'\n+ * method will be used, if not defined in the 'renderer' option.\n+ */\n+ renderers: ['SVG', 'VML', 'Canvas'],\n+\n+ /** \n+ * Property: renderer\n+ * {<OpenLayers.Renderer>}\n+ */\n+ renderer: null,\n+\n+ /**\n+ * APIProperty: rendererOptions\n+ * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n+ * supported options.\n+ */\n+ rendererOptions: null,\n+\n+ /** \n+ * APIProperty: geometryType\n+ * {String} geometryType allows you to limit the types of geometries this\n+ * layer supports. This should be set to something like\n+ * \"OpenLayers.Geometry.Point\" to limit types.\n+ */\n+ geometryType: null,\n+\n+ /** \n+ * Property: drawn\n+ * {Boolean} Whether the Vector Layer features have been drawn yet.\n+ */\n+ drawn: false,\n+\n+ /** \n+ * APIProperty: ratio\n+ * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n+ */\n+ ratio: 1,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Vector\n+ * Create a new vector layer\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} A new vector layer\n+ */\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+\n+ // allow user-set renderer, otherwise assign one\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.assignRenderer();\n+ }\n+\n+ // if no valid renderer found, display error\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.renderer = null;\n+ this.displayError();\n+ }\n+\n+ if (!this.styleMap) {\n+ this.styleMap = new OpenLayers.StyleMap();\n+ }\n+\n+ this.features = [];\n+ this.selectedFeatures = [];\n+ this.unrenderedFeatures = {};\n+\n+ // Allow for custom layer behavior\n+ if (this.strategies) {\n+ for (var i = 0, len = this.strategies.length; i < len; i++) {\n+ this.strategies[i].setLayer(this);\n+ }\n+ }\n+\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Destroy this layer\n+ */\n+ destroy: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoDestroy) {\n+ strategy.destroy();\n+ }\n+ }\n+ this.strategies = null;\n+ }\n+ if (this.protocol) {\n+ if (this.protocol.autoDestroy) {\n+ this.protocol.destroy();\n+ }\n+ this.protocol = null;\n+ }\n+ this.destroyFeatures();\n+ this.features = null;\n+ this.selectedFeatures = null;\n+ this.unrenderedFeatures = null;\n+ if (this.renderer) {\n+ this.renderer.destroy();\n+ }\n+ this.renderer = null;\n+ this.geometryType = null;\n+ this.drawn = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer.\n+ * \n+ * Note: Features of the layer are also cloned.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+ var features = this.features;\n+ var len = features.length;\n+ var clonedFeatures = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ clonedFeatures[i] = features[i].clone();\n+ }\n+ obj.features = clonedFeatures;\n+\n+ return obj;\n+ },\n+\n+ /**\n+ * Method: refresh\n+ * Ask the layer to request features again and redraw them. Triggers\n+ * the refresh event if the layer is in range and visible.\n+ *\n+ * Parameters:\n+ * obj - {Object} Optional object with properties for any listener of\n+ * the refresh event.\n+ */\n+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj);\n+ }\n+ },\n+\n+ /** \n+ * Method: assignRenderer\n+ * Iterates through the available renderer implementations and selects \n+ * and assigns the first one whose \"supported()\" function returns true.\n+ */\n+ assignRenderer: function() {\n+ for (var i = 0, len = this.renderers.length; i < len; i++) {\n+ var rendererClass = this.renderers[i];\n+ var renderer = (typeof rendererClass == \"function\") ?\n+ rendererClass :\n+ OpenLayers.Renderer[rendererClass];\n+ if (renderer && renderer.prototype.supported()) {\n+ this.renderer = new renderer(this.div, this.rendererOptions);\n+ break;\n+ }\n+ }\n+ },\n+\n+ /** \n+ * Method: displayError \n+ * Let the user know their browser isn't supported.\n+ */\n+ displayError: function() {\n+ if (this.reportError) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n+ renderers: this.renderers.join('\\n')\n+ }));\n+ }\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * The layer has been added to the map. \n+ * \n+ * If there is no renderer set, the layer can't be used. Remove it.\n+ * Otherwise, give the renderer a reference to the map and set its size.\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+\n+ if (!this.renderer) {\n+ this.map.removeLayer(this);\n+ } else {\n+ this.renderer.map = this.map;\n+\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n+ }\n+ },\n+\n+ /**\n+ * Method: afterAdd\n+ * Called at the end of the map.addLayer sequence. At this point, the map\n+ * will have a base layer. Any autoActivate strategies will be\n+ * activated here.\n+ */\n+ afterAdd: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.activate();\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: removeMap\n+ * The layer has been removed from the map.\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ removeMap: function(map) {\n+ this.drawn = false;\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.deactivate();\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: onMapResize\n+ * Notify the renderer of the change in size. \n+ * \n+ */\n+ onMapResize: function() {\n+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n+\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * Reset the vector layer's div so that it once again is lined up with \n+ * the map. Notify the renderer of the change of extent, and in the\n+ * case of a change of zoom level (resolution), have the \n+ * renderer redraw features.\n+ * \n+ * If the layer has not yet been drawn, cycle through the layer's \n+ * features and draw each one.\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} \n+ * zoomChanged - {Boolean} \n+ * dragging - {Boolean} \n+ */\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+\n+ var coordSysUnchanged = true;\n+ if (!dragging) {\n+ this.renderer.root.style.visibility = 'hidden';\n+\n+ var viewSize = this.map.getSize(),\n+ viewWidth = viewSize.w,\n+ viewHeight = viewSize.h,\n+ offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n+ offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n+ offsetLeft += this.map.layerContainerOriginPx.x;\n+ offsetLeft = -Math.round(offsetLeft);\n+ offsetTop += this.map.layerContainerOriginPx.y;\n+ offsetTop = -Math.round(offsetTop);\n+\n+ this.div.style.left = offsetLeft + 'px';\n+ this.div.style.top = offsetTop + 'px';\n+\n+ var extent = this.map.getExtent().scale(this.ratio);\n+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+\n+ this.renderer.root.style.visibility = 'visible';\n+\n+ // Force a reflow on gecko based browsers to prevent jump/flicker.\n+ // This seems to happen on only certain configurations; it was originally\n+ // noticed in FF 2.0 and Linux.\n+ if (OpenLayers.IS_GECKO === true) {\n+ this.div.scrollLeft = this.div.scrollLeft;\n+ }\n+\n+ if (!zoomChanged && coordSysUnchanged) {\n+ for (var i in this.unrenderedFeatures) {\n+ var feature = this.unrenderedFeatures[i];\n+ this.drawFeature(feature);\n+ }\n+ }\n+ }\n+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n+ this.drawn = true;\n+ var feature;\n+ for (var i = 0, len = this.features.length; i < len; i++) {\n+ this.renderer.locked = (i !== (len - 1));\n+ feature = this.features[i];\n+ this.drawFeature(feature);\n+ }\n+ }\n+ },\n+\n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer\n+ * \n+ * Parameters:\n+ * display - {Boolean}\n+ */\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ // we need to set the display style of the root in case it is attached\n+ // to a foreign layer\n+ var currentDisplay = this.div.style.display;\n+ if (currentDisplay != this.renderer.root.style.display) {\n+ this.renderer.root.style.display = currentDisplay;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: addFeatures\n+ * Add Features to the layer.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * options - {Object}\n+ */\n+ addFeatures: function(features, options) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ var event = {\n+ features: features\n+ };\n+ var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n+ if (ret === false) {\n+ return;\n+ }\n+ features = event.features;\n+ }\n+\n+ // Track successfully added features for featuresadded event, since\n+ // beforefeatureadded can veto single features.\n+ var featuresAdded = [];\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ if (i != (features.length - 1)) {\n+ this.renderer.locked = true;\n+ } else {\n+ this.renderer.locked = false;\n+ }\n+ var feature = features[i];\n+\n+ if (this.geometryType &&\n+ !(feature.geometry instanceof this.geometryType)) {\n+ throw new TypeError('addFeatures: component should be an ' +\n+ this.geometryType.prototype.CLASS_NAME);\n+ }\n+\n+ //give feature reference to its layer\n+ feature.layer = this;\n+\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style);\n+ }\n+\n+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue;\n+ }\n+ this.preFeatureInsert(feature);\n+ }\n+\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature);\n+ }\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ });\n+ }\n+ },\n+\n+\n+ /**\n+ * APIMethod: removeFeatures\n+ * Remove features from the layer. This erases any drawn features and\n+ * removes them from the layer's control. The beforefeatureremoved\n+ * and featureremoved events will be triggered for each feature. The\n+ * featuresremoved event will be triggered after all features have\n+ * been removed. To supress event triggering, use the silent option.\n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n+ * removed.\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n+ *\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n+ */\n+ removeFeatures: function(features, options) {\n+ if (!features || features.length === 0) {\n+ return;\n+ }\n+ if (features === this.features) {\n+ return this.removeAllFeatures(options);\n+ }\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ if (features === this.selectedFeatures) {\n+ features = features.slice();\n+ }\n+\n+ var notify = !options || !options.silent;\n+\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n+ }\n+ );\n+ }\n+\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ // We remain locked so long as we're not at 0\n+ // and the 'next' feature has a geometry. We do the geometry check\n+ // because if all the features after the current one are 'null', we\n+ // won't call eraseGeometry, so we break the 'renderer functions\n+ // will always be called with locked=false *last*' rule. The end result\n+ // is a possible gratiutious unlocking to save a loop through the rest \n+ // of the list checking the remaining features every time. So long as\n+ // null geoms are rare, this is probably okay. \n+ if (i != 0 && features[i - 1].geometry) {\n+ this.renderer.locked = true;\n+ } else {\n+ this.renderer.locked = false;\n+ }\n+\n+ var feature = features[i];\n+ delete this.unrenderedFeatures[feature.id];\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n+ }\n+\n+ this.features = OpenLayers.Util.removeItem(this.features, feature);\n+ // feature has no layer at this point\n+ feature.layer = null;\n+\n+ if (feature.geometry) {\n+ this.renderer.eraseFeatures(feature);\n+ }\n+\n+ //in the case that this feature is one of the selected features, \n+ // remove it from that array as well.\n+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n+ OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n+ }\n+ },\n+\n+ /** \n+ * APIMethod: removeAllFeatures\n+ * Remove all features from the layer.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n+ *\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n+ */\n+ removeAllFeatures: function(options) {\n+ var notify = !options || !options.silent;\n+ var features = this.features;\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n+ }\n+ );\n+ }\n+ var feature;\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ feature = features[i];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ feature.layer = null;\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ }\n+ this.renderer.clear();\n+ this.features = [];\n+ this.unrenderedFeatures = {};\n+ this.selectedFeatures = [];\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: destroyFeatures\n+ * Erase and destroy features on the layer.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n+ * features to destroy. If not supplied, all features on the layer\n+ * will be destroyed.\n+ * options - {Object}\n+ */\n+ destroyFeatures: function(features, options) {\n+ var all = (features == undefined); // evaluates to true if\n+ // features is null\n+ if (all) {\n+ features = this.features;\n+ }\n+ if (features) {\n+ this.removeFeatures(features, options);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ features[i].destroy();\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: drawFeature\n+ * Draw (or redraw) a feature on the layer. If the optional style argument\n+ * is included, this style will be used. If no style is included, the\n+ * feature's style will be used. If the feature doesn't have a style,\n+ * the layer's style will be used.\n+ * \n+ * This function is not designed to be used when adding features to \n+ * the layer (use addFeatures instead). It is meant to be used when\n+ * the style of a feature has changed, or in some other way needs to \n+ * visually updated *after* it has already been added to a layer. You\n+ * must add the feature to the layer for most layer-related events to \n+ * happen.\n+ *\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {String | Object} Named render intent or full symbolizer object.\n+ */\n+ drawFeature: function(feature, style) {\n+ // don't try to draw the feature with the renderer if the layer is not \n+ // drawn itself\n+ if (!this.drawn) {\n+ return;\n+ }\n+ if (typeof style != \"object\") {\n+ if (!style && feature.state === OpenLayers.State.DELETE) {\n+ style = \"delete\";\n+ }\n+ var renderIntent = style || feature.renderIntent;\n+ style = feature.style || this.style;\n+ if (!style) {\n+ style = this.styleMap.createSymbolizer(feature, renderIntent);\n+ }\n+ }\n+\n+ var drawn = this.renderer.drawFeature(feature, style);\n+ //TODO remove the check for null when we get rid of Renderer.SVG\n+ if (drawn === false || drawn === null) {\n+ this.unrenderedFeatures[feature.id] = feature;\n+ } else {\n+ delete this.unrenderedFeatures[feature.id];\n+ }\n+ },\n+\n+ /**\n+ * Method: eraseFeatures\n+ * Erase features from the layer.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features);\n+ },\n+\n+ /**\n+ * Method: getFeatureFromEvent\n+ * Given an event, return a feature if the event occurred over one.\n+ * Otherwise, return null.\n+ *\n+ * Parameters:\n+ * evt - {Event} \n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n+ */\n+ getFeatureFromEvent: function(evt) {\n+ if (!this.renderer) {\n+ throw new Error('getFeatureFromEvent called on layer with no ' +\n+ 'renderer. This usually means you destroyed a ' +\n+ 'layer, but not some handler which is associated ' +\n+ 'with it.');\n+ }\n+ var feature = null;\n+ var featureId = this.renderer.getFeatureIdFromEvent(evt);\n+ if (featureId) {\n+ if (typeof featureId === \"string\") {\n+ feature = this.getFeatureById(featureId);\n+ } else {\n+ feature = featureId;\n+ }\n+ }\n+ return feature;\n+ },\n+\n+ /**\n+ * APIMethod: getFeatureBy\n+ * Given a property value, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * property - {String}\n+ * value - {String}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * property value or null if there is no such feature.\n+ */\n+ getFeatureBy: function(property, value) {\n+ //TBD - would it be more efficient to use a hash for this.features?\n+ var feature = null;\n+ for (var i = 0, len = this.features.length; i < len; ++i) {\n+ if (this.features[i][property] == value) {\n+ feature = this.features[i];\n+ break;\n+ }\n+ }\n+ return feature;\n+ },\n+\n+ /**\n+ * APIMethod: getFeatureById\n+ * Given a feature id, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * featureId - {String}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureId or null if there is no such feature.\n+ */\n+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy('id', featureId);\n+ },\n+\n+ /**\n+ * APIMethod: getFeatureByFid\n+ * Given a feature fid, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * featureFid - {String}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureFid or null if there is no such feature.\n+ */\n+ getFeatureByFid: function(featureFid) {\n+ return this.getFeatureBy('fid', featureFid);\n+ },\n+\n+ /**\n+ * APIMethod: getFeaturesByAttribute\n+ * Returns an array of features that have the given attribute key set to the\n+ * given value. Comparison of attribute values takes care of datatypes, e.g.\n+ * the string '1234' is not equal to the number 1234.\n+ *\n+ * Parameters:\n+ * attrName - {String}\n+ * attrValue - {Mixed}\n+ *\n+ * Returns:\n+ * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n+ * passed named attribute set to the given value.\n+ */\n+ getFeaturesByAttribute: function(attrName, attrValue) {\n+ var i,\n+ feature,\n+ len = this.features.length,\n+ foundFeatures = [];\n+ for (i = 0; i < len; i++) {\n+ feature = this.features[i];\n+ if (feature && feature.attributes) {\n+ if (feature.attributes[attrName] === attrValue) {\n+ foundFeatures.push(feature);\n+ }\n+ }\n+ }\n+ return foundFeatures;\n+ },\n+\n+ /**\n+ * Unselect the selected features\n+ * i.e. clears the featureSelection array\n+ * change the style back\n+ clearSelection: function() {\n+\n+ var vectorLayer = this.map.vectorLayer;\n+ for (var i = 0; i < this.map.featureSelection.length; i++) {\n+ var featureSelection = this.map.featureSelection[i];\n+ vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n+ }\n+ this.map.featureSelection = [];\n+ },\n+ */\n+\n+\n+ /**\n+ * APIMethod: onFeatureInsert\n+ * method called after a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something on feature updates.\n+ *\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ onFeatureInsert: function(feature) {},\n+\n+ /**\n+ * APIMethod: preFeatureInsert\n+ * method called before a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something when features are first added to the\n+ * layer, but before they are drawn, such as adjust the style.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ preFeatureInsert: function(feature) {},\n+\n+ /** \n+ * APIMethod: getDataExtent\n+ * Calculates the max extent which includes all of the features.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} or null if the layer has no features with\n+ * geometries.\n+ */\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ var features = this.features;\n+ if (features && (features.length > 0)) {\n+ var geometry = null;\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ geometry = features[i].geometry;\n+ if (geometry) {\n+ if (maxExtent === null) {\n+ maxExtent = new OpenLayers.Bounds();\n+ }\n+ maxExtent.extend(geometry.getBounds());\n+ }\n+ }\n+ }\n+ return maxExtent;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Vector\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Vector/RootContainer.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector.RootContainer\n+ * A special layer type to combine multiple vector layers inside a single\n+ * renderer root container. This class is not supposed to be instantiated\n+ * from user space, it is a helper class for controls that require event\n+ * processing for multiple vector layers.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Vector>\n+ */\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+\n+ /**\n+ * Property: displayInLayerSwitcher\n+ * Set to false for this layer type\n+ */\n+ displayInLayerSwitcher: false,\n+\n+ /**\n+ * APIProperty: layers\n+ * Layers that are attached to this container. Required config option.\n+ */\n+ layers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Vector.RootContainer\n+ * Create a new root container for multiple vector layer. This constructor\n+ * is not supposed to be used from user space, it is only to be used by\n+ * controls that need feature selection across multiple vector layers.\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n+ * \n+ * Required options properties:\n+ * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n+ * container\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n+ * container\n+ */\n+\n+ /**\n+ * Method: display\n+ */\n+ display: function() {},\n+\n+ /**\n+ * Method: getFeatureFromEvent\n+ * walk through the layers to find the feature returned by the event\n+ * \n+ * Parameters:\n+ * evt - {Object} event object with a feature property\n+ * \n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>}\n+ */\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: setMap\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer);\n+ },\n+\n+ /**\n+ * Method: removeMap\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ removeMap: function(map) {\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: collectRoots\n+ * Collects the root nodes of all layers this control is configured with\n+ * and moveswien the nodes to this control's layer\n+ */\n+ collectRoots: function() {\n+ var layer;\n+ // walk through all map layers, because we want to keep the order\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: resetRoots\n+ * Resets the root nodes back into the layers they belong to.\n+ */\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: handleChangeLayer\n+ * Event handler for the map's changelayer event. We need to rebuild\n+ * this container's layer dom if order of one of its layers changes.\n+ * This handler is added with the setMap method, and removed with the\n+ * removeMap method.\n+ * \n+ * Parameters:\n+ * evt - {Object}\n+ */\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" &&\n+ OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n+});\n+/* ======================================================================\n+ OpenLayers/Control/SelectFeature.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Handler/Feature.js\n+ * @requires OpenLayers/Layer/Vector/RootContainer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.SelectFeature\n+ * The SelectFeature control selects vector features from a given layer on \n+ * click or hover. \n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n+\n+ /** \n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforefeaturehighlighted - Triggered before a feature is highlighted\n+ * featurehighlighted - Triggered when a feature is highlighted\n+ * featureunhighlighted - Triggered when a feature is unhighlighted\n+ * boxselectionstart - Triggered before box selection starts\n+ * boxselectionend - Triggered after box selection ends\n+ */\n+\n+ /**\n+ * Property: multipleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <multiple> property to true. Default is null.\n+ */\n+ multipleKey: null,\n+\n+ /**\n+ * Property: toggleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <toggle> property to true. Default is null.\n+ */\n+ toggleKey: null,\n+\n+ /**\n+ * APIProperty: multiple\n+ * {Boolean} Allow selection of multiple geometries. Default is false.\n+ */\n+ multiple: false,\n+\n+ /**\n+ * APIProperty: clickout\n+ * {Boolean} Unselect features when clicking outside any feature.\n+ * Default is true.\n+ */\n+ clickout: true,\n+\n+ /**\n+ * APIProperty: toggle\n+ * {Boolean} Unselect a selected feature on click. Default is false. Only\n+ * has meaning if hover is false.\n+ */\n+ toggle: false,\n+\n+ /**\n+ * APIProperty: hover\n+ * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n+ * ignores clicks and only listens to mouse moves.\n+ */\n+ hover: false,\n+\n+ /**\n+ * APIProperty: highlightOnly\n+ * {Boolean} If true do not actually select features (that is place them in \n+ * the layer's selected features array), just highlight them. This property\n+ * has no effect if hover is false. Defaults to false.\n+ */\n+ highlightOnly: false,\n+\n+ /**\n+ * APIProperty: box\n+ * {Boolean} Allow feature selection by drawing a box.\n+ */\n+ box: false,\n+\n+ /**\n+ * Property: onBeforeSelect \n+ * {Function} Optional function to be called before a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onBeforeSelect: function() {},\n+\n+ /**\n+ * APIProperty: onSelect \n+ * {Function} Optional function to be called when a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onSelect: function() {},\n+\n+ /**\n+ * APIProperty: onUnselect\n+ * {Function} Optional function to be called when a feature is unselected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onUnselect: function() {},\n+\n+ /**\n+ * Property: scope\n+ * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n+ * callbacks. If null the scope will be this control.\n+ */\n+ scope: null,\n+\n+ /**\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict selecting to a limited set of geometry types,\n+ * send a list of strings corresponding to the geometry class names.\n+ */\n+ geometryTypes: null,\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n+ * root for all layers this control is configured with (if an array of\n+ * layers was passed to the constructor), or the vector layer the control\n+ * was configured with (if a single layer was passed to the constructor).\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: layers\n+ * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n+ * or null if the control was configured with a single layer\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: callbacks\n+ * {Object} The functions that are sent to the handlers.feature for callback\n+ */\n+ callbacks: null,\n+\n+ /**\n+ * APIProperty: selectStyle \n+ * {Object} Hash of styles\n+ */\n+ selectStyle: null,\n+\n+ /**\n+ * Property: renderIntent\n+ * {String} key used to retrieve the select style from the layer's\n+ * style map.\n+ */\n+ renderIntent: \"select\",\n+\n+ /**\n+ * Property: handlers\n+ * {Object} Object with references to multiple <OpenLayers.Handler>\n+ * instances.\n+ */\n+ handlers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.SelectFeature\n+ * Create a new control for selecting features.\n+ *\n+ * Parameters:\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n+ * layer(s) this control will select features from.\n+ * options - {Object} \n+ */\n+ initialize: function(layers, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+\n+ if (this.scope === null) {\n+ this.scope = this;\n+ }\n+ this.initLayer(layers);\n+ var callbacks = {\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature\n+ };\n+ if (this.hover) {\n+ callbacks.over = this.overFeature;\n+ callbacks.out = this.outFeature;\n+ }\n+\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlers = {\n+ feature: new OpenLayers.Handler.Feature(\n+ this, this.layer, this.callbacks, {\n+ geometryTypes: this.geometryTypes\n+ }\n+ )\n+ };\n+\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(\n+ this, {\n+ done: this.selectBox\n+ }, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ }\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: initLayer\n+ * Assign the layer property. If layers is an array, we need to use\n+ * a RootContainer.\n+ *\n+ * Parameters:\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n+ */\n+ initLayer: function(layers) {\n+ if (OpenLayers.Util.isArray(layers)) {\n+ this.layers = layers;\n+ this.layer = new OpenLayers.Layer.Vector.RootContainer(\n+ this.id + \"_container\", {\n+ layers: layers\n+ }\n+ );\n+ } else {\n+ this.layer = layers;\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ if (this.active && this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.layers) {\n+ this.layer.destroy();\n+ }\n+ },\n+\n+ /**\n+ * Method: activate\n+ * Activates the control.\n+ * \n+ * Returns:\n+ * {Boolean} The control was effectively activated.\n+ */\n+ activate: function() {\n+ if (!this.active) {\n+ if (this.layers) {\n+ this.map.addLayer(this.layer);\n+ }\n+ this.handlers.feature.activate();\n+ if (this.box && this.handlers.box) {\n+ this.handlers.box.activate();\n+ }\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(\n+ this, arguments\n+ );\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ * Deactivates the control.\n+ * \n+ * Returns:\n+ * {Boolean} The control was effectively deactivated.\n+ */\n+ deactivate: function() {\n+ if (this.active) {\n+ this.handlers.feature.deactivate();\n+ if (this.handlers.box) {\n+ this.handlers.box.deactivate();\n+ }\n+ if (this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ }\n+ return OpenLayers.Control.prototype.deactivate.apply(\n+ this, arguments\n+ );\n+ },\n+\n+ /**\n+ * Method: unselectAll\n+ * Unselect all selected features. To unselect all except for a single\n+ * feature, set the options.except property to the feature.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional configuration object.\n+ */\n+ unselectAll: function(options) {\n+ // we'll want an option to supress notification here\n+ var layers = this.layers || [this.layer],\n+ layer, feature, l, numExcept;\n+ for (l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ numExcept = 0;\n+ //layer.selectedFeatures is null when layer is destroyed and \n+ //one of it's preremovelayer listener calls setLayer \n+ //with another layer on this control\n+ if (layer.selectedFeatures != null) {\n+ while (layer.selectedFeatures.length > numExcept) {\n+ feature = layer.selectedFeatures[numExcept];\n+ if (!options || options.except != feature) {\n+ this.unselect(feature);\n+ } else {\n+ ++numExcept;\n+ }\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: clickFeature\n+ * Called on click in a feature\n+ * Only responds if this.hover is false.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ clickFeature: function(feature) {\n+ if (!this.hover) {\n+ var selected = (OpenLayers.Util.indexOf(\n+ feature.layer.selectedFeatures, feature) > -1);\n+ if (selected) {\n+ if (this.toggleSelect()) {\n+ this.unselect(feature);\n+ } else if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n+ } else {\n+ if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n+ this.select(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: multipleSelect\n+ * Allow for multiple selected features based on <multiple> property and\n+ * <multipleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Allow for multiple selected features.\n+ */\n+ multipleSelect: function() {\n+ return this.multiple || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.multipleKey]);\n+ },\n+\n+ /**\n+ * Method: toggleSelect\n+ * Event should toggle the selected state of a feature based on <toggle>\n+ * property and <toggleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Toggle the selected state of a feature.\n+ */\n+ toggleSelect: function() {\n+ return this.toggle || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.toggleKey]);\n+ },\n+\n+ /**\n+ * Method: clickoutFeature\n+ * Called on click outside a previously clicked (selected) feature.\n+ * Only responds if this.hover is false.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Vector.Feature>} \n+ */\n+ clickoutFeature: function(feature) {\n+ if (!this.hover && this.clickout) {\n+ this.unselectAll();\n+ }\n+ },\n+\n+ /**\n+ * Method: overFeature\n+ * Called on over a feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ overFeature: function(feature) {\n+ var layer = feature.layer;\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ this.highlight(feature);\n+ } else if (OpenLayers.Util.indexOf(\n+ layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: outFeature\n+ * Called on out of a selected feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ outFeature: function(feature) {\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ // we do nothing if we're not the last highlighter of the\n+ // feature\n+ if (feature._lastHighlighter == this.id) {\n+ // if another select control had highlighted the feature before\n+ // we did it ourself then we use that control to highlight the\n+ // feature as it was before we highlighted it, else we just\n+ // unhighlight it\n+ if (feature._prevHighlighter &&\n+ feature._prevHighlighter != this.id) {\n+ delete feature._lastHighlighter;\n+ var control = this.map.getControl(\n+ feature._prevHighlighter);\n+ if (control) {\n+ control.highlight(feature);\n+ }\n+ } else {\n+ this.unhighlight(feature);\n+ }\n+ }\n+ } else {\n+ this.unselect(feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: highlight\n+ * Redraw feature with the select style.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ highlight: function(feature) {\n+ var layer = feature.layer;\n+ var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ feature._prevHighlighter = feature._lastHighlighter;\n+ feature._lastHighlighter = this.id;\n+ var style = this.selectStyle || this.renderIntent;\n+ layer.drawFeature(feature, style);\n+ this.events.triggerEvent(\"featurehighlighted\", {\n+ feature: feature\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: unhighlight\n+ * Redraw feature with the \"default\" style\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ unhighlight: function(feature) {\n+ var layer = feature.layer;\n+ // three cases:\n+ // 1. there's no other highlighter, in that case _prev is undefined,\n+ // and we just need to undef _last\n+ // 2. another control highlighted the feature after we did it, in\n+ // that case _last references this other control, and we just\n+ // need to undef _prev\n+ // 3. another control highlighted the feature before we did it, in\n+ // that case _prev references this other control, and we need to\n+ // set _last to _prev and undef _prev\n+ if (feature._prevHighlighter == undefined) {\n+ delete feature._lastHighlighter;\n+ } else if (feature._prevHighlighter == this.id) {\n+ delete feature._prevHighlighter;\n+ } else {\n+ feature._lastHighlighter = feature._prevHighlighter;\n+ delete feature._prevHighlighter;\n+ }\n+ layer.drawFeature(feature, feature.style || feature.layer.style ||\n+ \"default\");\n+ this.events.triggerEvent(\"featureunhighlighted\", {\n+ feature: feature\n+ });\n+ },\n+\n+ /**\n+ * Method: select\n+ * Add feature to the layer's selectedFeature array, render the feature as\n+ * selected, and call the onSelect function.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ select: function(feature) {\n+ var cont = this.onBeforeSelect.call(this.scope, feature);\n+ var layer = feature.layer;\n+ if (cont !== false) {\n+ cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ layer.selectedFeatures.push(feature);\n+ this.highlight(feature);\n+ // if the feature handler isn't involved in the feature\n+ // selection (because the box handler is used or the\n+ // feature is selected programatically) we fake the\n+ // feature handler to allow unselecting on click\n+ if (!this.handlers.feature.lastFeature) {\n+ this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n+ }\n+ layer.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ });\n+ this.onSelect.call(this.scope, feature);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: unselect\n+ * Remove feature from the layer's selectedFeature array, render the feature as\n+ * normal, and call the onUnselect function.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ unselect: function(feature) {\n+ var layer = feature.layer;\n+ // Store feature style for restoration later\n+ this.unhighlight(feature);\n+ OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n+ layer.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n+ });\n+ this.onUnselect.call(this.scope, feature);\n+ },\n+\n+ /**\n+ * Method: selectBox\n+ * Callback from the handlers.box set up when <box> selection is true\n+ * on.\n+ *\n+ * Parameters:\n+ * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n+ */\n+ selectBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ var bounds = new OpenLayers.Bounds(\n+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n+ );\n+\n+ // if multiple is false, first deselect currently selected features\n+ if (!this.multipleSelect()) {\n+ this.unselectAll();\n+ }\n+\n+ // because we're using a box, we consider we want multiple selection\n+ var prevMultiple = this.multiple;\n+ this.multiple = true;\n+ var layers = this.layers || [this.layer];\n+ this.events.triggerEvent(\"boxselectionstart\", {\n+ layers: layers\n+ });\n+ var layer;\n+ for (var l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ for (var i = 0, len = layer.features.length; i < len; ++i) {\n+ var feature = layer.features[i];\n+ // check if the feature is displayed\n+ if (!feature.getVisibility()) {\n+ continue;\n+ }\n+\n+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n+ this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n+ if (bounds.toGeometry().intersects(feature.geometry)) {\n+ if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n+ }\n+ }\n+ }\n+ }\n+ }\n+ this.multiple = prevMultiple;\n+ this.events.triggerEvent(\"boxselectionend\", {\n+ layers: layers\n+ });\n+ }\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * Set the map property for the control. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ this.handlers.feature.setMap(map);\n+ if (this.box) {\n+ this.handlers.box.setMap(map);\n+ }\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ },\n+\n+ /**\n+ * APIMethod: setLayer\n+ * Attach a new layer to the control, overriding any existing layers.\n+ *\n+ * Parameters:\n+ * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n+ * {<OpenLayers.Layer.Vector>}\n+ */\n+ setLayer: function(layers) {\n+ var isActive = this.active;\n+ this.unselectAll();\n+ this.deactivate();\n+ if (this.layers) {\n+ this.layer.destroy();\n+ this.layers = null;\n+ }\n+ this.initLayer(layers);\n+ this.handlers.feature.layer = this.layer;\n+ if (isActive) {\n+ this.activate();\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Popup.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+\n+/**\n+ * Class: OpenLayers.Popup\n+ * A popup is a small div that can opened and closed on the map.\n+ * Typically opened in response to clicking on a marker. \n+ * See <OpenLayers.Marker>. Popup's don't require their own\n+ * layer and are added the the map using the <OpenLayers.Map.addPopup>\n+ * method.\n+ *\n+ * Example:\n+ * (code)\n+ * popup = new OpenLayers.Popup(\"chicken\", \n+ * new OpenLayers.LonLat(5,40),\n+ * new OpenLayers.Size(200,200),\n+ * \"example popup\",\n+ * true);\n+ * \n+ * map.addPopup(popup);\n+ * (end)\n+ */\n+OpenLayers.Popup = OpenLayers.Class({\n+\n+ /** \n+ * Property: events \n+ * {<OpenLayers.Events>} custom event manager \n+ */\n+ events: null,\n+\n+ /** Property: id\n+ * {String} the unique identifier assigned to this popup.\n+ */\n+ id: \"\",\n+\n+ /** \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} the position of this popup on the map\n+ */\n+ lonlat: null,\n+\n+ /** \n+ * Property: div \n+ * {DOMElement} the div that contains this popup.\n+ */\n+ div: null,\n+\n+ /** \n+ * Property: contentSize \n+ * {<OpenLayers.Size>} the width and height of the content.\n+ */\n+ contentSize: null,\n+\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>} the width and height of the popup.\n+ */\n+ size: null,\n+\n+ /** \n+ * Property: contentHTML \n+ * {String} An HTML string for this popup to display.\n+ */\n+ contentHTML: null,\n+\n+ /** \n+ * Property: backgroundColor \n+ * {String} the background color used by the popup.\n+ */\n+ backgroundColor: \"\",\n+\n+ /** \n+ * Property: opacity \n+ * {float} the opacity of this popup (between 0.0 and 1.0)\n+ */\n+ opacity: \"\",\n+\n+ /** \n+ * Property: border \n+ * {String} the border size of the popup. (eg 2px)\n+ */\n+ border: \"\",\n+\n+ /** \n+ * Property: contentDiv \n+ * {DOMElement} a reference to the element that holds the content of\n+ * the div.\n+ */\n+ contentDiv: null,\n+\n+ /** \n+ * Property: groupDiv \n+ * {DOMElement} First and only child of 'div'. The group Div contains the\n+ * 'contentDiv' and the 'closeDiv'.\n+ */\n+ groupDiv: null,\n+\n+ /** \n+ * Property: closeDiv\n+ * {DOMElement} the optional closer image\n+ */\n+ closeDiv: null,\n+\n+ /** \n+ * APIProperty: autoSize\n+ * {Boolean} Resize the popup to auto-fit the contents.\n+ * Default is false.\n+ */\n+ autoSize: false,\n+\n+ /**\n+ * APIProperty: minSize\n+ * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.\n+ */\n+ minSize: null,\n+\n+ /**\n+ * APIProperty: maxSize\n+ * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.\n+ */\n+ maxSize: null,\n+\n+ /** \n+ * Property: displayClass\n+ * {String} The CSS class of the popup.\n+ */\n+ displayClass: \"olPopup\",\n+\n+ /** \n+ * Property: contentDisplayClass\n+ * {String} The CSS class of the popup content div.\n+ */\n+ contentDisplayClass: \"olPopupContent\",\n+\n+ /** \n+ * Property: padding \n+ * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal \n+ * padding of the content div inside the popup. This was originally\n+ * confused with the css padding as specified in style.css's \n+ * 'olPopupContent' class. We would like to get rid of this altogether,\n+ * except that it does come in handy for the framed and anchoredbubble\n+ * popups, who need to maintain yet another barrier between their \n+ * content and the outer border of the popup itself. \n+ * \n+ * Note that in order to not break API, we must continue to support \n+ * this property being set as an integer. Really, though, we'd like to \n+ * have this specified as a Bounds object so that user can specify\n+ * distinct left, top, right, bottom paddings. With the 3.0 release\n+ * we can make this only a bounds.\n+ */\n+ padding: 0,\n+\n+ /** \n+ * Property: disableFirefoxOverflowHack\n+ * {Boolean} The hack for overflow in Firefox causes all elements \n+ * to be re-drawn, which causes Flash elements to be \n+ * re-initialized, which is troublesome.\n+ * With this property the hack can be disabled.\n+ */\n+ disableFirefoxOverflowHack: false,\n+\n+ /**\n+ * Method: fixPadding\n+ * To be removed in 3.0, this function merely helps us to deal with the \n+ * case where the user may have set an integer value for padding, \n+ * instead of an <OpenLayers.Bounds> object.\n+ */\n+ fixPadding: function() {\n+ if (typeof this.padding == \"number\") {\n+ this.padding = new OpenLayers.Bounds(\n+ this.padding, this.padding, this.padding, this.padding\n+ );\n+ }\n+ },\n+\n+ /**\n+ * APIProperty: panMapIfOutOfView\n+ * {Boolean} When drawn, pan map such that the entire popup is visible in\n+ * the current viewport (if necessary).\n+ * Default is false.\n+ */\n+ panMapIfOutOfView: false,\n+\n+ /**\n+ * APIProperty: keepInMap \n+ * {Boolean} If panMapIfOutOfView is false, and this property is true, \n+ * contrain the popup such that it always fits in the available map\n+ * space. By default, this is not set on the base class. If you are\n+ * creating popups that are near map edges and not allowing pannning,\n+ * and especially if you have a popup which has a\n+ * fixedRelativePosition, setting this to false may be a smart thing to\n+ * do. Subclasses may want to override this setting.\n+ * \n+ * Default is false.\n+ */\n+ keepInMap: false,\n+\n+ /**\n+ * APIProperty: closeOnMove\n+ * {Boolean} When map pans, close the popup.\n+ * Default is false.\n+ */\n+ closeOnMove: false,\n+\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map\n+ */\n+ map: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Popup\n+ * Create a popup.\n+ * \n+ * Parameters: \n+ * id - {String} a unqiue identifier for this popup. If null is passed\n+ * an identifier will be automatically generated. \n+ * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will\n+ * be shown.\n+ * contentSize - {<OpenLayers.Size>} The size of the content.\n+ * contentHTML - {String} An HTML string to display inside the \n+ * popup.\n+ * closeBox - {Boolean} Whether to display a close box inside\n+ * the popup.\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ */\n+ initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n+ if (id == null) {\n+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ }\n+\n+ this.id = id;\n+ this.lonlat = lonlat;\n+\n+ this.contentSize = (contentSize != null) ? contentSize :\n+ new OpenLayers.Size(\n+ OpenLayers.Popup.WIDTH,\n+ OpenLayers.Popup.HEIGHT);\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n+ }\n+ this.backgroundColor = OpenLayers.Popup.COLOR;\n+ this.opacity = OpenLayers.Popup.OPACITY;\n+ this.border = OpenLayers.Popup.BORDER;\n+\n+ this.div = OpenLayers.Util.createDiv(this.id, null, null,\n+ null, null, null, \"hidden\");\n+ this.div.className = this.displayClass;\n+\n+ var groupDivId = this.id + \"_GroupDiv\";\n+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null,\n+ null, \"relative\", null,\n+ \"hidden\");\n+\n+ var id = this.div.id + \"_contentDiv\";\n+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(),\n+ null, \"relative\");\n+ this.contentDiv.className = this.contentDisplayClass;\n+ this.groupDiv.appendChild(this.contentDiv);\n+ this.div.appendChild(this.groupDiv);\n+\n+ if (closeBox) {\n+ this.addCloseBox(closeBoxCallback);\n+ }\n+\n+ this.registerEvents();\n+ },\n+\n+ /** \n+ * Method: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+\n+ this.id = null;\n+ this.lonlat = null;\n+ this.size = null;\n+ this.contentHTML = null;\n+\n+ this.backgroundColor = null;\n+ this.opacity = null;\n+ this.border = null;\n+\n+ if (this.closeOnMove && this.map) {\n+ this.map.events.unregister(\"movestart\", this, this.hide);\n+ }\n+\n+ this.events.destroy();\n+ this.events = null;\n+\n+ if (this.closeDiv) {\n+ OpenLayers.Event.stopObservingElement(this.closeDiv);\n+ this.groupDiv.removeChild(this.closeDiv);\n+ }\n+ this.closeDiv = null;\n+\n+ this.div.removeChild(this.groupDiv);\n+ this.groupDiv = null;\n+\n+ if (this.map != null) {\n+ this.map.removePopup(this);\n+ }\n+ this.map = null;\n+ this.div = null;\n+\n+ this.autoSize = null;\n+ this.minSize = null;\n+ this.maxSize = null;\n+ this.padding = null;\n+ this.panMapIfOutOfView = null;\n+ },\n+\n+ /** \n+ * Method: draw\n+ * Constructs the elements that make up the popup.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>} the position the popup in pixels.\n+ * \n+ * Returns:\n+ * {DOMElement} Reference to a div that contains the drawn popup\n+ */\n+ draw: function(px) {\n+ if (px == null) {\n+ if ((this.lonlat != null) && (this.map != null)) {\n+ px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ }\n+ }\n+\n+ // this assumes that this.map already exists, which is okay because \n+ // this.draw is only called once the popup has been added to the map.\n+ if (this.closeOnMove) {\n+ this.map.events.register(\"movestart\", this, this.hide);\n+ }\n+\n+ //listen to movestart, moveend to disable overflow (FF bug)\n+ if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {\n+ this.map.events.register(\"movestart\", this, function() {\n+ var style = document.defaultView.getComputedStyle(\n+ this.contentDiv, null\n+ );\n+ var currentOverflow = style.getPropertyValue(\"overflow\");\n+ if (currentOverflow != \"hidden\") {\n+ this.contentDiv._oldOverflow = currentOverflow;\n+ this.contentDiv.style.overflow = \"hidden\";\n+ }\n+ });\n+ this.map.events.register(\"moveend\", this, function() {\n+ var oldOverflow = this.contentDiv._oldOverflow;\n+ if (oldOverflow) {\n+ this.contentDiv.style.overflow = oldOverflow;\n+ this.contentDiv._oldOverflow = null;\n+ }\n+ });\n+ }\n+\n+ this.moveTo(px);\n+ if (!this.autoSize && !this.size) {\n+ this.setSize(this.contentSize);\n+ }\n+ this.setBackgroundColor();\n+ this.setOpacity();\n+ this.setBorder();\n+ this.setContentHTML();\n+\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n+ }\n+\n+ return this.div;\n+ },\n+\n+ /** \n+ * Method: updatePosition\n+ * if the popup has a lonlat and its map members set, \n+ * then have it move itself to its proper position\n+ */\n+ updatePosition: function() {\n+ if ((this.lonlat) && (this.map)) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ if (px) {\n+ this.moveTo(px);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * \n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>} the top and left position of the popup div. \n+ */\n+ moveTo: function(px) {\n+ if ((px != null) && (this.div != null)) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\";\n+ }\n+ },\n+\n+ /**\n+ * Method: visible\n+ *\n+ * Returns: \n+ * {Boolean} Boolean indicating whether or not the popup is visible\n+ */\n+ visible: function() {\n+ return OpenLayers.Element.visible(this.div);\n+ },\n+\n+ /**\n+ * Method: toggle\n+ * Toggles visibility of the popup.\n+ */\n+ toggle: function() {\n+ if (this.visible()) {\n+ this.hide();\n+ } else {\n+ this.show();\n+ }\n+ },\n+\n+ /**\n+ * Method: show\n+ * Makes the popup visible.\n+ */\n+ show: function() {\n+ this.div.style.display = '';\n+\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView();\n+ }\n+ },\n+\n+ /**\n+ * Method: hide\n+ * Makes the popup invisible.\n+ */\n+ hide: function() {\n+ this.div.style.display = 'none';\n+ },\n+\n+ /**\n+ * Method: setSize\n+ * Used to adjust the size of the popup. \n+ *\n+ * Parameters:\n+ * contentSize - {<OpenLayers.Size>} the new size for the popup's \n+ * contents div (in pixels).\n+ */\n+ setSize: function(contentSize) {\n+ this.size = contentSize.clone();\n+\n+ // if our contentDiv has a css 'padding' set on it by a stylesheet, we \n+ // must add that to the desired \"size\". \n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n+\n+ // take into account the popup's 'padding' property\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n+\n+ // make extra space for the close div\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right;\n+ }\n+\n+ //increase size of the main popup div to take into account the \n+ // users's desired padding and close div. \n+ this.size.w += wPadding;\n+ this.size.h += hPadding;\n+\n+ //now if our browser is IE, we need to actually make the contents \n+ // div itself bigger to take its own padding into effect. this makes \n+ // me want to shoot someone, but so it goes.\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.contentSize.w +=\n+ contentDivPadding.left + contentDivPadding.right;\n+ this.contentSize.h +=\n+ contentDivPadding.bottom + contentDivPadding.top;\n+ }\n+\n+ if (this.div != null) {\n+ this.div.style.width = this.size.w + \"px\";\n+ this.div.style.height = this.size.h + \"px\";\n+ }\n+ if (this.contentDiv != null) {\n+ this.contentDiv.style.width = contentSize.w + \"px\";\n+ this.contentDiv.style.height = contentSize.h + \"px\";\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: updateSize\n+ * Auto size the popup so that it precisely fits its contents (as \n+ * determined by this.contentDiv.innerHTML). Popup size will, of\n+ * course, be limited by the available space on the current map\n+ */\n+ updateSize: function() {\n+\n+ // determine actual render dimensions of the contents by putting its\n+ // contents into a fake contentDiv (for the CSS) and then measuring it\n+ var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" +\n+ this.contentDiv.innerHTML +\n+ \"</div>\";\n+\n+ var containerElement = (this.map) ? this.map.div : document.body;\n+ var realSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, null, {\n+ displayClass: this.displayClass,\n+ containerElement: containerElement\n+ }\n+ );\n+\n+ // is the \"real\" size of the div is safe to display in our map?\n+ var safeSize = this.getSafeContentSize(realSize);\n+\n+ var newSize = null;\n+ if (safeSize.equals(realSize)) {\n+ //real size of content is small enough to fit on the map, \n+ // so we use real size.\n+ newSize = realSize;\n+\n+ } else {\n+\n+ // make a new 'size' object with the clipped dimensions \n+ // set or null if not clipped.\n+ var fixedSize = {\n+ w: (safeSize.w < realSize.w) ? safeSize.w : null,\n+ h: (safeSize.h < realSize.h) ? safeSize.h : null\n+ };\n+\n+ if (fixedSize.w && fixedSize.h) {\n+ //content is too big in both directions, so we will use \n+ // max popup size (safeSize), knowing well that it will \n+ // overflow both ways. \n+ newSize = safeSize;\n+ } else {\n+ //content is clipped in only one direction, so we need to \n+ // run getRenderedDimensions() again with a fixed dimension\n+ var clippedSize = OpenLayers.Util.getRenderedDimensions(\n+ preparedHTML, fixedSize, {\n+ displayClass: this.contentDisplayClass,\n+ containerElement: containerElement\n+ }\n+ );\n+\n+ //if the clipped size is still the same as the safeSize, \n+ // that means that our content must be fixed in the \n+ // offending direction. If overflow is 'auto', this means \n+ // we are going to have a scrollbar for sure, so we must \n+ // adjust for that.\n+ //\n+ var currentOverflow = OpenLayers.Element.getStyle(\n+ this.contentDiv, \"overflow\"\n+ );\n+ if ((currentOverflow != \"hidden\") &&\n+ (clippedSize.equals(safeSize))) {\n+ var scrollBar = OpenLayers.Util.getScrollbarWidth();\n+ if (fixedSize.w) {\n+ clippedSize.h += scrollBar;\n+ } else {\n+ clippedSize.w += scrollBar;\n+ }\n+ }\n+\n+ newSize = this.getSafeContentSize(clippedSize);\n+ }\n+ }\n+ this.setSize(newSize);\n+ },\n+\n+ /**\n+ * Method: setBackgroundColor\n+ * Sets the background color of the popup.\n+ *\n+ * Parameters:\n+ * color - {String} the background color. eg \"#FFBBBB\"\n+ */\n+ setBackgroundColor: function(color) {\n+ if (color != undefined) {\n+ this.backgroundColor = color;\n+ }\n+\n+ if (this.div != null) {\n+ this.div.style.backgroundColor = this.backgroundColor;\n+ }\n+ },\n+\n+ /**\n+ * Method: setOpacity\n+ * Sets the opacity of the popup.\n+ * \n+ * Parameters:\n+ * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n+ */\n+ setOpacity: function(opacity) {\n+ if (opacity != undefined) {\n+ this.opacity = opacity;\n+ }\n+\n+ if (this.div != null) {\n+ // for Mozilla and Safari\n+ this.div.style.opacity = this.opacity;\n+\n+ // for IE\n+ this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';\n+ }\n+ },\n+\n+ /**\n+ * Method: setBorder\n+ * Sets the border style of the popup.\n+ *\n+ * Parameters:\n+ * border - {String} The border style value. eg 2px \n+ */\n+ setBorder: function(border) {\n+ if (border != undefined) {\n+ this.border = border;\n+ }\n+\n+ if (this.div != null) {\n+ this.div.style.border = this.border;\n+ }\n+ },\n+\n+ /**\n+ * Method: setContentHTML\n+ * Allows the user to set the HTML content of the popup.\n+ *\n+ * Parameters:\n+ * contentHTML - {String} HTML for the div.\n+ */\n+ setContentHTML: function(contentHTML) {\n+\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML;\n+ }\n+\n+ if ((this.contentDiv != null) &&\n+ (this.contentHTML != null) &&\n+ (this.contentHTML != this.contentDiv.innerHTML)) {\n+\n+ this.contentDiv.innerHTML = this.contentHTML;\n+\n+ if (this.autoSize) {\n+\n+ //if popup has images, listen for when they finish\n+ // loading and resize accordingly\n+ this.registerImageListeners();\n+\n+ //auto size the popup to its current contents\n+ this.updateSize();\n+ }\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: registerImageListeners\n+ * Called when an image contained by the popup loaded. this function\n+ * updates the popup size, then unregisters the image load listener.\n+ */\n registerImageListeners: function() {\n \n // As the images load, this function will call updateSize() to \n // resize the popup to fit the content div (which presumably is now\n // bigger than when the image was not loaded).\n // \n // If the 'panMapIfOutOfView' property is set, we will pan the newly\n@@ -21807,10847 +26503,7260 @@\n /**\n * APIProperty: maxSize\n * {<OpenLayers.Size>}\n */\n maxSize: new OpenLayers.Size(1200, 660),\n \n /** \n- * Constructor: OpenLayers.Popup.FramedCloud\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object to which we'll anchor the popup. Must expose \n- * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n- * (Note that this is generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n-\n- this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');\n- OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n- this.contentDiv.className = this.contentDisplayClass;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n- });\n-/* ======================================================================\n- OpenLayers/Format.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format\n- * Base class for format reading/writing a variety of formats. Subclasses\n- * of OpenLayers.Format are expected to have read and write methods.\n- */\n-OpenLayers.Format = OpenLayers.Class({\n-\n- /**\n- * Property: options\n- * {Object} A reference to options passed to the constructor.\n- */\n- options: null,\n-\n- /**\n- * APIProperty: externalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The externalProjection is the projection used by\n- * the content which is passed into read or which comes out of write.\n- * In order to reproject, a projection transformation function for the\n- * specified projections must be available. This support may be \n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- externalProjection: null,\n-\n- /**\n- * APIProperty: internalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The internalProjection is the projection used by\n- * the geometries which are returned by read or which are passed into\n- * write. In order to reproject, a projection transformation function\n- * for the specified projections must be available. This support may be\n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- internalProjection: null,\n-\n- /**\n- * APIProperty: data\n- * {Object} When <keepData> is true, this is the parsed string sent to\n- * <read>.\n- */\n- data: null,\n-\n- /**\n- * APIProperty: keepData\n- * {Object} Maintain a reference (<data>) to the most recently read data.\n- * Default is false.\n- */\n- keepData: false,\n-\n- /**\n- * Constructor: OpenLayers.Format\n- * Instances of this class are not useful. See one of the subclasses.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * format\n- *\n- * Valid options:\n- * keepData - {Boolean} If true, upon <read>, the data property will be\n- * set to the parsed object (e.g. the json or xml object).\n- *\n- * Returns:\n- * An instance of OpenLayers.Format\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up.\n- */\n- destroy: function() {},\n-\n- /**\n- * Method: read\n- * Read data from a string, and return an object whose type depends on the\n- * subclass. \n- * \n- * Parameters:\n- * data - {string} Data to read/parse.\n- *\n- * Returns:\n- * Depends on the subclass\n- */\n- read: function(data) {\n- throw new Error('Read not implemented.');\n- },\n-\n- /**\n- * Method: write\n- * Accept an object, and return a string. \n- *\n- * Parameters:\n- * object - {Object} Object to be serialized\n- *\n- * Returns:\n- * {String} A string representation of the object.\n- */\n- write: function(object) {\n- throw new Error('Write not implemented.');\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/JSON.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * Note:\n- * This work draws heavily from the public domain JSON serializer/deserializer\n- * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n- * basic data prototypes.\n- */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.JSON\n- * A parser to read/write JSON safely. Create a new instance with the\n- * <OpenLayers.Format.JSON> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * APIProperty: indent\n- * {String} For \"pretty\" printing, the indent string will be used once for\n- * each indentation level.\n- */\n- indent: \" \",\n-\n- /**\n- * APIProperty: space\n- * {String} For \"pretty\" printing, the space string will be used after\n- * the \":\" separating a name/value pair.\n- */\n- space: \" \",\n-\n- /**\n- * APIProperty: newline\n- * {String} For \"pretty\" printing, the newline string will be used at the\n- * end of each name/value pair or array item.\n- */\n- newline: \"\\n\",\n-\n- /**\n- * Property: level\n- * {Integer} For \"pretty\" printing, this is incremented/decremented during\n- * serialization.\n- */\n- level: 0,\n-\n- /**\n- * Property: pretty\n- * {Boolean} Serialize with extra whitespace for structure. This is set\n- * by the <write> method.\n- */\n- pretty: false,\n-\n- /**\n- * Property: nativeJSON\n- * {Boolean} Does the browser support native json?\n- */\n- nativeJSON: (function() {\n- return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n- })(),\n-\n- /**\n- * Constructor: OpenLayers.Format.JSON\n- * Create a new parser for JSON.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Deserialize a json string.\n- *\n- * Parameters:\n- * json - {String} A JSON string\n- * filter - {Function} A function which will be called for every key and\n- * value at every level of the final result. Each value will be\n- * replaced by the result of the filter function. This can be used to\n- * reform generic objects into instances of classes, or to transform\n- * date strings into Date objects.\n- * \n- * Returns:\n- * {Object} An object, array, string, or number .\n- */\n- read: function(json, filter) {\n- var object;\n- if (this.nativeJSON) {\n- object = JSON.parse(json, filter);\n- } else try {\n- /**\n- * Parsing happens in three stages. In the first stage, we run the\n- * text against a regular expression which looks for non-JSON\n- * characters. We are especially concerned with '()' and 'new'\n- * because they can cause invocation, and '=' because it can\n- * cause mutation. But just to be safe, we will reject all\n- * unexpected characters.\n- */\n- if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n-\n- /**\n- * In the second stage we use the eval function to compile the\n- * text into a JavaScript structure. The '{' operator is\n- * subject to a syntactic ambiguity in JavaScript - it can\n- * begin a block or an object literal. We wrap the text in\n- * parens to eliminate the ambiguity.\n- */\n- object = eval('(' + json + ')');\n-\n- /**\n- * In the optional third stage, we recursively walk the new\n- * structure, passing each name/value pair to a filter\n- * function for possible transformation.\n- */\n- if (typeof filter === 'function') {\n- function walk(k, v) {\n- if (v && typeof v === 'object') {\n- for (var i in v) {\n- if (v.hasOwnProperty(i)) {\n- v[i] = walk(i, v[i]);\n- }\n- }\n- }\n- return filter(k, v);\n- }\n- object = walk('', object);\n- }\n- }\n- } catch (e) {\n- // Fall through if the regexp test fails.\n- }\n-\n- if (this.keepData) {\n- this.data = object;\n- }\n-\n- return object;\n- },\n-\n- /**\n- * APIMethod: write\n- * Serialize an object into a JSON string.\n- *\n- * Parameters:\n- * value - {String} The object, array, string, number, boolean or date\n- * to be serialized.\n- * pretty - {Boolean} Structure the output with newlines and indentation.\n- * Default is false.\n- *\n- * Returns:\n- * {String} The JSON string representation of the input value.\n- */\n- write: function(value, pretty) {\n- this.pretty = !!pretty;\n- var json = null;\n- var type = typeof value;\n- if (this.serialize[type]) {\n- try {\n- json = (!this.pretty && this.nativeJSON) ?\n- JSON.stringify(value) :\n- this.serialize[type].apply(this, [value]);\n- } catch (err) {\n- OpenLayers.Console.error(\"Trouble serializing: \" + err);\n- }\n- }\n- return json;\n- },\n-\n- /**\n- * Method: writeIndent\n- * Output an indentation string depending on the indentation level.\n- *\n- * Returns:\n- * {String} An appropriate indentation string.\n- */\n- writeIndent: function() {\n- var pieces = [];\n- if (this.pretty) {\n- for (var i = 0; i < this.level; ++i) {\n- pieces.push(this.indent);\n- }\n- }\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: writeNewline\n- * Output a string representing a newline if in pretty printing mode.\n- *\n- * Returns:\n- * {String} A string representing a new line.\n- */\n- writeNewline: function() {\n- return (this.pretty) ? this.newline : '';\n- },\n-\n- /**\n- * Method: writeSpace\n- * Output a string representing a space if in pretty printing mode.\n- *\n- * Returns:\n- * {String} A space.\n- */\n- writeSpace: function() {\n- return (this.pretty) ? this.space : '';\n- },\n-\n- /**\n- * Property: serialize\n- * Object with properties corresponding to the serializable data types.\n- * Property values are functions that do the actual serializing.\n- */\n- serialize: {\n- /**\n- * Method: serialize.object\n- * Transform an object into a JSON string.\n- *\n- * Parameters:\n- * object - {Object} The object to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the object.\n- */\n- 'object': function(object) {\n- // three special objects that we want to treat differently\n- if (object == null) {\n- return \"null\";\n- }\n- if (object.constructor == Date) {\n- return this.serialize.date.apply(this, [object]);\n- }\n- if (object.constructor == Array) {\n- return this.serialize.array.apply(this, [object]);\n- }\n- var pieces = ['{'];\n- this.level += 1;\n- var key, keyJSON, valueJSON;\n-\n- var addComma = false;\n- for (key in object) {\n- if (object.hasOwnProperty(key)) {\n- // recursive calls need to allow for sub-classing\n- keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [key, this.pretty]);\n- valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [object[key], this.pretty]);\n- if (keyJSON != null && valueJSON != null) {\n- if (addComma) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(),\n- keyJSON, ':', this.writeSpace(), valueJSON);\n- addComma = true;\n- }\n- }\n- }\n-\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), '}');\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: serialize.array\n- * Transform an array into a JSON string.\n- *\n- * Parameters:\n- * array - {Array} The array to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the array.\n- */\n- 'array': function(array) {\n- var json;\n- var pieces = ['['];\n- this.level += 1;\n-\n- for (var i = 0, len = array.length; i < len; ++i) {\n- // recursive calls need to allow for sub-classing\n- json = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [array[i], this.pretty]);\n- if (json != null) {\n- if (i > 0) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), json);\n- }\n- }\n-\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), ']');\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: serialize.string\n- * Transform a string into a JSON string.\n- *\n- * Parameters:\n- * string - {String} The string to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the string.\n- */\n- 'string': function(string) {\n- // If the string contains no control characters, no quote characters, and no\n- // backslash characters, then we can simply slap some quotes around it.\n- // Otherwise we must also replace the offending characters with safe\n- // sequences. \n- var m = {\n- '\\b': '\\\\b',\n- '\\t': '\\\\t',\n- '\\n': '\\\\n',\n- '\\f': '\\\\f',\n- '\\r': '\\\\r',\n- '\"': '\\\\\"',\n- '\\\\': '\\\\\\\\'\n- };\n- if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n- return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n- var c = m[b];\n- if (c) {\n- return c;\n- }\n- c = b.charCodeAt();\n- return '\\\\u00' +\n- Math.floor(c / 16).toString(16) +\n- (c % 16).toString(16);\n- }) + '\"';\n- }\n- return '\"' + string + '\"';\n- },\n-\n- /**\n- * Method: serialize.number\n- * Transform a number into a JSON string.\n- *\n- * Parameters:\n- * number - {Number} The number to be serialized.\n- *\n- * Returns:\n- * {String} A JSON string representing the number.\n- */\n- 'number': function(number) {\n- return isFinite(number) ? String(number) : \"null\";\n- },\n-\n- /**\n- * Method: serialize.boolean\n- * Transform a boolean into a JSON string.\n- *\n- * Parameters:\n- * bool - {Boolean} The boolean to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the boolean.\n- */\n- 'boolean': function(bool) {\n- return String(bool);\n- },\n-\n- /**\n- * Method: serialize.object\n- * Transform a date into a JSON string.\n- *\n- * Parameters:\n- * date - {Date} The date to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the date.\n- */\n- 'date': function(date) {\n- function format(number) {\n- // Format integers to have at least two digits.\n- return (number < 10) ? '0' + number : number;\n- }\n- return '\"' + date.getFullYear() + '-' +\n- format(date.getMonth() + 1) + '-' +\n- format(date.getDate()) + 'T' +\n- format(date.getHours()) + ':' +\n- format(date.getMinutes()) + ':' +\n- format(date.getSeconds()) + '\"';\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.JSON\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Geometry.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry\n- * A Geometry is a description of a geographic object. Create an instance of\n- * this class with the <OpenLayers.Geometry> constructor. This is a base class,\n- * typical geometry types are described by subclasses of this class.\n- *\n- * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must\n- * explicitly include the OpenLayers.Format.WKT in your build.\n- */\n-OpenLayers.Geometry = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique identifier for this geometry.\n- */\n- id: null,\n-\n- /**\n- * Property: parent\n- * {<OpenLayers.Geometry>}This is set when a Geometry is added as component\n- * of another geometry\n- */\n- parent: null,\n-\n- /**\n- * Property: bounds \n- * {<OpenLayers.Bounds>} The bounds of this geometry\n- */\n- bounds: null,\n-\n- /**\n- * Constructor: OpenLayers.Geometry\n- * Creates a geometry object. \n- */\n- initialize: function() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /**\n- * Method: destroy\n- * Destroy this geometry.\n- */\n- destroy: function() {\n- this.id = null;\n- this.bounds = null;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Create a clone of this geometry. Does not set any non-standard\n- * properties of the cloned geometry.\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} An exact clone of this geometry.\n- */\n- clone: function() {\n- return new OpenLayers.Geometry();\n- },\n-\n- /**\n- * Method: setBounds\n- * Set the bounds for this Geometry.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- */\n- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone();\n- }\n- },\n-\n- /**\n- * Method: clearBounds\n- * Nullify this components bounds and that of its parent as well.\n- */\n- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds();\n- }\n- },\n-\n- /**\n- * Method: extendBounds\n- * Extend the existing bounds to include the new bounds. \n- * If geometry's bounds is not yet set, then set a new Bounds.\n- * \n- * Parameters:\n- * newBounds - {<OpenLayers.Bounds>} \n- */\n- extendBounds: function(newBounds) {\n- var bounds = this.getBounds();\n- if (!bounds) {\n- this.setBounds(newBounds);\n- } else {\n- this.bounds.extend(newBounds);\n- }\n- },\n-\n- /**\n- * APIMethod: getBounds\n- * Get the bounds for this Geometry. If bounds is not set, it \n- * is calculated again, this makes queries faster.\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds();\n- }\n- return this.bounds;\n- },\n-\n- /** \n- * APIMethod: calculateBounds\n- * Recalculate the bounds for the geometry. \n- */\n- calculateBounds: function() {\n- //\n- // This should be overridden by subclasses.\n- //\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options depend on the specific geometry type.\n- * \n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {},\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {},\n-\n- /**\n- * Method: atPoint\n- * Note - This is only an approximation based on the bounds of the \n- * geometry.\n- * \n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n- * object with a 'lon' and 'lat' properties.\n- * toleranceLon - {float} Optional tolerance in Geometric Coords\n- * toleranceLat - {float} Optional tolerance in Geographic Coords\n- * \n- * Returns:\n- * {Boolean} Whether or not the geometry is at the specified location\n- */\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if ((bounds != null) && (lonlat != null)) {\n-\n- var dX = (toleranceLon != null) ? toleranceLon : 0;\n- var dY = (toleranceLat != null) ? toleranceLat : 0;\n-\n- var toleranceBounds =\n- new OpenLayers.Bounds(this.bounds.left - dX,\n- this.bounds.bottom - dY,\n- this.bounds.right + dX,\n- this.bounds.top + dY);\n-\n- atPoint = toleranceBounds.containsLonLat(lonlat);\n- }\n- return atPoint;\n- },\n-\n- /**\n- * Method: getLength\n- * Calculate the length of this geometry. This method is defined in\n- * subclasses.\n- * \n- * Returns:\n- * {Float} The length of the collection by summing its parts\n- */\n- getLength: function() {\n- //to be overridden by geometries that actually have a length\n- //\n- return 0.0;\n- },\n-\n- /**\n- * Method: getArea\n- * Calculate the area of this geometry. This method is defined in subclasses.\n- * \n- * Returns:\n- * {Float} The area of the collection by summing its parts\n- */\n- getArea: function() {\n- //to be overridden by geometries that actually have an area\n- //\n- return 0.0;\n- },\n-\n- /**\n- * APIMethod: getCentroid\n- * Calculate the centroid of this geometry. This method is defined in subclasses.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} The centroid of the collection\n- */\n- getCentroid: function() {\n- return null;\n- },\n-\n- /**\n- * Method: toString\n- * Returns a text representation of the geometry. If the WKT format is\n- * included in a build, this will be the Well-Known Text \n- * representation.\n- *\n- * Returns:\n- * {String} String representation of this geometry.\n- */\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- string = OpenLayers.Format.WKT.prototype.write(\n- new OpenLayers.Feature.Vector(this)\n- );\n- } else {\n- string = Object.prototype.toString.call(this);\n- }\n- return string;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry\"\n-});\n-\n-/**\n- * Function: OpenLayers.Geometry.fromWKT\n- * Generate a geometry given a Well-Known Text string. For this method to\n- * work, you must include the OpenLayers.Format.WKT in your build \n- * explicitly.\n- *\n- * Parameters:\n- * wkt - {String} A string representing the geometry in Well-Known Text.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry of the appropriate class.\n- */\n-OpenLayers.Geometry.fromWKT = function(wkt) {\n- var geom;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- var format = OpenLayers.Geometry.fromWKT.format;\n- if (!format) {\n- format = new OpenLayers.Format.WKT();\n- OpenLayers.Geometry.fromWKT.format = format;\n- }\n- var result = format.read(wkt);\n- if (result instanceof OpenLayers.Feature.Vector) {\n- geom = result.geometry;\n- } else if (OpenLayers.Util.isArray(result)) {\n- var len = result.length;\n- var components = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- components[i] = result[i].geometry;\n- }\n- geom = new OpenLayers.Geometry.Collection(components);\n- }\n- }\n- return geom;\n-};\n-\n-/**\n- * Method: OpenLayers.Geometry.segmentsIntersect\n- * Determine whether two line segments intersect. Optionally calculates\n- * and returns the intersection point. This function is optimized for\n- * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those\n- * obvious cases where there is no intersection, the function should\n- * not be called.\n- *\n- * Parameters:\n- * seg1 - {Object} Object representing a segment with properties x1, y1, x2,\n- * and y2. The start point is represented by x1 and y1. The end point\n- * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n- * seg2 - {Object} Object representing a segment with properties x1, y1, x2,\n- * and y2. The start point is represented by x1 and y1. The end point\n- * is represented by x2 and y2. Start and end are ordered so that x1 < x2.\n- * options - {Object} Optional properties for calculating the intersection.\n- *\n- * Valid options:\n- * point - {Boolean} Return the intersection point. If false, the actual\n- * intersection point will not be calculated. If true and the segments\n- * intersect, the intersection point will be returned. If true and\n- * the segments do not intersect, false will be returned. If true and\n- * the segments are coincident, true will be returned.\n- * tolerance - {Number} If a non-null value is provided, if the segments are\n- * within the tolerance distance, this will be considered an intersection.\n- * In addition, if the point option is true and the calculated intersection\n- * is within the tolerance distance of an end point, the endpoint will be\n- * returned instead of the calculated intersection. Further, if the\n- * intersection is within the tolerance of endpoints on both segments, or\n- * if two segment endpoints are within the tolerance distance of eachother\n- * (but no intersection is otherwise calculated), an endpoint on the\n- * first segment provided will be returned.\n- *\n- * Returns:\n- * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect.\n- * If the point argument is true, the return will be the intersection\n- * point or false if none exists. If point is true and the segments\n- * are coincident, return will be true (and the instersection is equal\n- * to the shorter segment).\n- */\n-OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n- var point = options && options.point;\n- var tolerance = options && options.tolerance;\n- var intersection = false;\n- var x11_21 = seg1.x1 - seg2.x1;\n- var y11_21 = seg1.y1 - seg2.y1;\n- var x12_11 = seg1.x2 - seg1.x1;\n- var y12_11 = seg1.y2 - seg1.y1;\n- var y22_21 = seg2.y2 - seg2.y1;\n- var x22_21 = seg2.x2 - seg2.x1;\n- var d = (y22_21 * x12_11) - (x22_21 * y12_11);\n- var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);\n- var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);\n- if (d == 0) {\n- // parallel\n- if (n1 == 0 && n2 == 0) {\n- // coincident\n- intersection = true;\n- }\n- } else {\n- var along1 = n1 / d;\n- var along2 = n2 / d;\n- if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n- // intersect\n- if (!point) {\n- intersection = true;\n- } else {\n- // calculate the intersection point\n- var x = seg1.x1 + (along1 * x12_11);\n- var y = seg1.y1 + (along1 * y12_11);\n- intersection = new OpenLayers.Geometry.Point(x, y);\n- }\n- }\n- }\n- if (tolerance) {\n- var dist;\n- if (intersection) {\n- if (point) {\n- var segs = [seg1, seg2];\n- var seg, x, y;\n- // check segment endpoints for proximity to intersection\n- // set intersection to first endpoint within the tolerance\n- outer: for (var i = 0; i < 2; ++i) {\n- seg = segs[i];\n- for (var j = 1; j < 3; ++j) {\n- x = seg[\"x\" + j];\n- y = seg[\"y\" + j];\n- dist = Math.sqrt(\n- Math.pow(x - intersection.x, 2) +\n- Math.pow(y - intersection.y, 2)\n- );\n- if (dist < tolerance) {\n- intersection.x = x;\n- intersection.y = y;\n- break outer;\n- }\n- }\n- }\n-\n- }\n- } else {\n- // no calculated intersection, but segments could be within\n- // the tolerance of one another\n- var segs = [seg1, seg2];\n- var source, target, x, y, p, result;\n- // check segment endpoints for proximity to intersection\n- // set intersection to first endpoint within the tolerance\n- outer: for (var i = 0; i < 2; ++i) {\n- source = segs[i];\n- target = segs[(i + 1) % 2];\n- for (var j = 1; j < 3; ++j) {\n- p = {\n- x: source[\"x\" + j],\n- y: source[\"y\" + j]\n- };\n- result = OpenLayers.Geometry.distanceToSegment(p, target);\n- if (result.distance < tolerance) {\n- if (point) {\n- intersection = new OpenLayers.Geometry.Point(p.x, p.y);\n- } else {\n- intersection = true;\n- }\n- break outer;\n- }\n- }\n- }\n- }\n- }\n- return intersection;\n-};\n-\n-/**\n- * Function: OpenLayers.Geometry.distanceToSegment\n- *\n- * Parameters:\n- * point - {Object} An object with x and y properties representing the\n- * point coordinates.\n- * segment - {Object} An object with x1, y1, x2, and y2 properties\n- * representing endpoint coordinates.\n- *\n- * Returns:\n- * {Object} An object with distance, along, x, and y properties. The distance\n- * will be the shortest distance between the input point and segment.\n- * The x and y properties represent the coordinates along the segment\n- * where the shortest distance meets the segment. The along attribute\n- * describes how far between the two segment points the given point is.\n- */\n-OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n- var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n- result.distance = Math.sqrt(result.distance);\n- return result;\n-};\n-\n-/**\n- * Function: OpenLayers.Geometry.distanceSquaredToSegment\n- *\n- * Usually the distanceToSegment function should be used. This variant however\n- * can be used for comparisons where the exact distance is not important.\n- *\n- * Parameters:\n- * point - {Object} An object with x and y properties representing the\n- * point coordinates.\n- * segment - {Object} An object with x1, y1, x2, and y2 properties\n- * representing endpoint coordinates.\n- *\n- * Returns:\n- * {Object} An object with squared distance, along, x, and y properties.\n- * The distance will be the shortest distance between the input point and\n- * segment. The x and y properties represent the coordinates along the\n- * segment where the shortest distance meets the segment. The along\n- * attribute describes how far between the two segment points the given\n- * point is.\n- */\n-OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n- var x0 = point.x;\n- var y0 = point.y;\n- var x1 = segment.x1;\n- var y1 = segment.y1;\n- var x2 = segment.x2;\n- var y2 = segment.y2;\n- var dx = x2 - x1;\n- var dy = y2 - y1;\n- var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /\n- (Math.pow(dx, 2) + Math.pow(dy, 2));\n- var x, y;\n- if (along <= 0.0) {\n- x = x1;\n- y = y1;\n- } else if (along >= 1.0) {\n- x = x2;\n- y = y2;\n- } else {\n- x = x1 + along * dx;\n- y = y1 + along * dy;\n- }\n- return {\n- distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n- x: x,\n- y: y,\n- along: along\n- };\n-};\n-/* ======================================================================\n- OpenLayers/Geometry/Point.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Point\n- * Point geometry class. \n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry> \n- */\n-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n-\n- /** \n- * APIProperty: x \n- * {float} \n- */\n- x: null,\n-\n- /** \n- * APIProperty: y \n- * {float} \n- */\n- y: null,\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Point\n- * Construct a point geometry.\n- *\n- * Parameters:\n- * x - {float} \n- * y - {float}\n- * \n- */\n- initialize: function(x, y) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n-\n- this.x = parseFloat(x);\n- this.y = parseFloat(y);\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Returns:\n- * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y);\n- }\n-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(obj, this);\n-\n- return obj;\n- },\n-\n- /** \n- * Method: calculateBounds\n- * Create a new Bounds based on the lon/lat\n- */\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y,\n- this.x, this.y);\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options:\n- * details - {Boolean} Return details from the distance calculation.\n- * Default is false.\n- * edge - {Boolean} Calculate the distance from this geometry to the\n- * nearest edge of the target geometry. Default is true. If true,\n- * calling distanceTo from a geometry that is wholly contained within\n- * the target will result in a non-zero distance. If false, whenever\n- * geometries intersect, calling distanceTo will return 0. If false,\n- * details cannot be returned.\n- *\n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var distance, x0, y0, x1, y1, result;\n- if (geometry instanceof OpenLayers.Geometry.Point) {\n- x0 = this.x;\n- y0 = this.y;\n- x1 = geometry.x;\n- y1 = geometry.y;\n- distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));\n- result = !details ?\n- distance : {\n- x0: x0,\n- y0: y0,\n- x1: x1,\n- y1: y1,\n- distance: distance\n- };\n- } else {\n- result = geometry.distanceTo(this, options);\n- if (details) {\n- // switch coord order since this geom is target\n- result = {\n- x0: result.x1,\n- y0: result.y1,\n- x1: result.x0,\n- y1: result.y0,\n- distance: result.distance\n- };\n- }\n- }\n- return result;\n- },\n-\n- /** \n- * APIMethod: equals\n- * Determine whether another geometry is equivalent to this one. Geometries\n- * are considered equivalent if all components have the same coordinates.\n- * \n- * Parameters:\n- * geom - {<OpenLayers.Geometry.Point>} The geometry to test. \n- *\n- * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\n- */\n- equals: function(geom) {\n- var equals = false;\n- if (geom != null) {\n- equals = ((this.x == geom.x && this.y == geom.y) ||\n- (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));\n- }\n- return equals;\n- },\n-\n- /**\n- * Method: toShortString\n- *\n- * Returns:\n- * {String} Shortened String representation of Point object. \n- * (ex. <i>\"5, 42\"</i>)\n- */\n- toShortString: function() {\n- return (this.x + \", \" + this.y);\n- },\n-\n- /**\n- * APIMethod: move\n- * Moves a geometry by the given displacement along positive x and y axes.\n- * This modifies the position of the geometry and clears the cached\n- * bounds.\n- *\n- * Parameters:\n- * x - {Float} Distance to move geometry in positive x direction. \n- * y - {Float} Distance to move geometry in positive y direction.\n- */\n- move: function(x, y) {\n- this.x = this.x + x;\n- this.y = this.y + y;\n- this.clearBounds();\n- },\n-\n- /**\n- * APIMethod: rotate\n- * Rotate a point around another.\n- *\n- * Parameters:\n- * angle - {Float} Rotation angle in degrees (measured counterclockwise\n- * from the positive x-axis)\n- * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n- */\n- rotate: function(angle, origin) {\n- angle *= Math.PI / 180;\n- var radius = this.distanceTo(origin);\n- var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);\n- this.x = origin.x + (radius * Math.cos(theta));\n- this.y = origin.y + (radius * Math.sin(theta));\n- this.clearBounds();\n- },\n-\n- /**\n- * APIMethod: getCentroid\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} The centroid of the collection\n- */\n- getCentroid: function() {\n- return new OpenLayers.Geometry.Point(this.x, this.y);\n- },\n-\n- /**\n- * APIMethod: resize\n- * Resize a point relative to some origin. For points, this has the effect\n- * of scaling a vector (from the origin to the point). This method is\n- * more useful on geometry collection subclasses.\n- *\n- * Parameters:\n- * scale - {Float} Ratio of the new distance from the origin to the old\n- * distance from the origin. A scale of 2 doubles the\n- * distance between the point and origin.\n- * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n- * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} - The current geometry. \n- */\n- resize: function(scale, origin, ratio) {\n- ratio = (ratio == undefined) ? 1 : ratio;\n- this.x = origin.x + (scale * ratio * (this.x - origin.x));\n- this.y = origin.y + (scale * (this.y - origin.y));\n- this.clearBounds();\n- return this;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this one.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.equals(geometry);\n- } else {\n- intersect = geometry.intersects(this);\n- }\n- return intersect;\n- },\n-\n- /**\n- * APIMethod: transform\n- * Translate the x,y properties of the point from source to dest.\n- * \n- * Parameters:\n- * source - {<OpenLayers.Projection>} \n- * dest - {<OpenLayers.Projection>}\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} \n- */\n- transform: function(source, dest) {\n- if ((source && dest)) {\n- OpenLayers.Projection.transform(\n- this, source, dest);\n- this.bounds = null;\n- }\n- return this;\n- },\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {\n- return [this];\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.Point\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/Collection.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Collection\n- * A Collection is exactly what it sounds like: A collection of different \n- * Geometries. These are stored in the local parameter <components> (which\n- * can be passed as a parameter to the constructor). \n- * \n- * As new geometries are added to the collection, they are NOT cloned. \n- * When removing geometries, they need to be specified by reference (ie you \n- * have to pass in the *exact* geometry to be removed).\n- * \n- * The <getArea> and <getLength> functions here merely iterate through\n- * the components, summing their respective areas and lengths.\n- *\n- * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Geometry> \n- */\n-OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n-\n- /**\n- * APIProperty: components\n- * {Array(<OpenLayers.Geometry>)} The component parts of this geometry\n- */\n- components: null,\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: null,\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Collection\n- * Creates a Geometry Collection -- a list of geoms.\n- *\n- * Parameters: \n- * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries\n- *\n- */\n- initialize: function(components) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n- this.components = [];\n- if (components != null) {\n- this.addComponents(components);\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Destroy this geometry.\n- */\n- destroy: function() {\n- this.components.length = 0;\n- this.components = null;\n- OpenLayers.Geometry.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clone this geometry.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Collection>} An exact clone of this collection\n- */\n- clone: function() {\n- var geometry = eval(\"new \" + this.CLASS_NAME + \"()\");\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- geometry.addComponent(this.components[i].clone());\n- }\n-\n- // catch any randomly tagged-on properties\n- OpenLayers.Util.applyDefaults(geometry, this);\n-\n- return geometry;\n- },\n-\n- /**\n- * Method: getComponentsString\n- * Get a string representing the components for this collection\n- * \n- * Returns:\n- * {String} A string representation of the components of this geometry\n- */\n- getComponentsString: function() {\n- var strings = [];\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- strings.push(this.components[i].toShortString());\n- }\n- return strings.join(\",\");\n- },\n-\n- /**\n- * APIMethod: calculateBounds\n- * Recalculate the bounds by iterating through the components and \n- * calling calling extendBounds() on each item.\n- */\n- calculateBounds: function() {\n- this.bounds = null;\n- var bounds = new OpenLayers.Bounds();\n- var components = this.components;\n- if (components) {\n- for (var i = 0, len = components.length; i < len; i++) {\n- bounds.extend(components[i].getBounds());\n- }\n- }\n- // to preserve old behavior, we only set bounds if non-null\n- // in the future, we could add bounds.isEmpty()\n- if (bounds.left != null && bounds.bottom != null &&\n- bounds.right != null && bounds.top != null) {\n- this.setBounds(bounds);\n- }\n- },\n-\n- /**\n- * APIMethod: addComponents\n- * Add components to this geometry.\n- *\n- * Parameters:\n- * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add\n- */\n- addComponents: function(components) {\n- if (!(OpenLayers.Util.isArray(components))) {\n- components = [components];\n- }\n- for (var i = 0, len = components.length; i < len; i++) {\n- this.addComponent(components[i]);\n- }\n- },\n-\n- /**\n- * Method: addComponent\n- * Add a new component (geometry) to the collection. If this.componentTypes\n- * is set, then the component class name must be in the componentTypes array.\n- *\n- * The bounds cache is reset.\n- * \n- * Parameters:\n- * component - {<OpenLayers.Geometry>} A geometry to add\n- * index - {int} Optional index into the array to insert the component\n- *\n- * Returns:\n- * {Boolean} The component geometry was successfully added\n- */\n- addComponent: function(component, index) {\n- var added = false;\n- if (component) {\n- if (this.componentTypes == null ||\n- (OpenLayers.Util.indexOf(this.componentTypes,\n- component.CLASS_NAME) > -1)) {\n-\n- if (index != null && (index < this.components.length)) {\n- var components1 = this.components.slice(0, index);\n- var components2 = this.components.slice(index,\n- this.components.length);\n- components1.push(component);\n- this.components = components1.concat(components2);\n- } else {\n- this.components.push(component);\n- }\n- component.parent = this;\n- this.clearBounds();\n- added = true;\n- }\n- }\n- return added;\n- },\n-\n- /**\n- * APIMethod: removeComponents\n- * Remove components from this geometry.\n- *\n- * Parameters:\n- * components - {Array(<OpenLayers.Geometry>)} The components to be removed\n- *\n- * Returns: \n- * {Boolean} A component was removed.\n- */\n- removeComponents: function(components) {\n- var removed = false;\n-\n- if (!(OpenLayers.Util.isArray(components))) {\n- components = [components];\n- }\n- for (var i = components.length - 1; i >= 0; --i) {\n- removed = this.removeComponent(components[i]) || removed;\n- }\n- return removed;\n- },\n-\n- /**\n- * Method: removeComponent\n- * Remove a component from this geometry.\n- *\n- * Parameters:\n- * component - {<OpenLayers.Geometry>} \n- *\n- * Returns: \n- * {Boolean} The component was removed.\n- */\n- removeComponent: function(component) {\n-\n- OpenLayers.Util.removeItem(this.components, component);\n-\n- // clearBounds() so that it gets recalculated on the next call\n- // to this.getBounds();\n- this.clearBounds();\n- return true;\n- },\n-\n- /**\n- * APIMethod: getLength\n- * Calculate the length of this geometry\n- *\n- * Returns:\n- * {Float} The length of the geometry\n- */\n- getLength: function() {\n- var length = 0.0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- length += this.components[i].getLength();\n- }\n- return length;\n- },\n-\n- /**\n- * APIMethod: getArea\n- * Calculate the area of this geometry. Note how this function is overridden\n- * in <OpenLayers.Geometry.Polygon>.\n- *\n- * Returns:\n- * {Float} The area of the collection by summing its parts\n- */\n- getArea: function() {\n- var area = 0.0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- area += this.components[i].getArea();\n- }\n- return area;\n- },\n-\n- /** \n- * APIMethod: getGeodesicArea\n- * Calculate the approximate area of the polygon were it projected onto\n- * the earth.\n- *\n- * Parameters:\n- * projection - {<OpenLayers.Projection>} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Reference:\n- * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n- * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n- * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n- *\n- * Returns:\n- * {float} The approximate geodesic area of the geometry in square meters.\n- */\n- getGeodesicArea: function(projection) {\n- var area = 0.0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- area += this.components[i].getGeodesicArea(projection);\n- }\n- return area;\n- },\n-\n- /**\n- * APIMethod: getCentroid\n- *\n- * Compute the centroid for this geometry collection.\n- *\n- * Parameters:\n- * weighted - {Boolean} Perform the getCentroid computation recursively,\n- * returning an area weighted average of all geometries in this collection.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} The centroid of the collection\n- */\n- getCentroid: function(weighted) {\n- if (!weighted) {\n- return this.components.length && this.components[0].getCentroid();\n- }\n- var len = this.components.length;\n- if (!len) {\n- return false;\n- }\n-\n- var areas = [];\n- var centroids = [];\n- var areaSum = 0;\n- var minArea = Number.MAX_VALUE;\n- var component;\n- for (var i = 0; i < len; ++i) {\n- component = this.components[i];\n- var area = component.getArea();\n- var centroid = component.getCentroid(true);\n- if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {\n- continue;\n- }\n- areas.push(area);\n- areaSum += area;\n- minArea = (area < minArea && area > 0) ? area : minArea;\n- centroids.push(centroid);\n- }\n- len = areas.length;\n- if (areaSum === 0) {\n- // all the components in this collection have 0 area\n- // probably a collection of points -- weight all the points the same\n- for (var i = 0; i < len; ++i) {\n- areas[i] = 1;\n- }\n- areaSum = areas.length;\n- } else {\n- // normalize all the areas where the smallest area will get\n- // a value of 1\n- for (var i = 0; i < len; ++i) {\n- areas[i] /= minArea;\n- }\n- areaSum /= minArea;\n- }\n-\n- var xSum = 0,\n- ySum = 0,\n- centroid, area;\n- for (var i = 0; i < len; ++i) {\n- centroid = centroids[i];\n- area = areas[i];\n- xSum += centroid.x * area;\n- ySum += centroid.y * area;\n- }\n-\n- return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);\n- },\n-\n- /**\n- * APIMethod: getGeodesicLength\n- * Calculate the approximate length of the geometry were it projected onto\n- * the earth.\n- *\n- * projection - {<OpenLayers.Projection>} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n- */\n- getGeodesicLength: function(projection) {\n- var length = 0.0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- length += this.components[i].getGeodesicLength(projection);\n- }\n- return length;\n- },\n-\n- /**\n- * APIMethod: move\n- * Moves a geometry by the given displacement along positive x and y axes.\n- * This modifies the position of the geometry and clears the cached\n- * bounds.\n- *\n- * Parameters:\n- * x - {Float} Distance to move geometry in positive x direction. \n- * y - {Float} Distance to move geometry in positive y direction.\n- */\n- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- this.components[i].move(x, y);\n- }\n- },\n-\n- /**\n- * APIMethod: rotate\n- * Rotate a geometry around some origin\n- *\n- * Parameters:\n- * angle - {Float} Rotation angle in degrees (measured counterclockwise\n- * from the positive x-axis)\n- * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n- */\n- rotate: function(angle, origin) {\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- this.components[i].rotate(angle, origin);\n- }\n- },\n-\n- /**\n- * APIMethod: resize\n- * Resize a geometry relative to some origin. Use this method to apply\n- * a uniform scaling to a geometry.\n- *\n- * Parameters:\n- * scale - {Float} Factor by which to scale the geometry. A scale of 2\n- * doubles the size of the geometry in each dimension\n- * (lines, for example, will be twice as long, and polygons\n- * will have four times the area).\n- * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n- * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} - The current geometry. \n- */\n- resize: function(scale, origin, ratio) {\n- for (var i = 0; i < this.components.length; ++i) {\n- this.components[i].resize(scale, origin, ratio);\n- }\n- return this;\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options:\n- * details - {Boolean} Return details from the distance calculation.\n- * Default is false.\n- * edge - {Boolean} Calculate the distance from this geometry to the\n- * nearest edge of the target geometry. Default is true. If true,\n- * calling distanceTo from a geometry that is wholly contained within\n- * the target will result in a non-zero distance. If false, whenever\n- * geometries intersect, calling distanceTo will return 0. If false,\n- * details cannot be returned.\n- *\n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and y1 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var result, best, distance;\n- var min = Number.POSITIVE_INFINITY;\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- result = this.components[i].distanceTo(geometry, options);\n- distance = details ? result.distance : result;\n- if (distance < min) {\n- min = distance;\n- best = result;\n- if (min == 0) {\n- break;\n- }\n- }\n- }\n- return best;\n- },\n-\n- /** \n- * APIMethod: equals\n- * Determine whether another geometry is equivalent to this one. Geometries\n- * are considered equivalent if all components have the same coordinates.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The geometry to test. \n- *\n- * Returns:\n- * {Boolean} The supplied geometry is equivalent to this geometry.\n- */\n- equals: function(geometry) {\n- var equivalent = true;\n- if (!geometry || !geometry.CLASS_NAME ||\n- (this.CLASS_NAME != geometry.CLASS_NAME)) {\n- equivalent = false;\n- } else if (!(OpenLayers.Util.isArray(geometry.components)) ||\n- (geometry.components.length != this.components.length)) {\n- equivalent = false;\n- } else {\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- if (!this.components[i].equals(geometry.components[i])) {\n- equivalent = false;\n- break;\n- }\n- }\n- }\n- return equivalent;\n- },\n-\n- /**\n- * APIMethod: transform\n- * Reproject the components geometry from source to dest.\n- * \n- * Parameters:\n- * source - {<OpenLayers.Projection>} \n- * dest - {<OpenLayers.Projection>}\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} \n- */\n- transform: function(source, dest) {\n- if (source && dest) {\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- var component = this.components[i];\n- component.transform(source, dest);\n- }\n- this.bounds = null;\n- }\n- return this;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this one.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- intersect = geometry.intersects(this.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- return intersect;\n- },\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {\n- var vertices = [];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- Array.prototype.push.apply(\n- vertices, this.components[i].getVertices(nodes)\n- );\n- }\n- return vertices;\n- },\n-\n-\n- CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/MultiPoint.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/Point.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.MultiPoint\n- * MultiPoint is a collection of Points. Create a new instance with the\n- * <OpenLayers.Geometry.MultiPoint> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Geometry.Collection>\n- * - <OpenLayers.Geometry>\n- */\n-OpenLayers.Geometry.MultiPoint = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.MultiPoint\n- * Create a new MultiPoint Geometry\n- *\n- * Parameters:\n- * components - {Array(<OpenLayers.Geometry.Point>)} \n- *\n- * Returns:\n- * {<OpenLayers.Geometry.MultiPoint>}\n- */\n-\n- /**\n- * APIMethod: addPoint\n- * Wrapper for <OpenLayers.Geometry.Collection.addComponent>\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} Point to be added\n- * index - {Integer} Optional index\n- */\n- addPoint: function(point, index) {\n- this.addComponent(point, index);\n- },\n-\n- /**\n- * APIMethod: removePoint\n- * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} Point to be removed\n- */\n- removePoint: function(point) {\n- this.removeComponent(point);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/Curve.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Curve\n- * A Curve is a MultiPoint, whose points are assumed to be connected. To \n- * this end, we provide a \"getLength()\" function, which iterates through \n- * the points, summing the distances between them. \n- * \n- * Inherits: \n- * - <OpenLayers.Geometry.MultiPoint>\n- */\n-OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of \n- * components that the collection can include. A null \n- * value means the component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Curve\n- * \n- * Parameters:\n- * point - {Array(<OpenLayers.Geometry.Point>)}\n- */\n-\n- /**\n- * APIMethod: getLength\n- * \n- * Returns:\n- * {Float} The length of the curve\n- */\n- getLength: function() {\n- var length = 0.0;\n- if (this.components && (this.components.length > 1)) {\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- length += this.components[i - 1].distanceTo(this.components[i]);\n- }\n- }\n- return length;\n- },\n-\n- /**\n- * APIMethod: getGeodesicLength\n- * Calculate the approximate length of the geometry were it projected onto\n- * the earth.\n- *\n- * projection - {<OpenLayers.Projection>} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Returns:\n- * {Float} The appoximate geodesic length of the geometry in meters.\n- */\n- getGeodesicLength: function(projection) {\n- var geom = this; // so we can work with a clone if needed\n- if (projection) {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- if (!gg.equals(projection)) {\n- geom = this.clone().transform(projection, gg);\n- }\n- }\n- var length = 0.0;\n- if (geom.components && (geom.components.length > 1)) {\n- var p1, p2;\n- for (var i = 1, len = geom.components.length; i < len; i++) {\n- p1 = geom.components[i - 1];\n- p2 = geom.components[i];\n- // this returns km and requires lon/lat properties\n- length += OpenLayers.Util.distVincenty({\n- lon: p1.x,\n- lat: p1.y\n- }, {\n- lon: p2.x,\n- lat: p2.y\n- });\n- }\n- }\n- // convert to m\n- return length * 1000;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.Curve\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/LineString.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Curve.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.LineString\n- * A LineString is a Curve which, once two points have been added to it, can \n- * never be less than two points long.\n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry.Curve>\n- */\n-OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {\n-\n- /**\n- * Constructor: OpenLayers.Geometry.LineString\n- * Create a new LineString geometry\n- *\n- * Parameters:\n- * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to\n- * generate the linestring\n- *\n- */\n-\n- /**\n- * APIMethod: removeComponent\n- * Only allows removal of a point if there are three or more points in \n- * the linestring. (otherwise the result would be just a single point)\n- *\n- * Parameters: \n- * point - {<OpenLayers.Geometry.Point>} The point to be removed\n- *\n- * Returns: \n- * {Boolean} The component was removed.\n- */\n- removeComponent: function(point) {\n- var removed = this.components && (this.components.length > 2);\n- if (removed) {\n- OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n- arguments);\n- }\n- return removed;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Test for instersection between two geometries. This is a cheapo\n- * implementation of the Bently-Ottmann algorigithm. It doesn't\n- * really keep track of a sweep line data structure. It is closer\n- * to the brute force method, except that segments are sorted and\n- * potential intersections are only calculated when bounding boxes\n- * intersect.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this geometry.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- var type = geometry.CLASS_NAME;\n- if (type == \"OpenLayers.Geometry.LineString\" ||\n- type == \"OpenLayers.Geometry.LinearRing\" ||\n- type == \"OpenLayers.Geometry.Point\") {\n- var segs1 = this.getSortedSegments();\n- var segs2;\n- if (type == \"OpenLayers.Geometry.Point\") {\n- segs2 = [{\n- x1: geometry.x,\n- y1: geometry.y,\n- x2: geometry.x,\n- y2: geometry.y\n- }];\n- } else {\n- segs2 = geometry.getSortedSegments();\n- }\n- var seg1, seg1x1, seg1x2, seg1y1, seg1y2,\n- seg2, seg2y1, seg2y2;\n- // sweep right\n- outer: for (var i = 0, len = segs1.length; i < len; ++i) {\n- seg1 = segs1[i];\n- seg1x1 = seg1.x1;\n- seg1x2 = seg1.x2;\n- seg1y1 = seg1.y1;\n- seg1y2 = seg1.y2;\n- inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {\n- seg2 = segs2[j];\n- if (seg2.x1 > seg1x2) {\n- // seg1 still left of seg2\n- break;\n- }\n- if (seg2.x2 < seg1x1) {\n- // seg2 still left of seg1\n- continue;\n- }\n- seg2y1 = seg2.y1;\n- seg2y2 = seg2.y2;\n- if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {\n- // seg2 above seg1\n- continue;\n- }\n- if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {\n- // seg2 below seg1\n- continue;\n- }\n- if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {\n- intersect = true;\n- break outer;\n- }\n- }\n- }\n- } else {\n- intersect = geometry.intersects(this);\n- }\n- return intersect;\n- },\n-\n- /**\n- * Method: getSortedSegments\n- *\n- * Returns:\n- * {Array} An array of segment objects. Segment objects have properties\n- * x1, y1, x2, and y2. The start point is represented by x1 and y1.\n- * The end point is represented by x2 and y2. Start and end are\n- * ordered so that x1 < x2.\n- */\n- getSortedSegments: function() {\n- var numSeg = this.components.length - 1;\n- var segments = new Array(numSeg),\n- point1, point2;\n- for (var i = 0; i < numSeg; ++i) {\n- point1 = this.components[i];\n- point2 = this.components[i + 1];\n- if (point1.x < point2.x) {\n- segments[i] = {\n- x1: point1.x,\n- y1: point1.y,\n- x2: point2.x,\n- y2: point2.y\n- };\n- } else {\n- segments[i] = {\n- x1: point2.x,\n- y1: point2.y,\n- x2: point1.x,\n- y2: point1.y\n- };\n- }\n- }\n- // more efficient to define this somewhere static\n- function byX1(seg1, seg2) {\n- return seg1.x1 - seg2.x1;\n- }\n- return segments.sort(byX1);\n- },\n-\n- /**\n- * Method: splitWithSegment\n- * Split this geometry with the given segment.\n- *\n- * Parameters:\n- * seg - {Object} An object with x1, y1, x2, and y2 properties referencing\n- * segment endpoint coordinates.\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source segment must be within the\n- * tolerance distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of one of the source segment's\n- * endpoints will be assumed to occur at the endpoint.\n- *\n- * Returns:\n- * {Object} An object with *lines* and *points* properties. If the given\n- * segment intersects this linestring, the lines array will reference\n- * geometries that result from the split. The points array will contain\n- * all intersection points. Intersection points are sorted along the\n- * segment (in order from x1,y1 to x2,y2).\n- */\n- splitWithSegment: function(seg, options) {\n- var edge = !(options && options.edge === false);\n- var tolerance = options && options.tolerance;\n- var lines = [];\n- var verts = this.getVertices();\n- var points = [];\n- var intersections = [];\n- var split = false;\n- var vert1, vert2, point;\n- var node, vertex, target;\n- var interOptions = {\n- point: true,\n- tolerance: tolerance\n- };\n- var result = null;\n- for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n- vert1 = verts[i];\n- points.push(vert1.clone());\n- vert2 = verts[i + 1];\n- target = {\n- x1: vert1.x,\n- y1: vert1.y,\n- x2: vert2.x,\n- y2: vert2.y\n- };\n- point = OpenLayers.Geometry.segmentsIntersect(\n- seg, target, interOptions\n- );\n- if (point instanceof OpenLayers.Geometry.Point) {\n- if ((point.x === seg.x1 && point.y === seg.y1) ||\n- (point.x === seg.x2 && point.y === seg.y2) ||\n- point.equals(vert1) || point.equals(vert2)) {\n- vertex = true;\n- } else {\n- vertex = false;\n- }\n- if (vertex || edge) {\n- // push intersections different than the previous\n- if (!point.equals(intersections[intersections.length - 1])) {\n- intersections.push(point.clone());\n- }\n- if (i === 0) {\n- if (point.equals(vert1)) {\n- continue;\n- }\n- }\n- if (point.equals(vert2)) {\n- continue;\n- }\n- split = true;\n- if (!point.equals(vert1)) {\n- points.push(point);\n- }\n- lines.push(new OpenLayers.Geometry.LineString(points));\n- points = [point.clone()];\n- }\n- }\n- }\n- if (split) {\n- points.push(vert2.clone());\n- lines.push(new OpenLayers.Geometry.LineString(points));\n- }\n- if (intersections.length > 0) {\n- // sort intersections along segment\n- var xDir = seg.x1 < seg.x2 ? 1 : -1;\n- var yDir = seg.y1 < seg.y2 ? 1 : -1;\n- result = {\n- lines: lines,\n- points: intersections.sort(function(p1, p2) {\n- return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);\n- })\n- };\n- }\n- return result;\n- },\n-\n- /**\n- * Method: split\n- * Use this geometry (the source) to attempt to split a target geometry.\n- * \n- * Parameters:\n- * target - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * mutual - {Boolean} Split the source geometry in addition to the target\n- * geometry. Default is false.\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source must be within the tolerance\n- * distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of an existing vertex on the source\n- * will be assumed to occur at the vertex.\n- * \n- * Returns:\n- * {Array} A list of geometries (of this same type as the target) that\n- * result from splitting the target with the source geometry. The\n- * source and target geometry will remain unmodified. If no split\n- * results, null will be returned. If mutual is true and a split\n- * results, return will be an array of two arrays - the first will be\n- * all geometries that result from splitting the source geometry and\n- * the second will be all geometries that result from splitting the\n- * target geometry.\n- */\n- split: function(target, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var sourceSplit, targetSplit, sourceParts, targetParts;\n- if (target instanceof OpenLayers.Geometry.LineString) {\n- var verts = this.getVertices();\n- var vert1, vert2, seg, splits, lines, point;\n- var points = [];\n- sourceParts = [];\n- for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n- vert1 = verts[i];\n- vert2 = verts[i + 1];\n- seg = {\n- x1: vert1.x,\n- y1: vert1.y,\n- x2: vert2.x,\n- y2: vert2.y\n- };\n- targetParts = targetParts || [target];\n- if (mutual) {\n- points.push(vert1.clone());\n- }\n- for (var j = 0; j < targetParts.length; ++j) {\n- splits = targetParts[j].splitWithSegment(seg, options);\n- if (splits) {\n- // splice in new features\n- lines = splits.lines;\n- if (lines.length > 0) {\n- lines.unshift(j, 1);\n- Array.prototype.splice.apply(targetParts, lines);\n- j += lines.length - 2;\n- }\n- if (mutual) {\n- for (var k = 0, len = splits.points.length; k < len; ++k) {\n- point = splits.points[k];\n- if (!point.equals(vert1)) {\n- points.push(point);\n- sourceParts.push(new OpenLayers.Geometry.LineString(points));\n- if (point.equals(vert2)) {\n- points = [];\n- } else {\n- points = [point.clone()];\n- }\n- }\n- }\n- }\n- }\n- }\n- }\n- if (mutual && sourceParts.length > 0 && points.length > 0) {\n- points.push(vert2.clone());\n- sourceParts.push(new OpenLayers.Geometry.LineString(points));\n- }\n- } else {\n- results = target.splitWith(this, options);\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true;\n- } else {\n- targetParts = [];\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true;\n- } else {\n- sourceParts = [];\n- }\n- if (targetSplit || sourceSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\n- }\n- }\n- return results;\n- },\n-\n- /**\n- * Method: splitWith\n- * Split this geometry (the target) with the given geometry (the source).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} A geometry used to split this\n- * geometry (the source).\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * mutual - {Boolean} Split the source geometry in addition to the target\n- * geometry. Default is false.\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source must be within the tolerance\n- * distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of an existing vertex on the source\n- * will be assumed to occur at the vertex.\n- * \n- * Returns:\n- * {Array} A list of geometries (of this same type as the target) that\n- * result from splitting the target with the source geometry. The\n- * source and target geometry will remain unmodified. If no split\n- * results, null will be returned. If mutual is true and a split\n- * results, return will be an array of two arrays - the first will be\n- * all geometries that result from splitting the source geometry and\n- * the second will be all geometries that result from splitting the\n- * target geometry.\n- */\n- splitWith: function(geometry, options) {\n- return geometry.split(this, options);\n-\n- },\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {\n- var vertices;\n- if (nodes === true) {\n- vertices = [\n- this.components[0],\n- this.components[this.components.length - 1]\n- ];\n- } else if (nodes === false) {\n- vertices = this.components.slice(1, this.components.length - 1);\n- } else {\n- vertices = this.components.slice();\n- }\n- return vertices;\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options:\n- * details - {Boolean} Return details from the distance calculation.\n- * Default is false.\n- * edge - {Boolean} Calculate the distance from this geometry to the\n- * nearest edge of the target geometry. Default is true. If true,\n- * calling distanceTo from a geometry that is wholly contained within\n- * the target will result in a non-zero distance. If false, whenever\n- * geometries intersect, calling distanceTo will return 0. If false,\n- * details cannot be returned.\n- *\n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and x2 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var result, best = {};\n- var min = Number.POSITIVE_INFINITY;\n- if (geometry instanceof OpenLayers.Geometry.Point) {\n- var segs = this.getSortedSegments();\n- var x = geometry.x;\n- var y = geometry.y;\n- var seg;\n- for (var i = 0, len = segs.length; i < len; ++i) {\n- seg = segs[i];\n- result = OpenLayers.Geometry.distanceToSegment(geometry, seg);\n- if (result.distance < min) {\n- min = result.distance;\n- best = result;\n- if (min === 0) {\n- break;\n- }\n- } else {\n- // if distance increases and we cross y0 to the right of x0, no need to keep looking.\n- if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {\n- break;\n- }\n- }\n- }\n- if (details) {\n- best = {\n- distance: best.distance,\n- x0: best.x,\n- y0: best.y,\n- x1: x,\n- y1: y\n- };\n- } else {\n- best = best.distance;\n- }\n- } else if (geometry instanceof OpenLayers.Geometry.LineString) {\n- var segs0 = this.getSortedSegments();\n- var segs1 = geometry.getSortedSegments();\n- var seg0, seg1, intersection, x0, y0;\n- var len1 = segs1.length;\n- var interOptions = {\n- point: true\n- };\n- outer: for (var i = 0, len = segs0.length; i < len; ++i) {\n- seg0 = segs0[i];\n- x0 = seg0.x1;\n- y0 = seg0.y1;\n- for (var j = 0; j < len1; ++j) {\n- seg1 = segs1[j];\n- intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);\n- if (intersection) {\n- min = 0;\n- best = {\n- distance: 0,\n- x0: intersection.x,\n- y0: intersection.y,\n- x1: intersection.x,\n- y1: intersection.y\n- };\n- break outer;\n- } else {\n- result = OpenLayers.Geometry.distanceToSegment({\n- x: x0,\n- y: y0\n- }, seg1);\n- if (result.distance < min) {\n- min = result.distance;\n- best = {\n- distance: min,\n- x0: x0,\n- y0: y0,\n- x1: result.x,\n- y1: result.y\n- };\n- }\n- }\n- }\n- }\n- if (!details) {\n- best = best.distance;\n- }\n- if (min !== 0) {\n- // check the final vertex in this line's sorted segments\n- if (seg0) {\n- result = geometry.distanceTo(\n- new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),\n- options\n- );\n- var dist = details ? result.distance : result;\n- if (dist < min) {\n- if (details) {\n- best = {\n- distance: min,\n- x0: result.x1,\n- y0: result.y1,\n- x1: result.x0,\n- y1: result.y0\n- };\n- } else {\n- best = dist;\n- }\n- }\n- }\n- }\n- } else {\n- best = geometry.distanceTo(this, options);\n- // swap since target comes from this line\n- if (details) {\n- best = {\n- distance: best.distance,\n- x0: best.x1,\n- y0: best.y1,\n- x1: best.x0,\n- y1: best.y0\n- };\n- }\n- }\n- return best;\n- },\n-\n- /**\n- * APIMethod: simplify\n- * This function will return a simplified LineString.\n- * Simplification is based on the Douglas-Peucker algorithm.\n- *\n- *\n- * Parameters:\n- * tolerance - {number} threshhold for simplification in map units\n- *\n- * Returns:\n- * {OpenLayers.Geometry.LineString} the simplified LineString\n- */\n- simplify: function(tolerance) {\n- if (this && this !== null) {\n- var points = this.getVertices();\n- if (points.length < 3) {\n- return this;\n- }\n-\n- var compareNumbers = function(a, b) {\n- return (a - b);\n- };\n-\n- /**\n- * Private function doing the Douglas-Peucker reduction\n- */\n- var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) {\n- var maxDistance = 0;\n- var indexFarthest = 0;\n-\n- for (var index = firstPoint, distance; index < lastPoint; index++) {\n- distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);\n- if (distance > maxDistance) {\n- maxDistance = distance;\n- indexFarthest = index;\n- }\n- }\n-\n- if (maxDistance > tolerance && indexFarthest != firstPoint) {\n- //Add the largest point that exceeds the tolerance\n- pointIndexsToKeep.push(indexFarthest);\n- douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);\n- douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);\n- }\n- };\n-\n- /**\n- * Private function calculating the perpendicular distance\n- * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower\n- */\n- var perpendicularDistance = function(point1, point2, point) {\n- //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle\n- //Base = v((x1-x2)\u00b2+(x1-x2)\u00b2) *Base of Triangle*\n- //Area = .5*Base*H *Solve for height\n- //Height = Area/.5/Base\n-\n- var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));\n- var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));\n- var height = area / bottom * 2;\n-\n- return height;\n- };\n-\n- var firstPoint = 0;\n- var lastPoint = points.length - 1;\n- var pointIndexsToKeep = [];\n-\n- //Add the first and last index to the keepers\n- pointIndexsToKeep.push(firstPoint);\n- pointIndexsToKeep.push(lastPoint);\n-\n- //The first and the last point cannot be the same\n- while (points[firstPoint].equals(points[lastPoint])) {\n- lastPoint--;\n- //Addition: the first point not equal to first point in the LineString is kept as well\n- pointIndexsToKeep.push(lastPoint);\n- }\n-\n- douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);\n- var returnPoints = [];\n- pointIndexsToKeep.sort(compareNumbers);\n- for (var index = 0; index < pointIndexsToKeep.length; index++) {\n- returnPoints.push(points[pointIndexsToKeep[index]]);\n- }\n- return new OpenLayers.Geometry.LineString(returnPoints);\n-\n- } else {\n- return this;\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.LineString\"\n-});\n-/* ======================================================================\n- OpenLayers/Geometry/MultiLineString.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/LineString.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.MultiLineString\n- * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>\n- * components.\n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry.Collection>\n- * - <OpenLayers.Geometry> \n- */\n-OpenLayers.Geometry.MultiLineString = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.LineString\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.MultiLineString\n- * Constructor for a MultiLineString Geometry.\n- *\n- * Parameters: \n- * components - {Array(<OpenLayers.Geometry.LineString>)} \n- *\n- */\n-\n- /**\n- * Method: split\n- * Use this geometry (the source) to attempt to split a target geometry.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * mutual - {Boolean} Split the source geometry in addition to the target\n- * geometry. Default is false.\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source must be within the tolerance\n- * distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of an existing vertex on the source\n- * will be assumed to occur at the vertex.\n- * \n- * Returns:\n- * {Array} A list of geometries (of this same type as the target) that\n- * result from splitting the target with the source geometry. The\n- * source and target geometry will remain unmodified. If no split\n- * results, null will be returned. If mutual is true and a split\n- * results, return will be an array of two arrays - the first will be\n- * all geometries that result from splitting the source geometry and\n- * the second will be all geometries that result from splitting the\n- * target geometry.\n- */\n- split: function(geometry, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var splits, sourceLine, sourceLines, sourceSplit, targetSplit;\n- var sourceParts = [];\n- var targetParts = [geometry];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- sourceLine = this.components[i];\n- sourceSplit = false;\n- for (var j = 0; j < targetParts.length; ++j) {\n- splits = sourceLine.split(targetParts[j], options);\n- if (splits) {\n- if (mutual) {\n- sourceLines = splits[0];\n- for (var k = 0, klen = sourceLines.length; k < klen; ++k) {\n- if (k === 0 && sourceParts.length) {\n- sourceParts[sourceParts.length - 1].addComponent(\n- sourceLines[k]\n- );\n- } else {\n- sourceParts.push(\n- new OpenLayers.Geometry.MultiLineString([\n- sourceLines[k]\n- ])\n- );\n- }\n- }\n- sourceSplit = true;\n- splits = splits[1];\n- }\n- if (splits.length) {\n- // splice in new target parts\n- splits.unshift(j, 1);\n- Array.prototype.splice.apply(targetParts, splits);\n- break;\n- }\n- }\n- }\n- if (!sourceSplit) {\n- // source line was not hit\n- if (sourceParts.length) {\n- // add line to existing multi\n- sourceParts[sourceParts.length - 1].addComponent(\n- sourceLine.clone()\n- );\n- } else {\n- // create a fresh multi\n- sourceParts = [\n- new OpenLayers.Geometry.MultiLineString(\n- sourceLine.clone()\n- )\n- ];\n- }\n- }\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true;\n- } else {\n- sourceParts = [];\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true;\n- } else {\n- targetParts = [];\n- }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\n- }\n- }\n- return results;\n- },\n-\n- /**\n- * Method: splitWith\n- * Split this geometry (the target) with the given geometry (the source).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} A geometry used to split this\n- * geometry (the source).\n- * options - {Object} Properties of this object will be used to determine\n- * how the split is conducted.\n- *\n- * Valid options:\n- * mutual - {Boolean} Split the source geometry in addition to the target\n- * geometry. Default is false.\n- * edge - {Boolean} Allow splitting when only edges intersect. Default is\n- * true. If false, a vertex on the source must be within the tolerance\n- * distance of the intersection to be considered a split.\n- * tolerance - {Number} If a non-null value is provided, intersections\n- * within the tolerance distance of an existing vertex on the source\n- * will be assumed to occur at the vertex.\n- * \n- * Returns:\n- * {Array} A list of geometries (of this same type as the target) that\n- * result from splitting the target with the source geometry. The\n- * source and target geometry will remain unmodified. If no split\n- * results, null will be returned. If mutual is true and a split\n- * results, return will be an array of two arrays - the first will be\n- * all geometries that result from splitting the source geometry and\n- * the second will be all geometries that result from splitting the\n- * target geometry.\n- */\n- splitWith: function(geometry, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n- if (geometry instanceof OpenLayers.Geometry.LineString) {\n- targetParts = [];\n- sourceParts = [geometry];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- targetSplit = false;\n- targetLine = this.components[i];\n- for (var j = 0; j < sourceParts.length; ++j) {\n- splits = sourceParts[j].split(targetLine, options);\n- if (splits) {\n- if (mutual) {\n- sourceLines = splits[0];\n- if (sourceLines.length) {\n- // splice in new source parts\n- sourceLines.unshift(j, 1);\n- Array.prototype.splice.apply(sourceParts, sourceLines);\n- j += sourceLines.length - 2;\n- }\n- splits = splits[1];\n- if (splits.length === 0) {\n- splits = [targetLine.clone()];\n- }\n- }\n- for (var k = 0, klen = splits.length; k < klen; ++k) {\n- if (k === 0 && targetParts.length) {\n- targetParts[targetParts.length - 1].addComponent(\n- splits[k]\n- );\n- } else {\n- targetParts.push(\n- new OpenLayers.Geometry.MultiLineString([\n- splits[k]\n- ])\n- );\n- }\n- }\n- targetSplit = true;\n- }\n- }\n- if (!targetSplit) {\n- // target component was not hit\n- if (targetParts.length) {\n- // add it to any existing multi-line\n- targetParts[targetParts.length - 1].addComponent(\n- targetLine.clone()\n- );\n- } else {\n- // or start with a fresh multi-line\n- targetParts = [\n- new OpenLayers.Geometry.MultiLineString([\n- targetLine.clone()\n- ])\n- ];\n- }\n-\n- }\n- }\n- } else {\n- results = geometry.split(this);\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true;\n- } else {\n- sourceParts = [];\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true;\n- } else {\n- targetParts = [];\n- }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts];\n- } else {\n- results = targetParts;\n- }\n- }\n- return results;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/LinearRing.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/LineString.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.LinearRing\n- * \n- * A Linear Ring is a special LineString which is closed. It closes itself \n- * automatically on every addPoint/removePoint by adding a copy of the first\n- * point as the last point. \n- * \n- * Also, as it is the first in the line family to close itself, a getArea()\n- * function is defined to calculate the enclosed area of the linearRing\n- * \n- * Inherits:\n- * - <OpenLayers.Geometry.LineString>\n- */\n-OpenLayers.Geometry.LinearRing = OpenLayers.Class(\n- OpenLayers.Geometry.LineString, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of \n- * components that the collection can include. A null \n- * value means the component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.LinearRing\n- * Linear rings are constructed with an array of points. This array\n- * can represent a closed or open ring. If the ring is open (the last\n- * point does not equal the first point), the constructor will close\n- * the ring. If the ring is already closed (the last point does equal\n- * the first point), it will be left closed.\n- * \n- * Parameters:\n- * points - {Array(<OpenLayers.Geometry.Point>)} points\n- */\n-\n- /**\n- * APIMethod: addComponent\n- * Adds a point to geometry components. If the point is to be added to\n- * the end of the components array and it is the same as the last point\n- * already in that array, the duplicate point is not added. This has \n- * the effect of closing the ring if it is not already closed, and \n- * doing the right thing if it is already closed. This behavior can \n- * be overridden by calling the method with a non-null index as the \n- * second argument.\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- * index - {Integer} Index into the array to insert the component\n- * \n- * Returns:\n- * {Boolean} Was the Point successfully added?\n- */\n- addComponent: function(point, index) {\n- var added = false;\n-\n- //remove last point\n- var lastPoint = this.components.pop();\n-\n- // given an index, add the point\n- // without an index only add non-duplicate points\n- if (index != null || !point.equals(lastPoint)) {\n- added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- arguments);\n- }\n-\n- //append copy of first point\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- [firstPoint]);\n-\n- return added;\n- },\n-\n- /**\n- * APIMethod: removeComponent\n- * Removes a point from geometry components.\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns: \n- * {Boolean} The component was removed.\n- */\n- removeComponent: function(point) {\n- var removed = this.components && (this.components.length > 3);\n- if (removed) {\n- //remove last point\n- this.components.pop();\n-\n- //remove our point\n- OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,\n- arguments);\n- //append copy of first point\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this,\n- [firstPoint]);\n- }\n- return removed;\n- },\n-\n- /**\n- * APIMethod: move\n- * Moves a geometry by the given displacement along positive x and y axes.\n- * This modifies the position of the geometry and clears the cached\n- * bounds.\n- *\n- * Parameters:\n- * x - {Float} Distance to move geometry in positive x direction. \n- * y - {Float} Distance to move geometry in positive y direction.\n- */\n- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- this.components[i].move(x, y);\n- }\n- },\n-\n- /**\n- * APIMethod: rotate\n- * Rotate a geometry around some origin\n- *\n- * Parameters:\n- * angle - {Float} Rotation angle in degrees (measured counterclockwise\n- * from the positive x-axis)\n- * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation\n- */\n- rotate: function(angle, origin) {\n- for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n- this.components[i].rotate(angle, origin);\n- }\n- },\n-\n- /**\n- * APIMethod: resize\n- * Resize a geometry relative to some origin. Use this method to apply\n- * a uniform scaling to a geometry.\n- *\n- * Parameters:\n- * scale - {Float} Factor by which to scale the geometry. A scale of 2\n- * doubles the size of the geometry in each dimension\n- * (lines, for example, will be twice as long, and polygons\n- * will have four times the area).\n- * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing\n- * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} - The current geometry. \n- */\n- resize: function(scale, origin, ratio) {\n- for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n- this.components[i].resize(scale, origin, ratio);\n- }\n- return this;\n- },\n-\n- /**\n- * APIMethod: transform\n- * Reproject the components geometry from source to dest.\n- *\n- * Parameters:\n- * source - {<OpenLayers.Projection>}\n- * dest - {<OpenLayers.Projection>}\n- * \n- * Returns:\n- * {<OpenLayers.Geometry>} \n- */\n- transform: function(source, dest) {\n- if (source && dest) {\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- var component = this.components[i];\n- component.transform(source, dest);\n- }\n- this.bounds = null;\n- }\n- return this;\n- },\n-\n- /**\n- * APIMethod: getCentroid\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.Point>} The centroid of the collection\n- */\n- getCentroid: function() {\n- if (this.components) {\n- var len = this.components.length;\n- if (len > 0 && len <= 2) {\n- return this.components[0].clone();\n- } else if (len > 2) {\n- var sumX = 0.0;\n- var sumY = 0.0;\n- var x0 = this.components[0].x;\n- var y0 = this.components[0].y;\n- var area = -1 * this.getArea();\n- if (area != 0) {\n- for (var i = 0; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n- sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n- }\n- var x = x0 + sumX / (6 * area);\n- var y = y0 + sumY / (6 * area);\n- } else {\n- for (var i = 0; i < len - 1; i++) {\n- sumX += this.components[i].x;\n- sumY += this.components[i].y;\n- }\n- var x = sumX / (len - 1);\n- var y = sumY / (len - 1);\n- }\n- return new OpenLayers.Geometry.Point(x, y);\n- } else {\n- return null;\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: getArea\n- * Note - The area is positive if the ring is oriented CW, otherwise\n- * it will be negative.\n- * \n- * Returns:\n- * {Float} The signed area for a ring.\n- */\n- getArea: function() {\n- var area = 0.0;\n- if (this.components && (this.components.length > 2)) {\n- var sum = 0.0;\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sum += (b.x + c.x) * (c.y - b.y);\n- }\n- area = -sum / 2.0;\n- }\n- return area;\n- },\n-\n- /**\n- * APIMethod: getGeodesicArea\n- * Calculate the approximate area of the polygon were it projected onto\n- * the earth. Note that this area will be positive if ring is oriented\n- * clockwise, otherwise it will be negative.\n- *\n- * Parameters:\n- * projection - {<OpenLayers.Projection>} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Reference:\n- * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n- * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n- * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n- *\n- * Returns:\n- * {float} The approximate signed geodesic area of the polygon in square\n- * meters.\n- */\n- getGeodesicArea: function(projection) {\n- var ring = this; // so we can work with a clone if needed\n- if (projection) {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- if (!gg.equals(projection)) {\n- ring = this.clone().transform(projection, gg);\n- }\n- }\n- var area = 0.0;\n- var len = ring.components && ring.components.length;\n- if (len > 2) {\n- var p1, p2;\n- for (var i = 0; i < len - 1; i++) {\n- p1 = ring.components[i];\n- p2 = ring.components[i + 1];\n- area += OpenLayers.Util.rad(p2.x - p1.x) *\n- (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +\n- Math.sin(OpenLayers.Util.rad(p2.y)));\n- }\n- area = area * 6378137.0 * 6378137.0 / 2.0;\n- }\n- return area;\n- },\n-\n- /**\n- * Method: containsPoint\n- * Test if a point is inside a linear ring. For the case where a point\n- * is coincident with a linear ring edge, returns 1. Otherwise,\n- * returns boolean.\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {Boolean | Number} The point is inside the linear ring. Returns 1 if\n- * the point is coincident with an edge. Returns boolean otherwise.\n- */\n- containsPoint: function(point) {\n- var approx = OpenLayers.Number.limitSigDigs;\n- var digs = 14;\n- var px = approx(point.x, digs);\n- var py = approx(point.y, digs);\n-\n- function getX(y, x1, y1, x2, y2) {\n- return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;\n- }\n- var numSeg = this.components.length - 1;\n- var start, end, x1, y1, x2, y2, cx, cy;\n- var crosses = 0;\n- for (var i = 0; i < numSeg; ++i) {\n- start = this.components[i];\n- x1 = approx(start.x, digs);\n- y1 = approx(start.y, digs);\n- end = this.components[i + 1];\n- x2 = approx(end.x, digs);\n- y2 = approx(end.y, digs);\n-\n- /**\n- * The following conditions enforce five edge-crossing rules:\n- * 1. points coincident with edges are considered contained;\n- * 2. an upward edge includes its starting endpoint, and\n- * excludes its final endpoint;\n- * 3. a downward edge excludes its starting endpoint, and\n- * includes its final endpoint;\n- * 4. horizontal edges are excluded; and\n- * 5. the edge-ray intersection point must be strictly right\n- * of the point P.\n- */\n- if (y1 == y2) {\n- // horizontal edge\n- if (py == y1) {\n- // point on horizontal line\n- if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert\n- x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert\n- // point on edge\n- crosses = -1;\n- break;\n- }\n- }\n- // ignore other horizontal edges\n- continue;\n- }\n- cx = approx(getX(py, x1, y1, x2, y2), digs);\n- if (cx == px) {\n- // point on line\n- if (y1 < y2 && (py >= y1 && py <= y2) || // upward\n- y1 > y2 && (py <= y1 && py >= y2)) { // downward\n- // point on edge\n- crosses = -1;\n- break;\n- }\n- }\n- if (cx <= px) {\n- // no crossing to the right\n- continue;\n- }\n- if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n- // no crossing\n- continue;\n- }\n- if (y1 < y2 && (py >= y1 && py < y2) || // upward\n- y1 > y2 && (py < y1 && py >= y2)) { // downward\n- ++crosses;\n- }\n- }\n- var contained = (crosses == -1) ?\n- // on edge\n- 1 :\n- // even (out) or odd (in)\n- !!(crosses & 1);\n-\n- return contained;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this one.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n- intersect = geometry.intersects(this);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(\n- this, [geometry]\n- );\n- } else {\n- // check for component intersections\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = geometry.components[i].intersects(this);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- return intersect;\n- },\n-\n- /**\n- * APIMethod: getVertices\n- * Return a list of all points in this geometry.\n- *\n- * Parameters:\n- * nodes - {Boolean} For lines, only return vertices that are\n- * endpoints. If false, for lines, only vertices that are not\n- * endpoints will be returned. If not provided, all vertices will\n- * be returned.\n- *\n- * Returns:\n- * {Array} A list of all vertices in the geometry.\n- */\n- getVertices: function(nodes) {\n- return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n- });\n-/* ======================================================================\n- OpenLayers/Geometry/Polygon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/LinearRing.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.Polygon \n- * Polygon is a collection of Geometry.LinearRings. \n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry.Collection> \n- * - <OpenLayers.Geometry> \n- */\n-OpenLayers.Geometry.Polygon = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.Polygon\n- * Constructor for a Polygon geometry. \n- * The first ring (this.component[0])is the outer bounds of the polygon and \n- * all subsequent rings (this.component[1-n]) are internal holes.\n- *\n- *\n- * Parameters:\n- * components - {Array(<OpenLayers.Geometry.LinearRing>)} \n- */\n-\n- /** \n- * APIMethod: getArea\n- * Calculated by subtracting the areas of the internal holes from the \n- * area of the outer hole.\n- * \n- * Returns:\n- * {float} The area of the geometry\n- */\n- getArea: function() {\n- var area = 0.0;\n- if (this.components && (this.components.length > 0)) {\n- area += Math.abs(this.components[0].getArea());\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getArea());\n- }\n- }\n- return area;\n- },\n-\n- /** \n- * APIMethod: getGeodesicArea\n- * Calculate the approximate area of the polygon were it projected onto\n- * the earth.\n- *\n- * Parameters:\n- * projection - {<OpenLayers.Projection>} The spatial reference system\n- * for the geometry coordinates. If not provided, Geographic/WGS84 is\n- * assumed.\n- * \n- * Reference:\n- * Robert. G. Chamberlain and William H. Duquette, \"Some Algorithms for\n- * Polygons on a Sphere\", JPL Publication 07-03, Jet Propulsion\n- * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409\n- *\n- * Returns:\n- * {float} The approximate geodesic area of the polygon in square meters.\n- */\n- getGeodesicArea: function(projection) {\n- var area = 0.0;\n- if (this.components && (this.components.length > 0)) {\n- area += Math.abs(this.components[0].getGeodesicArea(projection));\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getGeodesicArea(projection));\n- }\n- }\n- return area;\n- },\n-\n- /**\n- * Method: containsPoint\n- * Test if a point is inside a polygon. Points on a polygon edge are\n- * considered inside.\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {Boolean | Number} The point is inside the polygon. Returns 1 if the\n- * point is on an edge. Returns boolean otherwise.\n- */\n- containsPoint: function(point) {\n- var numRings = this.components.length;\n- var contained = false;\n- if (numRings > 0) {\n- // check exterior ring - 1 means on edge, boolean otherwise\n- contained = this.components[0].containsPoint(point);\n- if (contained !== 1) {\n- if (contained && numRings > 1) {\n- // check interior rings\n- var hole;\n- for (var i = 1; i < numRings; ++i) {\n- hole = this.components[i].containsPoint(point);\n- if (hole) {\n- if (hole === 1) {\n- // on edge\n- contained = 1;\n- } else {\n- // in hole\n- contained = false;\n- }\n- break;\n- }\n- }\n- }\n- }\n- }\n- return contained;\n- },\n-\n- /**\n- * APIMethod: intersects\n- * Determine if the input geometry intersects this one.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} Any type of geometry.\n- *\n- * Returns:\n- * {Boolean} The input geometry intersects this one.\n- */\n- intersects: function(geometry) {\n- var intersect = false;\n- var i, len;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry);\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n- geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- // check if rings/linestrings intersect\n- for (i = 0, len = this.components.length; i < len; ++i) {\n- intersect = geometry.intersects(this.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- if (!intersect) {\n- // check if this poly contains points of the ring/linestring\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.containsPoint(geometry.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- } else {\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.intersects(geometry.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- // check case where this poly is wholly contained by another\n- if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n- // exterior ring points will be contained in the other geometry\n- var ring = this.components[0];\n- for (i = 0, len = ring.components.length; i < len; ++i) {\n- intersect = geometry.containsPoint(ring.components[i]);\n- if (intersect) {\n- break;\n- }\n- }\n- }\n- return intersect;\n- },\n-\n- /**\n- * APIMethod: distanceTo\n- * Calculate the closest distance between two geometries (on the x-y plane).\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} The target geometry.\n- * options - {Object} Optional properties for configuring the distance\n- * calculation.\n- *\n- * Valid options:\n- * details - {Boolean} Return details from the distance calculation.\n- * Default is false.\n- * edge - {Boolean} Calculate the distance from this geometry to the\n- * nearest edge of the target geometry. Default is true. If true,\n- * calling distanceTo from a geometry that is wholly contained within\n- * the target will result in a non-zero distance. If false, whenever\n- * geometries intersect, calling distanceTo will return 0. If false,\n- * details cannot be returned.\n- *\n- * Returns:\n- * {Number | Object} The distance between this geometry and the target.\n- * If details is true, the return will be an object with distance,\n- * x0, y0, x1, and y1 properties. The x0 and y0 properties represent\n- * the coordinates of the closest point on this geometry. The x1 and y1\n- * properties represent the coordinates of the closest point on the\n- * target geometry.\n- */\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var result;\n- // this is the case where we might not be looking for distance to edge\n- if (!edge && this.intersects(geometry)) {\n- result = 0;\n- } else {\n- result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(\n- this, [geometry, options]\n- );\n- }\n- return result;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n- });\n-\n-/**\n- * APIMethod: createRegularPolygon\n- * Create a regular polygon around a radius. Useful for creating circles \n- * and the like.\n- *\n- * Parameters:\n- * origin - {<OpenLayers.Geometry.Point>} center of polygon.\n- * radius - {Float} distance to vertex, in map units.\n- * sides - {Integer} Number of sides. 20 approximates a circle.\n- * rotation - {Float} original angle of rotation, in degrees.\n- */\n-OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n- var angle = Math.PI * ((1 / sides) - (1 / 2));\n- if (rotation) {\n- angle += (rotation / 180) * Math.PI;\n- }\n- var rotatedAngle, x, y;\n- var points = [];\n- for (var i = 0; i < sides; ++i) {\n- rotatedAngle = angle + (i * 2 * Math.PI / sides);\n- x = origin.x + (radius * Math.cos(rotatedAngle));\n- y = origin.y + (radius * Math.sin(rotatedAngle));\n- points.push(new OpenLayers.Geometry.Point(x, y));\n- }\n- var ring = new OpenLayers.Geometry.LinearRing(points);\n- return new OpenLayers.Geometry.Polygon([ring]);\n-};\n-/* ======================================================================\n- OpenLayers/Geometry/MultiPolygon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Geometry/Collection.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Geometry.MultiPolygon\n- * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>\n- * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Geometry.Collection>\n- */\n-OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(\n- OpenLayers.Geometry.Collection, {\n-\n- /**\n- * Property: componentTypes\n- * {Array(String)} An array of class names representing the types of\n- * components that the collection can include. A null value means the\n- * component types are not restricted.\n- */\n- componentTypes: [\"OpenLayers.Geometry.Polygon\"],\n-\n- /**\n- * Constructor: OpenLayers.Geometry.MultiPolygon\n- * Create a new MultiPolygon geometry\n- *\n- * Parameters:\n- * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons\n- * used to generate the MultiPolygon\n- *\n- */\n-\n- CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n- });\n-/* ======================================================================\n- OpenLayers/Format/GeoJSON.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/JSON.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/MultiPoint.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/MultiLineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- * @requires OpenLayers/Geometry/MultiPolygon.js\n- * @requires OpenLayers/Console.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.GeoJSON\n- * Read and write GeoJSON. Create a new parser with the\n- * <OpenLayers.Format.GeoJSON> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.JSON>\n- */\n-OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n-\n- /**\n- * APIProperty: ignoreExtraDims\n- * {Boolean} Ignore dimensions higher than 2 when reading geometry\n- * coordinates.\n- */\n- ignoreExtraDims: false,\n-\n- /**\n- * Constructor: OpenLayers.Format.GeoJSON\n- * Create a new parser for GeoJSON.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Deserialize a GeoJSON string.\n- *\n- * Parameters:\n- * json - {String} A GeoJSON string\n- * type - {String} Optional string that determines the structure of\n- * the output. Supported values are \"Geometry\", \"Feature\", and\n- * \"FeatureCollection\". If absent or null, a default of\n- * \"FeatureCollection\" is assumed.\n- * filter - {Function} A function which will be called for every key and\n- * value at every level of the final result. Each value will be\n- * replaced by the result of the filter function. This can be used to\n- * reform generic objects into instances of classes, or to transform\n- * date strings into Date objects.\n- *\n- * Returns: \n- * {Object} The return depends on the value of the type argument. If type\n- * is \"FeatureCollection\" (the default), the return will be an array\n- * of <OpenLayers.Feature.Vector>. If type is \"Geometry\", the input json\n- * must represent a single geometry, and the return will be an\n- * <OpenLayers.Geometry>. If type is \"Feature\", the input json must\n- * represent a single feature, and the return will be an\n- * <OpenLayers.Feature.Vector>.\n- */\n- read: function(json, type, filter) {\n- type = (type) ? type : \"FeatureCollection\";\n- var results = null;\n- var obj = null;\n- if (typeof json == \"string\") {\n- obj = OpenLayers.Format.JSON.prototype.read.apply(this,\n- [json, filter]);\n- } else {\n- obj = json;\n- }\n- if (!obj) {\n- OpenLayers.Console.error(\"Bad JSON: \" + json);\n- } else if (typeof(obj.type) != \"string\") {\n- OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json);\n- } else if (this.isValidType(obj, type)) {\n- switch (type) {\n- case \"Geometry\":\n- try {\n- results = this.parseGeometry(obj);\n- } catch (err) {\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"Feature\":\n- try {\n- results = this.parseFeature(obj);\n- results.type = \"Feature\";\n- } catch (err) {\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"FeatureCollection\":\n- // for type FeatureCollection, we allow input to be any type\n- results = [];\n- switch (obj.type) {\n- case \"Feature\":\n- try {\n- results.push(this.parseFeature(obj));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- break;\n- case \"FeatureCollection\":\n- for (var i = 0, len = obj.features.length; i < len; ++i) {\n- try {\n- results.push(this.parseFeature(obj.features[i]));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- }\n- break;\n- default:\n- try {\n- var geom = this.parseGeometry(obj);\n- results.push(new OpenLayers.Feature.Vector(geom));\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err);\n- }\n- }\n- break;\n- }\n- }\n- return results;\n- },\n-\n- /**\n- * Method: isValidType\n- * Check if a GeoJSON object is a valid representative of the given type.\n- *\n- * Returns:\n- * {Boolean} The object is valid GeoJSON object of the given type.\n- */\n- isValidType: function(obj, type) {\n- var valid = false;\n- switch (type) {\n- case \"Geometry\":\n- if (OpenLayers.Util.indexOf(\n- [\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\",\n- \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"\n- ],\n- obj.type) == -1) {\n- // unsupported geometry type\n- OpenLayers.Console.error(\"Unsupported geometry type: \" +\n- obj.type);\n- } else {\n- valid = true;\n- }\n- break;\n- case \"FeatureCollection\":\n- // allow for any type to be converted to a feature collection\n- valid = true;\n- break;\n- default:\n- // for Feature types must match\n- if (obj.type == type) {\n- valid = true;\n- } else {\n- OpenLayers.Console.error(\"Cannot convert types from \" +\n- obj.type + \" to \" + type);\n- }\n- }\n- return valid;\n- },\n-\n- /**\n- * Method: parseFeature\n- * Convert a feature object from GeoJSON into an\n- * <OpenLayers.Feature.Vector>.\n- *\n- * Parameters:\n- * obj - {Object} An object created from a GeoJSON object\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature.\n- */\n- parseFeature: function(obj) {\n- var feature, geometry, attributes, bbox;\n- attributes = (obj.properties) ? obj.properties : {};\n- bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;\n- try {\n- geometry = this.parseGeometry(obj.geometry);\n- } catch (err) {\n- // deal with bad geometries\n- throw err;\n- }\n- feature = new OpenLayers.Feature.Vector(geometry, attributes);\n- if (bbox) {\n- feature.bounds = OpenLayers.Bounds.fromArray(bbox);\n- }\n- if (obj.id) {\n- feature.fid = obj.id;\n- }\n- return feature;\n- },\n-\n- /**\n- * Method: parseGeometry\n- * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * obj - {Object} An object created from a GeoJSON object\n- *\n- * Returns: \n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- parseGeometry: function(obj) {\n- if (obj == null) {\n- return null;\n- }\n- var geometry, collection = false;\n- if (obj.type == \"GeometryCollection\") {\n- if (!(OpenLayers.Util.isArray(obj.geometries))) {\n- throw \"GeometryCollection must have geometries array: \" + obj;\n- }\n- var numGeom = obj.geometries.length;\n- var components = new Array(numGeom);\n- for (var i = 0; i < numGeom; ++i) {\n- components[i] = this.parseGeometry.apply(\n- this, [obj.geometries[i]]\n- );\n- }\n- geometry = new OpenLayers.Geometry.Collection(components);\n- collection = true;\n- } else {\n- if (!(OpenLayers.Util.isArray(obj.coordinates))) {\n- throw \"Geometry must have coordinates array: \" + obj;\n- }\n- if (!this.parseCoords[obj.type.toLowerCase()]) {\n- throw \"Unsupported geometry type: \" + obj.type;\n- }\n- try {\n- geometry = this.parseCoords[obj.type.toLowerCase()].apply(\n- this, [obj.coordinates]\n- );\n- } catch (err) {\n- // deal with bad coordinates\n- throw err;\n- }\n- }\n- // We don't reproject collections because the children are reprojected\n- // for us when they are created.\n- if (this.internalProjection && this.externalProjection && !collection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- return geometry;\n- },\n-\n- /**\n- * Property: parseCoords\n- * Object with properties corresponding to the GeoJSON geometry types.\n- * Property values are functions that do the actual parsing.\n- */\n- parseCoords: {\n- /**\n- * Method: parseCoords.point\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"point\": function(array) {\n- if (this.ignoreExtraDims == false &&\n- array.length != 2) {\n- throw \"Only 2D points are supported: \" + array;\n- }\n- return new OpenLayers.Geometry.Point(array[0], array[1]);\n- },\n-\n- /**\n- * Method: parseCoords.multipoint\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"multipoint\": function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- points.push(p);\n- }\n- return new OpenLayers.Geometry.MultiPoint(points);\n- },\n-\n- /**\n- * Method: parseCoords.linestring\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"linestring\": function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- points.push(p);\n- }\n- return new OpenLayers.Geometry.LineString(points);\n- },\n-\n- /**\n- * Method: parseCoords.multilinestring\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"multilinestring\": function(array) {\n- var lines = [];\n- var l = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- lines.push(l);\n- }\n- return new OpenLayers.Geometry.MultiLineString(lines);\n- },\n-\n- /**\n- * Method: parseCoords.polygon\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"polygon\": function(array) {\n- var rings = [];\n- var r, l;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- r = new OpenLayers.Geometry.LinearRing(l.components);\n- rings.push(r);\n- }\n- return new OpenLayers.Geometry.Polygon(rings);\n- },\n-\n- /**\n- * Method: parseCoords.multipolygon\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"multipolygon\": function(array) {\n- var polys = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"polygon\"].apply(this, [array[i]]);\n- } catch (err) {\n- throw err;\n- }\n- polys.push(p);\n- }\n- return new OpenLayers.Geometry.MultiPolygon(polys);\n- },\n-\n- /**\n- * Method: parseCoords.box\n- * Convert a coordinate array from GeoJSON into an\n- * <OpenLayers.Geometry>.\n- *\n- * Parameters:\n- * array - {Object} The coordinates array from the GeoJSON fragment.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry.\n- */\n- \"box\": function(array) {\n- if (array.length != 2) {\n- throw \"GeoJSON box coordinates must have 2 elements\";\n- }\n- return new OpenLayers.Geometry.Polygon([\n- new OpenLayers.Geometry.LinearRing([\n- new OpenLayers.Geometry.Point(array[0][0], array[0][1]),\n- new OpenLayers.Geometry.Point(array[1][0], array[0][1]),\n- new OpenLayers.Geometry.Point(array[1][0], array[1][1]),\n- new OpenLayers.Geometry.Point(array[0][0], array[1][1]),\n- new OpenLayers.Geometry.Point(array[0][0], array[0][1])\n- ])\n- ]);\n- }\n-\n- },\n-\n- /**\n- * APIMethod: write\n- * Serialize a feature, geometry, array of features into a GeoJSON string.\n- *\n- * Parameters:\n- * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,\n- * or an array of features.\n- * pretty - {Boolean} Structure the output with newlines and indentation.\n- * Default is false.\n- *\n- * Returns:\n- * {String} The GeoJSON string representation of the input geometry,\n- * features, or array of features.\n- */\n- write: function(obj, pretty) {\n- var geojson = {\n- \"type\": null\n- };\n- if (OpenLayers.Util.isArray(obj)) {\n- geojson.type = \"FeatureCollection\";\n- var numFeatures = obj.length;\n- geojson.features = new Array(numFeatures);\n- for (var i = 0; i < numFeatures; ++i) {\n- var element = obj[i];\n- if (!element instanceof OpenLayers.Feature.Vector) {\n- var msg = \"FeatureCollection only supports collections \" +\n- \"of features: \" + element;\n- throw msg;\n- }\n- geojson.features[i] = this.extract.feature.apply(\n- this, [element]\n- );\n- }\n- } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n- geojson = this.extract.geometry.apply(this, [obj]);\n- } else if (obj instanceof OpenLayers.Feature.Vector) {\n- geojson = this.extract.feature.apply(this, [obj]);\n- if (obj.layer && obj.layer.projection) {\n- geojson.crs = this.createCRSObject(obj);\n- }\n- }\n- return OpenLayers.Format.JSON.prototype.write.apply(this,\n- [geojson, pretty]);\n- },\n-\n- /**\n- * Method: createCRSObject\n- * Create the CRS object for an object.\n- *\n- * Parameters:\n- * object - {<OpenLayers.Feature.Vector>} \n- *\n- * Returns:\n- * {Object} An object which can be assigned to the crs property\n- * of a GeoJSON object.\n- */\n- createCRSObject: function(object) {\n- var proj = object.layer.projection.toString();\n- var crs = {};\n- if (proj.match(/epsg:/i)) {\n- var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n- if (code == 4326) {\n- crs = {\n- \"type\": \"name\",\n- \"properties\": {\n- \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n- }\n- };\n- } else {\n- crs = {\n- \"type\": \"name\",\n- \"properties\": {\n- \"name\": \"EPSG:\" + code\n- }\n- };\n- }\n- }\n- return crs;\n- },\n-\n- /**\n- * Property: extract\n- * Object with properties corresponding to the GeoJSON types.\n- * Property values are functions that do the actual value extraction.\n- */\n- extract: {\n- /**\n- * Method: extract.feature\n- * Return a partial GeoJSON object representing a single feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- *\n- * Returns:\n- * {Object} An object representing the point.\n- */\n- 'feature': function(feature) {\n- var geom = this.extract.geometry.apply(this, [feature.geometry]);\n- var json = {\n- \"type\": \"Feature\",\n- \"properties\": feature.attributes,\n- \"geometry\": geom\n- };\n- if (feature.fid != null) {\n- json.id = feature.fid;\n- }\n- return json;\n- },\n-\n- /**\n- * Method: extract.geometry\n- * Return a GeoJSON object representing a single geometry.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- *\n- * Returns:\n- * {Object} An object representing the geometry.\n- */\n- 'geometry': function(geometry) {\n- if (geometry == null) {\n- return null;\n- }\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- var geometryType = geometry.CLASS_NAME.split('.')[2];\n- var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n- var json;\n- if (geometryType == \"Collection\") {\n- json = {\n- \"type\": \"GeometryCollection\",\n- \"geometries\": data\n- };\n- } else {\n- json = {\n- \"type\": geometryType,\n- \"coordinates\": data\n- };\n- }\n-\n- return json;\n- },\n-\n- /**\n- * Method: extract.point\n- * Return an array of coordinates from a point.\n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns: \n- * {Array} An array of coordinates representing the point.\n- */\n- 'point': function(point) {\n- return [point.x, point.y];\n- },\n-\n- /**\n- * Method: extract.multipoint\n- * Return an array of point coordinates from a multipoint.\n- *\n- * Parameters:\n- * multipoint - {<OpenLayers.Geometry.MultiPoint>}\n- *\n- * Returns:\n- * {Array} An array of point coordinate arrays representing\n- * the multipoint.\n- */\n- 'multipoint': function(multipoint) {\n- var array = [];\n- for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [multipoint.components[i]]));\n- }\n- return array;\n- },\n-\n- /**\n- * Method: extract.linestring\n- * Return an array of coordinate arrays from a linestring.\n- *\n- * Parameters:\n- * linestring - {<OpenLayers.Geometry.LineString>}\n- *\n- * Returns:\n- * {Array} An array of coordinate arrays representing\n- * the linestring.\n- */\n- 'linestring': function(linestring) {\n- var array = [];\n- for (var i = 0, len = linestring.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [linestring.components[i]]));\n- }\n- return array;\n- },\n-\n- /**\n- * Method: extract.multilinestring\n- * Return an array of linestring arrays from a linestring.\n- * \n- * Parameters:\n- * multilinestring - {<OpenLayers.Geometry.MultiLineString>}\n- * \n- * Returns:\n- * {Array} An array of linestring arrays representing\n- * the multilinestring.\n- */\n- 'multilinestring': function(multilinestring) {\n- var array = [];\n- for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));\n- }\n- return array;\n- },\n-\n- /**\n- * Method: extract.polygon\n- * Return an array of linear ring arrays from a polygon.\n- *\n- * Parameters:\n- * polygon - {<OpenLayers.Geometry.Polygon>}\n- * \n- * Returns:\n- * {Array} An array of linear ring arrays representing the polygon.\n- */\n- 'polygon': function(polygon) {\n- var array = [];\n- for (var i = 0, len = polygon.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [polygon.components[i]]));\n- }\n- return array;\n- },\n-\n- /**\n- * Method: extract.multipolygon\n- * Return an array of polygon arrays from a multipolygon.\n- * \n- * Parameters:\n- * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}\n- * \n- * Returns:\n- * {Array} An array of polygon arrays representing\n- * the multipolygon\n- */\n- 'multipolygon': function(multipolygon) {\n- var array = [];\n- for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n- array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));\n- }\n- return array;\n- },\n-\n- /**\n- * Method: extract.collection\n- * Return an array of geometries from a geometry collection.\n- * \n- * Parameters:\n- * collection - {<OpenLayers.Geometry.Collection>}\n- * \n- * Returns:\n- * {Array} An array of geometry objects representing the geometry\n- * collection.\n- */\n- 'collection': function(collection) {\n- var len = collection.components.length;\n- var array = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- array[i] = this.extract.geometry.apply(\n- this, [collection.components[i]]\n- );\n- }\n- return array;\n- }\n-\n-\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Strategy.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Strategy\n- * Abstract vector layer strategy class. Not to be instantiated directly. Use\n- * one of the strategy subclasses instead.\n- */\n-OpenLayers.Strategy = OpenLayers.Class({\n-\n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n- */\n- layer: null,\n-\n- /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n- */\n- options: null,\n-\n- /** \n- * Property: active \n- * {Boolean} The control is active.\n- */\n- active: null,\n-\n- /**\n- * Property: autoActivate\n- * {Boolean} The creator of the strategy can set autoActivate to false\n- * to fully control when the protocol is activated and deactivated.\n- * Defaults to true.\n- */\n- autoActivate: true,\n-\n- /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the strategy can set autoDestroy to false\n- * to fully control when the strategy is destroyed. Defaults to\n- * true.\n- */\n- autoDestroy: true,\n-\n- /**\n- * Constructor: OpenLayers.Strategy\n- * Abstract class for vector strategies. Create instances of a subclass.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- // set the active property here, so that user cannot override it\n- this.active = false;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n- */\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n- },\n-\n- /**\n- * Method: setLayer\n- * Called to set the <layer> property.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>}\n- */\n- setLayer: function(layer) {\n- this.layer = layer;\n- },\n-\n- /**\n- * Method: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n- */\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n- }\n- return false;\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n- */\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true;\n- }\n- return false;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Strategy\"\n-});\n-/* ======================================================================\n- OpenLayers/Strategy/Fixed.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Strategy.js\n- */\n-\n-/**\n- * Class: OpenLayers.Strategy.Fixed\n- * A simple strategy that requests features once and never requests new data.\n- *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n- */\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n-\n- /**\n- * APIProperty: preload\n- * {Boolean} Load data before layer made visible. Enabling this may result\n- * in considerable overhead if your application loads many data layers\n- * that are not visible by default. Default is false.\n- */\n- preload: false,\n-\n- /**\n- * Constructor: OpenLayers.Strategy.Fixed\n- * Create a new Fixed strategy.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n-\n- /**\n- * Method: activate\n- * Activate the strategy: load data or add listener to load when visible\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n- */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- \"refresh\": this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load();\n- } else {\n- this.layer.events.on({\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- }\n- }\n- return activated;\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivate the strategy. Undo what is done in <activate>.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n- */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- \"refresh\": this.load,\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- }\n- return deactivated;\n- },\n-\n- /**\n- * Method: load\n- * Tells protocol to load data and unhooks the visibilitychanged event\n- *\n- * Parameters:\n- * options - {Object} options to pass to protocol read.\n- */\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: merge\n- * Add all features to the layer.\n- * If the layer projection differs from the map projection, features\n- * will be transformed from the layer projection to the map projection.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object passed\n- * by the protocol.\n- */\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local);\n- }\n- }\n- }\n- layer.addFeatures(features);\n- }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- });\n- },\n+ * Constructor: OpenLayers.Popup.FramedCloud\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * lonlat - {<OpenLayers.LonLat>}\n+ * contentSize - {<OpenLayers.Size>}\n+ * contentHTML - {String}\n+ * anchor - {Object} Object to which we'll anchor the popup. Must expose \n+ * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n+ * (Note that this is generally an <OpenLayers.Icon>).\n+ * closeBox - {Boolean}\n+ * closeBoxCallback - {Function} Function to be called on closeBox click.\n+ */\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n+ closeBoxCallback) {\n \n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n-});\n+ this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');\n+ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n+ this.contentDiv.className = this.contentDisplayClass;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n+ });\n /* ======================================================================\n- OpenLayers/Filter/Spatial.js\n+ OpenLayers/Layer/HTTPRequest.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Filter.js\n+ * @requires OpenLayers/Layer.js\n */\n \n /**\n- * Class: OpenLayers.Filter.Spatial\n- * This class represents a spatial filter.\n- * Currently implemented: BBOX, DWithin and Intersects\n+ * Class: OpenLayers.Layer.HTTPRequest\n * \n- * Inherits from:\n- * - <OpenLayers.Filter>\n+ * Inherits from: \n+ * - <OpenLayers.Layer>\n */\n-OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n-\n- /**\n- * APIProperty: type\n- * {String} Type of spatial filter.\n- *\n- * The type should be one of:\n- * - OpenLayers.Filter.Spatial.BBOX\n- * - OpenLayers.Filter.Spatial.INTERSECTS\n- * - OpenLayers.Filter.Spatial.DWITHIN\n- * - OpenLayers.Filter.Spatial.WITHIN\n- * - OpenLayers.Filter.Spatial.CONTAINS\n- */\n- type: null,\n-\n- /**\n- * APIProperty: property\n- * {String} Name of the context property to compare.\n- */\n- property: null,\n+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n \n- /**\n- * APIProperty: value\n- * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry\n- * to be used by the filter. Use bounds for BBOX filters and geometry\n- * for INTERSECTS or DWITHIN filters.\n+ /** \n+ * Constant: URL_HASH_FACTOR\n+ * {Float} Used to hash URL param strings for multi-WMS server selection.\n+ * Set to the Golden Ratio per Knuth's recommendation.\n */\n- value: null,\n+ URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n \n- /**\n- * APIProperty: distance\n- * {Number} The distance to use in a DWithin spatial filter.\n+ /** \n+ * Property: url\n+ * {Array(String) or String} This is either an array of url strings or \n+ * a single url string. \n */\n- distance: null,\n+ url: null,\n \n- /**\n- * APIProperty: distanceUnits\n- * {String} The units to use for the distance, e.g. 'm'.\n+ /** \n+ * Property: params\n+ * {Object} Hashtable of key/value parameters\n */\n- distanceUnits: null,\n+ params: null,\n \n /** \n- * Constructor: OpenLayers.Filter.Spatial\n- * Creates a spatial filter.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * filter.\n- * \n- * Returns:\n- * {<OpenLayers.Filter.Spatial>}\n+ * APIProperty: reproject\n+ * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html\n+ * for information on the replacement for this functionality. \n+ * {Boolean} Whether layer should reproject itself based on base layer \n+ * locations. This allows reprojection onto commercial layers. \n+ * Default is false: Most layers can't reproject, but layers \n+ * which can create non-square geographic pixels can, like WMS.\n+ * \n */\n+ reproject: false,\n \n /**\n- * Method: evaluate\n- * Evaluates this filter for a specific feature.\n+ * Constructor: OpenLayers.Layer.HTTPRequest\n * \n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.\n- * \n- * Returns:\n- * {Boolean} The feature meets filter criteria.\n+ * name - {String}\n+ * url - {Array(String) or String}\n+ * params - {Object}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- evaluate: function(feature) {\n- var intersect = false;\n- switch (this.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- case OpenLayers.Filter.Spatial.INTERSECTS:\n- if (feature.geometry) {\n- var geom = this.value;\n- if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n- geom = this.value.toGeometry();\n- }\n- if (feature.geometry.intersects(geom)) {\n- intersect = true;\n- }\n- }\n- break;\n- default:\n- throw new Error('evaluate is not implemented for this filter type.');\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n+ this.url = url;\n+ if (!this.params) {\n+ this.params = OpenLayers.Util.extend({}, params);\n }\n- return intersect;\n },\n \n /**\n- * APIMethod: clone\n- * Clones this filter.\n- * \n- * Returns:\n- * {<OpenLayers.Filter.Spatial>} Clone of this filter.\n+ * APIMethod: destroy\n */\n- clone: function() {\n- var options = OpenLayers.Util.applyDefaults({\n- value: this.value && this.value.clone && this.value.clone()\n- }, this);\n- return new OpenLayers.Filter.Spatial(options);\n+ destroy: function() {\n+ this.url = null;\n+ this.params = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n },\n- CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n-});\n-\n-OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n-OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n-OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n-OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n-OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n-/* ======================================================================\n- OpenLayers/Strategy/BBOX.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Strategy.js\n- * @requires OpenLayers/Filter/Spatial.js\n- */\n-\n-/**\n- * Class: OpenLayers.Strategy.BBOX\n- * A simple strategy that reads new features when the viewport invalidates\n- * some bounds.\n- *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n- */\n-OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * Property: bounds\n- * {<OpenLayers.Bounds>} The current data bounds (in the same projection\n- * as the layer - not always the same projection as the map).\n- */\n- bounds: null,\n-\n- /** \n- * Property: resolution \n- * {Float} The current data resolution. \n+ * APIMethod: clone\n+ * \n+ * Parameters:\n+ * obj - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this \n+ * <OpenLayers.Layer.HTTPRequest>\n */\n- resolution: null,\n+ clone: function(obj) {\n \n- /**\n- * APIProperty: ratio\n- * {Float} The ratio of the data bounds to the viewport bounds (in each\n- * dimension). Default is 2.\n- */\n- ratio: 2,\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.HTTPRequest(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n \n- /** \n- * Property: resFactor \n- * {Float} Optional factor used to determine when previously requested \n- * features are invalid. If set, the resFactor will be compared to the\n- * resolution of the previous request to the current map resolution.\n- * If resFactor > (old / new) and 1/resFactor < (old / new). If you\n- * set a resFactor of 1, data will be requested every time the\n- * resolution changes. If you set a resFactor of 3, data will be\n- * requested if the old resolution is 3 times the new, or if the new is\n- * 3 times the old. If the old bounds do not contain the new bounds\n- * new data will always be requested (with or without considering\n- * resFactor). \n- */\n- resFactor: null,\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n \n- /**\n- * Property: response\n- * {<OpenLayers.Protocol.Response>} The protocol response object returned\n- * by the layer protocol.\n- */\n- response: null,\n+ // copy/set any non-init, non-simple values here\n \n- /**\n- * Constructor: OpenLayers.Strategy.BBOX\n- * Create a new BBOX strategy.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n+ return obj;\n+ },\n \n- /**\n- * Method: activate\n- * Set up strategy with regard to reading new batches of remote data.\n+ /** \n+ * APIMethod: setUrl\n * \n- * Returns:\n- * {Boolean} The strategy was successfully activated.\n+ * Parameters:\n+ * newUrl - {String}\n */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- \"moveend\": this.update,\n- \"refresh\": this.update,\n- \"visibilitychanged\": this.update,\n- scope: this\n- });\n- this.update();\n- }\n- return activated;\n+ setUrl: function(newUrl) {\n+ this.url = newUrl;\n },\n \n /**\n- * Method: deactivate\n- * Tear down strategy with regard to reading new batches of remote data.\n+ * APIMethod: mergeNewParams\n * \n+ * Parameters:\n+ * newParams - {Object}\n+ *\n * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- \"moveend\": this.update,\n- \"refresh\": this.update,\n- \"visibilitychanged\": this.update,\n- scope: this\n+ mergeNewParams: function(newParams) {\n+ this.params = OpenLayers.Util.extend(this.params, newParams);\n+ var ret = this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"params\"\n });\n }\n- return deactivated;\n+ return ret;\n },\n \n /**\n- * Method: update\n- * Callback function called on \"moveend\" or \"refresh\" layer events.\n+ * APIMethod: redraw\n+ * Redraws the layer. Returns true if the layer was redrawn, false if not.\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will determine\n- * the behaviour of this Strategy\n- *\n- * Valid options include:\n- * force - {Boolean} if true, new data must be unconditionally read.\n- * noAbort - {Boolean} if true, do not abort previous requests.\n- */\n- update: function(options) {\n- var mapBounds = this.getMapBounds();\n- if (mapBounds !== null && ((options && options.force) ||\n- (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {\n- this.calculateBounds(mapBounds);\n- this.resolution = this.layer.map.getResolution();\n- this.triggerRead(options);\n- }\n- },\n-\n- /**\n- * Method: getMapBounds\n- * Get the map bounds expressed in the same projection as this layer.\n+ * force - {Boolean} Force redraw by adding random parameter.\n *\n * Returns:\n- * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.\n+ * {Boolean} The layer was redrawn.\n */\n- getMapBounds: function() {\n- if (this.layer.map === null) {\n- return null;\n- }\n- var bounds = this.layer.map.getExtent();\n- if (bounds && !this.layer.projection.equals(\n- this.layer.map.getProjectionObject())) {\n- bounds = bounds.clone().transform(\n- this.layer.map.getProjectionObject(), this.layer.projection\n- );\n+ redraw: function(force) {\n+ if (force) {\n+ return this.mergeNewParams({\n+ \"_olSalt\": Math.random()\n+ });\n+ } else {\n+ return OpenLayers.Layer.prototype.redraw.apply(this, []);\n }\n- return bounds;\n },\n \n /**\n- * Method: invalidBounds\n- * Determine whether the previously requested set of features is invalid. \n- * This occurs when the new map bounds do not contain the previously \n- * requested bounds. In addition, if <resFactor> is set, it will be \n- * considered.\n+ * Method: selectUrl\n+ * selectUrl() implements the standard floating-point multiplicative\n+ * hash function described by Knuth, and hashes the contents of the \n+ * given param string into a float between 0 and 1. This float is then\n+ * scaled to the size of the provided urls array, and used to select\n+ * a URL.\n *\n * Parameters:\n- * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n- * retrieved from the map object if not provided\n- *\n+ * paramString - {String}\n+ * urls - {Array(String)}\n+ * \n * Returns:\n- * {Boolean} \n+ * {String} An entry from the urls array, deterministically selected based\n+ * on the paramString.\n */\n- invalidBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds();\n- }\n- var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n- if (!invalid && this.resFactor) {\n- var ratio = this.resolution / this.layer.map.getResolution();\n- invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));\n+ selectUrl: function(paramString, urls) {\n+ var product = 1;\n+ for (var i = 0, len = paramString.length; i < len; i++) {\n+ product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n+ product -= Math.floor(product);\n }\n- return invalid;\n+ return urls[Math.floor(product * urls.length)];\n },\n \n- /**\n- * Method: calculateBounds\n+ /** \n+ * Method: getFullRequestString\n+ * Combine url with layer's params and these newParams. \n+ * \n+ * does checking on the serverPath variable, allowing for cases when it \n+ * is supplied with trailing ? or &, as well as cases where not. \n *\n- * Parameters:\n- * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n- * retrieved from the map object if not provided\n- */\n- calculateBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds();\n- }\n- var center = mapBounds.getCenterLonLat();\n- var dataWidth = mapBounds.getWidth() * this.ratio;\n- var dataHeight = mapBounds.getHeight() * this.ratio;\n- this.bounds = new OpenLayers.Bounds(\n- center.lon - (dataWidth / 2),\n- center.lat - (dataHeight / 2),\n- center.lon + (dataWidth / 2),\n- center.lat + (dataHeight / 2)\n- );\n- },\n-\n- /**\n- * Method: triggerRead\n+ * return in formatted string like this:\n+ * \"server?key1=value1&key2=value2&key3=value3\"\n+ * \n+ * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.\n *\n * Parameters:\n- * options - {Object} Additional options for the protocol's read method \n- * (optional)\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} The protocol response object\n- * returned by the layer protocol.\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n+ * \n+ * Returns: \n+ * {String}\n */\n- triggerRead: function(options) {\n- if (this.response && !(options && options.noAbort === true)) {\n- this.layer.protocol.abort(this.response);\n- this.layer.events.triggerEvent(\"loadend\");\n- }\n- var evt = {\n- filter: this.createFilter()\n- };\n- this.layer.events.triggerEvent(\"loadstart\", evt);\n- this.response = this.layer.protocol.read(\n- OpenLayers.Util.applyDefaults({\n- filter: evt.filter,\n- callback: this.merge,\n- scope: this\n- }, options));\n- },\n+ getFullRequestString: function(newParams, altUrl) {\n \n- /**\n- * Method: createFilter\n- * Creates a spatial BBOX filter. If the layer that this strategy belongs\n- * to has a filter property, this filter will be combined with the BBOX \n- * filter.\n- * \n- * Returns\n- * {<OpenLayers.Filter>} The filter object.\n- */\n- createFilter: function() {\n- var filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- value: this.bounds,\n- projection: this.layer.projection\n- });\n- if (this.layer.filter) {\n- filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.layer.filter, filter]\n- });\n+ // if not altUrl passed in, use layer's url\n+ var url = altUrl || this.url;\n+\n+ // create a new params hashtable with all the layer params and the \n+ // new params together. then convert to string\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+\n+ // if url is not a string, it should be an array of strings, \n+ // in which case we will deterministically select one of them in \n+ // order to evenly distribute requests to different urls.\n+ //\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url);\n }\n- return filter;\n- },\n \n- /**\n- * Method: merge\n- * Given a list of features, determine which ones to add to the layer.\n- * If the layer projection differs from the map projection, features\n- * will be transformed from the layer projection to the map projection.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object passed\n- * by the protocol.\n- */\n- merge: function(resp) {\n- this.layer.destroyFeatures();\n- if (resp.success()) {\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local);\n- }\n- }\n- }\n- this.layer.addFeatures(features);\n+ // ignore parameters that are already in the url search string\n+ var urlParams =\n+ OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key];\n }\n- } else {\n- this.bounds = null;\n }\n- this.response = null;\n- this.layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- });\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n+\n+ return OpenLayers.Util.urlAppend(url, paramsString);\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n+ CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n });\n /* ======================================================================\n- OpenLayers/Control.js\n+ OpenLayers/Tile.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Control\n- * Controls affect the display or behavior of the map. They allow everything\n- * from panning and zooming to displaying a scale indicator. Controls by \n- * default are added to the map they are contained within however it is\n- * possible to add a control to an external div by passing the div in the\n- * options parameter.\n- * \n- * Example:\n- * The following example shows how to add many of the common controls\n- * to a map.\n+ * Class: OpenLayers.Tile \n+ * This is a class designed to designate a single tile, however\n+ * it is explicitly designed to do relatively little. Tiles store \n+ * information about themselves -- such as the URL that they are related\n+ * to, and their size - but do not add themselves to the layer div \n+ * automatically, for example. Create a new tile with the \n+ * <OpenLayers.Tile> constructor, or a subclass. \n * \n- * > var map = new OpenLayers.Map('map', { controls: [] });\n- * >\n- * > map.addControl(new OpenLayers.Control.PanZoomBar());\n- * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));\n- * > map.addControl(new OpenLayers.Control.Permalink());\n- * > map.addControl(new OpenLayers.Control.Permalink('permalink'));\n- * > map.addControl(new OpenLayers.Control.MousePosition());\n- * > map.addControl(new OpenLayers.Control.OverviewMap());\n- * > map.addControl(new OpenLayers.Control.KeyboardDefaults());\n- *\n- * The next code fragment is a quick example of how to intercept \n- * shift-mouse click to display the extent of the bounding box\n- * dragged out by the user. Usually controls are not created\n- * in exactly this manner. See the source for a more complete \n- * example:\n- *\n- * > var control = new OpenLayers.Control();\n- * > OpenLayers.Util.extend(control, {\n- * > draw: function () {\n- * > // this Handler.Box will intercept the shift-mousedown\n- * > // before Control.MouseDefault gets to see it\n- * > this.box = new OpenLayers.Handler.Box( control, \n- * > {\"done\": this.notice},\n- * > {keyMask: OpenLayers.Handler.MOD_SHIFT});\n- * > this.box.activate();\n- * > },\n- * >\n- * > notice: function (bounds) {\n- * > OpenLayers.Console.userError(bounds);\n- * > }\n- * > }); \n- * > map.addControl(control);\n+ * TBD 3.0 - remove reference to url in above paragraph\n * \n */\n-OpenLayers.Control = OpenLayers.Class({\n-\n- /** \n- * Property: id \n- * {String} \n- */\n- id: null,\n-\n- /** \n- * Property: map \n- * {<OpenLayers.Map>} this gets set in the addControl() function in\n- * OpenLayers.Map \n- */\n- map: null,\n+OpenLayers.Tile = OpenLayers.Class({\n \n- /** \n- * APIProperty: div \n- * {DOMElement} The element that contains the control, if not present the \n- * control is placed inside the map.\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types:\n+ * beforedraw - Triggered before the tile is drawn. Used to defer\n+ * drawing to an animation queue. To defer drawing, listeners need\n+ * to return false, which will abort drawing. The queue handler needs\n+ * to call <draw>(true) to actually draw the tile.\n+ * loadstart - Triggered when tile loading starts.\n+ * loadend - Triggered when tile loading ends.\n+ * loaderror - Triggered before the loadend event (i.e. when the tile is\n+ * still hidden) if the tile could not be loaded.\n+ * reload - Triggered when an already loading tile is reloaded.\n+ * unload - Triggered before a tile is unloaded.\n */\n- div: null,\n+ events: null,\n \n- /** \n- * APIProperty: type \n- * {Number} Controls can have a 'type'. The type determines the type of\n- * interactions which are possible with them when they are placed in an\n- * <OpenLayers.Control.Panel>. \n+ /**\n+ * APIProperty: eventListeners\n+ * {Object} If set as an option at construction, the eventListeners\n+ * object will be registered with <OpenLayers.Events.on>. Object\n+ * structure must be a listeners object as shown in the example for\n+ * the events.on method.\n+ *\n+ * This options can be set in the ``tileOptions`` option from\n+ * <OpenLayers.Layer.Grid>. For example, to be notified of the\n+ * ``loadend`` event of each tiles:\n+ * (code)\n+ * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {\n+ * tileOptions: {\n+ * eventListeners: {\n+ * 'loadend': function(evt) {\n+ * // do something on loadend\n+ * }\n+ * }\n+ * }\n+ * });\n+ * (end)\n */\n- type: null,\n+ eventListeners: null,\n \n- /** \n- * Property: allowSelection\n- * {Boolean} By default, controls do not allow selection, because\n- * it may interfere with map dragging. If this is true, OpenLayers\n- * will not prevent selection of the control.\n- * Default is false.\n+ /**\n+ * Property: id \n+ * {String} null\n */\n- allowSelection: false,\n+ id: null,\n \n /** \n- * Property: displayClass \n- * {string} This property is used for CSS related to the drawing of the\n- * Control. \n- */\n- displayClass: \"\",\n-\n- /**\n- * APIProperty: title \n- * {string} This property is used for showing a tooltip over the \n- * Control. \n+ * Property: layer \n+ * {<OpenLayers.Layer>} layer the tile is attached to \n */\n- title: \"\",\n+ layer: null,\n \n /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * false.\n+ * Property: url\n+ * {String} url of the request.\n+ *\n+ * TBD 3.0 \n+ * Deprecated. The base tile class does not need an url. This should be \n+ * handled in subclasses. Does not belong here.\n */\n- autoActivate: false,\n+ url: null,\n \n /** \n- * APIProperty: active \n- * {Boolean} The control is active (read-only). Use <activate> and \n- * <deactivate> to change control state.\n+ * APIProperty: bounds \n+ * {<OpenLayers.Bounds>} null\n */\n- active: null,\n+ bounds: null,\n \n- /**\n- * Property: handlerOptions\n- * {Object} Used to set non-default properties on the control's handler\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>} null\n */\n- handlerOptions: null,\n+ size: null,\n \n /** \n- * Property: handler \n- * {<OpenLayers.Handler>} null\n+ * Property: position \n+ * {<OpenLayers.Pixel>} Top Left pixel of the tile\n */\n- handler: null,\n+ position: null,\n \n /**\n- * APIProperty: eventListeners\n- * {Object} If set as an option at construction, the eventListeners\n- * object will be registered with <OpenLayers.Events.on>. Object\n- * structure must be a listeners object as shown in the example for\n- * the events.on method.\n+ * Property: isLoading\n+ * {Boolean} Is the tile loading?\n */\n- eventListeners: null,\n+ isLoading: false,\n \n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to control.events.object (a reference\n- * to the control).\n- * element - {DOMElement} A reference to control.events.element (which\n- * will be null unless documented otherwise).\n- *\n- * Supported map event types:\n- * activate - Triggered when activated.\n- * deactivate - Triggered when deactivated.\n+ /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.\n+ * there is no need for the base tile class to have a url.\n */\n- events: null,\n \n- /**\n- * Constructor: OpenLayers.Control\n- * Create an OpenLayers Control. The options passed as a parameter\n- * directly extend the control. For example passing the following:\n- * \n- * > var control = new OpenLayers.Control({div: myDiv});\n- *\n- * Overrides the default div attribute value of null.\n+ /** \n+ * Constructor: OpenLayers.Tile\n+ * Constructor for a new <OpenLayers.Tile> instance.\n * \n * Parameters:\n- * options - {Object} \n+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n+ * position - {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * url - {<String>}\n+ * size - {<OpenLayers.Size>}\n+ * options - {Object}\n */\n- initialize: function(options) {\n- // We do this before the extend so that instances can override\n- // className in options.\n- this.displayClass =\n- this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ this.layer = layer;\n+ this.position = position.clone();\n+ this.setBounds(bounds);\n+ this.url = url;\n+ if (size) {\n+ this.size = size.clone();\n+ }\n+\n+ //give the tile a unique id based on its BBOX.\n+ this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n \n OpenLayers.Util.extend(this, options);\n \n this.events = new OpenLayers.Events(this);\n if (this.eventListeners instanceof Object) {\n this.events.on(this.eventListeners);\n }\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- }\n },\n \n /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ * Method: unload\n+ * Call immediately before destroying if you are listening to tile\n+ * events, so that counters are properly handled if tile is still\n+ * loading at destroy-time. Will only fire an event if the tile is\n+ * still loading.\n */\n- destroy: function() {\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- }\n- this.events.destroy();\n- this.events = null;\n- }\n- this.eventListeners = null;\n-\n- // eliminate circular references\n- if (this.handler) {\n- this.handler.destroy();\n- this.handler = null;\n- }\n- if (this.handlers) {\n- for (var key in this.handlers) {\n- if (this.handlers.hasOwnProperty(key) &&\n- typeof this.handlers[key].destroy == \"function\") {\n- this.handlers[key].destroy();\n- }\n- }\n- this.handlers = null;\n- }\n- if (this.map) {\n- this.map.removeControl(this);\n- this.map = null;\n+ unload: function() {\n+ if (this.isLoading) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"unload\");\n }\n- this.div = null;\n },\n \n /** \n- * Method: setMap\n- * Set the map property for the control. This is done through an accessor\n- * so that subclasses can override this and take special action once \n- * they have their map variable set. \n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} \n+ * APIMethod: destroy\n+ * Nullify references to prevent circular references and memory leaks.\n */\n- setMap: function(map) {\n- this.map = map;\n- if (this.handler) {\n- this.handler.setMap(map);\n+ destroy: function() {\n+ this.layer = null;\n+ this.bounds = null;\n+ this.size = null;\n+ this.position = null;\n+\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n }\n+ this.events.destroy();\n+ this.eventListeners = null;\n+ this.events = null;\n },\n \n /**\n * Method: draw\n- * The draw method is called when the control is ready to be displayed\n- * on the page. If a div has not been created one is created. Controls\n- * with a visual component will almost always want to override this method \n- * to customize the look of control. \n+ * Clear whatever is currently in the tile, then return whether or not \n+ * it should actually be re-drawn. This is an example implementation\n+ * that can be overridden by subclasses. The minimum thing to do here\n+ * is to call <clear> and return the result from <shouldDraw>.\n *\n * Parameters:\n- * px - {<OpenLayers.Pixel>} The top-left pixel position of the control\n- * or null.\n- *\n+ * force - {Boolean} If true, the tile will not be cleared and no beforedraw\n+ * event will be fired. This is used for drawing tiles asynchronously\n+ * after drawing has been cancelled by returning false from a beforedraw\n+ * listener.\n+ * \n * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the control\n+ * {Boolean} Whether or not the tile should actually be drawn. Returns null\n+ * if a beforedraw listener returned false.\n */\n- draw: function(px) {\n- if (this.div == null) {\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.className = this.displayClass;\n- if (!this.allowSelection) {\n- this.div.className += \" olControlNoSelect\";\n- this.div.setAttribute(\"unselectable\", \"on\", 0);\n- this.div.onselectstart = OpenLayers.Function.False;\n- }\n- if (this.title != \"\") {\n- this.div.title = this.title;\n- }\n+ draw: function(force) {\n+ if (!force) {\n+ //clear tile's contents and mark as not drawn\n+ this.clear();\n }\n- if (px != null) {\n- this.position = px.clone();\n+ var draw = this.shouldDraw();\n+ if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n+ draw = null;\n }\n- this.moveTo(this.position);\n- return this.div;\n+ return draw;\n },\n \n /**\n- * Method: moveTo\n- * Sets the left and top style attributes to the passed in pixel \n- * coordinates.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n+ * Method: shouldDraw\n+ * Return whether or not the tile should actually be (re-)drawn. The only\n+ * case where we *wouldn't* want to draw the tile is if the tile is outside\n+ * its layer's maxExtent\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the tile should actually be drawn.\n */\n- moveTo: function(px) {\n- if ((px != null) && (this.div != null)) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\";\n+ shouldDraw: function() {\n+ var withinMaxExtent = false,\n+ maxExtent = this.layer.maxExtent;\n+ if (maxExtent) {\n+ var map = this.layer.map;\n+ var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n+ if (this.bounds.intersectsBounds(maxExtent, {\n+ inclusive: false,\n+ worldBounds: worldBounds\n+ })) {\n+ withinMaxExtent = true;\n+ }\n }\n+\n+ return withinMaxExtent || this.layer.displayOutsideMaxExtent;\n },\n \n /**\n- * APIMethod: activate\n- * Explicitly activates a control and it's associated\n- * handler if one has been set. Controls can be\n- * deactivated by calling the deactivate() method.\n- * \n- * Returns:\n- * {Boolean} True if the control was successfully activated or\n- * false if the control was already active.\n+ * Method: setBounds\n+ * Sets the bounds on this instance\n+ *\n+ * Parameters:\n+ * bounds {<OpenLayers.Bounds>}\n */\n- activate: function() {\n- if (this.active) {\n- return false;\n+ setBounds: function(bounds) {\n+ bounds = bounds.clone();\n+ if (this.layer.map.baseLayer.wrapDateLine) {\n+ var worldExtent = this.layer.map.getMaxExtent(),\n+ tolerance = this.layer.map.getResolution();\n+ bounds = bounds.wrapDateLine(worldExtent, {\n+ leftTolerance: tolerance,\n+ rightTolerance: tolerance\n+ });\n }\n- if (this.handler) {\n- this.handler.activate();\n+ this.bounds = bounds;\n+ },\n+\n+ /** \n+ * Method: moveTo\n+ * Reposition the tile.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * position - {<OpenLayers.Pixel>}\n+ * redraw - {Boolean} Call draw method on tile after moving.\n+ * Default is true\n+ */\n+ moveTo: function(bounds, position, redraw) {\n+ if (redraw == null) {\n+ redraw = true;\n }\n- this.active = true;\n- if (this.map) {\n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv,\n- this.displayClass.replace(/ /g, \"\") + \"Active\"\n- );\n+\n+ this.setBounds(bounds);\n+ this.position = position.clone();\n+ if (redraw) {\n+ this.draw();\n }\n- this.events.triggerEvent(\"activate\");\n- return true;\n },\n \n- /**\n- * APIMethod: deactivate\n- * Deactivates a control and it's associated handler if any. The exact\n- * effect of this depends on the control itself.\n- * \n- * Returns:\n- * {Boolean} True if the control was effectively deactivated or false\n- * if the control was already inactive.\n+ /** \n+ * Method: clear\n+ * Clear the tile of any bounds/position-related data so that it can \n+ * be reused in a new location.\n */\n- deactivate: function() {\n- if (this.active) {\n- if (this.handler) {\n- this.handler.deactivate();\n- }\n- this.active = false;\n- if (this.map) {\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv,\n- this.displayClass.replace(/ /g, \"\") + \"Active\"\n- );\n- }\n- this.events.triggerEvent(\"deactivate\");\n- return true;\n- }\n- return false;\n+ clear: function(draw) {\n+ // to be extended by subclasses\n },\n \n- CLASS_NAME: \"OpenLayers.Control\"\n+ CLASS_NAME: \"OpenLayers.Tile\"\n });\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_BUTTON\n- */\n-OpenLayers.Control.TYPE_BUTTON = 1;\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_TOGGLE\n- */\n-OpenLayers.Control.TYPE_TOGGLE = 2;\n-\n-/**\n- * Constant: OpenLayers.Control.TYPE_TOOL\n- */\n-OpenLayers.Control.TYPE_TOOL = 3;\n /* ======================================================================\n- OpenLayers/Handler.js\n+ OpenLayers/Tile/Image.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Tile.js\n+ * @requires OpenLayers/Animation.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Handler\n- * Base class to construct a higher-level handler for event sequences. All\n- * handlers have activate and deactivate methods. In addition, they have\n- * methods named like browser events. When a handler is activated, any\n- * additional methods named like a browser event is registered as a\n- * listener for the corresponding event. When a handler is deactivated,\n- * those same methods are unregistered as event listeners.\n+ * Class: OpenLayers.Tile.Image\n+ * Instances of OpenLayers.Tile.Image are used to manage the image tiles\n+ * used by various layers. Create a new image tile with the\n+ * <OpenLayers.Tile.Image> constructor.\n *\n- * Handlers also typically have a callbacks object with keys named like\n- * the abstracted events or event sequences that they are in charge of\n- * handling. The controls that wrap handlers define the methods that\n- * correspond to these abstract events - so instead of listening for\n- * individual browser events, they only listen for the abstract events\n- * defined by the handler.\n- * \n- * Handlers are created by controls, which ultimately have the responsibility\n- * of making changes to the the state of the application. Handlers\n- * themselves may make temporary changes, but in general are expected to\n- * return the application in the same state that they found it.\n+ * Inherits from:\n+ * - <OpenLayers.Tile>\n */\n-OpenLayers.Handler = OpenLayers.Class({\n+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n \n /**\n- * Property: id\n- * {String}\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the tile.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * tile.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to the <OpenLayers.Tile> events):\n+ * beforeload - Triggered before an image is prepared for loading, when the\n+ * url for the image is known already. Listeners may call <setImage> on\n+ * the tile instance. If they do so, that image will be used and no new\n+ * one will be created.\n */\n- id: null,\n \n- /**\n- * APIProperty: control\n- * {<OpenLayers.Control>}. The control that initialized this handler. The\n- * control is assumed to have a valid map property - that map is used\n- * in the handler's own setMap method.\n+ /** \n+ * APIProperty: url\n+ * {String} The URL of the image being requested. No default. Filled in by\n+ * layer.getURL() function. May be modified by loadstart listeners.\n */\n- control: null,\n+ url: null,\n+\n+ /** \n+ * Property: imgDiv\n+ * {HTMLImageElement} The image for this tile.\n+ */\n+ imgDiv: null,\n \n /**\n- * Property: map\n- * {<OpenLayers.Map>}\n+ * Property: frame\n+ * {DOMElement} The image element is appended to the frame. Any gutter on\n+ * the image will be hidden behind the frame. If no gutter is set,\n+ * this will be null.\n */\n- map: null,\n+ frame: null,\n+\n+ /** \n+ * Property: imageReloadAttempts\n+ * {Integer} Attempts to load the image.\n+ */\n+ imageReloadAttempts: null,\n \n /**\n- * APIProperty: keyMask\n- * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n- * constants to construct a keyMask. The keyMask is used by\n- * <checkModifiers>. If the keyMask matches the combination of keys\n- * down on an event, checkModifiers returns true.\n- *\n- * Example:\n- * (code)\n- * // handler only responds if the Shift key is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n- *\n- * // handler only responds if Ctrl-Shift is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n- * OpenLayers.Handler.MOD_CTRL;\n- * (end)\n+ * Property: layerAlphaHack\n+ * {Boolean} True if the png alpha hack needs to be applied on the layer's div.\n */\n- keyMask: null,\n+ layerAlphaHack: null,\n \n /**\n- * Property: active\n- * {Boolean}\n+ * Property: asyncRequestId\n+ * {Integer} ID of an request to see if request is still valid. This is a\n+ * number which increments by 1 for each asynchronous request.\n */\n- active: false,\n+ asyncRequestId: null,\n \n /**\n- * Property: evt\n- * {Event} This property references the last event handled by the handler.\n- * Note that this property is not part of the stable API. Use of the\n- * evt property should be restricted to controls in the library\n- * or other applications that are willing to update with changes to\n- * the OpenLayers code.\n+ * APIProperty: maxGetUrlLength\n+ * {Number} If set, requests that would result in GET urls with more\n+ * characters than the number provided will be made using form-encoded\n+ * HTTP POST. It is good practice to avoid urls that are longer than 2048\n+ * characters.\n+ *\n+ * Caution:\n+ * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most\n+ * Opera versions do not fully support this option. On all browsers,\n+ * transition effects are not supported if POST requests are used.\n */\n- evt: null,\n+ maxGetUrlLength: null,\n \n /**\n- * Property: touch\n- * {Boolean} Indicates the support of touch events. When touch events are \n- * started touch will be true and all mouse related listeners will do \n- * nothing.\n+ * Property: canvasContext\n+ * {CanvasRenderingContext2D} A canvas context associated with\n+ * the tile image.\n */\n- touch: false,\n+ canvasContext: null,\n \n /**\n- * Constructor: OpenLayers.Handler\n- * Construct a handler.\n- *\n+ * APIProperty: crossOriginKeyword\n+ * The value of the crossorigin keyword to use when loading images. This is\n+ * only relevant when using <getCanvasContext> for tiles from remote\n+ * origins and should be set to either 'anonymous' or 'use-credentials'\n+ * for servers that send Access-Control-Allow-Origin headers with their\n+ * tiles.\n+ */\n+ crossOriginKeyword: null,\n+\n+ /** TBD 3.0 - reorder the parameters to the init function to remove \n+ * URL. the getUrl() function on the layer gets called on \n+ * each draw(), so no need to specify it here.\n+ */\n+\n+ /** \n+ * Constructor: OpenLayers.Tile.Image\n+ * Constructor for a new <OpenLayers.Tile.Image> instance.\n+ * \n * Parameters:\n- * control - {<OpenLayers.Control>} The control that initialized this\n- * handler. The control is assumed to have a valid map property; that\n- * map is used in the handler's own setMap method. If a map property\n- * is present in the options argument it will be used instead.\n- * callbacks - {Object} An object whose properties correspond to abstracted\n- * events or sequences of browser events. The values for these\n- * properties are functions defined by the control that get called by\n- * the handler.\n- * options - {Object} An optional object whose properties will be set on\n- * the handler.\n+ * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n+ * position - {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * url - {<String>} Deprecated. Remove me in 3.0.\n+ * size - {<OpenLayers.Size>}\n+ * options - {Object}\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Util.extend(this, options);\n- this.control = control;\n- this.callbacks = callbacks;\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n \n- var map = this.map || control.map;\n- if (map) {\n- this.setMap(map);\n- }\n+ this.url = url; //deprecated remove me\n \n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n+\n+ if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n+ // only create frame if it's needed\n+ this.frame = document.createElement(\"div\");\n+ this.frame.style.position = \"absolute\";\n+ this.frame.style.overflow = \"hidden\";\n+ }\n+ if (this.maxGetUrlLength != null) {\n+ OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);\n+ }\n },\n \n- /**\n- * Method: setMap\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- setMap: function(map) {\n- this.map = map;\n+ destroy: function() {\n+ if (this.imgDiv) {\n+ this.clear();\n+ this.imgDiv = null;\n+ this.frame = null;\n+ }\n+ // don't handle async requests any more\n+ this.asyncRequestId = null;\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n },\n \n /**\n- * Method: checkModifiers\n- * Check the keyMask on the handler. If no <keyMask> is set, this always\n- * returns true. If a <keyMask> is set and it matches the combination\n- * of keys down on an event, this returns true.\n- *\n+ * Method: draw\n+ * Check that a tile should be drawn, and draw it.\n+ * \n * Returns:\n- * {Boolean} The keyMask matches the keys down on an event.\n+ * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned\n+ * false.\n */\n- checkModifiers: function(evt) {\n- if (this.keyMask == null) {\n- return true;\n+ draw: function() {\n+ var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (shouldDraw) {\n+ // The layer's reproject option is deprecated.\n+ if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n+ // getBoundsFromBaseLayer is defined in deprecated.js.\n+ this.bounds = this.getBoundsFromBaseLayer(this.position);\n+ }\n+ if (this.isLoading) {\n+ //if we're already loading, send 'reload' instead of 'loadstart'.\n+ this._loadEvent = \"reload\";\n+ } else {\n+ this.isLoading = true;\n+ this._loadEvent = \"loadstart\";\n+ }\n+ this.renderTile();\n+ this.positionTile();\n+ } else if (shouldDraw === false) {\n+ this.unload();\n }\n- /* calculate the keyboard modifier mask for this event */\n- var keyModifiers =\n- (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n- (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n- (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n- (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n-\n- /* if it differs from the handler object's key mask,\n- bail out of the event handler */\n- return (keyModifiers == this.keyMask);\n+ return shouldDraw;\n },\n \n /**\n- * APIMethod: activate\n- * Turn on the handler. Returns false if the handler was already active.\n- * \n- * Returns: \n- * {Boolean} The handler was activated.\n+ * Method: renderTile\n+ * Internal function to actually initialize the image tile,\n+ * position it correctly, and set its url.\n */\n- activate: function() {\n- if (this.active) {\n- return false;\n- }\n- // register for event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.register(events[i], this[events[i]]);\n- }\n+ renderTile: function() {\n+ if (this.layer.async) {\n+ // Asynchronous image requests call the asynchronous getURL method\n+ // on the layer to fetch an image that covers 'this.bounds'.\n+ var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n+ this.layer.getURLasync(this.bounds, function(url) {\n+ if (id == this.asyncRequestId) {\n+ this.url = url;\n+ this.initImage();\n+ }\n+ }, this);\n+ } else {\n+ // synchronous image requests get the url immediately.\n+ this.url = this.layer.getURL(this.bounds);\n+ this.initImage();\n }\n- this.active = true;\n- return true;\n },\n \n /**\n- * APIMethod: deactivate\n- * Turn off the handler. Returns false if the handler was already inactive.\n- * \n- * Returns:\n- * {Boolean} The handler was deactivated.\n+ * Method: positionTile\n+ * Using the properties currenty set on the layer, position the tile correctly.\n+ * This method is used both by the async and non-async versions of the Tile.Image\n+ * code.\n */\n- deactivate: function() {\n- if (!this.active) {\n- return false;\n+ positionTile: function() {\n+ var style = this.getTile().style,\n+ size = this.frame ? this.size :\n+ this.layer.getImageSize(this.bounds),\n+ ratio = 1;\n+ if (this.layer instanceof OpenLayers.Layer.Grid) {\n+ ratio = this.layer.getServerResolution() / this.layer.map.getResolution();\n }\n- // unregister event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n+ style.left = this.position.x + \"px\";\n+ style.top = this.position.y + \"px\";\n+ style.width = Math.round(ratio * size.w) + \"px\";\n+ style.height = Math.round(ratio * size.h) + \"px\";\n+ },\n+\n+ /** \n+ * Method: clear\n+ * Remove the tile from the DOM, clear it of any image related data so that\n+ * it can be reused in a new location.\n+ */\n+ clear: function() {\n+ OpenLayers.Tile.prototype.clear.apply(this, arguments);\n+ var img = this.imgDiv;\n+ if (img) {\n+ var tile = this.getTile();\n+ if (tile.parentNode === this.layer.div) {\n+ this.layer.div.removeChild(tile);\n }\n+ this.setImgSrc();\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"\";\n+ }\n+ OpenLayers.Element.removeClass(img, \"olImageLoadError\");\n }\n- this.touch = false;\n- this.active = false;\n- return true;\n+ this.canvasContext = null;\n },\n \n /**\n- * Method: startTouch\n- * Start touch events, this method must be called by subclasses in \n- * \"touchstart\" method. When touch events are started <touch> will be\n- * true and all mouse related listeners will do nothing.\n+ * Method: getImage\n+ * Returns or creates and returns the tile image.\n */\n- startTouch: function() {\n- if (!this.touch) {\n- this.touch = true;\n- var events = [\n- \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n- \"mouseout\"\n- ];\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n+ getImage: function() {\n+ if (!this.imgDiv) {\n+ this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n+\n+ var style = this.imgDiv.style;\n+ if (this.frame) {\n+ var left = 0,\n+ top = 0;\n+ if (this.layer.gutter) {\n+ left = this.layer.gutter / this.layer.tileSize.w * 100;\n+ top = this.layer.gutter / this.layer.tileSize.h * 100;\n }\n+ style.left = -left + \"%\";\n+ style.top = -top + \"%\";\n+ style.width = (2 * left + 100) + \"%\";\n+ style.height = (2 * top + 100) + \"%\";\n+ }\n+ style.visibility = \"hidden\";\n+ style.opacity = 0;\n+ if (this.layer.opacity < 1) {\n+ style.filter = 'alpha(opacity=' +\n+ (this.layer.opacity * 100) +\n+ ')';\n+ }\n+ style.position = \"absolute\";\n+ if (this.layerAlphaHack) {\n+ // move the image out of sight\n+ style.paddingTop = style.height;\n+ style.height = \"0\";\n+ style.width = \"100%\";\n+ }\n+ if (this.frame) {\n+ this.frame.appendChild(this.imgDiv);\n }\n }\n+\n+ return this.imgDiv;\n },\n \n /**\n- * Method: callback\n- * Trigger the control's named callback with the given arguments\n+ * APIMethod: setImage\n+ * Sets the image element for this tile. This method should only be called\n+ * from beforeload listeners.\n *\n- * Parameters:\n- * name - {String} The key for the callback that is one of the properties\n- * of the handler's callbacks object.\n- * args - {Array(*)} An array of arguments (any type) with which to call \n- * the callback (defined by the control).\n+ * Parameters\n+ * img - {HTMLImageElement} The image to use for this tile.\n */\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args);\n- }\n+ setImage: function(img) {\n+ this.imgDiv = img;\n },\n \n /**\n- * Method: register\n- * register an event on the map\n+ * Method: initImage\n+ * Creates the content for the frame on the tile.\n */\n- register: function(name, method) {\n- // TODO: deal with registerPriority in 3.0\n- this.map.events.registerPriority(name, this, method);\n- this.map.events.registerPriority(name, this, this.setEvent);\n+ initImage: function() {\n+ if (!this.url && !this.imgDiv) {\n+ // fast path out - if there is no tile url and no previous image\n+ this.isLoading = false;\n+ return;\n+ }\n+ this.events.triggerEvent('beforeload');\n+ this.layer.div.appendChild(this.getTile());\n+ this.events.triggerEvent(this._loadEvent);\n+ var img = this.getImage();\n+ var src = img.getAttribute('src') || '';\n+ if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n+ this._loadTimeout = window.setTimeout(\n+ OpenLayers.Function.bind(this.onImageLoad, this), 0\n+ );\n+ } else {\n+ this.stopLoading();\n+ if (this.crossOriginKeyword) {\n+ img.removeAttribute(\"crossorigin\");\n+ }\n+ OpenLayers.Event.observe(img, \"load\",\n+ OpenLayers.Function.bind(this.onImageLoad, this)\n+ );\n+ OpenLayers.Event.observe(img, \"error\",\n+ OpenLayers.Function.bind(this.onImageError, this)\n+ );\n+ this.imageReloadAttempts = 0;\n+ this.setImgSrc(this.url);\n+ }\n },\n \n /**\n- * Method: unregister\n- * unregister an event from the map\n+ * Method: setImgSrc\n+ * Sets the source for the tile image\n+ *\n+ * Parameters:\n+ * url - {String} or undefined to hide the image\n */\n- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent);\n+ setImgSrc: function(url) {\n+ var img = this.imgDiv;\n+ if (url) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ // don't set crossOrigin if the url is a data URL\n+ if (this.crossOriginKeyword) {\n+ if (url.substr(0, 5) !== 'data:') {\n+ img.setAttribute(\"crossorigin\", this.crossOriginKeyword);\n+ } else {\n+ img.removeAttribute(\"crossorigin\");\n+ }\n+ }\n+ img.src = url;\n+ } else {\n+ // Remove reference to the image, and leave it to the browser's\n+ // caching and garbage collection.\n+ this.stopLoading();\n+ this.imgDiv = null;\n+ if (img.parentNode) {\n+ img.parentNode.removeChild(img);\n+ }\n+ }\n },\n \n /**\n- * Method: setEvent\n- * With each registered browser event, the handler sets its own evt\n- * property. This property can be accessed by controls if needed\n- * to get more information about the event that the handler is\n- * processing.\n+ * Method: getTile\n+ * Get the tile's markup.\n *\n- * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n- * and meta cannot be checked with the keyboard handler). For a\n- * control to determine which modifier keys are associated with the\n- * event that a handler is currently processing, it should access\n- * (code)handler.evt.altKey || handler.evt.shiftKey ||\n- * handler.evt.ctrlKey || handler.evt.metaKey(end).\n+ * Returns:\n+ * {DOMElement} The tile's markup\n+ */\n+ getTile: function() {\n+ return this.frame ? this.frame : this.getImage();\n+ },\n+\n+ /**\n+ * Method: createBackBuffer\n+ * Create a backbuffer for this tile. A backbuffer isn't exactly a clone\n+ * of the tile's markup, because we want to avoid the reloading of the\n+ * image. So we clone the frame, and steal the image from the tile.\n *\n- * Parameters:\n- * evt - {Event} The browser event.\n+ * Returns:\n+ * {DOMElement} The markup, or undefined if the tile has no image\n+ * or if it's currently loading.\n */\n- setEvent: function(evt) {\n- this.evt = evt;\n- return true;\n+ createBackBuffer: function() {\n+ if (!this.imgDiv || this.isLoading) {\n+ return;\n+ }\n+ var backBuffer;\n+ if (this.frame) {\n+ backBuffer = this.frame.cloneNode(false);\n+ backBuffer.appendChild(this.imgDiv);\n+ } else {\n+ backBuffer = this.imgDiv;\n+ }\n+ this.imgDiv = null;\n+ return backBuffer;\n },\n \n /**\n- * Method: destroy\n- * Deconstruct the handler.\n+ * Method: onImageLoad\n+ * Handler for the image onload event\n */\n- destroy: function() {\n- // unregister event listeners\n- this.deactivate();\n- // eliminate circular references\n- this.control = this.map = null;\n+ onImageLoad: function() {\n+ var img = this.imgDiv;\n+ this.stopLoading();\n+ img.style.visibility = 'inherit';\n+ img.style.opacity = this.layer.opacity;\n+ this.isLoading = false;\n+ this.canvasContext = null;\n+ this.events.triggerEvent(\"loadend\");\n+\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter =\n+ \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" +\n+ img.src + \"', sizingMethod='scale')\";\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Handler\"\n-});\n+ /**\n+ * Method: onImageError\n+ * Handler for the image onerror event\n+ */\n+ onImageError: function() {\n+ var img = this.imgDiv;\n+ if (img.src != null) {\n+ this.imageReloadAttempts++;\n+ if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n+ this.setImgSrc(this.layer.getURL(this.bounds));\n+ } else {\n+ OpenLayers.Element.addClass(img, \"olImageLoadError\");\n+ this.events.triggerEvent(\"loaderror\");\n+ this.onImageLoad();\n+ }\n+ }\n+ },\n \n-/**\n- * Constant: OpenLayers.Handler.MOD_NONE\n- * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n- */\n-OpenLayers.Handler.MOD_NONE = 0;\n+ /**\n+ * Method: stopLoading\n+ * Stops a loading sequence so <onImageLoad> won't be executed.\n+ */\n+ stopLoading: function() {\n+ OpenLayers.Event.stopObservingElement(this.imgDiv);\n+ window.clearTimeout(this._loadTimeout);\n+ delete this._loadTimeout;\n+ },\n \n-/**\n- * Constant: OpenLayers.Handler.MOD_SHIFT\n- * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n- */\n-OpenLayers.Handler.MOD_SHIFT = 1;\n+ /**\n+ * APIMethod: getCanvasContext\n+ * Returns a canvas context associated with the tile image (with\n+ * the image drawn on it).\n+ * Returns undefined if the browser does not support canvas, if\n+ * the tile has no image or if it's currently loading.\n+ *\n+ * The function returns a canvas context instance but the\n+ * underlying canvas is still available in the 'canvas' property:\n+ * (code)\n+ * var context = tile.getCanvasContext();\n+ * if (context) {\n+ * var data = context.canvas.toDataURL('image/jpeg');\n+ * }\n+ * (end)\n+ *\n+ * Returns:\n+ * {Boolean}\n+ */\n+ getCanvasContext: function() {\n+ if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n+ if (!this.canvasContext) {\n+ var canvas = document.createElement(\"canvas\");\n+ canvas.width = this.size.w;\n+ canvas.height = this.size.h;\n+ this.canvasContext = canvas.getContext(\"2d\");\n+ this.canvasContext.drawImage(this.imgDiv, 0, 0);\n+ }\n+ return this.canvasContext;\n+ }\n+ },\n \n-/**\n- * Constant: OpenLayers.Handler.MOD_CTRL\n- * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n- */\n-OpenLayers.Handler.MOD_CTRL = 2;\n+ CLASS_NAME: \"OpenLayers.Tile.Image\"\n \n-/**\n- * Constant: OpenLayers.Handler.MOD_ALT\n- * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n- */\n-OpenLayers.Handler.MOD_ALT = 4;\n+});\n \n-/**\n- * Constant: OpenLayers.Handler.MOD_META\n- * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n+/** \n+ * Constant: OpenLayers.Tile.Image.IMAGE\n+ * {HTMLImageElement} The image for a tile.\n */\n-OpenLayers.Handler.MOD_META = 8;\n-\n+OpenLayers.Tile.Image.IMAGE = (function() {\n+ var img = new Image();\n+ img.className = \"olTileImage\";\n+ // avoid image gallery menu in IE6\n+ img.galleryImg = \"no\";\n+ return img;\n+}());\n \n /* ======================================================================\n- OpenLayers/Handler/Drag.js\n+ OpenLayers/Layer/Grid.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Layer/HTTPRequest.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Drag\n- * The drag handler is used to deal with sequences of browser events related\n- * to dragging. The handler is used by controls that want to know when\n- * a drag sequence begins, when a drag is happening, and when it has\n- * finished.\n- *\n- * Controls that use the drag handler typically construct it with callbacks\n- * for 'down', 'move', and 'done'. Callbacks for these keys are called\n- * when the drag begins, with each move, and when the drag is done. In\n- * addition, controls can have callbacks keyed to 'up' and 'out' if they\n- * care to differentiate between the types of events that correspond with\n- * the end of a drag sequence. If no drag actually occurs (no mouse move)\n- * the 'down' and 'up' callbacks will be called, but not the 'done'\n- * callback.\n- *\n- * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n+ * Class: OpenLayers.Layer.Grid\n+ * Base class for layers that use a lattice of tiles. Create a new grid\n+ * layer with the <OpenLayers.Layer.Grid> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Handler>\n+ * - <OpenLayers.Layer.HTTPRequest>\n */\n-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n \n- /** \n- * Property: started\n- * {Boolean} When a mousedown or touchstart event is received, we want to\n- * record it, but not set 'dragging' until the mouse moves after starting.\n+ /**\n+ * APIProperty: tileSize\n+ * {<OpenLayers.Size>}\n */\n- started: false,\n+ tileSize: null,\n \n /**\n- * Property: stopDown\n- * {Boolean} Stop propagation of mousedown events from getting to listeners\n- * on the same element. Default is true.\n+ * Property: tileOriginCorner\n+ * {String} If the <tileOrigin> property is not provided, the tile origin \n+ * will be derived from the layer's <maxExtent>. The corner of the \n+ * <maxExtent> used is determined by this property. Acceptable values\n+ * are \"tl\" (top left), \"tr\" (top right), \"bl\" (bottom left), and \"br\"\n+ * (bottom right). Default is \"bl\".\n */\n- stopDown: true,\n+ tileOriginCorner: \"bl\",\n \n- /** \n- * Property: dragging \n- * {Boolean} \n+ /**\n+ * APIProperty: tileOrigin\n+ * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n+ * If provided, requests for tiles at all resolutions will be aligned\n+ * with this location (no tiles shall overlap this location). If\n+ * not provided, the grid of tiles will be aligned with the layer's\n+ * <maxExtent>. Default is ``null``.\n */\n- dragging: false,\n+ tileOrigin: null,\n \n- /** \n- * Property: last\n- * {<OpenLayers.Pixel>} The last pixel location of the drag.\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer, if supported by the tile class.\n */\n- last: null,\n+ tileOptions: null,\n \n- /** \n- * Property: start\n- * {<OpenLayers.Pixel>} The first pixel location of the drag.\n+ /**\n+ * APIProperty: tileClass\n+ * {<OpenLayers.Tile>} The tile class to use for this layer.\n+ * Defaults is OpenLayers.Tile.Image.\n */\n- start: null,\n+ tileClass: OpenLayers.Tile.Image,\n \n /**\n- * Property: lastMoveEvt\n- * {Object} The last mousemove event that occurred. Used to\n- * position the map correctly when our \"delay drag\"\n- * timeout expired.\n+ * Property: grid\n+ * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is \n+ * an array of tiles.\n */\n- lastMoveEvt: null,\n+ grid: null,\n \n /**\n- * Property: oldOnselectstart\n- * {Function}\n+ * APIProperty: singleTile\n+ * {Boolean} Moves the layer into single-tile mode, meaning that one tile \n+ * will be loaded. The tile's size will be determined by the 'ratio'\n+ * property. When the tile is dragged such that it does not cover the \n+ * entire viewport, it is reloaded.\n */\n- oldOnselectstart: null,\n+ singleTile: false,\n+\n+ /** APIProperty: ratio\n+ * {Float} Used only when in single-tile mode, this specifies the \n+ * ratio of the size of the single tile to the size of the map.\n+ * Default value is 1.5.\n+ */\n+ ratio: 1.5,\n \n /**\n- * Property: interval\n- * {Integer} In order to increase performance, an interval (in \n- * milliseconds) can be set to reduce the number of drag events \n- * called. If set, a new drag event will not be set until the \n- * interval has passed. \n- * Defaults to 0, meaning no interval. \n+ * APIProperty: buffer\n+ * {Integer} Used only when in gridded mode, this specifies the number of \n+ * extra rows and colums of tiles on each side which will\n+ * surround the minimum grid tiles to cover the map.\n+ * For very slow loading layers, a larger value may increase\n+ * performance somewhat when dragging, but will increase bandwidth\n+ * use significantly. \n */\n- interval: 0,\n+ buffer: 0,\n \n /**\n- * Property: timeoutId\n- * {String} The id of the timeout used for the mousedown interval.\n- * This is \"private\", and should be left alone.\n+ * APIProperty: transitionEffect\n+ * {String} The transition effect to use when the map is zoomed.\n+ * Two posible values:\n+ *\n+ * \"resize\" - Existing tiles are resized on zoom to provide a visual\n+ * effect of the zoom having taken place immediately. As the\n+ * new tiles become available, they are drawn on top of the\n+ * resized tiles (this is the default setting).\n+ * \"map-resize\" - Existing tiles are resized on zoom and placed below the\n+ * base layer. New tiles for the base layer will cover existing tiles.\n+ * This setting is recommended when having an overlay duplicated during\n+ * the transition is undesirable (e.g. street labels or big transparent\n+ * fills). \n+ * null - No transition effect.\n+ *\n+ * Using \"resize\" on non-opaque layers can cause undesired visual\n+ * effects. Set transitionEffect to null in this case.\n */\n- timeoutId: null,\n+ transitionEffect: \"resize\",\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, the handler will also handle mouse moves when\n- * the cursor has moved out of the map viewport. Default is false.\n+ * APIProperty: numLoadingTiles\n+ * {Integer} How many tiles are still loading?\n */\n- documentDrag: false,\n+ numLoadingTiles: 0,\n \n /**\n- * Property: documentEvents\n- * {Boolean} Are we currently observing document events?\n+ * Property: serverResolutions\n+ * {Array(Number}} This property is documented in subclasses as\n+ * an API property.\n */\n- documentEvents: null,\n+ serverResolutions: null,\n \n /**\n- * Constructor: OpenLayers.Handler.Drag\n- * Returns OpenLayers.Handler.Drag\n- * \n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handlers setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object containing a single function to be\n- * called when the drag operation is finished. The callback should\n- * expect to recieve a single argument, the pixel location of the event.\n- * Callbacks for 'move' and 'done' are supported. You can also speficy\n- * callbacks for 'down', 'up', and 'out' to respond to those events.\n- * options - {Object} \n+ * Property: loading\n+ * {Boolean} Indicates if tiles are being loaded.\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ loading: false,\n \n- if (this.documentDrag === true) {\n- var me = this;\n- this._docMove = function(evt) {\n- me.mousemove({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- },\n- element: document\n- });\n- };\n- this._docUp = function(evt) {\n- me.mouseup({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- }\n- });\n- };\n- }\n- },\n+ /**\n+ * Property: backBuffer\n+ * {DOMElement} The back buffer.\n+ */\n+ backBuffer: null,\n \n+ /**\n+ * Property: gridResolution\n+ * {Number} The resolution of the current grid. Used for backbuffer and\n+ * client zoom. This property is updated every time the grid is\n+ * initialized.\n+ */\n+ gridResolution: null,\n \n /**\n- * Method: dragstart\n- * This private method is factorized from mousedown and touchstart methods\n- *\n- * Parameters:\n- * evt - {Event} The event\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * Property: backBufferResolution\n+ * {Number} The resolution of the current back buffer. This property is\n+ * updated each time a back buffer is created.\n */\n- dragstart: function(evt) {\n- var propagate = true;\n- this.dragging = false;\n- if (this.checkModifiers(evt) &&\n- (OpenLayers.Event.isLeftClick(evt) ||\n- OpenLayers.Event.isSingleTouch(evt))) {\n- this.started = true;\n- this.start = evt.xy;\n- this.last = evt.xy;\n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- this.down(evt);\n- this.callback(\"down\", [evt.xy]);\n+ backBufferResolution: null,\n \n- // prevent document dragging\n- OpenLayers.Event.preventDefault(evt);\n+ /**\n+ * Property: backBufferLonLat\n+ * {Object} The top-left corner of the current back buffer. Includes lon\n+ * and lat properties. This object is updated each time a back buffer\n+ * is created.\n+ */\n+ backBufferLonLat: null,\n \n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart ?\n- document.onselectstart : OpenLayers.Function.True;\n- }\n- document.onselectstart = OpenLayers.Function.False;\n+ /**\n+ * Property: backBufferTimerId\n+ * {Number} The id of the back buffer timer. This timer is used to\n+ * delay the removal of the back buffer, thereby preventing\n+ * flash effects caused by tile animation.\n+ */\n+ backBufferTimerId: null,\n \n- propagate = !this.stopDown;\n- } else {\n- this.started = false;\n- this.start = null;\n- this.last = null;\n- }\n- return propagate;\n- },\n+ /**\n+ * APIProperty: removeBackBufferDelay\n+ * {Number} Delay for removing the backbuffer when all tiles have finished\n+ * loading. Can be set to 0 when no css opacity transitions for the\n+ * olTileImage class are used. Default is 0 for <singleTile> layers,\n+ * 2500 for tiled layers. See <className> for more information on\n+ * tile animation.\n+ */\n+ removeBackBufferDelay: null,\n \n /**\n- * Method: dragmove\n- * This private method is factorized from mousemove and touchmove methods\n+ * APIProperty: className\n+ * {String} Name of the class added to the layer div. If not set in the\n+ * options passed to the constructor then className defaults to\n+ * \"olLayerGridSingleTile\" for single tile layers (see <singleTile>),\n+ * and \"olLayerGrid\" for non single tile layers.\n *\n- * Parameters:\n- * evt - {Event} The event\n+ * Note:\n *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * The displaying of tiles is not animated by default for single tile\n+ * layers - OpenLayers' default theme (style.css) includes this:\n+ * (code)\n+ * .olLayerGrid .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * To animate tile displaying for any grid layer the following\n+ * CSS rule can be used:\n+ * (code)\n+ * .olTileImage {\n+ * -webkit-transition: opacity 0.2s linear;\n+ * -moz-transition: opacity 0.2s linear;\n+ * -o-transition: opacity 0.2s linear;\n+ * transition: opacity 0.2s linear;\n+ * }\n+ * (end)\n+ * In that case, to avoid flash effects, <removeBackBufferDelay>\n+ * should not be zero.\n */\n- dragmove: function(evt) {\n- this.lastMoveEvt = evt;\n- if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||\n- evt.xy.y != this.last.y)) {\n- if (this.documentDrag === true && this.documentEvents) {\n- if (evt.element === document) {\n- this.adjustXY(evt);\n- // do setEvent manually because the documentEvents are not\n- // registered with the map\n- this.setEvent(evt);\n- } else {\n- this.removeDocumentEvents();\n- }\n- }\n- if (this.interval > 0) {\n- this.timeoutId = setTimeout(\n- OpenLayers.Function.bind(this.removeTimeout, this),\n- this.interval);\n- }\n- this.dragging = true;\n-\n- this.move(evt);\n- this.callback(\"move\", [evt.xy]);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart;\n- document.onselectstart = OpenLayers.Function.False;\n- }\n- this.last = evt.xy;\n- }\n- return true;\n- },\n+ className: null,\n \n /**\n- * Method: dragend\n- * This private method is factorized from mouseup and touchend methods\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n *\n- * Parameters:\n- * evt - {Event} The event\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported event types:\n+ * addtile - Triggered when a tile is added to this layer. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that has been added.\n+ * tileloadstart - Triggered when a tile starts loading. Listeners receive\n+ * an object as first argument, which has a tile property that\n+ * references the tile that starts loading.\n+ * tileloaded - Triggered when each new tile is\n+ * loaded, as a means of progress update to listeners.\n+ * listeners can access 'numLoadingTiles' if they wish to keep\n+ * track of the loading progress. Listeners are called with an object\n+ * with a 'tile' property as first argument, making the loaded tile\n+ * available to the listener, and an 'aborted' property, which will be\n+ * true when loading was aborted and no tile data is available.\n+ * tileerror - Triggered before the tileloaded event (i.e. when the tile is\n+ * still hidden) if a tile failed to load. Listeners receive an object\n+ * as first argument, which has a tile property that references the\n+ * tile that could not be loaded.\n+ * retile - Triggered when the layer recreates its tile grid.\n */\n- dragend: function(evt) {\n- if (this.started) {\n- if (this.documentDrag === true && this.documentEvents) {\n- this.adjustXY(evt);\n- this.removeDocumentEvents();\n- }\n- var dragged = (this.start != this.last);\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- this.up(evt);\n- this.callback(\"up\", [evt.xy]);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy]);\n- }\n- document.onselectstart = this.oldOnselectstart;\n- }\n- return true;\n- },\n \n /**\n- * The four methods below (down, move, up, and out) are used by subclasses\n- * to do their own processing related to these mouse events.\n+ * Property: gridLayout\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n */\n+ gridLayout: null,\n \n /**\n- * Method: down\n- * This method is called during the handling of the mouse down event.\n- * Subclasses can do their own processing here.\n- *\n- * Parameters:\n- * evt - {Event} The mouse down event\n+ * Property: rowSign\n+ * {Number} 1 for grids starting at the top, -1 for grids starting at the\n+ * bottom. This is used for several grid index and offset calculations.\n */\n- down: function(evt) {},\n+ rowSign: null,\n \n /**\n- * Method: move\n- * This method is called during the handling of the mouse move event.\n- * Subclasses can do their own processing here.\n+ * Property: transitionendEvents\n+ * {Array} Event names for transitionend\n+ */\n+ transitionendEvents: [\n+ 'transitionend', 'webkitTransitionEnd', 'otransitionend',\n+ 'oTransitionEnd'\n+ ],\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Grid\n+ * Create a new grid layer\n *\n * Parameters:\n- * evt - {Event} The mouse move event\n- *\n+ * name - {String}\n+ * url - {String}\n+ * params - {Object}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- move: function(evt) {},\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,\n+ arguments);\n+ this.grid = [];\n+ this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n+\n+ this.initProperties();\n+\n+ this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1;\n+ },\n \n /**\n- * Method: up\n- * This method is called during the handling of the mouse up event.\n- * Subclasses can do their own processing here.\n+ * Method: initProperties\n+ * Set any properties that depend on the value of singleTile.\n+ * Currently sets removeBackBufferDelay and className\n+ */\n+ initProperties: function() {\n+ if (this.options.removeBackBufferDelay === undefined) {\n+ this.removeBackBufferDelay = this.singleTile ? 0 : 2500;\n+ }\n+\n+ if (this.options.className === undefined) {\n+ this.className = this.singleTile ? 'olLayerGridSingleTile' :\n+ 'olLayerGrid';\n+ }\n+ },\n+\n+ /**\n+ * Method: setMap\n *\n * Parameters:\n- * evt - {Event} The mouse up event\n+ * map - {<OpenLayers.Map>} The map.\n */\n- up: function(evt) {},\n+ setMap: function(map) {\n+ OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n+ OpenLayers.Element.addClass(this.div, this.className);\n+ },\n \n /**\n- * Method: out\n- * This method is called during the handling of the mouse out event.\n- * Subclasses can do their own processing here.\n+ * Method: removeMap\n+ * Called when the layer is removed from the map.\n *\n * Parameters:\n- * evt - {Event} The mouse out event\n+ * map - {<OpenLayers.Map>} The map.\n */\n- out: function(evt) {},\n+ removeMap: function(map) {\n+ this.removeBackBuffer();\n+ },\n \n /**\n- * The methods below are part of the magic of event handling. Because\n- * they are named like browser events, they are registered as listeners\n- * for the events they represent.\n+ * APIMethod: destroy\n+ * Deconstruct the layer and clear the grid.\n */\n+ destroy: function() {\n+ this.removeBackBuffer();\n+ this.clearGrid();\n+\n+ this.grid = null;\n+ this.tileSize = null;\n+ OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);\n+ },\n \n /**\n- * Method: mousedown\n- * Handle mousedown events\n- *\n+ * APIMethod: mergeNewParams\n+ * Refetches tiles with new params merged, keeping a backbuffer. Each\n+ * loading new tile will have a css class of '.olTileReplacing'. If a\n+ * stylesheet applies a 'display: none' style to that class, any fade-in\n+ * transition will not apply, and backbuffers for each tile will be removed\n+ * as soon as the tile is loaded.\n+ * \n * Parameters:\n- * evt - {Event}\n+ * newParams - {Object}\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * redrawn: {Boolean} whether the layer was actually redrawn.\n */\n- mousedown: function(evt) {\n- return this.dragstart(evt);\n+\n+ /**\n+ * Method: clearGrid\n+ * Go through and remove all tiles from the grid, calling\n+ * destroy() on each of them to kill circular references\n+ */\n+ clearGrid: function() {\n+ if (this.grid) {\n+ for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n+ var row = this.grid[iRow];\n+ for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n+ var tile = row[iCol];\n+ this.destroyTile(tile);\n+ }\n+ }\n+ this.grid = [];\n+ this.gridResolution = null;\n+ this.gridLayout = null;\n+ }\n },\n \n /**\n- * Method: touchstart\n- * Handle touchstart events\n- *\n+ * APIMethod: addOptions\n+ * \n * Parameters:\n- * evt - {Event}\n+ * newOptions - {Object}\n+ * reinitialize - {Boolean} If set to true, and if resolution options of the\n+ * current baseLayer were changed, the map will be recentered to make\n+ * sure that it is displayed with a valid resolution, and a\n+ * changebaselayer event will be triggered.\n+ */\n+ addOptions: function(newOptions, reinitialize) {\n+ var singleTileChanged = newOptions.singleTile !== undefined &&\n+ newOptions.singleTile !== this.singleTile;\n+ OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n+ if (this.map && singleTileChanged) {\n+ this.initProperties();\n+ this.clearGrid();\n+ this.tileSize = this.options.tileSize;\n+ this.setTileSize();\n+ this.moveTo(null, true);\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Create a clone of this layer\n *\n+ * Parameters:\n+ * obj - {Object} Is this ever used?\n+ * \n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid\n */\n- touchstart: function(evt) {\n- this.startTouch();\n- return this.dragstart(evt);\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Grid(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone();\n+ }\n+\n+ // we do not want to copy reference to grid, so we make a new array\n+ obj.grid = [];\n+ obj.gridResolution = null;\n+ // same for backbuffer\n+ obj.backBuffer = null;\n+ obj.backBufferTimerId = null;\n+ obj.loading = false;\n+ obj.numLoadingTiles = 0;\n+\n+ return obj;\n },\n \n /**\n- * Method: mousemove\n- * Handle mousemove events\n+ * Method: moveTo\n+ * This function is called whenever the map is moved. All the moving\n+ * of actual 'tiles' is done by the map, but moveTo's role is to accept\n+ * a bounds and make sure the data that that bounds requires is pre-loaded.\n *\n * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- mousemove: function(evt) {\n- return this.dragmove(evt);\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+\n+ OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n+\n+ bounds = bounds || this.map.getExtent();\n+\n+ if (bounds != null) {\n+\n+ // if grid is empty or zoom has changed, we *must* re-tile\n+ var forceReTile = !this.grid.length || zoomChanged;\n+\n+ // total bounds of the tiles\n+ var tilesBounds = this.getTilesBounds();\n+\n+ // the new map resolution\n+ var resolution = this.map.getResolution();\n+\n+ // the server-supported resolution for the new map resolution\n+ var serverResolution = this.getServerResolution(resolution);\n+\n+ if (this.singleTile) {\n+\n+ // We want to redraw whenever even the slightest part of the \n+ // current bounds is not contained by our tile.\n+ // (thus, we do not specify partial -- its default is false)\n+\n+ if (forceReTile ||\n+ (!dragging && !tilesBounds.containsBounds(bounds))) {\n+\n+ // In single tile mode with no transition effect, we insert\n+ // a non-scaled backbuffer when the layer is moved. But if\n+ // a zoom occurs right after a move, i.e. before the new\n+ // image is received, we need to remove the backbuffer, or\n+ // an ill-positioned image will be visible during the zoom\n+ // transition.\n+\n+ if (zoomChanged && this.transitionEffect !== 'resize') {\n+ this.removeBackBuffer();\n+ }\n+\n+ if (!zoomChanged || this.transitionEffect === 'resize') {\n+ this.applyBackBuffer(resolution);\n+ }\n+\n+ this.initSingleTile(bounds);\n+ }\n+ } else {\n+\n+ // if the bounds have changed such that they are not even \n+ // *partially* contained by our tiles (e.g. when user has \n+ // programmatically panned to the other side of the earth on\n+ // zoom level 18), then moveGriddedTiles could potentially have\n+ // to run through thousands of cycles, so we want to reTile\n+ // instead (thus, partial true). \n+ forceReTile = forceReTile ||\n+ !tilesBounds.intersectsBounds(bounds, {\n+ worldBounds: this.map.baseLayer.wrapDateLine &&\n+ this.map.getMaxExtent()\n+ });\n+\n+ if (forceReTile) {\n+ if (zoomChanged && (this.transitionEffect === 'resize' ||\n+ this.gridResolution === resolution)) {\n+ this.applyBackBuffer(resolution);\n+ }\n+ this.initGriddedTiles(bounds);\n+ } else {\n+ this.moveGriddedTiles();\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: touchmove\n- * Handle touchmove events\n+ * Method: getTileData\n+ * Given a map location, retrieve a tile and the pixel offset within that\n+ * tile corresponding to the location. If there is not an existing \n+ * tile in the grid that covers the given location, null will be \n+ * returned.\n *\n * Parameters:\n- * evt - {Event}\n+ * loc - {<OpenLayers.LonLat>} map location\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),\n+ * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel\n+ * offset from top left).\n */\n- touchmove: function(evt) {\n- return this.dragmove(evt);\n+ getTileData: function(loc) {\n+ var data = null,\n+ x = loc.lon,\n+ y = loc.lat,\n+ numRows = this.grid.length;\n+\n+ if (this.map && numRows) {\n+ var res = this.map.getResolution(),\n+ tileWidth = this.tileSize.w,\n+ tileHeight = this.tileSize.h,\n+ bounds = this.grid[0][0].bounds,\n+ left = bounds.left,\n+ top = bounds.top;\n+\n+ if (x < left) {\n+ // deal with multiple worlds\n+ if (this.map.baseLayer.wrapDateLine) {\n+ var worldWidth = this.map.getMaxExtent().getWidth();\n+ var worldsAway = Math.ceil((left - x) / worldWidth);\n+ x += worldWidth * worldsAway;\n+ }\n+ }\n+ // tile distance to location (fractional number of tiles);\n+ var dtx = (x - left) / (res * tileWidth);\n+ var dty = (top - y) / (res * tileHeight);\n+ // index of tile in grid\n+ var col = Math.floor(dtx);\n+ var row = Math.floor(dty);\n+ if (row >= 0 && row < numRows) {\n+ var tile = this.grid[row][col];\n+ if (tile) {\n+ data = {\n+ tile: tile,\n+ // pixel index within tile\n+ i: Math.floor((dtx - col) * tileWidth),\n+ j: Math.floor((dty - row) * tileHeight)\n+ };\n+ }\n+ }\n+ }\n+ return data;\n },\n \n /**\n- * Method: removeTimeout\n- * Private. Called by mousemove() to remove the drag timeout.\n+ * Method: destroyTile\n+ *\n+ * Parameters:\n+ * tile - {<OpenLayers.Tile>}\n */\n- removeTimeout: function() {\n- this.timeoutId = null;\n- // if timeout expires while we're still dragging (mouseup\n- // hasn't occurred) then call mousemove to move to the\n- // correct position\n- if (this.dragging) {\n- this.mousemove(this.lastMoveEvt);\n- }\n+ destroyTile: function(tile) {\n+ this.removeTileMonitoringHooks(tile);\n+ tile.destroy();\n },\n \n /**\n- * Method: mouseup\n- * Handle mouseup events\n+ * Method: getServerResolution\n+ * Return the closest server-supported resolution.\n *\n * Parameters:\n- * evt - {Event}\n+ * resolution - {Number} The base resolution. If undefined the\n+ * map resolution is used.\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {Number} The closest server resolution value.\n */\n- mouseup: function(evt) {\n- return this.dragend(evt);\n+ getServerResolution: function(resolution) {\n+ var distance = Number.POSITIVE_INFINITY;\n+ resolution = resolution || this.map.getResolution();\n+ if (this.serverResolutions &&\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n+ var i, newDistance, newResolution, serverResolution;\n+ for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n+ newResolution = this.serverResolutions[i];\n+ newDistance = Math.abs(newResolution - resolution);\n+ if (newDistance > distance) {\n+ break;\n+ }\n+ distance = newDistance;\n+ serverResolution = newResolution;\n+ }\n+ resolution = serverResolution;\n+ }\n+ return resolution;\n },\n \n /**\n- * Method: touchend\n- * Handle touchend events\n- *\n- * Parameters:\n- * evt - {Event}\n+ * Method: getServerZoom\n+ * Return the zoom value corresponding to the best matching server\n+ * resolution, taking into account <serverResolutions> and <zoomOffset>.\n *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {Number} The closest server supported zoom. This is not the map zoom\n+ * level, but an index of the server's resolutions array.\n */\n- touchend: function(evt) {\n- // override evt.xy with last position since touchend does not have\n- // any touch position\n- evt.xy = this.last;\n- return this.dragend(evt);\n+ getServerZoom: function() {\n+ var resolution = this.getServerResolution();\n+ return this.serverResolutions ?\n+ OpenLayers.Util.indexOf(this.serverResolutions, resolution) :\n+ this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);\n },\n \n /**\n- * Method: mouseout\n- * Handle mouseout events\n+ * Method: applyBackBuffer\n+ * Create, insert, scale and position a back buffer for the layer.\n *\n * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n+ * resolution - {Number} The resolution to transition to.\n */\n- mouseout: function(evt) {\n- if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- if (this.documentDrag === true) {\n- this.addDocumentEvents();\n+ applyBackBuffer: function(resolution) {\n+ if (this.backBufferTimerId !== null) {\n+ this.removeBackBuffer();\n+ }\n+ var backBuffer = this.backBuffer;\n+ if (!backBuffer) {\n+ backBuffer = this.createBackBuffer();\n+ if (!backBuffer) {\n+ return;\n+ }\n+ if (resolution === this.gridResolution) {\n+ this.div.insertBefore(backBuffer, this.div.firstChild);\n } else {\n- var dragged = (this.start != this.last);\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n- this.out(evt);\n- this.callback(\"out\", []);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy]);\n- }\n- if (document.onselectstart) {\n- document.onselectstart = this.oldOnselectstart;\n- }\n+ this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);\n }\n+ this.backBuffer = backBuffer;\n+\n+ // set some information in the instance for subsequent\n+ // calls to applyBackBuffer where the same back buffer\n+ // is reused\n+ var topLeftTileBounds = this.grid[0][0].bounds;\n+ this.backBufferLonLat = {\n+ lon: topLeftTileBounds.left,\n+ lat: topLeftTileBounds.top\n+ };\n+ this.backBufferResolution = this.gridResolution;\n }\n- return true;\n+\n+ var ratio = this.backBufferResolution / resolution;\n+\n+ // scale the tiles inside the back buffer\n+ var tiles = backBuffer.childNodes,\n+ tile;\n+ for (var i = tiles.length - 1; i >= 0; --i) {\n+ tile = tiles[i];\n+ tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';\n+ tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';\n+ tile.style.width = Math.round(ratio * tile._w) + 'px';\n+ tile.style.height = Math.round(ratio * tile._h) + 'px';\n+ }\n+\n+ // and position it (based on the grid's top-left corner)\n+ var position = this.getViewPortPxFromLonLat(\n+ this.backBufferLonLat, resolution);\n+ var leftOffset = this.map.layerContainerOriginPx.x;\n+ var topOffset = this.map.layerContainerOriginPx.y;\n+ backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';\n+ backBuffer.style.top = Math.round(position.y - topOffset) + 'px';\n },\n \n /**\n- * Method: click\n- * The drag handler captures the click event. If something else registers\n- * for clicks on the same element, its listener will not be called \n- * after a drag.\n- * \n- * Parameters: \n- * evt - {Event} \n- * \n+ * Method: createBackBuffer\n+ * Create a back buffer.\n+ *\n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {DOMElement} The DOM element for the back buffer, undefined if the\n+ * grid isn't initialized yet.\n */\n- click: function(evt) {\n- // let the click event propagate only if the mouse moved\n- return (this.start == this.last);\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.grid.length > 0) {\n+ backBuffer = document.createElement('div');\n+ backBuffer.id = this.div.id + '_bb';\n+ backBuffer.className = 'olBackBuffer';\n+ backBuffer.style.position = 'absolute';\n+ var map = this.map;\n+ backBuffer.style.zIndex = this.transitionEffect === 'resize' ?\n+ this.getZIndex() - 1 :\n+ // 'map-resize':\n+ map.Z_INDEX_BASE.BaseLayer -\n+ (map.getNumLayers() - map.getLayerIndex(this));\n+ for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n+ for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n+ var tile = this.grid[i][j],\n+ markup = this.grid[i][j].createBackBuffer();\n+ if (markup) {\n+ markup._i = i;\n+ markup._j = j;\n+ markup._w = tile.size.w;\n+ markup._h = tile.size.h;\n+ markup.id = tile.id + '_bb';\n+ backBuffer.appendChild(markup);\n+ }\n+ }\n+ }\n+ }\n+ return backBuffer;\n },\n \n /**\n- * Method: activate\n- * Activate the handler.\n- * \n- * Returns:\n- * {Boolean} The handler was successfully activated.\n+ * Method: removeBackBuffer\n+ * Remove back buffer from DOM.\n */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragging = false;\n- activated = true;\n+ removeBackBuffer: function() {\n+ if (this._transitionElement) {\n+ for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.stopObserving(this._transitionElement,\n+ this.transitionendEvents[i], this._removeBackBuffer);\n+ }\n+ delete this._transitionElement;\n+ }\n+ if (this.backBuffer) {\n+ if (this.backBuffer.parentNode) {\n+ this.backBuffer.parentNode.removeChild(this.backBuffer);\n+ }\n+ this.backBuffer = null;\n+ this.backBufferResolution = null;\n+ if (this.backBufferTimerId !== null) {\n+ window.clearTimeout(this.backBufferTimerId);\n+ this.backBufferTimerId = null;\n+ }\n }\n- return activated;\n },\n \n /**\n- * Method: deactivate \n- * Deactivate the handler.\n- * \n- * Returns:\n- * {Boolean} The handler was successfully deactivated.\n+ * Method: moveByPx\n+ * Move the layer based on pixel vector.\n+ *\n+ * Parameters:\n+ * dx - {Number}\n+ * dy - {Number}\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.started = false;\n- this.dragging = false;\n- this.start = null;\n- this.last = null;\n- deactivated = true;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDragDown\"\n- );\n+ moveByPx: function(dx, dy) {\n+ if (!this.singleTile) {\n+ this.moveGriddedTiles();\n }\n- return deactivated;\n },\n \n /**\n- * Method: adjustXY\n- * Converts event coordinates that are relative to the document body to\n- * ones that are relative to the map viewport. The latter is the default in\n- * OpenLayers.\n+ * APIMethod: setTileSize\n+ * Check if we are in singleTile mode and if so, set the size as a ratio\n+ * of the map size (as specified by the layer's 'ratio' property).\n * \n * Parameters:\n- * evt - {Object}\n+ * size - {<OpenLayers.Size>}\n */\n- adjustXY: function(evt) {\n- var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n- evt.xy.x -= pos[0];\n- evt.xy.y -= pos[1];\n+ setTileSize: function(size) {\n+ if (this.singleTile) {\n+ size = this.map.getSize();\n+ size.h = parseInt(size.h * this.ratio, 10);\n+ size.w = parseInt(size.w * this.ratio, 10);\n+ }\n+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);\n },\n \n /**\n- * Method: addDocumentEvents\n- * Start observing document events when documentDrag is true and the mouse\n- * cursor leaves the map viewport while dragging.\n+ * APIMethod: getTilesBounds\n+ * Return the bounds of the tile grid.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the\n+ * currently loaded tiles (including those partially or not at all seen \n+ * onscreen).\n */\n- addDocumentEvents: function() {\n- OpenLayers.Element.addClass(document.body, \"olDragDown\");\n- this.documentEvents = true;\n- OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.observe(document, \"mouseup\", this._docUp);\n+ getTilesBounds: function() {\n+ var bounds = null;\n+\n+ var length = this.grid.length;\n+ if (length) {\n+ var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n+ width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n+ height = this.grid.length * bottomLeftTileBounds.getHeight();\n+\n+ bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left,\n+ bottomLeftTileBounds.bottom,\n+ bottomLeftTileBounds.left + width,\n+ bottomLeftTileBounds.bottom + height);\n+ }\n+ return bounds;\n },\n \n /**\n- * Method: removeDocumentEvents\n- * Stops observing document events when documentDrag is true and the mouse\n- * cursor re-enters the map viewport while dragging.\n+ * Method: initSingleTile\n+ * \n+ * Parameters: \n+ * bounds - {<OpenLayers.Bounds>}\n */\n- removeDocumentEvents: function() {\n- OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n- this.documentEvents = false;\n- OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp);\n- },\n+ initSingleTile: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n \n- CLASS_NAME: \"OpenLayers.Handler.Drag\"\n-});\n-/* ======================================================================\n- OpenLayers/Handler/Box.js\n- ====================================================================== */\n+ //determine new tile bounds\n+ var center = bounds.getCenterLonLat();\n+ var tileWidth = bounds.getWidth() * this.ratio;\n+ var tileHeight = bounds.getHeight() * this.ratio;\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ var tileBounds =\n+ new OpenLayers.Bounds(center.lon - (tileWidth / 2),\n+ center.lat - (tileHeight / 2),\n+ center.lon + (tileWidth / 2),\n+ center.lat + (tileHeight / 2));\n \n-/**\n- * @requires OpenLayers/Handler.js\n- * @requires OpenLayers/Handler/Drag.js\n- */\n+ var px = this.map.getLayerPxFromLonLat({\n+ lon: tileBounds.left,\n+ lat: tileBounds.top\n+ });\n \n-/**\n- * Class: OpenLayers.Handler.Box\n- * Handler for dragging a rectangle across the map. Box is displayed \n- * on mouse down, moves on mouse move, and is finished on mouse up.\n- *\n- * Inherits from:\n- * - <OpenLayers.Handler> \n- */\n-OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n+ if (!this.grid.length) {\n+ this.grid[0] = [];\n+ }\n+\n+ var tile = this.grid[0][0];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+\n+ this.addTileMonitoringHooks(tile);\n+ tile.draw();\n+ this.grid[0][0] = tile;\n+ } else {\n+ tile.moveTo(tileBounds, px);\n+ }\n+\n+ //remove all but our single tile\n+ this.removeExcessTiles(1, 1);\n+\n+ // store the resolution of the grid\n+ this.gridResolution = this.getServerResolution();\n+ },\n \n /** \n- * Property: dragHandler \n- * {<OpenLayers.Handler.Drag>} \n+ * Method: calculateGridLayout\n+ * Generate parameters for the grid layout.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an\n+ * object with a 'left' and 'top' properties.\n+ * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an\n+ * object with a 'lon' and 'lat' properties.\n+ * resolution - {Number}\n+ *\n+ * Returns:\n+ * {Object} Object containing properties tilelon, tilelat, startcol,\n+ * startrow\n */\n- dragHandler: null,\n+ calculateGridLayout: function(bounds, origin, resolution) {\n+ var tilelon = resolution * this.tileSize.w;\n+ var tilelat = resolution * this.tileSize.h;\n \n- /**\n- * APIProperty: boxDivClassName\n- * {String} The CSS class to use for drawing the box. Default is\n- * olHandlerBoxZoomBox\n- */\n- boxDivClassName: 'olHandlerBoxZoomBox',\n+ var offsetlon = bounds.left - origin.lon;\n+ var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n+\n+ var rowSign = this.rowSign;\n+\n+ var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n+ var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign;\n+\n+ return {\n+ tilelon: tilelon,\n+ tilelat: tilelat,\n+ startcol: tilecol,\n+ startrow: tilerow\n+ };\n+\n+ },\n \n /**\n- * Property: boxOffsets\n- * {Object} Caches box offsets from css. This is used by the getBoxOffsets\n- * method.\n+ * Method: getTileOrigin\n+ * Determine the origin for aligning the grid of tiles. If a <tileOrigin>\n+ * property is supplied, that will be returned. Otherwise, the origin\n+ * will be derived from the layer's <maxExtent> property. In this case,\n+ * the tile origin will be the corner of the <maxExtent> given by the \n+ * <tileOriginCorner> property.\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} The tile origin.\n */\n- boxOffsets: null,\n+ getTileOrigin: function() {\n+ var origin = this.tileOrigin;\n+ if (!origin) {\n+ var extent = this.getMaxExtent();\n+ var edges = ({\n+ \"tl\": [\"left\", \"top\"],\n+ \"tr\": [\"right\", \"top\"],\n+ \"bl\": [\"left\", \"bottom\"],\n+ \"br\": [\"right\", \"bottom\"]\n+ })[this.tileOriginCorner];\n+ origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);\n+ }\n+ return origin;\n+ },\n \n /**\n- * Constructor: OpenLayers.Handler.Box\n+ * Method: getTileBoundsForGridIndex\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} \n- * callbacks - {Object} An object with a properties whose values are\n- * functions. Various callbacks described below.\n- * options - {Object} \n+ * row - {Number} The row of the grid\n+ * col - {Number} The column of the grid\n *\n- * Named callbacks:\n- * start - Called when the box drag operation starts.\n- * done - Called when the box drag operation is finished.\n- * The callback should expect to receive a single argument, the box \n- * bounds or a pixel. If the box dragging didn't span more than a 5 \n- * pixel distance, a pixel will be returned instead of a bounds object.\n+ * Returns:\n+ * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.dragHandler = new OpenLayers.Handler.Drag(\n- this, {\n- down: this.startBox,\n- move: this.moveBox,\n- out: this.removeBox,\n- up: this.endBox\n- }, {\n- keyMask: this.keyMask\n- }\n+ getTileBoundsForGridIndex: function(row, col) {\n+ var origin = this.getTileOrigin();\n+ var tileLayout = this.gridLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var startcol = tileLayout.startcol;\n+ var startrow = tileLayout.startrow;\n+ var rowSign = this.rowSign;\n+ return new OpenLayers.Bounds(\n+ origin.lon + (startcol + col) * tilelon,\n+ origin.lat - (startrow + row * rowSign) * tilelat * rowSign,\n+ origin.lon + (startcol + col + 1) * tilelon,\n+ origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign\n );\n },\n \n /**\n- * Method: destroy\n+ * Method: initGriddedTiles\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.destroy();\n- this.dragHandler = null;\n+ initGriddedTiles: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+\n+ // work out mininum number of rows and columns; this is the number of\n+ // tiles required to cover the viewport plus at least one for panning\n+\n+ var viewSize = this.map.getSize();\n+\n+ var origin = this.getTileOrigin();\n+ var resolution = this.map.getResolution(),\n+ serverResolution = this.getServerResolution(),\n+ ratio = resolution / serverResolution,\n+ tileSize = {\n+ w: this.tileSize.w / ratio,\n+ h: this.tileSize.h / ratio\n+ };\n+\n+ var minRows = Math.ceil(viewSize.h / tileSize.h) +\n+ 2 * this.buffer + 1;\n+ var minCols = Math.ceil(viewSize.w / tileSize.w) +\n+ 2 * this.buffer + 1;\n+\n+ var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n+ this.gridLayout = tileLayout;\n+\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+\n+ var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n+ var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n+\n+ var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n+ var startPx = this.map.getViewPortPxFromLonLat(\n+ new OpenLayers.LonLat(tileBounds.left, tileBounds.top)\n+ );\n+ startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n+ startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n+\n+ var tileData = [],\n+ center = this.map.getCenter();\n+\n+ var rowidx = 0;\n+ do {\n+ var row = this.grid[rowidx];\n+ if (!row) {\n+ row = [];\n+ this.grid.push(row);\n+ }\n+\n+ var colidx = 0;\n+ do {\n+ tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n+ var px = startPx.clone();\n+ px.x = px.x + colidx * Math.round(tileSize.w);\n+ px.y = px.y + rowidx * Math.round(tileSize.h);\n+ var tile = row[colidx];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ row.push(tile);\n+ } else {\n+ tile.moveTo(tileBounds, px, false);\n+ }\n+ var tileCenter = tileBounds.getCenterLonLat();\n+ tileData.push({\n+ tile: tile,\n+ distance: Math.pow(tileCenter.lon - center.lon, 2) +\n+ Math.pow(tileCenter.lat - center.lat, 2)\n+ });\n+\n+ colidx += 1;\n+ } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) ||\n+ colidx < minCols);\n+\n+ rowidx += 1;\n+ } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) ||\n+ rowidx < minRows);\n+\n+ //shave off exceess rows and colums\n+ this.removeExcessTiles(rowidx, colidx);\n+\n+ var resolution = this.getServerResolution();\n+ // store the resolution of the grid\n+ this.gridResolution = resolution;\n+\n+ //now actually draw the tiles\n+ tileData.sort(function(a, b) {\n+ return a.distance - b.distance;\n+ });\n+ for (var i = 0, ii = tileData.length; i < ii; ++i) {\n+ tileData[i].tile.draw();\n }\n },\n \n /**\n- * Method: setMap\n+ * Method: getMaxExtent\n+ * Get this layer's maximum extent. (Implemented as a getter for\n+ * potential specific implementations in sub-classes.)\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>}\n */\n- setMap: function(map) {\n- OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.setMap(map);\n- }\n+ getMaxExtent: function() {\n+ return this.maxExtent;\n },\n \n /**\n- * Method: startBox\n+ * APIMethod: addTile\n+ * Create a tile, initialize it, and add it to the layer div. \n *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>}\n+ * Parameters\n+ * bounds - {<OpenLayers.Bounds>}\n+ * position - {<OpenLayers.Pixel>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Tile>} The added OpenLayers.Tile\n */\n- startBox: function(xy) {\n- this.callback(\"start\", []);\n- this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {\n- x: -9999,\n- y: -9999\n+ addTile: function(bounds, position) {\n+ var tile = new this.tileClass(\n+ this, position, bounds, null, this.tileSize, this.tileOptions\n+ );\n+ this.events.triggerEvent(\"addtile\", {\n+ tile: tile\n });\n- this.zoomBox.className = this.boxDivClassName;\n- this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n+ return tile;\n+ },\n \n- this.map.viewPortDiv.appendChild(this.zoomBox);\n+ /** \n+ * Method: addTileMonitoringHooks\n+ * This function takes a tile as input and adds the appropriate hooks to \n+ * the tile so that the layer can keep track of the loading tiles.\n+ * \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n+ */\n+ addTileMonitoringHooks: function(tile) {\n \n- OpenLayers.Element.addClass(\n- this.map.viewPortDiv, \"olDrawBox\"\n- );\n+ var replacingCls = 'olTileReplacing';\n+\n+ tile.onLoadStart = function() {\n+ //if that was first tile then trigger a 'loadstart' on the layer\n+ if (this.loading === false) {\n+ this.loading = true;\n+ this.events.triggerEvent(\"loadstart\");\n+ }\n+ this.events.triggerEvent(\"tileloadstart\", {\n+ tile: tile\n+ });\n+ this.numLoadingTiles++;\n+ if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ OpenLayers.Element.addClass(tile.getTile(), replacingCls);\n+ }\n+ };\n+\n+ tile.onLoadEnd = function(evt) {\n+ this.numLoadingTiles--;\n+ var aborted = evt.type === 'unload';\n+ this.events.triggerEvent(\"tileloaded\", {\n+ tile: tile,\n+ aborted: aborted\n+ });\n+ if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ var tileDiv = tile.getTile();\n+ if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {\n+ var bufferTile = document.getElementById(tile.id + '_bb');\n+ if (bufferTile) {\n+ bufferTile.parentNode.removeChild(bufferTile);\n+ }\n+ }\n+ OpenLayers.Element.removeClass(tileDiv, replacingCls);\n+ }\n+ //if that was the last tile, then trigger a 'loadend' on the layer\n+ if (this.numLoadingTiles === 0) {\n+ if (this.backBuffer) {\n+ if (this.backBuffer.childNodes.length === 0) {\n+ // no tiles transitioning, remove immediately\n+ this.removeBackBuffer();\n+ } else {\n+ // wait until transition has ended or delay has passed\n+ this._transitionElement = aborted ?\n+ this.div.lastChild : tile.imgDiv;\n+ var transitionendEvents = this.transitionendEvents;\n+ for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.observe(this._transitionElement,\n+ transitionendEvents[i],\n+ this._removeBackBuffer);\n+ }\n+ // the removal of the back buffer is delayed to prevent\n+ // flash effects due to the animation of tile displaying\n+ this.backBufferTimerId = window.setTimeout(\n+ this._removeBackBuffer, this.removeBackBufferDelay\n+ );\n+ }\n+ }\n+ this.loading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ }\n+ };\n+\n+ tile.onLoadError = function() {\n+ this.events.triggerEvent(\"tileerror\", {\n+ tile: tile\n+ });\n+ };\n+\n+ tile.events.on({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n },\n \n- /**\n- * Method: moveBox\n+ /** \n+ * Method: removeTileMonitoringHooks\n+ * This function takes a tile as input and removes the tile hooks \n+ * that were added in addTileMonitoringHooks()\n+ * \n+ * Parameters: \n+ * tile - {<OpenLayers.Tile>}\n */\n- moveBox: function(xy) {\n- var startX = this.dragHandler.start.x;\n- var startY = this.dragHandler.start.y;\n- var deltaX = Math.abs(startX - xy.x);\n- var deltaY = Math.abs(startY - xy.y);\n-\n- var offset = this.getBoxOffsets();\n- this.zoomBox.style.width = (deltaX + offset.width + 1) + \"px\";\n- this.zoomBox.style.height = (deltaY + offset.height + 1) + \"px\";\n- this.zoomBox.style.left = (xy.x < startX ?\n- startX - deltaX - offset.left : startX - offset.left) + \"px\";\n- this.zoomBox.style.top = (xy.y < startY ?\n- startY - deltaY - offset.top : startY - offset.top) + \"px\";\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ \"loadstart\": tile.onLoadStart,\n+ \"loadend\": tile.onLoadEnd,\n+ \"unload\": tile.onLoadEnd,\n+ \"loaderror\": tile.onLoadError,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: endBox\n+ * Method: moveGriddedTiles\n */\n- endBox: function(end) {\n- var result;\n- if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||\n- Math.abs(this.dragHandler.start.y - end.y) > 5) {\n- var start = this.dragHandler.start;\n- var top = Math.min(start.y, end.y);\n- var bottom = Math.max(start.y, end.y);\n- var left = Math.min(start.x, end.x);\n- var right = Math.max(start.x, end.x);\n- result = new OpenLayers.Bounds(left, bottom, right, top);\n- } else {\n- result = this.dragHandler.start.clone(); // i.e. OL.Pixel\n+ moveGriddedTiles: function() {\n+ var buffer = this.buffer + 1;\n+ while (true) {\n+ var tlTile = this.grid[0][0];\n+ var tlViewPort = {\n+ x: tlTile.position.x +\n+ this.map.layerContainerOriginPx.x,\n+ y: tlTile.position.y +\n+ this.map.layerContainerOriginPx.y\n+ };\n+ var ratio = this.getServerResolution() / this.map.getResolution();\n+ var tileSize = {\n+ w: Math.round(this.tileSize.w * ratio),\n+ h: Math.round(this.tileSize.h * ratio)\n+ };\n+ if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n+ this.shiftColumn(true, tileSize);\n+ } else if (tlViewPort.x < -tileSize.w * buffer) {\n+ this.shiftColumn(false, tileSize);\n+ } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n+ this.shiftRow(true, tileSize);\n+ } else if (tlViewPort.y < -tileSize.h * buffer) {\n+ this.shiftRow(false, tileSize);\n+ } else {\n+ break;\n+ }\n }\n- this.removeBox();\n-\n- this.callback(\"done\", [result]);\n },\n \n /**\n- * Method: removeBox\n- * Remove the zoombox from the screen and nullify our reference to it.\n+ * Method: shiftRow\n+ * Shifty grid work\n+ *\n+ * Parameters:\n+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- removeBox: function() {\n- this.map.viewPortDiv.removeChild(this.zoomBox);\n- this.zoomBox = null;\n- this.boxOffsets = null;\n- OpenLayers.Element.removeClass(\n- this.map.viewPortDiv, \"olDrawBox\"\n- );\n+ shiftRow: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var rowIndex = prepend ? 0 : (grid.length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var rowSign = this.rowSign;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startrow += sign * rowSign;\n \n+ var modelRow = grid[rowIndex];\n+ var row = grid[prepend ? 'pop' : 'shift']();\n+ for (var i = 0, len = row.length; i < len; i++) {\n+ var tile = row[i];\n+ var position = modelRow[i].position.clone();\n+ position.y += tileSize.h * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);\n+ }\n+ grid[prepend ? 'unshift' : 'push'](row);\n },\n \n /**\n- * Method: activate\n+ * Method: shiftColumn\n+ * Shift grid work in the other dimension\n+ *\n+ * Parameters:\n+ * prepend - {Boolean} if true, prepend to beginning.\n+ * if false, then append to end\n+ * tileSize - {Object} rendered tile size; object with w and h properties\n */\n- activate: function() {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragHandler.activate();\n- return true;\n- } else {\n- return false;\n+ shiftColumn: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var colIndex = prepend ? 0 : (grid[0].length - 1);\n+ var sign = prepend ? -1 : 1;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startcol += sign;\n+\n+ for (var i = 0, len = grid.length; i < len; i++) {\n+ var row = grid[i];\n+ var position = row[colIndex].position.clone();\n+ var tile = row[prepend ? 'pop' : 'shift']();\n+ position.x += tileSize.w * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n+ row[prepend ? 'unshift' : 'push'](tile);\n }\n },\n \n /**\n- * Method: deactivate\n+ * Method: removeExcessTiles\n+ * When the size of the map or the buffer changes, we may need to\n+ * remove some excess rows and columns.\n+ * \n+ * Parameters:\n+ * rows - {Integer} Maximum number of rows we want our grid to have.\n+ * columns - {Integer} Maximum number of columns we want our grid to have.\n */\n- deactivate: function() {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- if (this.dragHandler.deactivate()) {\n- if (this.zoomBox) {\n- this.removeBox();\n- }\n+ removeExcessTiles: function(rows, columns) {\n+ var i, l;\n+\n+ // remove extra rows\n+ while (this.grid.length > rows) {\n+ var row = this.grid.pop();\n+ for (i = 0, l = row.length; i < l; i++) {\n+ var tile = row[i];\n+ this.destroyTile(tile);\n+ }\n+ }\n+\n+ // remove extra columns\n+ for (i = 0, l = this.grid.length; i < l; i++) {\n+ while (this.grid[i].length > columns) {\n+ var row = this.grid[i];\n+ var tile = row.pop();\n+ this.destroyTile(tile);\n }\n- return true;\n- } else {\n- return false;\n }\n },\n \n /**\n- * Method: getBoxOffsets\n- * Determines border offsets for a box, according to the box model.\n- * \n- * Returns:\n- * {Object} an object with the following offsets:\n- * - left\n- * - right\n- * - top\n- * - bottom\n- * - width\n- * - height\n+ * Method: onMapResize\n+ * For singleTile layers, this will set a new tile size according to the\n+ * dimensions of the map pane.\n */\n- getBoxOffsets: function() {\n- if (!this.boxOffsets) {\n- // Determine the box model. If the testDiv's clientWidth is 3, then\n- // the borders are outside and we are dealing with the w3c box\n- // model. Otherwise, the browser uses the traditional box model and\n- // the borders are inside the box bounds, leaving us with a\n- // clientWidth of 1.\n- var testDiv = document.createElement(\"div\");\n- //testDiv.style.visibility = \"hidden\";\n- testDiv.style.position = \"absolute\";\n- testDiv.style.border = \"1px solid black\";\n- testDiv.style.width = \"3px\";\n- document.body.appendChild(testDiv);\n- var w3cBoxModel = testDiv.clientWidth == 3;\n- document.body.removeChild(testDiv);\n-\n- var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n- \"border-left-width\"));\n- var right = parseInt(OpenLayers.Element.getStyle(\n- this.zoomBox, \"border-right-width\"));\n- var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,\n- \"border-top-width\"));\n- var bottom = parseInt(OpenLayers.Element.getStyle(\n- this.zoomBox, \"border-bottom-width\"));\n- this.boxOffsets = {\n- left: left,\n- right: right,\n- top: top,\n- bottom: bottom,\n- width: w3cBoxModel === false ? left + right : 0,\n- height: w3cBoxModel === false ? top + bottom : 0\n- };\n+ onMapResize: function() {\n+ if (this.singleTile) {\n+ this.clearGrid();\n+ this.setTileSize();\n }\n- return this.boxOffsets;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Box\"\n+ /**\n+ * APIMethod: getTileBounds\n+ * Returns The tile bounds for a layer given a pixel location.\n+ *\n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n+ */\n+ getTileBounds: function(viewPortPx) {\n+ var maxExtent = this.maxExtent;\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = maxExtent.left + (tileMapWidth *\n+ Math.floor((mapPoint.lon -\n+ maxExtent.left) /\n+ tileMapWidth));\n+ var tileBottom = maxExtent.bottom + (tileMapHeight *\n+ Math.floor((mapPoint.lat -\n+ maxExtent.bottom) /\n+ tileMapHeight));\n+ return new OpenLayers.Bounds(tileLeft, tileBottom,\n+ tileLeft + tileMapWidth,\n+ tileBottom + tileMapHeight);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.Grid\"\n });\n /* ======================================================================\n- OpenLayers/Control/ZoomBox.js\n+ OpenLayers/Layer/XYZ.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Box.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n-/**\n- * Class: OpenLayers.Control.ZoomBox\n- * The ZoomBox control enables zooming directly to a given extent, by drawing \n- * a box on the map. The box is drawn by holding down shift, whilst dragging \n- * the mouse.\n- *\n+/** \n+ * Class: OpenLayers.Layer.XYZ\n+ * The XYZ class is designed to make it easier for people who have tiles\n+ * arranged by a standard XYZ grid. \n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n /**\n- * Property: type\n- * {OpenLayers.Control.TYPE}\n+ * APIProperty: isBaseLayer\n+ * Default is true, as this is designed to be a base tile source. \n */\n- type: OpenLayers.Control.TYPE_TOOL,\n+ isBaseLayer: true,\n \n /**\n- * Property: out\n- * {Boolean} Should the control be used for zooming out?\n+ * APIProperty: sphericalMercator\n+ * Whether the tile extents should be set to the defaults for \n+ * spherical mercator. Useful for things like OpenStreetMap.\n+ * Default is false, except for the OSM subclass.\n */\n- out: false,\n+ sphericalMercator: false,\n \n /**\n- * APIProperty: keyMask\n- * {Integer} Zoom only occurs if the keyMask matches the combination of \n- * keys down. Use bitwise operators and one or more of the\n- * <OpenLayers.Handler> constants to construct a keyMask. Leave null if \n- * not used mask. Default is null.\n+ * APIProperty: zoomOffset\n+ * {Number} If your cache has more zoom levels than you want to provide\n+ * access to with this layer, supply a zoomOffset. This zoom offset\n+ * is added to the current map zoom level to determine the level\n+ * for a requested tile. For example, if you supply a zoomOffset\n+ * of 3, when the map is at the zoom 0, tiles will be requested from\n+ * level 3 of your cache. Default is 0 (assumes cache level and map\n+ * zoom are equivalent). Using <zoomOffset> is an alternative to\n+ * setting <serverResolutions> if you only want to expose a subset\n+ * of the server resolutions.\n */\n- keyMask: null,\n+ zoomOffset: 0,\n \n /**\n- * APIProperty: alwaysZoom\n- * {Boolean} Always zoom in/out when box drawn, even if the zoom level does\n- * not change.\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer; you can also look at <zoomOffset>, which is\n+ * an alternative to <serverResolutions> for that specific purpose.\n+ * (b) The map can work with resolutions that aren't supported by\n+ * the server, i.e. that aren't in <serverResolutions>. When the\n+ * map is displayed in such a resolution data for the closest\n+ * server-supported resolution is loaded and the layer div is\n+ * stretched as necessary.\n */\n- alwaysZoom: false,\n+ serverResolutions: null,\n \n /**\n- * APIProperty: zoomOnClick\n- * {Boolean} Should we zoom when no box was dragged, i.e. the user only\n- * clicked? Default is true.\n+ * Constructor: OpenLayers.Layer.XYZ\n+ *\n+ * Parameters:\n+ * name - {String}\n+ * url - {String}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- zoomOnClick: true,\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options);\n+ }\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n+ name || this.name, url || this.url, {},\n+ options\n+ ]);\n+ },\n \n /**\n- * Method: draw\n+ * APIMethod: clone\n+ * Create a clone of this layer\n+ *\n+ * Parameters:\n+ * obj - {Object} Is this ever used?\n+ * \n+ * Returns:\n+ * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n */\n- draw: function() {\n- this.handler = new OpenLayers.Handler.Box(this, {\n- done: this.zoomBox\n- }, {\n- keyMask: this.keyMask\n- });\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name,\n+ this.url,\n+ this.getOptions());\n+ }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ return obj;\n },\n \n /**\n- * Method: zoomBox\n+ * Method: getURL\n *\n * Parameters:\n- * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as\n+ * parameters\n */\n- zoomBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var bounds,\n- targetCenterPx = position.getCenterPixel();\n- if (!this.out) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,\n- maxXY.lon, maxXY.lat);\n- } else {\n- var pixWidth = position.right - position.left;\n- var pixHeight = position.bottom - position.top;\n- var zoomFactor = Math.min((this.map.size.h / pixHeight),\n- (this.map.size.w / pixWidth));\n- var extent = this.map.getExtent();\n- var center = this.map.getLonLatFromPixel(targetCenterPx);\n- var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor;\n- var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor;\n- var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor;\n- var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor;\n- bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);\n- }\n- // always zoom in/out \n- var lastZoom = this.map.getZoom(),\n- size = this.map.getSize(),\n- centerPx = {\n- x: size.w / 2,\n- y: size.h / 2\n- },\n- zoom = this.map.getZoomForExtent(bounds),\n- oldRes = this.map.getResolution(),\n- newRes = this.map.getResolutionForZoom(zoom);\n- if (oldRes == newRes) {\n- this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));\n- } else {\n- var zoomOriginPx = {\n- x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /\n- (oldRes - newRes),\n- y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /\n- (oldRes - newRes)\n- };\n- this.map.zoomTo(zoom, zoomOriginPx);\n- }\n- if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n- this.map.zoomTo(lastZoom + (this.out ? -1 : 1));\n- }\n- } else if (this.zoomOnClick) { // it's a pixel\n- if (!this.out) {\n- this.map.zoomTo(this.map.getZoom() + 1, position);\n- } else {\n- this.map.zoomTo(this.map.getZoom() - 1, position);\n- }\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = '' + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url);\n }\n+\n+ return OpenLayers.String.format(url, xyz);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n+ /**\n+ * Method: getXYZ\n+ * Calculates x, y and z for the given bounds.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n+ * Returns:\n+ * {Object} - an object with x, y and z properties.\n+ */\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) /\n+ (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) /\n+ (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = ((x % limit) + limit) % limit;\n+ }\n+\n+ return {\n+ 'x': x,\n+ 'y': y,\n+ 'z': z\n+ };\n+ },\n+\n+ /* APIMethod: setMap\n+ * When the layer is added to a map, then we can fetch our origin \n+ * (if we don't have one.) \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n+ this.maxExtent.bottom);\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n });\n /* ======================================================================\n- OpenLayers/Control/DragPan.js\n+ OpenLayers/Layer/OSM.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Handler/Drag.js\n+ * @requires OpenLayers/Layer/XYZ.js\n */\n \n /**\n- * Class: OpenLayers.Control.DragPan\n- * The DragPan control pans the map with a drag of the mouse.\n+ * Class: OpenLayers.Layer.OSM\n+ * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n+ * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n+ * a different layer instead, you need to provide a different\n+ * URL to the constructor. Here's an example for using OpenCycleMap:\n+ * \n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.XYZ>\n */\n-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * Property: type\n- * {OpenLayers.Control.TYPES}\n- */\n- type: OpenLayers.Control.TYPE_TOOL,\n-\n- /**\n- * Property: panned\n- * {Boolean} The map moved.\n- */\n- panned: false,\n-\n- /**\n- * Property: interval\n- * {Integer} The number of milliseconds that should ellapse before\n- * panning the map again. Defaults to 0 milliseconds, which means that\n- * no separate cycle is used for panning. In most cases you won't want\n- * to change this value. For slow machines/devices larger values can be\n- * tried out.\n- */\n- interval: 0,\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} If set to true, mouse dragging will continue even if the\n- * mouse cursor leaves the map viewport. Default is false.\n+ * APIProperty: name\n+ * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n+ * argument to the constructor is null or undefined.\n */\n- documentDrag: false,\n+ name: \"OpenStreetMap\",\n \n /**\n- * Property: kinetic\n- * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.\n+ * APIProperty: url\n+ * {String} The tileset URL scheme. Defaults to\n+ * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n+ * (the official OSM tileset) if the second argument to the constructor\n+ * is null or undefined. To use another tileset you can have something\n+ * like this:\n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n */\n- kinetic: null,\n+ url: [\n+ 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n+ ],\n \n /**\n- * APIProperty: enableKinetic\n- * {Boolean} Set this option to enable \"kinetic dragging\". Can be\n- * set to true or to an object. If set to an object this\n- * object will be passed to the {<OpenLayers.Kinetic>}\n- * constructor. Defaults to true.\n- * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is\n- * included in your build config.\n+ * Property: attribution\n+ * {String} The layer attribution.\n */\n- enableKinetic: true,\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n \n /**\n- * APIProperty: kineticInterval\n- * {Integer} Interval in milliseconds between 2 steps in the \"kinetic\n- * scrolling\". Applies only if enableKinetic is set. Defaults\n- * to 10 milliseconds.\n+ * Property: sphericalMercator\n+ * {Boolean}\n */\n- kineticInterval: 10,\n-\n+ sphericalMercator: true,\n \n /**\n- * Method: draw\n- * Creates a Drag handler, using <panMap> and\n- * <panMapDone> as callbacks.\n+ * Property: wrapDateLine\n+ * {Boolean}\n */\n- draw: function() {\n- if (this.enableKinetic && OpenLayers.Kinetic) {\n- var config = {\n- interval: this.kineticInterval\n- };\n- if (typeof this.enableKinetic === \"object\") {\n- config = OpenLayers.Util.extend(config, this.enableKinetic);\n- }\n- this.kinetic = new OpenLayers.Kinetic(config);\n- }\n- this.handler = new OpenLayers.Handler.Drag(this, {\n- \"move\": this.panMap,\n- \"done\": this.panMapDone,\n- \"down\": this.panMapStart\n- }, {\n- interval: this.interval,\n- documentDrag: this.documentDrag\n- });\n- },\n+ wrapDateLine: true,\n \n- /**\n- * Method: panMapStart\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n+ *\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n+ *\n+ * When using OSM tilesets other than the default ones, it may be\n+ * necessary to set this to\n+ *\n+ * (code)\n+ * {crossOriginKeyword: null}\n+ * (end)\n+ *\n+ * if the server does not send Access-Control-Allow-Origin headers.\n */\n- panMapStart: function() {\n- if (this.kinetic) {\n- this.kinetic.begin();\n- }\n- },\n+ tileOptions: null,\n \n /**\n- * Method: panMap\n+ * Constructor: OpenLayers.Layer.OSM\n *\n * Parameters:\n- * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n+ * name - {String} The layer name.\n+ * url - {String} The tileset URL scheme.\n+ * options - {Object} Configuration options for the layer. Any inherited\n+ * layer option can be set in this object (e.g.\n+ * <OpenLayers.Layer.Grid.buffer>).\n */\n- panMap: function(xy) {\n- if (this.kinetic) {\n- this.kinetic.update(xy);\n- }\n- this.panned = true;\n- this.map.pan(\n- this.handler.last.x - xy.x,\n- this.handler.last.y - xy.y, {\n- dragging: true,\n- animate: false\n- }\n- );\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options && this.options.tileOptions);\n },\n \n /**\n- * Method: panMapDone\n- * Finish the panning operation. Only call setCenter (through <panMap>)\n- * if the map has actually been moved.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} Pixel of the mouse position\n+ * Method: clone\n */\n- panMapDone: function(xy) {\n- if (this.panned) {\n- var res = null;\n- if (this.kinetic) {\n- res = this.kinetic.end(xy);\n- }\n- this.map.pan(\n- this.handler.last.x - xy.x,\n- this.handler.last.y - xy.y, {\n- dragging: !!res,\n- animate: false\n- }\n- );\n- if (res) {\n- var self = this;\n- this.kinetic.move(res, function(x, y, end) {\n- self.map.pan(x, y, {\n- dragging: !end,\n- animate: false\n- });\n- });\n- }\n- this.panned = false;\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(\n+ this.name, this.url, this.getOptions());\n }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.DragPan\"\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n });\n /* ======================================================================\n- OpenLayers/Handler/MouseWheel.js\n+ OpenLayers/Layer/WMS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Layer/Grid.js\n */\n \n /**\n- * Class: OpenLayers.Handler.MouseWheel\n- * Handler for wheel up/down events.\n+ * Class: OpenLayers.Layer.WMS\n+ * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n+ * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n+ * constructor.\n * \n * Inherits from:\n- * - <OpenLayers.Handler>\n+ * - <OpenLayers.Layer.Grid>\n */\n-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n- /** \n- * Property: wheelListener \n- * {function} \n- */\n- wheelListener: null,\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n /**\n- * Property: interval\n- * {Integer} In order to increase server performance, an interval (in \n- * milliseconds) can be set to reduce the number of up/down events \n- * called. If set, a new up/down event will not be set until the \n- * interval has passed. \n- * Defaults to 0, meaning no interval. \n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs \n */\n- interval: 0,\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n+ },\n \n /**\n- * Property: maxDelta\n- * {Integer} Maximum delta to collect before breaking from the current\n- * interval. In cumulative mode, this also limits the maximum delta\n- * returned from the handler. Default is Number.POSITIVE_INFINITY.\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Default is true for WMS layer\n */\n- maxDelta: Number.POSITIVE_INFINITY,\n+ isBaseLayer: true,\n \n /**\n- * Property: delta\n- * {Integer} When interval is set, delta collects the mousewheel z-deltas\n- * of the events that occur within the interval.\n- * See also the cumulative option\n+ * APIProperty: encodeBBOX\n+ * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n+ * but some services want it that way. Default false.\n */\n- delta: 0,\n+ encodeBBOX: false,\n+\n+ /** \n+ * APIProperty: noMagic \n+ * {Boolean} If true, the image format will not be automagicaly switched \n+ * from image/jpeg to image/png or image/gif when using \n+ * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n+ * constructor. Default false. \n+ */\n+ noMagic: false,\n \n /**\n- * Property: cumulative\n- * {Boolean} When interval is set: true to collect all the mousewheel \n- * z-deltas, false to only record the delta direction (positive or\n- * negative)\n+ * Property: yx\n+ * {Object} Keys in this object are EPSG codes for which the axis order\n+ * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n+ * true as value. This is only relevant for WMS versions >= 1.3.0, and\n+ * only if yx is not set in <OpenLayers.Projection.defaults> for the\n+ * used projection.\n */\n- cumulative: true,\n+ yx: {},\n \n /**\n- * Constructor: OpenLayers.Handler.MouseWheel\n+ * Constructor: OpenLayers.Layer.WMS\n+ * Create a new WMS layer object\n+ *\n+ * Examples:\n+ *\n+ * The code below creates a simple WMS layer using the image/jpeg format.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {layers: \"modis,global_mosaic\"});\n+ * (end)\n+ * Note the 3rd argument (params). Properties added to this object will be\n+ * added to the WMS GetMap requests used for this layer's tiles. The only\n+ * mandatory parameter is \"layers\". Other common WMS params include\n+ * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n+ * always be ignored. Instead, it will be derived from the baseLayer's or\n+ * map's projection.\n+ *\n+ * The code below creates a transparent WMS layer with additional options.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {\n+ * layers: \"modis,global_mosaic\",\n+ * transparent: true\n+ * }, {\n+ * opacity: 0.5,\n+ * singleTile: true\n+ * });\n+ * (end)\n+ * Note that by default, a WMS layer is configured as baseLayer. Setting\n+ * the \"transparent\" param to true will apply some magic (see <noMagic>).\n+ * The default image format changes from image/jpeg to image/png, and the\n+ * layer is not configured as baseLayer.\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} \n- * callbacks - {Object} An object containing a single function to be\n- * called when the drag operation is finished.\n- * The callback should expect to recieve a single\n- * argument, the point geometry.\n- * options - {Object} \n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the WMS\n+ * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n+ * params - {Object} An object with key/value pairs representing the\n+ * GetMap query string parameters and parameter values.\n+ * options - {Object} Hashtable of extra options to tag onto the layer.\n+ * These options include all properties listed above, plus the ones\n+ * inherited from superclasses.\n */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.wheelListener = OpenLayers.Function.bindAsEventListener(\n- this.onWheelEvent, this\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ //uppercase params\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\";\n+ }\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n );\n- },\n \n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- this.wheelListener = null;\n- },\n \n- /**\n- * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/\n- */\n+ //layer is transparent \n+ if (!this.noMagic && this.params.TRANSPARENT &&\n+ this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n \n- /** \n- * Method: onWheelEvent\n- * Catch the wheel event and handle it xbrowserly\n- * \n- * Parameters:\n- * e - {Event} \n- */\n- onWheelEvent: function(e) {\n+ // unless explicitly set in options, make layer an overlay\n+ if ((options == null) || (!options.isBaseLayer)) {\n+ this.isBaseLayer = false;\n+ }\n \n- // make sure we have a map and check keyboard modifiers\n- if (!this.map || !this.checkModifiers(e)) {\n- return;\n+ // jpegs can never be transparent, so intelligently switch the \n+ // format, depending on the browser's capabilities\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n+ \"image/png\";\n+ }\n }\n \n- // Ride up the element's DOM hierarchy to determine if it or any of \n- // its ancestors was: \n- // * specifically marked as scrollable (CSS overflow property)\n- // * one of our layer divs or a div marked as scrollable\n- // ('olScrollable' CSS class)\n- // * the map div\n- //\n- var overScrollableDiv = false;\n- var allowScroll = false;\n- var overMapDiv = false;\n+ },\n \n- var elem = OpenLayers.Event.element(e);\n- while ((elem != null) && !overMapDiv && !overScrollableDiv) {\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n \n- if (!overScrollableDiv) {\n- try {\n- var overflow;\n- if (elem.currentStyle) {\n- overflow = elem.currentStyle[\"overflow\"];\n- } else {\n- var style =\n- document.defaultView.getComputedStyle(elem, null);\n- overflow = style.getPropertyValue(\"overflow\");\n- }\n- overScrollableDiv = (overflow &&\n- (overflow == \"auto\") || (overflow == \"scroll\"));\n- } catch (err) {\n- //sometimes when scrolling in a popup, this causes \n- // obscure browser error\n- }\n- }\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n+ }\n \n- if (!allowScroll) {\n- allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');\n- if (!allowScroll) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- // Are we in the layer div? Note that we have two cases\n- // here: one is to catch EventPane layers, which have a\n- // pane above the layer (layer.pane)\n- var layer = this.map.layers[i];\n- if (elem == layer.div || elem == layer.pane) {\n- allowScroll = true;\n- break;\n- }\n- }\n- }\n- }\n- overMapDiv = (elem == this.map.div);\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n \n- elem = elem.parentNode;\n- }\n+ // copy/set any non-init, non-simple values here\n \n- // Logic below is the following:\n- //\n- // If we are over a scrollable div or not over the map div:\n- // * do nothing (let the browser handle scrolling)\n- //\n- // otherwise \n- // \n- // If we are over the layer div or a 'olScrollable' div:\n- // * zoom/in out\n- // then\n- // * kill event (so as not to also scroll the page after zooming)\n- //\n- // otherwise\n- //\n- // Kill the event (dont scroll the page if we wheel over the \n- // layerswitcher or the pan/zoom control)\n- //\n- if (!overScrollableDiv && overMapDiv) {\n- if (allowScroll) {\n- var delta = 0;\n+ return obj;\n+ },\n \n- if (e.wheelDelta) {\n- delta = e.wheelDelta;\n- if (delta % 160 === 0) {\n- // opera have steps of 160 instead of 120\n- delta = delta * 0.75;\n- }\n- delta = delta / 120;\n- } else if (e.detail) {\n- // detail in Firefox on OS X is 1/3 of Windows\n- // so force delta 1 / -1\n- delta = -(e.detail / Math.abs(e.detail));\n- }\n- this.delta += delta;\n+ /**\n+ * APIMethod: reverseAxisOrder\n+ * Returns true if the axis order is reversed for the WMS version and\n+ * projection of the layer.\n+ * \n+ * Returns:\n+ * {Boolean} true if the axis order is reversed, false otherwise.\n+ */\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 &&\n+ !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n+ OpenLayers.Projection.defaults[projCode].yx));\n+ },\n \n- window.clearTimeout(this._timeoutId);\n- if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n- // store e because window.event might change during delay\n- var evt = OpenLayers.Util.extend({}, e);\n- this._timeoutId = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.wheelZoom(evt);\n- }, this),\n- this.interval\n- );\n- } else {\n- this.wheelZoom(e);\n- }\n- }\n- OpenLayers.Event.stop(e);\n- }\n+ /**\n+ * Method: getURL\n+ * Return a GetMap query string for this layer\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n+ *\n+ * Returns:\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters.\n+ */\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ // WMS 1.3 introduced axis order\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ?\n+ bounds.toBBOX(null, reverseAxisOrder) :\n+ bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString;\n },\n \n /**\n- * Method: wheelZoom\n- * Given the wheel event, we carry out the appropriate zooming in or out,\n- * based on the 'wheelDelta' or 'detail' property of the event.\n+ * APIMethod: mergeNewParams\n+ * Catch changeParams and uppercase the new params to be merged in\n+ * before calling changeParams on the super class.\n+ * \n+ * Once params have been changed, the tiles will be reloaded with\n+ * the new parameters.\n * \n * Parameters:\n- * e - {Event}\n+ * newParams - {Object} Hashtable of new params to use\n */\n- wheelZoom: function(e) {\n- var delta = this.delta;\n- this.delta = 0;\n-\n- if (delta) {\n- e.xy = this.map.events.getMousePosition(e);\n- if (delta < 0) {\n- this.callback(\"down\",\n- [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);\n- } else {\n- this.callback(\"up\",\n- [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);\n- }\n- }\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n+ newArguments);\n },\n \n- /**\n- * Method: activate \n+ /** \n+ * APIMethod: getFullRequestString\n+ * Combine the layer's url with its params and these newParams. \n+ * \n+ * Add the SRS parameter from projection -- this is probably\n+ * more eloquently done via a setProjection() method, but this \n+ * works for now and always.\n+ *\n+ * Parameters:\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n+ * \n+ * Returns:\n+ * {String} \n */\n- activate: function(evt) {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- //register mousewheel events specifically on the window and document\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n- return true;\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n+ this.projection.getCode() :\n+ mapProjection.getCode();\n+ var value = (projectionCode == \"none\") ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value;\n } else {\n- return false;\n+ this.params.SRS = value;\n }\n- },\n \n- /**\n- * Method: deactivate \n- */\n- deactivate: function(evt) {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- // unregister mousewheel events specifically on the window and document\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n- return true;\n- } else {\n- return false;\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n }\n+\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n+ this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Click.js\n+ OpenLayers/Layer/Bing.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Layer/XYZ.js\n */\n \n-/**\n- * Class: OpenLayers.Handler.Click\n- * A handler for mouse clicks. The intention of this handler is to give\n- * controls more flexibility with handling clicks. Browsers trigger\n- * click events twice for a double-click. In addition, the mousedown,\n- * mousemove, mouseup sequence fires a click event. With this handler,\n- * controls can decide whether to ignore clicks associated with a double\n- * click. By setting a <pixelTolerance>, controls can also ignore clicks\n- * that include a drag. Create a new instance with the\n- * <OpenLayers.Handler.Click> constructor.\n+/** \n+ * Class: OpenLayers.Layer.Bing\n+ * Bing layer using direct tile access as provided by Bing Maps REST Services.\n+ * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n+ * information. Note: Terms of Service compliant use requires the map to be\n+ * configured with an <OpenLayers.Control.Attribution> control and the\n+ * attribution placed on or near the map.\n * \n * Inherits from:\n- * - <OpenLayers.Handler> \n+ * - <OpenLayers.Layer.XYZ>\n */\n-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n- /**\n- * APIProperty: delay\n- * {Number} Number of milliseconds between clicks before the event is\n- * considered a double-click.\n- */\n- delay: 300,\n-\n- /**\n- * APIProperty: single\n- * {Boolean} Handle single clicks. Default is true. If false, clicks\n- * will not be reported. If true, single-clicks will be reported.\n- */\n- single: true,\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n \n /**\n- * APIProperty: double\n- * {Boolean} Handle double-clicks. Default is false.\n+ * Property: key\n+ * {String} API key for Bing maps, get your own key \n+ * at http://bingmapsportal.com/ .\n */\n- 'double': false,\n+ key: null,\n \n /**\n- * APIProperty: pixelTolerance\n- * {Number} Maximum number of pixels between mouseup and mousedown for an\n- * event to be considered a click. Default is 0. If set to an\n- * integer value, clicks with a drag greater than the value will be\n- * ignored. This property can only be set when the handler is\n- * constructed.\n+ * Property: serverResolutions\n+ * {Array} the resolutions provided by the Bing servers.\n */\n- pixelTolerance: 0,\n+ serverResolutions: [\n+ 156543.03390625, 78271.516953125, 39135.7584765625,\n+ 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n+ 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n+ 305.74811309814453, 152.87405654907226, 76.43702827453613,\n+ 38.218514137268066, 19.109257068634033, 9.554628534317017,\n+ 4.777314267158508, 2.388657133579254, 1.194328566789627,\n+ 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n+ 0.07464553542435169\n+ ],\n \n /**\n- * APIProperty: dblclickTolerance\n- * {Number} Maximum distance in pixels between clicks for a sequence of \n- * events to be considered a double click. Default is 13. If the\n- * distance between two clicks is greater than this value, a double-\n- * click will not be fired.\n+ * Property: attributionTemplate\n+ * {String}\n */\n- dblclickTolerance: 13,\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n+ '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n+ '<img src=\"${logo}\" /></a></div>${copyrights}' +\n+ '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n+ 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n+ 'Terms of Use</a></span>',\n \n /**\n- * APIProperty: stopSingle\n- * {Boolean} Stop other listeners from being notified of clicks. Default\n- * is false. If true, any listeners registered before this one for \n- * click or rightclick events will not be notified.\n+ * Property: metadata\n+ * {Object} Metadata for this layer, as returned by the callback script\n */\n- stopSingle: false,\n+ metadata: null,\n \n /**\n- * APIProperty: stopDouble\n- * {Boolean} Stop other listeners from being notified of double-clicks.\n- * Default is false. If true, any click listeners registered before\n- * this one will not be notified of *any* double-click events.\n- * \n- * The one caveat with stopDouble is that given a map with two click\n- * handlers, one with stopDouble true and the other with stopSingle\n- * true, the stopSingle handler should be activated last to get\n- * uniform cross-browser performance. Since IE triggers one click\n- * with a dblclick and FF triggers two, if a stopSingle handler is\n- * activated first, all it gets in IE is a single click when the\n- * second handler stops propagation on the dblclick.\n+ * Property: protocolRegex\n+ * {RegExp} Regular expression to match and replace http: in bing urls\n */\n- stopDouble: false,\n+ protocolRegex: /^http:/i,\n \n /**\n- * Property: timerId\n- * {Number} The id of the timeout waiting to clear the <delayedCall>.\n+ * APIProperty: type\n+ * {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used. Default is \"Road\".\n */\n- timerId: null,\n+ type: \"Road\",\n \n /**\n- * Property: down\n- * {Object} Object that store relevant information about the last\n- * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives\n- * the average location of the mouse/touch event. Its 'touches'\n- * property records clientX/clientY of each touches.\n+ * APIProperty: culture\n+ * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n+ * for the definition and the possible values. Default is \"en-US\".\n */\n- down: null,\n+ culture: \"en-US\",\n \n /**\n- * Property: last\n- * {Object} Object that store relevant information about the last\n- * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives\n- * the average location of the mouse/touch event. Its 'touches'\n- * property records clientX/clientY of each touches.\n+ * APIProperty: metadataParams\n+ * {Object} Optional url parameters for the Get Imagery Metadata request\n+ * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n */\n- last: null,\n+ metadataParams: null,\n \n- /** \n- * Property: first\n- * {Object} When waiting for double clicks, this object will store \n- * information about the first click in a two click sequence.\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n+ *\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n */\n- first: null,\n+ tileOptions: null,\n \n- /**\n- * Property: rightclickTimerId\n- * {Number} The id of the right mouse timeout waiting to clear the \n- * <delayedEvent>.\n+ /** APIProperty: protocol\n+ * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n+ * Can be 'http:' 'https:' or ''\n+ *\n+ * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n+ * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n+ * this is undocumented and the Imagery Metadata API always returns HTTP\n+ * urls.\n+ *\n+ * Default is '', unless when executed from a file:/// uri, in which case\n+ * it is 'http:'.\n */\n- rightclickTimerId: null,\n+ protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n \n /**\n- * Constructor: OpenLayers.Handler.Click\n- * Create a new click handler.\n- * \n+ * Constructor: OpenLayers.Layer.Bing\n+ * Create a new Bing layer.\n+ *\n+ * Example:\n+ * (code)\n+ * var road = new OpenLayers.Layer.Bing({\n+ * name: \"My Bing Aerial Layer\",\n+ * type: \"Aerial\",\n+ * key: \"my-api-key-here\",\n+ * });\n+ * (end)\n+ *\n * Parameters:\n- * control - {<OpenLayers.Control>} The control that is making use of\n- * this handler. If a handler is being used without a control, the\n- * handler's setMap method must be overridden to deal properly with\n- * the map.\n- * callbacks - {Object} An object with keys corresponding to callbacks\n- * that will be called by the handler. The callbacks should\n- * expect to recieve a single argument, the click event.\n- * Callbacks for 'click' and 'dblclick' are supported.\n- * options - {Object} Optional object whose properties will be set on the\n- * handler.\n- */\n-\n- /**\n- * Method: touchstart\n- * Handle touchstart.\n+ * options - {Object} Configuration properties for the layer.\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Required configuration properties:\n+ * key - {String} Bing Maps API key for your application. Get one at\n+ * http://bingmapsportal.com/.\n+ * type - {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used.\n+ *\n+ * Any other documented layer properties can be provided in the config object.\n */\n- touchstart: function(evt) {\n- this.startTouch();\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true;\n+ initialize: function(options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options.tileOptions);\n+ this.loadMetadata();\n },\n \n /**\n- * Method: touchmove\n- * Store position of last move, because touchend event can have\n- * an empty \"touches\" property.\n- *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Method: loadMetadata\n */\n- touchmove: function(evt) {\n- this.last = this.getEventInfo(evt);\n- return true;\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ // link the processMetadata method to the global scope and bind it\n+ // to this instance\n+ window[this._callbackId] = OpenLayers.Function.bind(\n+ OpenLayers.Layer.Bing.processMetadata, this\n+ );\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n+ this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script);\n },\n \n /**\n- * Method: touchend\n- * Correctly set event xy property, and add lastTouches to have\n- * touches property from last touchstart or touchmove\n+ * Method: initLayer\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Sets layer properties according to the metadata provided by the API\n */\n- touchend: function(evt) {\n- // touchstart may not have been allowed to propagate\n- if (this.down) {\n- evt.xy = this.last.xy;\n- evt.lastTouches = this.last.touches;\n- this.handleSingle(evt);\n- this.down = null;\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n }\n- return true;\n+ this.addOptions({\n+ maxResolution: Math.min(\n+ this.serverResolutions[res.zoomMin],\n+ this.maxResolution || Number.POSITIVE_INFINITY\n+ ),\n+ numZoomLevels: Math.min(\n+ res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n+ )\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw();\n+ }\n+ this.updateAttribution();\n },\n \n /**\n- * Method: mousedown\n- * Handle mousedown.\n+ * Method: getURL\n *\n- * Returns:\n- * {Boolean} Continue propagating this event.\n- */\n- mousedown: function(evt) {\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true;\n- },\n-\n- /**\n- * Method: mouseup\n- * Handle mouseup. Installed to support collection of right mouse events.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Paramters:\n+ * bounds - {<OpenLayers.Bounds>}\n */\n- mouseup: function(evt) {\n- var propagate = true;\n-\n- // Collect right mouse clicks from the mouseup\n- // IE - ignores the second right click in mousedown so using\n- // mouseup instead\n- if (this.checkModifiers(evt) && this.control.handleRightClicks &&\n- OpenLayers.Event.isRightClick(evt)) {\n- propagate = this.rightclick(evt);\n+ getURL: function(bounds) {\n+ if (!this.url) {\n+ return;\n+ }\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = '0';\n+ var mask = 1 << (i - 1);\n+ if ((x & mask) != 0) {\n+ digit++;\n+ }\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++;\n+ }\n+ quadDigits.push(digit);\n }\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl('' + x + y + z, this.url);\n \n- return propagate;\n+ return OpenLayers.String.format(url, {\n+ 'quadkey': quadKey\n+ });\n },\n \n /**\n- * Method: rightclick\n- * Handle rightclick. For a dblrightclick, we get two clicks so we need \n- * to always register for dblrightclick to properly handle single \n- * clicks.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Method: updateAttribution\n+ * Updates the attribution according to the requirements outlined in\n+ * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n */\n- rightclick: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.rightclickTimerId != null) {\n- //Second click received before timeout this must be \n- // a double click\n- this.clearTimer();\n- this.callback('dblrightclick', [evt]);\n- return !this.stopDouble;\n- } else {\n- //Set the rightclickTimerId, send evt only if double is \n- // true else trigger single\n- var clickEvent = this['double'] ?\n- OpenLayers.Util.extend({}, evt) :\n- this.callback('rightclick', [evt]);\n-\n- var delayedRightCall = OpenLayers.Function.bind(\n- this.delayedRightCall,\n- this,\n- clickEvent\n- );\n- this.rightclickTimerId = window.setTimeout(\n- delayedRightCall, this.delay\n- );\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ return;\n+ }\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(\n+ this.map.getProjectionObject(),\n+ new OpenLayers.Projection(\"EPSG:4326\")\n+ );\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n+ this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ // axis order provided is Y,X\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) &&\n+ zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \";\n+ }\n }\n }\n- return !this.stopSingle;\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ });\n },\n \n /**\n- * Method: delayedRightCall\n- * Sets <rightclickTimerId> to null. And optionally triggers the \n- * rightclick callback if evt is set.\n+ * Method: setMap\n */\n- delayedRightCall: function(evt) {\n- this.rightclickTimerId = null;\n- if (evt) {\n- this.callback('rightclick', [evt]);\n- }\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution);\n },\n \n /**\n- * Method: click\n- * Handle click events from the browser. This is registered as a listener\n- * for click events and should not be called from other events in this\n- * handler.\n- *\n+ * APIMethod: clone\n+ * \n+ * Parameters:\n+ * obj - {Object}\n+ * \n * Returns:\n- * {Boolean} Continue propagating this event.\n+ * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n */\n- click: function(evt) {\n- if (!this.last) {\n- this.last = this.getEventInfo(evt);\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options);\n }\n- this.handleSingle(evt);\n- return !this.stopSingle;\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ // copy/set any non-init, non-simple values here\n+ return obj;\n },\n \n /**\n- * Method: dblclick\n- * Handle dblclick. For a dblclick, we get two clicks in some browsers\n- * (FF) and one in others (IE). So we need to always register for\n- * dblclick to properly handle single clicks. This method is registered\n- * as a listener for the dblclick browser event. It should *not* be\n- * called by other methods in this handler.\n- * \n- * Returns:\n- * {Boolean} Continue propagating this event.\n+ * Method: destroy\n */\n- dblclick: function(evt) {\n- this.handleDouble(evt);\n- return !this.stopDouble;\n+ destroy: function() {\n+ this.map &&\n+ this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n },\n \n- /** \n- * Method: handleDouble\n- * Handle double-click sequence.\n- */\n- handleDouble: function(evt) {\n- if (this.passesDblclickTolerance(evt)) {\n- if (this[\"double\"]) {\n- this.callback(\"dblclick\", [evt]);\n- }\n- // to prevent a dblclick from firing the click callback in IE\n- this.clearTimer();\n- }\n- },\n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+});\n \n- /** \n- * Method: handleSingle\n- * Handle single click sequence.\n- */\n- handleSingle: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.timerId != null) {\n- // already received a click\n- if (this.last.touches && this.last.touches.length === 1) {\n- // touch device, no dblclick event - this may be a double\n- if (this[\"double\"]) {\n- // on Android don't let the browser zoom on the page\n- OpenLayers.Event.preventDefault(evt);\n- }\n- this.handleDouble(evt);\n- }\n- // if we're not in a touch environment we clear the click timer\n- // if we've got a second touch, we'll get two touchend events\n- if (!this.last.touches || this.last.touches.length !== 2) {\n- this.clearTimer();\n- }\n- } else {\n- // remember the first click info so we can compare to the second\n- this.first = this.getEventInfo(evt);\n- // set the timer, send evt only if single is true\n- //use a clone of the event object because it will no longer \n- //be a valid event object in IE in the timer callback\n- var clickEvent = this.single ?\n- OpenLayers.Util.extend({}, evt) : null;\n- this.queuePotentialClick(clickEvent);\n- }\n- }\n- },\n+/**\n+ * Function: OpenLayers.Layer.Bing.processMetadata\n+ * This function will be bound to an instance, linked to the global scope with\n+ * an id, and called by the JSONP script returned by the API.\n+ *\n+ * Parameters:\n+ * metadata - {Object} metadata as returned by the API\n+ */\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined; // cannot delete from window in IE\n+ delete this._callbackId;\n+};\n+/* ======================================================================\n+ OpenLayers/Layer/SphericalMercator.js\n+ ====================================================================== */\n \n- /** \n- * Method: queuePotentialClick\n- * This method is separated out largely to make testing easier (so we\n- * don't have to override window.setTimeout)\n- */\n- queuePotentialClick: function(evt) {\n- this.timerId = window.setTimeout(\n- OpenLayers.Function.bind(this.delayedCall, this, evt),\n- this.delay\n- );\n- },\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Projection.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.SphericalMercator\n+ * A mixin for layers that wraps up the pieces neccesary to have a coordinate\n+ * conversion for working with commercial APIs which use a spherical\n+ * mercator projection. Using this layer as a base layer, additional\n+ * layers can be used as overlays if they are in the same projection.\n+ *\n+ * A layer is given properties of this object by setting the sphericalMercator\n+ * property to true.\n+ *\n+ * More projection information:\n+ * - http://spatialreference.org/ref/user/google-projection/\n+ *\n+ * Proj4 Text:\n+ * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0\n+ * +k=1.0 +units=m +nadgrids=@null +no_defs\n+ *\n+ * WKT:\n+ * 900913=PROJCS[\"WGS84 / Simple Mercator\", GEOGCS[\"WGS 84\",\n+ * DATUM[\"WGS_1984\", SPHEROID[\"WGS_1984\", 6378137.0, 298.257223563]], \n+ * PRIMEM[\"Greenwich\", 0.0], UNIT[\"degree\", 0.017453292519943295], \n+ * AXIS[\"Longitude\", EAST], AXIS[\"Latitude\", NORTH]],\n+ * PROJECTION[\"Mercator_1SP_Google\"], \n+ * PARAMETER[\"latitude_of_origin\", 0.0], PARAMETER[\"central_meridian\", 0.0], \n+ * PARAMETER[\"scale_factor\", 1.0], PARAMETER[\"false_easting\", 0.0], \n+ * PARAMETER[\"false_northing\", 0.0], UNIT[\"m\", 1.0], AXIS[\"x\", EAST],\n+ * AXIS[\"y\", NORTH], AUTHORITY[\"EPSG\",\"900913\"]]\n+ */\n+OpenLayers.Layer.SphericalMercator = {\n \n /**\n- * Method: passesTolerance\n- * Determine whether the event is within the optional pixel tolerance. Note\n- * that the pixel tolerance check only works if mousedown events get to\n- * the listeners registered here. If they are stopped by other elements,\n- * the <pixelTolerance> will have no effect here (this method will always\n- * return true).\n+ * Method: getExtent\n+ * Get the map's extent.\n *\n * Returns:\n- * {Boolean} The click is within the pixel tolerance (if specified).\n+ * {<OpenLayers.Bounds>} The map extent.\n */\n- passesTolerance: function(evt) {\n- var passes = true;\n- if (this.pixelTolerance != null && this.down && this.down.xy) {\n- passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n- // for touch environments, we also enforce that all touches\n- // start and end within the given tolerance to be considered a click\n- if (passes && this.touch &&\n- this.down.touches.length === this.last.touches.length) {\n- // the touchend event doesn't come with touches, so we check\n- // down and last\n- for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n- if (this.getTouchDistance(\n- this.down.touches[i],\n- this.last.touches[i]\n- ) > this.pixelTolerance) {\n- passes = false;\n- break;\n- }\n- }\n- }\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.sphericalMercator) {\n+ extent = this.map.calculateBounds();\n+ } else {\n+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);\n }\n- return passes;\n+ return extent;\n },\n \n- /** \n- * Method: getTouchDistance\n+ /**\n+ * Method: getLonLatFromViewPortPx\n+ * Get a map location from a pixel location\n+ * \n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>}\n *\n * Returns:\n- * {Boolean} The pixel displacement between two touches.\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n+ * port OpenLayers.Pixel, translated into lon/lat by map lib\n+ * If the map lib is not loaded or not centered, returns null\n */\n- getTouchDistance: function(from, to) {\n- return Math.sqrt(\n- Math.pow(from.clientX - to.clientX, 2) +\n- Math.pow(from.clientY - to.clientY, 2)\n- );\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);\n },\n \n /**\n- * Method: passesDblclickTolerance\n- * Determine whether the event is within the optional double-cick pixel \n- * tolerance.\n+ * Method: getViewPortPxFromLonLat\n+ * Get a pixel location from a map location\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>}\n *\n * Returns:\n- * {Boolean} The click is within the double-click pixel tolerance.\n- */\n- passesDblclickTolerance: function(evt) {\n- var passes = true;\n- if (this.down && this.first) {\n- passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;\n- }\n- return passes;\n- },\n-\n- /**\n- * Method: clearTimer\n- * Clear the timer and set <timerId> to null.\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n+ * OpenLayers.LonLat, translated into view port pixels by map lib\n+ * If map lib is not loaded or not centered, returns null\n */\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null;\n- }\n- if (this.rightclickTimerId != null) {\n- window.clearTimeout(this.rightclickTimerId);\n- this.rightclickTimerId = null;\n- }\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);\n },\n \n- /**\n- * Method: delayedCall\n- * Sets <timerId> to null. And optionally triggers the click callback if\n- * evt is set.\n+ /** \n+ * Method: initMercatorParameters \n+ * Set up the mercator parameters on the layer: resolutions,\n+ * projection, units.\n */\n- delayedCall: function(evt) {\n- this.timerId = null;\n- if (evt) {\n- this.callback(\"click\", [evt]);\n+ initMercatorParameters: function() {\n+ // set up properties for Mercator - assume EPSG:900913\n+ this.RESOLUTIONS = [];\n+ var maxResolution = 156543.03390625;\n+ for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);\n }\n+ this.units = \"m\";\n+ this.projection = this.projection || \"EPSG:900913\";\n },\n \n /**\n- * Method: getEventInfo\n- * This method allows us to store event information without storing the\n- * actual event. In touch devices (at least), the same event is \n- * modified between touchstart, touchmove, and touchend.\n+ * APIMethod: forwardMercator\n+ * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.\n *\n+ * Parameters:\n+ * lon - {float} \n+ * lat - {float}\n+ * \n * Returns:\n- * {Object} An object with event related info.\n+ * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.\n */\n- getEventInfo: function(evt) {\n- var touches;\n- if (evt.touches) {\n- var len = evt.touches.length;\n- touches = new Array(len);\n- var touch;\n- for (var i = 0; i < len; i++) {\n- touch = evt.touches[i];\n- touches[i] = {\n- clientX: touch.olClientX,\n- clientY: touch.olClientY\n- };\n- }\n- }\n- return {\n- xy: evt.xy,\n- touches: touches\n+ forwardMercator: (function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(lon, lat) {\n+ var point = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ }, gg, sm);\n+ return new OpenLayers.LonLat(point.x, point.y);\n };\n- },\n+ })(),\n \n /**\n- * APIMethod: deactivate\n- * Deactivate the handler.\n+ * APIMethod: inverseMercator\n+ * Given a x,y in Spherical Mercator, return a point in EPSG:4326.\n *\n+ * Parameters:\n+ * x - {float} A map x in Spherical Mercator.\n+ * y - {float} A map y in Spherical Mercator.\n+ * \n * Returns:\n- * {Boolean} The handler was successfully deactivated.\n+ * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- this.down = null;\n- this.first = null;\n- this.last = null;\n- deactivated = true;\n- }\n- return deactivated;\n- },\n+ inverseMercator: (function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(x, y) {\n+ var point = OpenLayers.Projection.transform({\n+ x: x,\n+ y: y\n+ }, sm, gg);\n+ return new OpenLayers.LonLat(point.x, point.y);\n+ };\n+ })()\n \n- CLASS_NAME: \"OpenLayers.Handler.Click\"\n-});\n+};\n /* ======================================================================\n- OpenLayers/Control/Navigation.js\n+ OpenLayers/Layer/EventPane.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control/ZoomBox.js\n- * @requires OpenLayers/Control/DragPan.js\n- * @requires OpenLayers/Handler/MouseWheel.js\n- * @requires OpenLayers/Handler/Click.js\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Util.js\n */\n \n /**\n- * Class: OpenLayers.Control.Navigation\n- * The navigation control handles map browsing with mouse events (dragging,\n- * double-clicking, and scrolling the wheel). Create a new navigation \n- * control with the <OpenLayers.Control.Navigation> control. \n- * \n- * Note that this control is added to the map by default (if no controls \n- * array is sent in the options object to the <OpenLayers.Map> \n- * constructor).\n+ * Class: OpenLayers.Layer.EventPane\n+ * Base class for 3rd party layers, providing a DOM element which isolates\n+ * the 3rd-party layer from mouse events.\n+ * Only used by Google layers.\n+ *\n+ * Automatically instantiated by the Google constructor, and not usually instantiated directly.\n+ *\n+ * Create a new event pane layer with the\n+ * <OpenLayers.Layer.EventPane> constructor.\n * \n- * Inherits:\n- * - <OpenLayers.Control>\n+ * Inherits from:\n+ * - <OpenLayers.Layer>\n */\n-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * Property: dragPan\n- * {<OpenLayers.Control.DragPan>} \n- */\n- dragPan: null,\n+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n \n /**\n- * APIProperty: dragPanOptions\n- * {Object} Options passed to the DragPan control.\n+ * APIProperty: smoothDragPan\n+ * {Boolean} smoothDragPan determines whether non-public/internal API\n+ * methods are used for better performance while dragging EventPane \n+ * layers. When not in sphericalMercator mode, the smoother dragging \n+ * doesn't actually move north/south directly with the number of \n+ * pixels moved, resulting in a slight offset when you drag your mouse \n+ * north south with this option on. If this visual disparity bothers \n+ * you, you should turn this option off, or use spherical mercator. \n+ * Default is on.\n */\n- dragPanOptions: null,\n+ smoothDragPan: true,\n \n /**\n- * Property: pinchZoom\n- * {<OpenLayers.Control.PinchZoom>}\n+ * Property: isBaseLayer\n+ * {Boolean} EventPaned layers are always base layers, by necessity.\n */\n- pinchZoom: null,\n+ isBaseLayer: true,\n \n /**\n- * APIProperty: pinchZoomOptions\n- * {Object} Options passed to the PinchZoom control.\n+ * APIProperty: isFixed\n+ * {Boolean} EventPaned layers are fixed by default.\n */\n- pinchZoomOptions: null,\n+ isFixed: true,\n \n /**\n- * APIProperty: documentDrag\n- * {Boolean} Allow panning of the map by dragging outside map viewport.\n- * Default is false.\n- */\n- documentDrag: false,\n-\n- /** \n- * Property: zoomBox\n- * {<OpenLayers.Control.ZoomBox>}\n+ * Property: pane\n+ * {DOMElement} A reference to the element that controls the events.\n */\n- zoomBox: null,\n+ pane: null,\n \n- /**\n- * APIProperty: zoomBoxEnabled\n- * {Boolean} Whether the user can draw a box to zoom\n- */\n- zoomBoxEnabled: true,\n \n /**\n- * APIProperty: zoomWheelEnabled\n- * {Boolean} Whether the mousewheel should zoom the map\n+ * Property: mapObject\n+ * {Object} This is the object which will be used to load the 3rd party library\n+ * in the case of the google layer, this will be of type GMap, \n+ * in the case of the ve layer, this will be of type VEMap\n */\n- zoomWheelEnabled: true,\n+ mapObject: null,\n \n- /**\n- * Property: mouseWheelOptions\n- * {Object} Options passed to the MouseWheel control (only useful if\n- * <zoomWheelEnabled> is set to true). Default is no options for maps\n- * with fractionalZoom set to true, otherwise\n- * {cumulative: false, interval: 50, maxDelta: 6} \n- */\n- mouseWheelOptions: null,\n \n /**\n- * APIProperty: handleRightClicks\n- * {Boolean} Whether or not to handle right clicks. Default is false.\n+ * Constructor: OpenLayers.Layer.EventPane\n+ * Create a new event pane layer\n+ *\n+ * Parameters:\n+ * name - {String}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- handleRightClicks: false,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (this.pane == null) {\n+ this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\");\n+ }\n+ },\n \n /**\n- * APIProperty: zoomBoxKeyMask\n- * {Integer} <OpenLayers.Handler> key code of the key, which has to be\n- * pressed, while drawing the zoom box with the mouse on the screen. \n- * You should probably set handleRightClicks to true if you use this\n- * with MOD_CTRL, to disable the context menu for machines which use\n- * CTRL-Click as a right click.\n- * Default: <OpenLayers.Handler.MOD_SHIFT>\n+ * APIMethod: destroy\n+ * Deconstruct this layer.\n */\n- zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n+ destroy: function() {\n+ this.mapObject = null;\n+ this.pane = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ },\n \n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n- */\n- autoActivate: true,\n \n /**\n- * Constructor: OpenLayers.Control.Navigation\n- * Create a new navigation control\n- * \n+ * Method: setMap\n+ * Set the map property for the layer. This is done through an accessor\n+ * so that subclasses can override this and take special action once \n+ * they have their map variable set. \n+ *\n * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * the control\n- */\n- initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- },\n-\n- /**\n- * Method: destroy\n- * The destroy method is used to perform any clean up before the control\n- * is dereferenced. Typically this is where event listeners are removed\n- * to prevent memory leaks.\n+ * map - {<OpenLayers.Map>}\n */\n- destroy: function() {\n- this.deactivate();\n-\n- if (this.dragPan) {\n- this.dragPan.destroy();\n- }\n- this.dragPan = null;\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n \n- if (this.zoomBox) {\n- this.zoomBox.destroy();\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n+ this.pane.style.display = this.div.style.display;\n+ this.pane.style.width = \"100%\";\n+ this.pane.style.height = \"100%\";\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.pane.style.background =\n+ \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\";\n }\n- this.zoomBox = null;\n \n- if (this.pinchZoom) {\n- this.pinchZoom.destroy();\n+ if (this.isFixed) {\n+ this.map.viewPortDiv.appendChild(this.pane);\n+ } else {\n+ this.map.layerContainerDiv.appendChild(this.pane);\n }\n- this.pinchZoom = null;\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- },\n+ // once our layer has been added to the map, we can load it\n+ this.loadMapObject();\n \n- /**\n- * Method: activate\n- */\n- activate: function() {\n- this.dragPan.activate();\n- if (this.zoomWheelEnabled) {\n- this.handlers.wheel.activate();\n- }\n- this.handlers.click.activate();\n- if (this.zoomBoxEnabled) {\n- this.zoomBox.activate();\n- }\n- if (this.pinchZoom) {\n- this.pinchZoom.activate();\n+ // if map didn't load, display warning\n+ if (this.mapObject == null) {\n+ this.loadWarningMessage();\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments);\n },\n \n /**\n- * Method: deactivate\n+ * APIMethod: removeMap\n+ * On being removed from the map, we'll like to remove the invisible 'pane'\n+ * div that we added to it on creation. \n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- deactivate: function() {\n- if (this.pinchZoom) {\n- this.pinchZoom.deactivate();\n+ removeMap: function(map) {\n+ if (this.pane && this.pane.parentNode) {\n+ this.pane.parentNode.removeChild(this.pane);\n }\n- this.zoomBox.deactivate();\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.handlers.wheel.deactivate();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments);\n+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments);\n },\n \n /**\n- * Method: draw\n+ * Method: loadWarningMessage\n+ * If we can't load the map lib, then display an error message to the \n+ * user and tell them where to go for help.\n+ * \n+ * This function sets up the layout for the warning message. Each 3rd\n+ * party layer must implement its own getWarningHTML() function to \n+ * provide the actual warning message.\n */\n- draw: function() {\n- // disable right mouse context menu for support of right click events\n- if (this.handleRightClicks) {\n- this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;\n- }\n+ loadWarningMessage: function() {\n \n- var clickCallbacks = {\n- 'click': this.defaultClick,\n- 'dblclick': this.defaultDblClick,\n- 'dblrightclick': this.defaultDblRightClick\n- };\n- var clickOptions = {\n- 'double': true,\n- 'stopDouble': true\n- };\n- this.handlers.click = new OpenLayers.Handler.Click(\n- this, clickCallbacks, clickOptions\n- );\n- this.dragPan = new OpenLayers.Control.DragPan(\n- OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions)\n- );\n- this.zoomBox = new OpenLayers.Control.ZoomBox({\n- map: this.map,\n- keyMask: this.zoomBoxKeyMask\n- });\n- this.dragPan.draw();\n- this.zoomBox.draw();\n- var wheelOptions = this.map.fractionalZoom ? {} : {\n- cumulative: false,\n- interval: 50,\n- maxDelta: 6\n- };\n- this.handlers.wheel = new OpenLayers.Handler.MouseWheel(\n- this, {\n- up: this.wheelUp,\n- down: this.wheelDown\n- },\n- OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)\n- );\n- if (OpenLayers.Control.PinchZoom) {\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(\n- OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions));\n- }\n+ this.div.style.backgroundColor = \"darkblue\";\n+\n+ var viewSize = this.map.getSize();\n+\n+ var msgW = Math.min(viewSize.w, 300);\n+ var msgH = Math.min(viewSize.h, 200);\n+ var size = new OpenLayers.Size(msgW, msgH);\n+\n+ var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n+\n+ var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n+\n+ var div = OpenLayers.Util.createDiv(this.name + \"_warning\",\n+ topLeft,\n+ size,\n+ null,\n+ null,\n+ null,\n+ \"auto\");\n+\n+ div.style.padding = \"7px\";\n+ div.style.backgroundColor = \"yellow\";\n+\n+ div.innerHTML = this.getWarningHTML();\n+ this.div.appendChild(div);\n },\n \n- /**\n- * Method: defaultClick\n- *\n- * Parameters:\n- * evt - {Event}\n+ /** \n+ * Method: getWarningHTML\n+ * To be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {String} String with information on why layer is broken, how to get\n+ * it working.\n */\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut();\n- }\n+ getWarningHTML: function() {\n+ //should be implemented by subclasses\n+ return \"\";\n },\n \n /**\n- * Method: defaultDblClick \n- * \n+ * Method: display\n+ * Set the display on the pane\n+ *\n * Parameters:\n- * evt - {Event} \n+ * display - {Boolean}\n */\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy);\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ this.pane.style.display = this.div.style.display;\n },\n \n /**\n- * Method: defaultDblRightClick \n+ * Method: setZIndex\n+ * Set the z-index order for the pane.\n * \n * Parameters:\n- * evt - {Event} \n+ * zIndex - {int}\n */\n- defaultDblRightClick: function(evt) {\n- this.map.zoomTo(this.map.zoom - 1, evt.xy);\n+ setZIndex: function(zIndex) {\n+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n },\n \n /**\n- * Method: wheelChange \n+ * Method: moveByPx\n+ * Move the layer based on pixel vector. To be implemented by subclasses.\n *\n * Parameters:\n- * evt - {Event}\n- * deltaZ - {Integer}\n+ * dx - {Number} The x coord of the displacement vector.\n+ * dy - {Number} The y coord of the displacement vector.\n */\n- wheelChange: function(evt, deltaZ) {\n- if (!this.map.fractionalZoom) {\n- deltaZ = Math.round(deltaZ);\n- }\n- var currentZoom = this.map.getZoom(),\n- newZoom = currentZoom + deltaZ;\n- newZoom = Math.max(newZoom, 0);\n- newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n- if (newZoom === currentZoom) {\n- return;\n+ moveByPx: function(dx, dy) {\n+ OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n+\n+ if (this.dragPanMapObject) {\n+ this.dragPanMapObject(dx, -dy);\n+ } else {\n+ this.moveTo(this.map.getCachedCenter());\n }\n- this.map.zoomTo(newZoom, evt.xy);\n },\n \n- /** \n- * Method: wheelUp\n- * User spun scroll wheel up\n+ /**\n+ * Method: moveTo\n+ * Handle calls to move the layer.\n * \n * Parameters:\n- * evt - {Event}\n- * delta - {Integer}\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean}\n+ * dragging - {Boolean}\n */\n- wheelUp: function(evt, delta) {\n- this.wheelChange(evt, delta || 1);\n- },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n \n- /** \n- * Method: wheelDown\n- * User spun scroll wheel down\n- * \n- * Parameters:\n- * evt - {Event}\n- * delta - {Integer}\n- */\n- wheelDown: function(evt, delta) {\n- this.wheelChange(evt, delta || -1);\n- },\n+ if (this.mapObject != null) {\n \n- /**\n- * Method: disableZoomBox\n- */\n- disableZoomBox: function() {\n- this.zoomBoxEnabled = false;\n- this.zoomBox.deactivate();\n- },\n+ var newCenter = this.map.getCenter();\n+ var newZoom = this.map.getZoom();\n \n- /**\n- * Method: enableZoomBox\n- */\n- enableZoomBox: function() {\n- this.zoomBoxEnabled = true;\n- if (this.active) {\n- this.zoomBox.activate();\n- }\n- },\n+ if (newCenter != null) {\n \n- /**\n- * Method: disableZoomWheel\n- */\n+ var moOldCenter = this.getMapObjectCenter();\n+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n \n- disableZoomWheel: function() {\n- this.zoomWheelEnabled = false;\n- this.handlers.wheel.deactivate();\n- },\n+ var moOldZoom = this.getMapObjectZoom();\n+ var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n \n- /**\n- * Method: enableZoomWheel\n- */\n+ if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {\n \n- enableZoomWheel: function() {\n- this.zoomWheelEnabled = true;\n- if (this.active) {\n- this.handlers.wheel.activate();\n+ if (!zoomChanged && oldCenter && this.dragPanMapObject &&\n+ this.smoothDragPan) {\n+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n+ this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y);\n+ } else {\n+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n+ this.setMapObjectCenter(center, zoom, dragging);\n+ }\n+ }\n+ }\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Navigation\"\n-});\n-/* ======================================================================\n- OpenLayers/Events/buttonclick.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Events.js\n- */\n-\n-/**\n- * Class: OpenLayers.Events.buttonclick\n- * Extension event type for handling buttons on top of a dom element. This\n- * event type fires \"buttonclick\" on its <target> when a button was\n- * clicked. Buttons are detected by the \"olButton\" class.\n- *\n- * This event type makes sure that button clicks do not interfere with other\n- * events that are registered on the same <element>.\n- *\n- * Event types provided by this extension:\n- * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n- * object with a *buttonElement* property referencing the dom element of\n- * the clicked button, and an *buttonXY* property with the click position\n- * relative to the button.\n- */\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n-\n- /**\n- * Property: target\n- * {<OpenLayers.Events>} The events instance that the buttonclick event will\n- * be triggered on.\n- */\n- target: null,\n-\n- /**\n- * Property: events\n- * {Array} Events to observe and conditionally stop from propagating when\n- * an element with the olButton class (or its olAlphaImg child) is\n- * clicked.\n- */\n- events: [\n- 'mousedown', 'mouseup', 'click', 'dblclick',\n- 'touchstart', 'touchmove', 'touchend', 'keydown'\n- ],\n-\n- /**\n- * Property: startRegEx\n- * {RegExp} Regular expression to test Event.type for events that start\n- * a buttonclick sequence.\n- */\n- startRegEx: /^mousedown|touchstart$/,\n \n- /**\n- * Property: cancelRegEx\n- * {RegExp} Regular expression to test Event.type for events that cancel\n- * a buttonclick sequence.\n- */\n- cancelRegEx: /^touchmove$/,\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /********************************************************/\n \n /**\n- * Property: completeRegEx\n- * {RegExp} Regular expression to test Event.type for events that complete\n- * a buttonclick sequence.\n+ * Method: getLonLatFromViewPortPx\n+ * Get a map location from a pixel location\n+ * \n+ * Parameters:\n+ * viewPortPx - {<OpenLayers.Pixel>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view\n+ * port OpenLayers.Pixel, translated into lon/lat by map lib\n+ * If the map lib is not loaded or not centered, returns null\n */\n- completeRegEx: /^mouseup|touchend$/,\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if ((this.mapObject != null) &&\n+ (this.getMapObjectCenter() != null)) {\n+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);\n+ }\n+ return lonlat;\n+ },\n \n- /**\n- * Property: startEvt\n- * {Event} The event that started the click sequence\n- */\n \n /**\n- * Constructor: OpenLayers.Events.buttonclick\n- * Construct a buttonclick event type. Applications are not supposed to\n- * create instances of this class - they are created on demand by\n- * <OpenLayers.Events> instances.\n+ * Method: getViewPortPxFromLonLat\n+ * Get a pixel location from a map location\n *\n * Parameters:\n- * target - {<OpenLayers.Events>} The events instance that the buttonclick\n- * event will be triggered on.\n+ * lonlat - {<OpenLayers.LonLat>}\n+ *\n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in\n+ * OpenLayers.LonLat, translated into view port pixels by map lib\n+ * If map lib is not loaded or not centered, returns null\n */\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- });\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var viewPortPx = null;\n+ if ((this.mapObject != null) &&\n+ (this.getMapObjectCenter() != null)) {\n+\n+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n+\n+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);\n }\n+ return viewPortPx;\n },\n \n+ /********************************************************/\n+ /* */\n+ /* Translation Functions */\n+ /* */\n+ /* The following functions translate Map Object and */\n+ /* OL formats for Pixel, LonLat */\n+ /* */\n+ /********************************************************/\n+\n+ //\n+ // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat\n+ //\n+\n /**\n- * Method: destroy\n+ * Method: getOLLonLatFromMapObjectLonLat\n+ * Get an OL style map location from a 3rd party style map location\n+ *\n+ * Parameters\n+ * moLonLat - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in \n+ * MapObject LonLat\n+ * Returns null if null value is passed in\n */\n- destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick);\n+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n+ var olLonLat = null;\n+ if (moLonLat != null) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ olLonLat = new OpenLayers.LonLat(lon, lat);\n }\n- delete this.target;\n+ return olLonLat;\n },\n \n /**\n- * Method: getPressedButton\n- * Get the pressed button, if any. Returns undefined if no button\n- * was pressed.\n- *\n- * Arguments:\n- * element - {DOMElement} The event target.\n+ * Method: getMapObjectLonLatFromOLLonLat\n+ * Get a 3rd party map location from an OL map location.\n *\n+ * Parameters:\n+ * olLonLat - {<OpenLayers.LonLat>}\n+ * \n * Returns:\n- * {DOMElement} The button element, or undefined.\n+ * {Object} A MapObject LonLat, translated from the passed in \n+ * OpenLayers.LonLat\n+ * Returns null if null value is passed in\n */\n- getPressedButton: function(element) {\n- var depth = 3, // limit the search depth\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- // hit!\n- button = element;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return button;\n+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n+ var moLatLng = null;\n+ if (olLonLat != null) {\n+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,\n+ olLonLat.lat);\n+ }\n+ return moLatLng;\n },\n \n+\n+ //\n+ // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel\n+ //\n+\n /**\n- * Method: ignore\n- * Check for event target elements that should be ignored by OpenLayers.\n+ * Method: getOLPixelFromMapObjectPixel\n+ * Get an OL pixel location from a 3rd party pixel location.\n *\n * Parameters:\n- * element - {DOMElement} The event target.\n+ * moPixel - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in \n+ * MapObject Pixel\n+ * Returns null if null value is passed in\n */\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === 'a') {\n- ignore = true;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return ignore;\n+ getOLPixelFromMapObjectPixel: function(moPixel) {\n+ var olPixel = null;\n+ if (moPixel != null) {\n+ var x = this.getXFromMapObjectPixel(moPixel);\n+ var y = this.getYFromMapObjectPixel(moPixel);\n+ olPixel = new OpenLayers.Pixel(x, y);\n+ }\n+ return olPixel;\n },\n \n /**\n- * Method: buttonClick\n- * Check if a button was clicked, and fire the buttonclick event\n+ * Method: getMapObjectPixelFromOLPixel\n+ * Get a 3rd party pixel location from an OL pixel location\n *\n * Parameters:\n- * evt - {Event}\n+ * olPixel - {<OpenLayers.Pixel>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Pixel, translated from the passed in \n+ * OpenLayers.Pixel\n+ * Returns null if null value is passed in\n */\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- // was a button pressed?\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- break;\n- }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n-\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- });\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt;\n- }\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt;\n- }\n+ getMapObjectPixelFromOLPixel: function(olPixel) {\n+ var moPixel = null;\n+ if (olPixel != null) {\n+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);\n }\n- return propagate;\n- }\n+ return moPixel;\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n });\n /* ======================================================================\n- OpenLayers/Control/Panel.js\n+ OpenLayers/Layer/FixedZoomLevels.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Layer.js\n */\n \n /**\n- * Class: OpenLayers.Control.Panel\n- * The Panel control is a container for other controls. With it toolbars\n- * may be composed.\n+ * Class: OpenLayers.Layer.FixedZoomLevels\n+ * Some Layers will already have established zoom levels (like google \n+ * or ve). Instead of trying to determine them and populate a resolutions[]\n+ * Array with those values, we will hijack the resolution functionality\n+ * here.\n+ * \n+ * When you subclass FixedZoomLevels: \n+ * \n+ * The initResolutions() call gets nullified, meaning no resolutions[] array \n+ * is set up. Which would be a big problem getResolution() in Layer, since \n+ * it merely takes map.zoom and indexes into resolutions[]... but....\n+ * \n+ * The getResolution() call is also overridden. Instead of using the \n+ * resolutions[] array, we simply calculate the current resolution based\n+ * on the current extent and the current map size. But how will we be able\n+ * to calculate the current extent without knowing the resolution...?\n+ * \n+ * The getExtent() function is also overridden. Instead of calculating extent\n+ * based on the center point and the current resolution, we instead \n+ * calculate the extent by getting the lonlats at the top-left and \n+ * bottom-right by using the getLonLatFromViewPortPx() translation function,\n+ * taken from the pixel locations (0,0) and the size of the map. But how \n+ * will we be able to do lonlat-px translation without resolution....?\n+ * \n+ * The getZoomForResolution() method is overridden. Instead of indexing into\n+ * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in\n+ * the desired resolution. With this extent, we then call getZoomForExtent() \n+ * \n+ * \n+ * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, \n+ * it is your responsibility to provide the following three functions:\n+ * \n+ * - getLonLatFromViewPortPx\n+ * - getViewPortPxFromLonLat\n+ * - getZoomForExtent\n+ * \n+ * ...those three functions should generally be provided by any reasonable \n+ * API that you might be working from.\n *\n- * Inherits from:\n- * - <OpenLayers.Control>\n */\n-OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n- /**\n- * Property: controls\n- * {Array(<OpenLayers.Control>)}\n- */\n- controls: null,\n-\n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n- */\n- autoActivate: true,\n-\n- /** \n- * APIProperty: defaultControl\n- * {<OpenLayers.Control>} The control which is activated when the control is\n- * activated (turned on), which also happens at instantiation.\n- * If <saveState> is true, <defaultControl> will be nullified after the\n- * first activation of the panel.\n- */\n- defaultControl: null,\n+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n \n- /**\n- * APIProperty: saveState\n- * {Boolean} If set to true, the active state of this panel's controls will\n- * be stored on panel deactivation, and restored on reactivation. Default\n- * is false.\n- */\n- saveState: false,\n+ /********************************************************/\n+ /* */\n+ /* Baselayer Functions */\n+ /* */\n+ /* The following functions must all be implemented */\n+ /* by all base layers */\n+ /* */\n+ /********************************************************/\n \n /**\n- * APIProperty: allowDepress\n- * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n- * be deactivated by clicking the icon that represents them. Default \n- * is false.\n+ * Constructor: OpenLayers.Layer.FixedZoomLevels\n+ * Create a new fixed zoom levels layer.\n */\n- allowDepress: false,\n+ initialize: function() {\n+ //this class is only just to add the following functions... \n+ // nothing to actually do here... but it is probably a good\n+ // idea to have layers that use these functions call this \n+ // inititalize() anyways, in case at some point we decide we \n+ // do want to put some functionality or state in here. \n+ },\n \n /**\n- * Property: activeState\n- * {Object} stores the active state of this panel's controls.\n+ * Method: initResolutions\n+ * Populate the resolutions array\n */\n- activeState: null,\n+ initResolutions: function() {\n \n- /**\n- * Constructor: OpenLayers.Control.Panel\n- * Create a new control panel.\n- *\n- * Each control in the panel is represented by an icon. When clicking \n- * on an icon, the <activateControl> method is called.\n- *\n- * Specific properties for controls on a panel:\n- * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n- * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n- * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n- * title - {string} Text displayed when mouse is over the icon that \n- * represents the control. \n- *\n- * The <OpenLayers.Control.type> of a control determines the behavior when\n- * clicking its icon:\n- * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n- * controls of this type in the same panel are deactivated. This is\n- * the default type.\n- * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n- * toggled.\n- * <OpenLayers.Control.TYPE_BUTTON> - The\n- * <OpenLayers.Control.Button.trigger> method of the control is called,\n- * but its active state is not changed.\n- *\n- * If a control is <OpenLayers.Control.active>, it will be drawn with the\n- * olControl[Name]ItemActive class, otherwise with the\n- * olControl[Name]ItemInactive class.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n- */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.controls = [];\n- this.activeState = {};\n- },\n+ var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];\n \n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n- ctl = this.controls[i];\n- if (ctl.events) {\n- ctl.events.un({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n- }\n- ctl.panel_div = null;\n+ for (var i = 0, len = props.length; i < len; i++) {\n+ var property = props[i];\n+ this[property] = (this.options[property] != null) ?\n+ this.options[property] :\n+ this.map[property];\n }\n- this.activeState = null;\n- },\n \n- /**\n- * APIMethod: activate\n- */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- if (control === this.defaultControl ||\n- (this.saveState && this.activeState[control.id])) {\n- control.activate();\n- }\n- }\n- if (this.saveState === true) {\n- this.defaultControl = null;\n- }\n- this.redraw();\n- return true;\n- } else {\n- return false;\n+ if ((this.minZoomLevel == null) ||\n+ (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) {\n+ this.minZoomLevel = this.MIN_ZOOM_LEVEL;\n }\n- },\n \n- /**\n- * APIMethod: deactivate\n- */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- this.activeState[control.id] = control.deactivate();\n- }\n- this.redraw();\n- return true;\n+ //\n+ // At this point, we know what the minimum desired zoom level is, and\n+ // we must calculate the total number of zoom levels. \n+ // \n+ // Because we allow for the setting of either the 'numZoomLevels'\n+ // or the 'maxZoomLevel' properties... on either the layer or the \n+ // map, we have to define some rules to see which we take into\n+ // account first in this calculation. \n+ //\n+ // The following is the precedence list for these properties:\n+ // \n+ // (1) numZoomLevels set on layer\n+ // (2) maxZoomLevel set on layer\n+ // (3) numZoomLevels set on map\n+ // (4) maxZoomLevel set on map*\n+ // (5) none of the above*\n+ //\n+ // *Note that options (4) and (5) are only possible if the user \n+ // _explicitly_ sets the 'numZoomLevels' property on the map to \n+ // null, since it is set by default to 16. \n+ //\n+\n+ //\n+ // Note to future: In 3.0, I think we should remove the default \n+ // value of 16 for map.numZoomLevels. Rather, I think that value \n+ // should be set as a default on the Layer.WMS class. If someone\n+ // creates a 3rd party layer and does not specify any 'minZoomLevel', \n+ // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly \n+ // specified any of those on the map object either.. then I think\n+ // it is fair to say that s/he wants all the zoom levels available.\n+ // \n+ // By making map.numZoomLevels *null* by default, that will be the \n+ // case. As it is, I don't feel comfortable changing that right now\n+ // as it would be a glaring API change and actually would probably\n+ // break many peoples' codes. \n+ //\n+\n+ //the number of zoom levels we'd like to have.\n+ var desiredZoomLevels;\n+\n+ //this is the maximum number of zoom levels the layer will allow, \n+ // given the specified starting minimum zoom level.\n+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n+\n+ if (((this.options.numZoomLevels == null) &&\n+ (this.options.maxZoomLevel != null)) // (2)\n+ ||\n+ ((this.numZoomLevels == null) &&\n+ (this.maxZoomLevel != null)) // (4)\n+ ) {\n+ //calculate based on specified maxZoomLevel (on layer or map)\n+ desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;\n } else {\n- return false;\n+ //calculate based on specified numZoomLevels (on layer or map)\n+ // this covers cases (1) and (3)\n+ desiredZoomLevels = this.numZoomLevels;\n }\n- },\n \n- /**\n- * Method: draw\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ if (desiredZoomLevels != null) {\n+ //Now that we know what we would *like* the number of zoom levels\n+ // to be, based on layer or map options, we have to make sure that\n+ // it does not conflict with the actual limit, as specified by \n+ // the constants on the layer itself (and calculated into the\n+ // 'limitZoomLevels' variable). \n+ this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);\n } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was \n+ // set on either the layer or the map. So we just use the \n+ // maximum limit as calculated by the layer's constants.\n+ this.numZoomLevels = limitZoomLevels;\n }\n- this.addControlsToMap(this.controls);\n- return this.div;\n- },\n \n- /**\n- * Method: redraw\n- */\n- redraw: function() {\n- for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n- this.div.removeChild(this.div.childNodes[i]);\n- }\n- this.div.innerHTML = \"\";\n- if (this.active) {\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.div.appendChild(this.controls[i].panel_div);\n+ //now that the 'numZoomLevels' is appropriately, safely set, \n+ // we go back and re-calculate the 'maxZoomLevel'.\n+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n+\n+ if (this.RESOLUTIONS != null) {\n+ var resolutionsIndex = 0;\n+ this.resolutions = [];\n+ for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];\n }\n+ this.maxResolution = this.resolutions[0];\n+ this.minResolution = this.resolutions[this.resolutions.length - 1];\n }\n },\n \n /**\n- * APIMethod: activateControl\n- * This method is called when the user click on the icon representing a \n- * control in the panel.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>}\n+ * APIMethod: getResolution\n+ * Get the current map resolution\n+ * \n+ * Returns:\n+ * {Float} Map units per Pixel\n */\n- activateControl: function(control) {\n- if (!this.active) {\n- return false;\n- }\n- if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n- control.trigger();\n- return;\n- }\n- if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n- if (control.active) {\n- control.deactivate();\n- } else {\n- control.activate();\n- }\n- return;\n- }\n- if (this.allowDepress && control.active) {\n- control.deactivate();\n+ getResolution: function() {\n+\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);\n } else {\n- var c;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- c = this.controls[i];\n- if (c != control &&\n- (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n- c.deactivate();\n- }\n+ var resolution = null;\n+\n+ var viewSize = this.map.getSize();\n+ var extent = this.getExtent();\n+\n+ if ((viewSize != null) && (extent != null)) {\n+ resolution = Math.max(extent.getWidth() / viewSize.w,\n+ extent.getHeight() / viewSize.h);\n }\n- control.activate();\n+ return resolution;\n }\n },\n \n /**\n- * APIMethod: addControls\n- * To build a toolbar, you add a set of controls to it. addControls\n- * lets you add a single control or a list of controls to the \n- * Control Panel.\n- *\n- * Parameters:\n- * controls - {<OpenLayers.Control>} Controls to add in the panel.\n+ * APIMethod: getExtent\n+ * Calculates using px-> lonlat translation functions on tl and br \n+ * corners of viewport\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat \n+ * bounds of the current viewPort.\n */\n- addControls: function(controls) {\n- if (!(OpenLayers.Util.isArray(controls))) {\n- controls = [controls];\n- }\n- this.controls = this.controls.concat(controls);\n-\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var control = controls[i],\n- element = this.createControlMarkup(control);\n- OpenLayers.Element.addClass(element,\n- control.displayClass + \"ItemInactive\");\n- OpenLayers.Element.addClass(element, \"olButton\");\n- if (control.title != \"\" && !element.title) {\n- element.title = control.title;\n- }\n- control.panel_div = element;\n- }\n+ getExtent: function() {\n+ var size = this.map.getSize();\n+ var tl = this.getLonLatFromViewPortPx({\n+ x: 0,\n+ y: 0\n+ });\n+ var br = this.getLonLatFromViewPortPx({\n+ x: size.w,\n+ y: size.h\n+ });\n \n- if (this.map) { // map.addControl() has already been called on the panel\n- this.addControlsToMap(controls);\n- this.redraw();\n+ if ((tl != null) && (br != null)) {\n+ return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);\n+ } else {\n+ return null;\n }\n },\n \n /**\n- * APIMethod: createControlMarkup\n- * This function just creates a div for the control. If specific HTML\n- * markup is needed this function can be overridden in specific classes,\n- * or at panel instantiation time:\n- *\n- * Example:\n- * (code)\n- * var panel = new OpenLayers.Control.Panel({\n- * defaultControl: control,\n- * // ovverride createControlMarkup to create actual buttons\n- * // including texts wrapped into span elements.\n- * createControlMarkup: function(control) {\n- * var button = document.createElement('button'),\n- * span = document.createElement('span');\n- * if (control.text) {\n- * span.innerHTML = control.text;\n- * }\n- * return button;\n- * }\n- * });\n- * (end)\n+ * Method: getZoomForResolution\n+ * Get the zoom level for a given resolution\n *\n * Parameters:\n- * control - {<OpenLayers.Control>} The control to create the HTML\n- * markup for.\n+ * resolution - {Float}\n *\n * Returns:\n- * {DOMElement} The markup.\n+ * {Integer} A suitable zoom level for the specified resolution.\n+ * If no baselayer is set, returns null.\n */\n- createControlMarkup: function(control) {\n- return document.createElement(\"div\");\n- },\n+ getZoomForResolution: function(resolution) {\n \n- /**\n- * Method: addControlsToMap\n- * Only for internal use in draw() and addControls() methods.\n- *\n- * Parameters:\n- * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n- */\n- addControlsToMap: function(controls) {\n- var control;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- control = controls[i];\n- if (control.autoActivate === true) {\n- control.autoActivate = false;\n- this.map.addControl(control);\n- control.autoActivate = true;\n- } else {\n- this.map.addControl(control);\n- control.deactivate();\n- }\n- control.events.on({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);\n+ } else {\n+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n+ return this.getZoomForExtent(extent);\n }\n },\n \n- /**\n- * Method: iconOn\n- * Internal use, for use only with \"controls[i].events.on/un\".\n- */\n- iconOn: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n- d.className = d.className.replace(re, \"$1Active\");\n- },\n \n- /**\n- * Method: iconOff\n- * Internal use, for use only with \"controls[i].events.on/un\".\n- */\n- iconOff: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n- d.className = d.className.replace(re, \"$1Inactive\");\n- },\n \n- /**\n- * Method: onButtonClick\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- onButtonClick: function(evt) {\n- var controls = this.controls,\n- button = evt.buttonElement;\n- for (var i = controls.length - 1; i >= 0; --i) {\n- if (controls[i].panel_div === button) {\n- this.activateControl(controls[i]);\n- break;\n- }\n- }\n- },\n \n- /**\n- * APIMethod: getControlsBy\n- * Get a list of controls with properties matching the given criteria.\n- *\n- * Parameters:\n- * property - {String} A control property to be matched.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(control[property]) evaluates to true, the control will be\n- * included in the array returned. If no controls are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n- * An empty array is returned if no matches are found.\n- */\n- getControlsBy: function(property, match) {\n- var test = (typeof match.test == \"function\");\n- var found = OpenLayers.Array.filter(this.controls, function(item) {\n- return item[property] == match || (test && match.test(item[property]));\n- });\n- return found;\n- },\n+ /********************************************************/\n+ /* */\n+ /* Translation Functions */\n+ /* */\n+ /* The following functions translate GMaps and OL */\n+ /* formats for Pixel, LonLat, Bounds, and Zoom */\n+ /* */\n+ /********************************************************/\n+\n+\n+ //\n+ // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom\n+ //\n \n /**\n- * APIMethod: getControlsByName\n- * Get a list of contorls with names matching the given name.\n+ * Method: getOLZoomFromMapObjectZoom\n+ * Get the OL zoom index from the map object zoom level\n *\n * Parameters:\n- * match - {String | Object} A control name. The name can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * name.test(control.name) evaluates to true, the control will be included\n- * in the list of controls returned. If no controls are found, an empty\n- * array is returned.\n- *\n+ * moZoom - {Integer}\n+ * \n * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n- * An empty array is returned if no matches are found.\n+ * {Integer} An OpenLayers Zoom level, translated from the passed in zoom\n+ * Returns null if null value is passed in\n */\n- getControlsByName: function(match) {\n- return this.getControlsBy(\"name\", match);\n+ getOLZoomFromMapObjectZoom: function(moZoom) {\n+ var zoom = null;\n+ if (moZoom != null) {\n+ zoom = moZoom - this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.map.baseLayer.getZoomForResolution(\n+ this.getResolutionForZoom(zoom)\n+ );\n+ }\n+ }\n+ return zoom;\n },\n \n /**\n- * APIMethod: getControlsByClass\n- * Get a list of controls of a given type (CLASS_NAME).\n+ * Method: getMapObjectZoomFromOLZoom\n+ * Get the map object zoom level from the OL zoom level\n *\n * Parameters:\n- * match - {String | Object} A control class name. The type can also be a\n- * regular expression literal or object. In addition, it can be any\n- * object with a method named test. For reqular expressions or other,\n- * if type.test(control.CLASS_NAME) evaluates to true, the control will\n- * be included in the list of controls returned. If no controls are\n- * found, an empty array is returned.\n- *\n+ * olZoom - {Integer}\n+ * \n * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n- * An empty array is returned if no matches are found.\n+ * {Integer} A MapObject level, translated from the passed in olZoom\n+ * Returns null if null value is passed in\n */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\n+ getMapObjectZoomFromOLZoom: function(olZoom) {\n+ var zoom = null;\n+ if (olZoom != null) {\n+ zoom = olZoom + this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.getZoomForResolution(\n+ this.map.baseLayer.getResolutionForZoom(zoom)\n+ );\n+ }\n+ }\n+ return zoom;\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Panel\"\n+ CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n });\n \n /* ======================================================================\n- OpenLayers/Control/Attribution.js\n+ OpenLayers/Layer/Google.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Layer/SphericalMercator.js\n+ * @requires OpenLayers/Layer/EventPane.js\n+ * @requires OpenLayers/Layer/FixedZoomLevels.js\n+ * @requires OpenLayers/Lang.js\n */\n \n /**\n- * Class: OpenLayers.Control.Attribution\n- * The attribution control adds attribution from layers to the map display. \n- * It uses 'attribution' property of each layer.\n- *\n+ * Class: OpenLayers.Layer.Google\n+ * \n+ * Provides a wrapper for Google's Maps API\n+ * Normally the Terms of Use for this API do not allow wrapping, but Google\n+ * have provided written consent to OpenLayers for this - see email in \n+ * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html\n+ * \n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Layer.SphericalMercator>\n+ * - <OpenLayers.Layer.EventPane>\n+ * - <OpenLayers.Layer.FixedZoomLevels>\n */\n-OpenLayers.Control.Attribution =\n- OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Layer.Google = OpenLayers.Class(\n+ OpenLayers.Layer.EventPane,\n+ OpenLayers.Layer.FixedZoomLevels, {\n+\n+ /** \n+ * Constant: MIN_ZOOM_LEVEL\n+ * {Integer} 0 \n+ */\n+ MIN_ZOOM_LEVEL: 0,\n+\n+ /** \n+ * Constant: MAX_ZOOM_LEVEL\n+ * {Integer} 21\n+ */\n+ MAX_ZOOM_LEVEL: 21,\n+\n+ /** \n+ * Constant: RESOLUTIONS\n+ * {Array(Float)} Hardcode these resolutions so that they are more closely\n+ * tied with the standard wms projection\n+ */\n+ RESOLUTIONS: [\n+ 1.40625,\n+ 0.703125,\n+ 0.3515625,\n+ 0.17578125,\n+ 0.087890625,\n+ 0.0439453125,\n+ 0.02197265625,\n+ 0.010986328125,\n+ 0.0054931640625,\n+ 0.00274658203125,\n+ 0.001373291015625,\n+ 0.0006866455078125,\n+ 0.00034332275390625,\n+ 0.000171661376953125,\n+ 0.0000858306884765625,\n+ 0.00004291534423828125,\n+ 0.00002145767211914062,\n+ 0.00001072883605957031,\n+ 0.00000536441802978515,\n+ 0.00000268220901489257,\n+ 0.0000013411045074462891,\n+ 0.00000067055225372314453\n+ ],\n \n /**\n- * APIProperty: separator\n- * {String} String used to separate layers.\n+ * APIProperty: type\n+ * {GMapType}\n */\n- separator: \", \",\n+ type: null,\n \n /**\n- * APIProperty: template\n- * {String} Template for the attribution. This has to include the substring\n- * \"${layers}\", which will be replaced by the layer specific\n- * attributions, separated by <separator>. The default is \"${layers}\".\n+ * APIProperty: wrapDateLine\n+ * {Boolean} Allow user to pan forever east/west. Default is true. \n+ * Setting this to false only restricts panning if \n+ * <sphericalMercator> is true. \n */\n- template: \"${layers}\",\n+ wrapDateLine: true,\n \n /**\n- * Constructor: OpenLayers.Control.Attribution \n+ * APIProperty: sphericalMercator\n+ * {Boolean} Should the map act as a mercator-projected map? This will\n+ * cause all interactions with the map to be in the actual map \n+ * projection, which allows support for vector drawing, overlaying \n+ * other maps, etc. \n+ */\n+ sphericalMercator: false,\n+\n+ /**\n+ * Property: version\n+ * {Number} The version of the Google Maps API\n+ */\n+ version: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Layer.Google\n * \n * Parameters:\n- * options - {Object} Options for control.\n+ * name - {String} A name for the layer.\n+ * options - {Object} An optional object whose properties will be set\n+ * on the layer.\n+ */\n+ initialize: function(name, options) {\n+ options = options || {};\n+ if (!options.version) {\n+ options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\";\n+ }\n+ var mixin = OpenLayers.Layer.Google[\"v\" +\n+ options.version.replace(/\\./g, \"_\")];\n+ if (mixin) {\n+ OpenLayers.Util.applyDefaults(options, mixin);\n+ } else {\n+ throw \"Unsupported Google Maps API version: \" + options.version;\n+ }\n+\n+ OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n+ if (options.maxExtent) {\n+ options.maxExtent = options.maxExtent.clone();\n+ }\n+\n+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this,\n+ [name, options]);\n+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,\n+ [name, options]);\n+\n+ if (this.sphericalMercator) {\n+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n+ this.initMercatorParameters();\n+ }\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Google>} An exact clone of this layer\n+ */\n+ clone: function() {\n+ /**\n+ * This method isn't intended to be called by a subclass and it\n+ * doesn't call the same method on the superclass. We don't call\n+ * the super's clone because we don't want properties that are set\n+ * on this layer after initialize (i.e. this.mapObject etc.).\n+ */\n+ return new OpenLayers.Layer.Google(\n+ this.name, this.getOptions()\n+ );\n+ },\n+\n+ /**\n+ * APIMethod: setVisibility\n+ * Set the visibility flag for the layer and hide/show & redraw \n+ * accordingly. Fire event unless otherwise specified\n+ * \n+ * Note that visibility is no longer simply whether or not the layer's\n+ * style.display is set to \"block\". Now we store a 'visibility' state \n+ * property on the layer class, this allows us to remember whether or \n+ * not we *desire* for a layer to be visible. In the case where the \n+ * map's resolution is out of the layer's range, this desire may be \n+ * subverted.\n+ * \n+ * Parameters:\n+ * visible - {Boolean} Display the layer (if in range)\n */\n+ setVisibility: function(visible) {\n+ // sharing a map container, opacity has to be set per layer\n+ var opacity = this.opacity == null ? 1 : this.opacity;\n+ OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n+ this.setOpacity(opacity);\n+ },\n \n /** \n- * Method: destroy\n- * Destroy control.\n+ * APIMethod: display\n+ * Hide or show the Layer\n+ * \n+ * Parameters:\n+ * visible - {Boolean}\n */\n- destroy: function() {\n- this.map.events.un({\n- \"removelayer\": this.updateAttribution,\n- \"addlayer\": this.updateAttribution,\n- \"changelayer\": this.updateAttribution,\n- \"changebaselayer\": this.updateAttribution,\n- scope: this\n- });\n+ display: function(visible) {\n+ if (!this._dragging) {\n+ this.setGMapVisibility(visible);\n+ }\n+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);\n+ },\n \n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ /**\n+ * Method: moveTo\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n+ * do some init work in that case.\n+ * dragging - {Boolean}\n+ */\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ this._dragging = dragging;\n+ OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n+ delete this._dragging;\n },\n \n /**\n- * Method: draw\n- * Initialize control.\n+ * APIMethod: setOpacity\n+ * Sets the opacity for the entire layer (all images)\n * \n- * Returns: \n- * {DOMElement} A reference to the DIV DOMElement containing the control\n+ * Parameters:\n+ * opacity - {Float}\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ setOpacity: function(opacity) {\n+ if (opacity !== this.opacity) {\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ });\n+ }\n+ this.opacity = opacity;\n+ }\n+ // Though this layer's opacity may not change, we're sharing a container\n+ // and need to update the opacity for the entire container.\n+ if (this.getVisibility()) {\n+ var container = this.getMapContainer();\n+ OpenLayers.Util.modifyDOMElement(\n+ container, null, null, null, null, null, null, opacity\n+ );\n+ }\n+ },\n \n- this.map.events.on({\n- 'changebaselayer': this.updateAttribution,\n- 'changelayer': this.updateAttribution,\n- 'addlayer': this.updateAttribution,\n- 'removelayer': this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n+ /**\n+ * APIMethod: destroy\n+ * Clean up this layer.\n+ */\n+ destroy: function() {\n+ /**\n+ * We have to override this method because the event pane destroy\n+ * deletes the mapObject reference before removing this layer from\n+ * the map.\n+ */\n+ if (this.map) {\n+ this.setGMapVisibility(false);\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache && cache.count <= 1) {\n+ this.removeGMapElements();\n+ }\n+ }\n+ OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);\n+ },\n \n- return this.div;\n+ /**\n+ * Method: removeGMapElements\n+ * Remove all elements added to the dom. This should only be called if\n+ * this is the last of the Google layers for the given map.\n+ */\n+ removeGMapElements: function() {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // remove shared elements from dom\n+ var container = this.mapObject && this.getMapContainer();\n+ if (container && container.parentNode) {\n+ container.parentNode.removeChild(container);\n+ }\n+ var termsOfUse = cache.termsOfUse;\n+ if (termsOfUse && termsOfUse.parentNode) {\n+ termsOfUse.parentNode.removeChild(termsOfUse);\n+ }\n+ var poweredBy = cache.poweredBy;\n+ if (poweredBy && poweredBy.parentNode) {\n+ poweredBy.parentNode.removeChild(poweredBy);\n+ }\n+ if (this.mapObject && window.google && google.maps &&\n+ google.maps.event && google.maps.event.clearListeners) {\n+ google.maps.event.clearListeners(this.mapObject, 'tilesloaded');\n+ }\n+ }\n },\n \n /**\n- * Method: updateAttribution\n- * Update attribution string.\n+ * APIMethod: removeMap\n+ * On being removed from the map, also remove termsOfUse and poweredBy divs\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- // add attribution only if attribution text is unique\n- if (OpenLayers.Util.indexOf(\n- attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution);\n- }\n- }\n+ removeMap: function(map) {\n+ // hide layer before removing\n+ if (this.visibility && this.mapObject) {\n+ this.setGMapVisibility(false);\n+ }\n+ // check to see if last Google layer in this map\n+ var cache = OpenLayers.Layer.Google.cache[map.id];\n+ if (cache) {\n+ if (cache.count <= 1) {\n+ this.removeGMapElements();\n+ delete OpenLayers.Layer.Google.cache[map.id];\n+ } else {\n+ // decrement the layer count\n+ --cache.count;\n }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- });\n }\n+ // remove references to gmap elements\n+ delete this.termsOfUse;\n+ delete this.poweredBy;\n+ delete this.mapObject;\n+ delete this.dragObject;\n+ OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n- });\n-/* ======================================================================\n- OpenLayers/Control/Zoom.js\n- ====================================================================== */\n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * APIMethod: getOLBoundsFromMapObjectBounds\n+ * \n+ * Parameters:\n+ * moBounds - {Object}\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the \n+ * passed-in MapObject Bounds.\n+ * Returns null if null value is passed in.\n+ */\n+ getOLBoundsFromMapObjectBounds: function(moBounds) {\n+ var olBounds = null;\n+ if (moBounds != null) {\n+ var sw = moBounds.getSouthWest();\n+ var ne = moBounds.getNorthEast();\n+ if (this.sphericalMercator) {\n+ sw = this.forwardMercator(sw.lng(), sw.lat());\n+ ne = this.forwardMercator(ne.lng(), ne.lat());\n+ } else {\n+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat());\n+ }\n+ olBounds = new OpenLayers.Bounds(sw.lon,\n+ sw.lat,\n+ ne.lon,\n+ ne.lat);\n+ }\n+ return olBounds;\n+ },\n \n-/**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n- */\n+ /** \n+ * APIMethod: getWarningHTML\n+ * \n+ * Returns: \n+ * {String} String with information on why layer is broken, how to get\n+ * it working.\n+ */\n+ getWarningHTML: function() {\n+ return OpenLayers.i18n(\"googleWarning\");\n+ },\n \n-/**\n- * Class: OpenLayers.Control.Zoom\n- * The Zoom control is a pair of +/- links for zooming in and out.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n \n- /**\n- * APIProperty: zoomInText\n- * {String}\n- * Text for zoom-in link. Default is \"+\".\n- */\n- zoomInText: \"+\",\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n \n- /**\n- * APIProperty: zoomInId\n- * {String}\n- * Instead of having the control create a zoom in link, you can provide \n- * the identifier for an anchor element already added to the document.\n- * By default, an element with id \"olZoomInLink\" will be searched for\n- * and used if it exists.\n- */\n- zoomInId: \"olZoomInLink\",\n \n- /**\n- * APIProperty: zoomOutText\n- * {String}\n- * Text for zoom-out link. Default is \"\\u2212\".\n- */\n- zoomOutText: \"\\u2212\",\n+ // Get&Set Center, Zoom\n \n- /**\n- * APIProperty: zoomOutId\n- * {String}\n- * Instead of having the control create a zoom out link, you can provide \n- * the identifier for an anchor element already added to the document.\n- * By default, an element with id \"olZoomOutLink\" will be searched for\n- * and used if it exists.\n- */\n- zoomOutId: \"olZoomOutLink\",\n+ /**\n+ * APIMethod: getMapObjectCenter\n+ * \n+ * Returns: \n+ * {Object} The mapObject's current center in Map Object format\n+ */\n+ getMapObjectCenter: function() {\n+ return this.mapObject.getCenter();\n+ },\n \n- /**\n- * Method: draw\n- *\n- * Returns:\n- * {DOMElement} A reference to the DOMElement containing the zoom links.\n- */\n- draw: function() {\n- var div = OpenLayers.Control.prototype.draw.apply(this),\n- links = this.getOrCreateLinks(div),\n- zoomIn = links.zoomIn,\n- zoomOut = links.zoomOut,\n- eventsInstance = this.map.events;\n+ /** \n+ * APIMethod: getMapObjectZoom\n+ * \n+ * Returns:\n+ * {Integer} The mapObject's current zoom, in Map Object format\n+ */\n+ getMapObjectZoom: function() {\n+ return this.mapObject.getZoom();\n+ },\n \n- if (zoomOut.parentNode !== div) {\n- eventsInstance = this.events;\n- eventsInstance.attachToElement(zoomOut.parentNode);\n- }\n- eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n \n- this.zoomInLink = zoomIn;\n- this.zoomOutLink = zoomOut;\n- return div;\n- },\n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n \n- /**\n- * Method: getOrCreateLinks\n- * \n- * Parameters:\n- * el - {DOMElement}\n- *\n- * Return: \n- * {Object} Object with zoomIn and zoomOut properties referencing links.\n- */\n- getOrCreateLinks: function(el) {\n- var zoomIn = document.getElementById(this.zoomInId),\n- zoomOut = document.getElementById(this.zoomOutId);\n- if (!zoomIn) {\n- zoomIn = document.createElement(\"a\");\n- zoomIn.href = \"#zoomIn\";\n- zoomIn.appendChild(document.createTextNode(this.zoomInText));\n- zoomIn.className = \"olControlZoomIn\";\n- el.appendChild(zoomIn);\n- }\n- OpenLayers.Element.addClass(zoomIn, \"olButton\");\n- if (!zoomOut) {\n- zoomOut = document.createElement(\"a\");\n- zoomOut.href = \"#zoomOut\";\n- zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n- zoomOut.className = \"olControlZoomOut\";\n- el.appendChild(zoomOut);\n- }\n- OpenLayers.Element.addClass(zoomOut, \"olButton\");\n- return {\n- zoomIn: zoomIn,\n- zoomOut: zoomOut\n- };\n- },\n \n- /**\n- * Method: onZoomClick\n- * Called when zoomin/out link is clicked.\n- */\n- onZoomClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.zoomInLink) {\n- this.map.zoomIn();\n- } else if (button === this.zoomOutLink) {\n- this.map.zoomOut();\n- }\n- },\n+ // LonLat\n \n- /** \n- * Method: destroy\n- * Clean up.\n- */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onZoomClick);\n- }\n- delete this.zoomInLink;\n- delete this.zoomOutLink;\n- OpenLayers.Control.prototype.destroy.apply(this);\n- },\n+ /**\n+ * APIMethod: getLongitudeFromMapObjectLonLat\n+ * \n+ * Parameters:\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n+ * Returns:\n+ * {Float} Longitude of the given MapObject LonLat\n+ */\n+ getLongitudeFromMapObjectLonLat: function(moLonLat) {\n+ return this.sphericalMercator ?\n+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :\n+ moLonLat.lng();\n+ },\n \n- CLASS_NAME: \"OpenLayers.Control.Zoom\"\n-});\n-/* ======================================================================\n- OpenLayers/Handler/Feature.js\n- ====================================================================== */\n+ /**\n+ * APIMethod: getLatitudeFromMapObjectLonLat\n+ * \n+ * Parameters:\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n+ * Returns:\n+ * {Float} Latitude of the given MapObject LonLat\n+ */\n+ getLatitudeFromMapObjectLonLat: function(moLonLat) {\n+ var lat = this.sphericalMercator ?\n+ this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :\n+ moLonLat.lat();\n+ return lat;\n+ },\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ // Pixel\n+\n+ /**\n+ * APIMethod: getXFromMapObjectPixel\n+ * \n+ * Parameters:\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Integer} X value of the MapObject Pixel\n+ */\n+ getXFromMapObjectPixel: function(moPixel) {\n+ return moPixel.x;\n+ },\n+\n+ /**\n+ * APIMethod: getYFromMapObjectPixel\n+ * \n+ * Parameters:\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n+ * Returns:\n+ * {Integer} Y value of the MapObject Pixel\n+ */\n+ getYFromMapObjectPixel: function(moPixel) {\n+ return moPixel.y;\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Layer.Google\"\n+ });\n \n /**\n- * @requires OpenLayers/Handler.js\n+ * Property: OpenLayers.Layer.Google.cache\n+ * {Object} Cache for elements that should only be created once per map.\n */\n+OpenLayers.Layer.Google.cache = {};\n+\n \n /**\n- * Class: OpenLayers.Handler.Feature \n- * Handler to respond to mouse events related to a drawn feature. Callbacks\n- * with the following keys will be notified of the following events\n- * associated with features: click, clickout, over, out, and dblclick.\n- *\n- * This handler stops event propagation for mousedown and mouseup if those\n- * browser events target features that can be selected.\n- *\n- * Inherits from:\n- * - <OpenLayers.Handler>\n+ * Constant: OpenLayers.Layer.Google.v2\n+ * \n+ * Mixin providing functionality specific to the Google Maps API v2.\n+ * \n+ * This API has been deprecated by Google.\n+ * Developers are encouraged to migrate to v3 of the API; support for this\n+ * is provided by <OpenLayers.Layer.Google.v3>\n */\n-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Layer.Google.v2 = {\n \n /**\n- * Property: EVENTMAP\n- * {Object} A object mapping the browser events to objects with callback\n- * keys for in and out.\n+ * Property: termsOfUse\n+ * {DOMElement} Div for Google's copyright and terms of use link\n */\n- EVENTMAP: {\n- 'click': {\n- 'in': 'click',\n- 'out': 'clickout'\n- },\n- 'mousemove': {\n- 'in': 'over',\n- 'out': 'out'\n- },\n- 'dblclick': {\n- 'in': 'dblclick',\n- 'out': null\n- },\n- 'mousedown': {\n- 'in': null,\n- 'out': null\n- },\n- 'mouseup': {\n- 'in': null,\n- 'out': null\n- },\n- 'touchstart': {\n- 'in': 'click',\n- 'out': 'clickout'\n- }\n- },\n+ termsOfUse: null,\n \n /**\n- * Property: feature\n- * {<OpenLayers.Feature.Vector>} The last feature that was hovered.\n+ * Property: poweredBy\n+ * {DOMElement} Div for Google's powered by logo and link\n */\n- feature: null,\n+ poweredBy: null,\n \n /**\n- * Property: lastFeature\n- * {<OpenLayers.Feature.Vector>} The last feature that was handled.\n+ * Property: dragObject\n+ * {GDraggableObject} Since 2.93, Google has exposed the ability to get\n+ * the maps GDraggableObject. We can now use this for smooth panning\n */\n- lastFeature: null,\n+ dragObject: null,\n \n- /**\n- * Property: down\n- * {<OpenLayers.Pixel>} The location of the last mousedown.\n+ /** \n+ * Method: loadMapObject\n+ * Load the GMap and register appropriate event listeners. If we can't \n+ * load GMap2, then display a warning message.\n */\n- down: null,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = G_NORMAL_MAP;\n+ }\n+ var mapObject, termsOfUse, poweredBy;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // there are already Google layers added to this map\n+ mapObject = cache.mapObject;\n+ termsOfUse = cache.termsOfUse;\n+ poweredBy = cache.poweredBy;\n+ // increment the layer count\n+ ++cache.count;\n+ } else {\n+ // this is the first Google layer for this map\n \n- /**\n- * Property: up\n- * {<OpenLayers.Pixel>} The location of the last mouseup.\n- */\n- up: null,\n+ var container = this.map.viewPortDiv;\n+ var div = document.createElement(\"div\");\n+ div.id = this.map.id + \"_GMap2Container\";\n+ div.style.position = \"absolute\";\n+ div.style.width = \"100%\";\n+ div.style.height = \"100%\";\n+ container.appendChild(div);\n \n- /**\n- * Property: clickTolerance\n- * {Number} The number of pixels the mouse can move between mousedown\n- * and mouseup for the event to still be considered a click.\n- * Dragging the map should not trigger the click and clickout callbacks\n- * unless the map is moved by less than this tolerance. Defaults to 4.\n- */\n- clickTolerance: 4,\n+ // create GMap and shuffle elements\n+ try {\n+ mapObject = new GMap2(div);\n \n- /**\n- * Property: geometryTypes\n- * To restrict dragging to a limited set of geometry types, send a list\n- * of strings corresponding to the geometry class names.\n- * \n- * @type Array(String)\n- */\n- geometryTypes: null,\n+ // move the ToS and branding stuff up to the container div\n+ termsOfUse = div.lastChild;\n+ container.appendChild(termsOfUse);\n+ termsOfUse.style.zIndex = \"1100\";\n+ termsOfUse.style.right = \"\";\n+ termsOfUse.style.bottom = \"\";\n+ termsOfUse.className = \"olLayerGoogleCopyright\";\n \n- /**\n- * Property: stopClick\n- * {Boolean} If stopClick is set to true, handled clicks do not\n- * propagate to other click listeners. Otherwise, handled clicks\n- * do propagate. Unhandled clicks always propagate, whatever the\n- * value of stopClick. Defaults to true.\n- */\n- stopClick: true,\n+ poweredBy = div.lastChild;\n+ container.appendChild(poweredBy);\n+ poweredBy.style.zIndex = \"1100\";\n+ poweredBy.style.right = \"\";\n+ poweredBy.style.bottom = \"\";\n+ poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\";\n \n- /**\n- * Property: stopDown\n- * {Boolean} If stopDown is set to true, handled mousedowns do not\n- * propagate to other mousedown listeners. Otherwise, handled\n- * mousedowns do propagate. Unhandled mousedowns always propagate,\n- * whatever the value of stopDown. Defaults to true.\n- */\n- stopDown: true,\n+ } catch (e) {\n+ throw (e);\n+ }\n+ // cache elements for use by any other google layers added to\n+ // this same map\n+ OpenLayers.Layer.Google.cache[this.map.id] = {\n+ mapObject: mapObject,\n+ termsOfUse: termsOfUse,\n+ poweredBy: poweredBy,\n+ count: 1\n+ };\n+ }\n+\n+ this.mapObject = mapObject;\n+ this.termsOfUse = termsOfUse;\n+ this.poweredBy = poweredBy;\n+\n+ // ensure this layer type is one of the mapObject types\n+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),\n+ this.type) === -1) {\n+ this.mapObject.addMapType(this.type);\n+ }\n+\n+ //since v 2.93 getDragObject is now available.\n+ if (typeof mapObject.getDragObject == \"function\") {\n+ this.dragObject = mapObject.getDragObject();\n+ } else {\n+ this.dragPanMapObject = null;\n+ }\n+\n+ if (this.isBaseLayer === false) {\n+ this.setGMapVisibility(this.div.style.display !== \"none\");\n+ }\n+\n+ },\n \n /**\n- * Property: stopUp\n- * {Boolean} If stopUp is set to true, handled mouseups do not\n- * propagate to other mouseup listeners. Otherwise, handled mouseups\n- * do propagate. Unhandled mouseups always propagate, whatever the\n- * value of stopUp. Defaults to false.\n+ * APIMethod: onMapResize\n */\n- stopUp: false,\n+ onMapResize: function() {\n+ // workaround for resizing of invisible or not yet fully loaded layers\n+ // where GMap2.checkResize() does not work. We need to load the GMap\n+ // for the old div size, then checkResize(), and then call\n+ // layer.moveTo() to trigger GMap.setCenter() (which will finish\n+ // the GMap initialization).\n+ if (this.visibility && this.mapObject.isLoaded()) {\n+ this.mapObject.checkResize();\n+ } else {\n+ if (!this._resized) {\n+ var layer = this;\n+ var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n+ GEvent.removeListener(handle);\n+ delete layer._resized;\n+ layer.mapObject.checkResize();\n+ layer.moveTo(layer.map.getCenter(), layer.map.getZoom());\n+ });\n+ }\n+ this._resized = true;\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Handler.Feature\n- *\n+ * Method: setGMapVisibility\n+ * Display the GMap container and associated elements.\n+ * \n * Parameters:\n- * control - {<OpenLayers.Control>} \n- * layer - {<OpenLayers.Layer.Vector>}\n- * callbacks - {Object} An object with a 'over' property whos value is\n- * a function to be called when the mouse is over a feature. The \n- * callback should expect to recieve a single argument, the feature.\n- * options - {Object} \n+ * visible - {Boolean} Display the GMap elements.\n */\n- initialize: function(control, layer, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.layer = layer;\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject.getContainer();\n+ if (visible === true) {\n+ this.mapObject.setMapType(this.type);\n+ container.style.display = \"\";\n+ this.termsOfUse.style.left = \"\";\n+ this.termsOfUse.style.display = \"\";\n+ this.poweredBy.style.display = \"\";\n+ cache.displayed = this.id;\n+ } else {\n+ if (cache.displayed === this.id) {\n+ delete cache.displayed;\n+ }\n+ if (!cache.displayed) {\n+ container.style.display = \"none\";\n+ this.termsOfUse.style.display = \"none\";\n+ // move ToU far to the left in addition to setting display\n+ // to \"none\", because at the end of the GMap2 load\n+ // sequence, display: none will be unset and ToU would be\n+ // visible after loading a map with a google layer that is\n+ // initially hidden. \n+ this.termsOfUse.style.left = \"-9999px\";\n+ this.poweredBy.style.display = \"none\";\n+ }\n+ }\n+ }\n },\n \n /**\n- * Method: touchstart\n- * Handle touchstart events\n- *\n- * Parameters:\n- * evt - {Event}\n- *\n+ * Method: getMapContainer\n+ * \n * Returns:\n- * {Boolean} Let the event propagate.\n+ * {DOMElement} the GMap container's div\n */\n- touchstart: function(evt) {\n- this.startTouch();\n- return OpenLayers.Event.isMultiTouch(evt) ?\n- true : this.mousedown(evt);\n+ getMapContainer: function() {\n+ return this.mapObject.getContainer();\n },\n \n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n+\n /**\n- * Method: touchmove\n- * Handle touchmove events. We just prevent the browser default behavior,\n- * for Android Webkit not to select text when moving the finger after\n- * selecting a feature.\n- *\n+ * APIMethod: getMapObjectBoundsFromOLBounds\n+ * \n * Parameters:\n- * evt - {Event}\n+ * olBounds - {<OpenLayers.Bounds>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Bounds, translated from olBounds\n+ * Returns null if null value is passed in\n */\n- touchmove: function(evt) {\n- OpenLayers.Event.preventDefault(evt);\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.bottom, olBounds.left) :\n+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.top, olBounds.right) :\n+ new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),\n+ new GLatLng(ne.lat, ne.lon));\n+ }\n+ return moBounds;\n },\n \n- /**\n- * Method: mousedown\n- * Handle mouse down. Stop propagation if a feature is targeted by this\n- * event (stops map dragging during feature selection).\n+\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n+\n+\n+ // Get&Set Center, Zoom\n+\n+ /** \n+ * APIMethod: setMapObjectCenter\n+ * Set the mapObject to the specified center and zoom\n * \n * Parameters:\n- * evt - {Event} \n+ * center - {Object} MapObject LonLat format\n+ * zoom - {int} MapObject zoom format\n */\n- mousedown: function(evt) {\n- // Feature selection is only done with a left click. Other handlers may stop the\n- // propagation of left-click mousedown events but not right-click mousedown events.\n- // This mismatch causes problems when comparing the location of the down and up\n- // events in the click function so it is important ignore right-clicks.\n- if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n- this.down = evt.xy;\n- }\n- return this.handle(evt) ? !this.stopDown : true;\n+ setMapObjectCenter: function(center, zoom) {\n+ this.mapObject.setCenter(center, zoom);\n },\n \n /**\n- * Method: mouseup\n- * Handle mouse up. Stop propagation if a feature is targeted by this\n- * event.\n+ * APIMethod: dragPanMapObject\n * \n * Parameters:\n- * evt - {Event} \n+ * dX - {Integer}\n+ * dY - {Integer}\n */\n- mouseup: function(evt) {\n- this.up = evt.xy;\n- return this.handle(evt) ? !this.stopUp : true;\n+ dragPanMapObject: function(dX, dY) {\n+ this.dragObject.moveBy(new GSize(-dX, dY));\n },\n \n+\n+ // LonLat - Pixel Translation\n+\n /**\n- * Method: click\n- * Handle click. Call the \"click\" callback if click on a feature,\n- * or the \"clickout\" callback if click outside any feature.\n+ * APIMethod: getMapObjectLonLatFromMapObjectPixel\n * \n * Parameters:\n- * evt - {Event} \n- *\n+ * moPixel - {Object} MapObject Pixel format\n+ * \n * Returns:\n- * {Boolean}\n+ * {Object} MapObject LonLat translated from MapObject Pixel\n */\n- click: function(evt) {\n- return this.handle(evt) ? !this.stopClick : true;\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ return this.mapObject.fromContainerPixelToLatLng(moPixel);\n },\n \n /**\n- * Method: mousemove\n- * Handle mouse moves. Call the \"over\" callback if moving in to a feature,\n- * or the \"out\" callback if moving out of a feature.\n+ * APIMethod: getMapObjectPixelFromMapObjectLonLat\n * \n * Parameters:\n- * evt - {Event} \n- *\n+ * moLonLat - {Object} MapObject LonLat format\n+ * \n * Returns:\n- * {Boolean}\n+ * {Object} MapObject Pixel transtlated from MapObject LonLat\n */\n- mousemove: function(evt) {\n- if (!this.callbacks['over'] && !this.callbacks['out']) {\n- return true;\n- }\n- this.handle(evt);\n- return true;\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ return this.mapObject.fromLatLngToContainerPixel(moLonLat);\n },\n \n- /**\n- * Method: dblclick\n- * Handle dblclick. Call the \"dblclick\" callback if dblclick on a feature.\n- *\n+\n+ // Bounds\n+\n+ /** \n+ * APIMethod: getMapObjectZoomFromMapObjectBounds\n+ * \n * Parameters:\n- * evt - {Event} \n- *\n+ * moBounds - {Object} MapObject Bounds format\n+ * \n * Returns:\n- * {Boolean}\n+ * {Object} MapObject Zoom for specified MapObject Bounds\n */\n- dblclick: function(evt) {\n- return !this.handle(evt);\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds);\n },\n \n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n+\n+\n+ // LonLat\n+\n /**\n- * Method: geometryTypeMatches\n- * Return true if the geometry type of the passed feature matches\n- * one of the geometry types in the geometryTypes array.\n- *\n+ * APIMethod: getMapObjectLonLatFromLonLat\n+ * \n * Parameters:\n- * feature - {<OpenLayers.Vector.Feature>}\n- *\n+ * lon - {Float}\n+ * lat - {Float}\n+ * \n * Returns:\n- * {Boolean}\n+ * {Object} MapObject LonLat built from lon and lat params\n */\n- geometryTypeMatches: function(feature) {\n- return this.geometryTypes == null ||\n- OpenLayers.Util.indexOf(this.geometryTypes,\n- feature.geometry.CLASS_NAME) > -1;\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon);\n+ } else {\n+ gLatLng = new GLatLng(lat, lon);\n+ }\n+ return gLatLng;\n },\n \n+ // Pixel\n+\n /**\n- * Method: handle\n- *\n+ * APIMethod: getMapObjectPixelFromXY\n+ * \n * Parameters:\n- * evt - {Event}\n- *\n+ * x - {Integer}\n+ * y - {Integer}\n+ * \n * Returns:\n- * {Boolean} The event occurred over a relevant feature.\n+ * {Object} MapObject Pixel from x and y parameters\n */\n- handle: function(evt) {\n- if (this.feature && !this.feature.layer) {\n- // feature has been destroyed\n- this.feature = null;\n- }\n- var type = evt.type;\n- var handled = false;\n- var previouslyIn = !!(this.feature); // previously in a feature\n- var click = (type == \"click\" || type == \"dblclick\" || type == \"touchstart\");\n- this.feature = this.layer.getFeatureFromEvent(evt);\n- if (this.feature && !this.feature.layer) {\n- // feature has been destroyed\n- this.feature = null;\n- }\n- if (this.lastFeature && !this.lastFeature.layer) {\n- // last feature has been destroyed\n- this.lastFeature = null;\n- }\n- if (this.feature) {\n- if (type === \"touchstart\") {\n- // stop the event to prevent Android Webkit from\n- // \"flashing\" the map div\n- OpenLayers.Event.preventDefault(evt);\n- }\n- var inNew = (this.feature != this.lastFeature);\n- if (this.geometryTypeMatches(this.feature)) {\n- // in to a feature\n- if (previouslyIn && inNew) {\n- // out of last feature and in to another\n- if (this.lastFeature) {\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- this.triggerCallback(type, 'in', [this.feature]);\n- } else if (!previouslyIn || click) {\n- // in feature for the first time\n- this.triggerCallback(type, 'in', [this.feature]);\n- }\n- this.lastFeature = this.feature;\n- handled = true;\n- } else {\n- // not in to a feature\n- if (this.lastFeature && (previouslyIn && inNew || click)) {\n- // out of last feature for the first time\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- // next time the mouse goes in a feature whose geometry type\n- // doesn't match we don't want to call the 'out' callback\n- // again, so let's set this.feature to null so that\n- // previouslyIn will evaluate to false the next time\n- // we enter handle. Yes, a bit hackish...\n- this.feature = null;\n- }\n- } else if (this.lastFeature && (previouslyIn || click)) {\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- return handled;\n- },\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new GPoint(x, y);\n+ }\n+\n+};\n+/* ======================================================================\n+ OpenLayers/Layer/Google/v3.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/Layer/Google.js\n+ */\n+\n+/**\n+ * Constant: OpenLayers.Layer.Google.v3\n+ * \n+ * Mixin providing functionality specific to the Google Maps API v3.\n+ * \n+ * To use this layer, you must include the GMaps v3 API in your html.\n+ * \n+ * Note that this layer configures the google.maps.map object with the\n+ * \"disableDefaultUI\" option set to true. Using UI controls that the Google\n+ * Maps API provides is not supported by the OpenLayers API.\n+ */\n+OpenLayers.Layer.Google.v3 = {\n \n /**\n- * Method: triggerCallback\n- * Call the callback keyed in the event map with the supplied arguments.\n- * For click and clickout, the <clickTolerance> is checked first.\n- *\n- * Parameters:\n- * type - {String}\n+ * Constant: DEFAULTS\n+ * {Object} It is not recommended to change the properties set here. Note\n+ * that Google.v3 layers only work when sphericalMercator is set to true.\n+ * \n+ * (code)\n+ * {\n+ * sphericalMercator: true,\n+ * projection: \"EPSG:900913\"\n+ * }\n+ * (end)\n */\n- triggerCallback: function(type, mode, args) {\n- var key = this.EVENTMAP[type][mode];\n- if (key) {\n- if (type == 'click' && this.up && this.down) {\n- // for click/clickout, only trigger callback if tolerance is met\n- var dpx = Math.sqrt(\n- Math.pow(this.up.x - this.down.x, 2) +\n- Math.pow(this.up.y - this.down.y, 2)\n- );\n- if (dpx <= this.clickTolerance) {\n- this.callback(key, args);\n- }\n- // we're done with this set of events now: clear the cached\n- // positions so we can't trip over them later (this can occur\n- // if one of the up/down events gets eaten before it gets to us\n- // but we still get the click)\n- this.up = this.down = null;\n- } else {\n- this.callback(key, args);\n- }\n- }\n+ DEFAULTS: {\n+ sphericalMercator: true,\n+ projection: \"EPSG:900913\"\n },\n \n /**\n- * Method: activate \n- * Turn on the handler. Returns false if the handler was already active.\n- *\n- * Returns:\n- * {Boolean}\n+ * APIProperty: animationEnabled\n+ * {Boolean} If set to true, the transition between zoom levels will be\n+ * animated (if supported by the GMaps API for the device used). Set to\n+ * false to match the zooming experience of other layer types. Default\n+ * is true. Note that the GMaps API does not give us control over zoom\n+ * animation, so if set to false, when zooming, this will make the\n+ * layer temporarily invisible, wait until GMaps reports the map being\n+ * idle, and make it visible again. The result will be a blank layer\n+ * for a few moments while zooming.\n */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.moveLayerToTop();\n- this.map.events.on({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n+ animationEnabled: true,\n+\n+ /** \n+ * Method: loadMapObject\n+ * Load the GMap and register appropriate event listeners.\n+ */\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = google.maps.MapTypeId.ROADMAP;\n+ }\n+ var mapObject;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ // there are already Google layers added to this map\n+ mapObject = cache.mapObject;\n+ // increment the layer count\n+ ++cache.count;\n+ } else {\n+ // this is the first Google layer for this map\n+ // create GMap\n+ var center = this.map.getCenter();\n+ var container = document.createElement('div');\n+ container.className = \"olForeignContainer\";\n+ container.style.width = '100%';\n+ container.style.height = '100%';\n+ mapObject = new google.maps.Map(container, {\n+ center: center ?\n+ new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n+ zoom: this.map.getZoom() || 0,\n+ mapTypeId: this.type,\n+ disableDefaultUI: true,\n+ keyboardShortcuts: false,\n+ draggable: false,\n+ disableDoubleClickZoom: true,\n+ scrollwheel: false,\n+ streetViewControl: false\n });\n- activated = true;\n+ var googleControl = document.createElement('div');\n+ googleControl.style.width = '100%';\n+ googleControl.style.height = '100%';\n+ mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n+\n+ // cache elements for use by any other google layers added to\n+ // this same map\n+ cache = {\n+ googleControl: googleControl,\n+ mapObject: mapObject,\n+ count: 1\n+ };\n+ OpenLayers.Layer.Google.cache[this.map.id] = cache;\n }\n- return activated;\n+ this.mapObject = mapObject;\n+ this.setGMapVisibility(this.visibility);\n },\n \n /**\n- * Method: deactivate \n- * Turn off the handler. Returns false if the handler was already active.\n- *\n- * Returns: \n- * {Boolean}\n+ * APIMethod: onMapResize\n */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.feature = null;\n- this.lastFeature = null;\n- this.down = null;\n- this.up = null;\n- this.map.events.un({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n- });\n- deactivated = true;\n+ onMapResize: function() {\n+ if (this.visibility) {\n+ google.maps.event.trigger(this.mapObject, \"resize\");\n }\n- return deactivated;\n },\n \n /**\n- * Method: handleMapEvents\n+ * Method: setGMapVisibility\n+ * Display the GMap container and associated elements.\n * \n * Parameters:\n- * evt - {Object}\n+ * visible - {Boolean} Display the GMap elements.\n */\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop();\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ var map = this.map;\n+ if (cache) {\n+ var type = this.type;\n+ var layers = map.layers;\n+ var layer;\n+ for (var i = layers.length - 1; i >= 0; --i) {\n+ layer = layers[i];\n+ if (layer instanceof OpenLayers.Layer.Google &&\n+ layer.visibility === true && layer.inRange === true) {\n+ type = layer.type;\n+ visible = true;\n+ break;\n+ }\n+ }\n+ var container = this.mapObject.getDiv();\n+ if (visible === true) {\n+ if (container.parentNode !== map.div) {\n+ if (!cache.rendered) {\n+ var me = this;\n+ google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {\n+ cache.rendered = true;\n+ me.setGMapVisibility(me.getVisibility());\n+ me.moveTo(me.map.getCenter());\n+ });\n+ } else {\n+ map.div.appendChild(container);\n+ cache.googleControl.appendChild(map.viewPortDiv);\n+ google.maps.event.trigger(this.mapObject, 'resize');\n+ }\n+ }\n+ this.mapObject.setMapTypeId(type);\n+ } else if (cache.googleControl.hasChildNodes()) {\n+ map.div.appendChild(map.viewPortDiv);\n+ map.div.removeChild(container);\n+ }\n }\n },\n \n /**\n- * Method: moveLayerToTop\n- * Moves the layer for this handler to the top, so mouse events can reach\n- * it.\n+ * Method: getMapContainer\n+ * \n+ * Returns:\n+ * {DOMElement} the GMap container's div\n */\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n- this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index);\n-\n+ getMapContainer: function() {\n+ return this.mapObject.getDiv();\n },\n \n+ //\n+ // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n+ //\n+\n /**\n- * Method: moveLayerBack\n- * Moves the layer back to the position determined by the map's layers\n- * array.\n+ * APIMethod: getMapObjectBoundsFromOLBounds\n+ * \n+ * Parameters:\n+ * olBounds - {<OpenLayers.Bounds>}\n+ * \n+ * Returns:\n+ * {Object} A MapObject Bounds, translated from olBounds\n+ * Returns null if null value is passed in\n */\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE['Feature']) {\n- this.layer.setZIndex(index);\n- } else {\n- this.map.setLayerZIndex(this.layer,\n- this.map.getLayerIndex(this.layer));\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.bottom, olBounds.left) :\n+ new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ?\n+ this.inverseMercator(olBounds.top, olBounds.right) :\n+ new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new google.maps.LatLngBounds(\n+ new google.maps.LatLng(sw.lat, sw.lon),\n+ new google.maps.LatLng(ne.lat, ne.lon)\n+ );\n }\n+ return moBounds;\n },\n \n- CLASS_NAME: \"OpenLayers.Handler.Feature\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Vector/RootContainer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- */\n \n-/**\n- * Class: OpenLayers.Layer.Vector.RootContainer\n- * A special layer type to combine multiple vector layers inside a single\n- * renderer root container. This class is not supposed to be instantiated\n- * from user space, it is a helper class for controls that require event\n- * processing for multiple vector layers.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n- */\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ /************************************\n+ * *\n+ * MapObject Interface Controls *\n+ * *\n+ ************************************/\n \n- /**\n- * Property: displayInLayerSwitcher\n- * Set to false for this layer type\n- */\n- displayInLayerSwitcher: false,\n \n- /**\n- * APIProperty: layers\n- * Layers that are attached to this container. Required config option.\n- */\n- layers: null,\n+ // LonLat - Pixel Translation\n \n /**\n- * Constructor: OpenLayers.Layer.Vector.RootContainer\n- * Create a new root container for multiple vector layer. This constructor\n- * is not supposed to be used from user space, it is only to be used by\n- * controls that need feature selection across multiple vector layers.\n- *\n+ * APIMethod: getMapObjectLonLatFromMapObjectPixel\n+ * \n * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n+ * moPixel - {Object} MapObject Pixel format\n * \n- * Required options properties:\n- * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n- * container\n- *\n * Returns:\n- * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n- * container\n+ * {Object} MapObject LonLat translated from MapObject Pixel\n */\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ var size = this.map.getSize();\n+ var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n+ var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n+ var res = this.map.getResolution();\n \n- /**\n- * Method: display\n- */\n- display: function() {},\n+ var delta_x = moPixel.x - (size.w / 2);\n+ var delta_y = moPixel.y - (size.h / 2);\n+\n+ var lonlat = new OpenLayers.LonLat(\n+ lon + delta_x * res,\n+ lat - delta_y * res\n+ );\n+\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent);\n+ }\n+ return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);\n+ },\n \n /**\n- * Method: getFeatureFromEvent\n- * walk through the layers to find the feature returned by the event\n+ * APIMethod: getMapObjectPixelFromMapObjectLonLat\n * \n * Parameters:\n- * evt - {Object} event object with a feature property\n+ * moLonLat - {Object} MapObject LonLat format\n * \n * Returns:\n- * {<OpenLayers.Feature.Vector>}\n+ * {Object} MapObject Pixel transtlated from MapObject LonLat\n */\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature;\n- }\n- }\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ var res = this.map.getResolution();\n+ var extent = this.map.getExtent();\n+ return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)),\n+ (1 / res * (extent.top - lat)));\n },\n \n- /**\n- * Method: setMap\n+\n+ /** \n+ * APIMethod: setMapObjectCenter\n+ * Set the mapObject to the specified center and zoom\n * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * center - {Object} MapObject LonLat format\n+ * zoom - {int} MapObject zoom format\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer);\n+ setMapObjectCenter: function(center, zoom) {\n+ if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n+ var mapContainer = this.getMapContainer();\n+ google.maps.event.addListenerOnce(\n+ this.mapObject,\n+ \"idle\",\n+ function() {\n+ mapContainer.style.visibility = \"\";\n+ }\n+ );\n+ mapContainer.style.visibility = \"hidden\";\n+ }\n+ this.mapObject.setOptions({\n+ center: center,\n+ zoom: zoom\n+ });\n },\n \n- /**\n- * Method: removeMap\n+\n+ // Bounds\n+\n+ /** \n+ * APIMethod: getMapObjectZoomFromMapObjectBounds\n * \n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * moBounds - {Object} MapObject Bounds format\n+ * \n+ * Returns:\n+ * {Object} MapObject Zoom for specified MapObject Bounds\n */\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds);\n },\n \n- /**\n- * Method: collectRoots\n- * Collects the root nodes of all layers this control is configured with\n- * and moveswien the nodes to this control's layer\n- */\n- collectRoots: function() {\n- var layer;\n- // walk through all map layers, because we want to keep the order\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer);\n- }\n- }\n- },\n+ /************************************\n+ * *\n+ * MapObject Primitives *\n+ * *\n+ ************************************/\n+\n+\n+ // LonLat\n \n /**\n- * Method: resetRoots\n- * Resets the root nodes back into the layers they belong to.\n+ * APIMethod: getMapObjectLonLatFromLonLat\n+ * \n+ * Parameters:\n+ * lon - {Float}\n+ * lat - {Float}\n+ * \n+ * Returns:\n+ * {Object} MapObject LonLat built from lon and lat params\n */\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer);\n- }\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);\n+ } else {\n+ gLatLng = new google.maps.LatLng(lat, lon);\n }\n+ return gLatLng;\n },\n \n+ // Pixel\n+\n /**\n- * Method: handleChangeLayer\n- * Event handler for the map's changelayer event. We need to rebuild\n- * this container's layer dom if order of one of its layers changes.\n- * This handler is added with the setMap method, and removed with the\n- * removeMap method.\n+ * APIMethod: getMapObjectPixelFromXY\n * \n * Parameters:\n- * evt - {Object}\n+ * x - {Integer}\n+ * y - {Integer}\n+ * \n+ * Returns:\n+ * {Object} MapObject Pixel from x and y parameters\n */\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" &&\n- OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots();\n- }\n- },\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new google.maps.Point(x, y);\n+ }\n \n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n-});\n+};\n /* ======================================================================\n- OpenLayers/Control/SelectFeature.js\n+ OpenLayers/Protocol.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Handler/Feature.js\n- * @requires OpenLayers/Layer/Vector/RootContainer.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Control.SelectFeature\n- * The SelectFeature control selects vector features from a given layer on \n- * click or hover. \n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n+ * Class: OpenLayers.Protocol\n+ * Abstract vector layer protocol class. Not to be instantiated directly. Use\n+ * one of the protocol subclasses instead.\n */\n-OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Protocol = OpenLayers.Class({\n \n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforefeaturehighlighted - Triggered before a feature is highlighted\n- * featurehighlighted - Triggered when a feature is highlighted\n- * featureunhighlighted - Triggered when a feature is unhighlighted\n- * boxselectionstart - Triggered before box selection starts\n- * boxselectionend - Triggered after box selection ends\n+ /**\n+ * Property: format\n+ * {<OpenLayers.Format>} The format used by this protocol.\n */\n+ format: null,\n \n /**\n- * Property: multipleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <multiple> property to true. Default is null.\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n */\n- multipleKey: null,\n+ options: null,\n \n /**\n- * Property: toggleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <toggle> property to true. Default is null.\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the protocol can set autoDestroy to false\n+ * to fully control when the protocol is destroyed. Defaults to\n+ * true.\n */\n- toggleKey: null,\n+ autoDestroy: true,\n \n /**\n- * APIProperty: multiple\n- * {Boolean} Allow selection of multiple geometries. Default is false.\n+ * Property: defaultFilter\n+ * {<OpenLayers.Filter>} Optional default filter to read requests\n */\n- multiple: false,\n+ defaultFilter: null,\n \n /**\n- * APIProperty: clickout\n- * {Boolean} Unselect features when clicking outside any feature.\n- * Default is true.\n+ * Constructor: OpenLayers.Protocol\n+ * Abstract class for vector protocols. Create instances of a subclass.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- clickout: true,\n+ initialize: function(options) {\n+ options = options || {};\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ },\n \n /**\n- * APIProperty: toggle\n- * {Boolean} Unselect a selected feature on click. Default is false. Only\n- * has meaning if hover is false.\n+ * Method: mergeWithDefaultFilter\n+ * Merge filter passed to the read method with the default one\n+ *\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>}\n */\n- toggle: false,\n+ mergeWithDefaultFilter: function(filter) {\n+ var merged;\n+ if (filter && this.defaultFilter) {\n+ merged = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.defaultFilter, filter]\n+ });\n+ } else {\n+ merged = filter || this.defaultFilter || undefined;\n+ }\n+ return merged;\n+ },\n \n /**\n- * APIProperty: hover\n- * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n- * ignores clicks and only listens to mouse moves.\n+ * APIMethod: destroy\n+ * Clean up the protocol.\n */\n- hover: false,\n+ destroy: function() {\n+ this.options = null;\n+ this.format = null;\n+ },\n \n /**\n- * APIProperty: highlightOnly\n- * {Boolean} If true do not actually select features (that is place them in \n- * the layer's selected features array), just highlight them. This property\n- * has no effect if hover is false. Defaults to false.\n+ * APIMethod: read\n+ * Construct a request for reading new features.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- highlightOnly: false,\n+ read: function(options) {\n+ options = options || {};\n+ options.filter = this.mergeWithDefaultFilter(options.filter);\n+ },\n+\n \n /**\n- * APIProperty: box\n- * {Boolean} Allow feature selection by drawing a box.\n+ * APIMethod: create\n+ * Construct a request for writing newly created features.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- box: false,\n+ create: function() {},\n \n /**\n- * Property: onBeforeSelect \n- * {Function} Optional function to be called before a feature is selected.\n- * The function should expect to be called with a feature.\n+ * APIMethod: update\n+ * Construct a request updating modified features.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- onBeforeSelect: function() {},\n+ update: function() {},\n \n /**\n- * APIProperty: onSelect \n- * {Function} Optional function to be called when a feature is selected.\n- * The function should expect to be called with a feature.\n+ * APIMethod: delete\n+ * Construct a request deleting a removed feature.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, the same object will be passed to the callback function passed\n+ * if one exists in the options object.\n */\n- onSelect: function() {},\n+ \"delete\": function() {},\n \n /**\n- * APIProperty: onUnselect\n- * {Function} Optional function to be called when a feature is unselected.\n- * The function should expect to be called with a feature.\n+ * APIMethod: commit\n+ * Go over the features and for each take action\n+ * based on the feature state. Possible actions are create,\n+ * update and delete.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})}\n+ * options - {Object} Object whose possible keys are \"create\", \"update\",\n+ * \"delete\", \"callback\" and \"scope\", the values referenced by the\n+ * first three are objects as passed to the \"create\", \"update\", and\n+ * \"delete\" methods, the value referenced by the \"callback\" key is\n+ * a function which is called when the commit operation is complete\n+ * using the scope referenced by the \"scope\" key.\n+ *\n+ * Returns:\n+ * {Array({<OpenLayers.Protocol.Response>})} An array of\n+ * <OpenLayers.Protocol.Response> objects.\n */\n- onUnselect: function() {},\n+ commit: function() {},\n \n /**\n- * Property: scope\n- * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n- * callbacks. If null the scope will be this control.\n+ * Method: abort\n+ * Abort an ongoing request.\n+ *\n+ * Parameters:\n+ * response - {<OpenLayers.Protocol.Response>}\n */\n- scope: null,\n+ abort: function(response) {},\n \n /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict selecting to a limited set of geometry types,\n- * send a list of strings corresponding to the geometry class names.\n+ * Method: createCallback\n+ * Returns a function that applies the given public method with resp and\n+ * options arguments.\n+ *\n+ * Parameters:\n+ * method - {Function} The method to be applied by the callback.\n+ * response - {<OpenLayers.Protocol.Response>} The protocol response object.\n+ * options - {Object} Options sent to the protocol method\n */\n- geometryTypes: null,\n+ createCallback: function(method, response, options) {\n+ return OpenLayers.Function.bind(function() {\n+ method.apply(this, [response, options]);\n+ }, this);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Protocol\"\n+});\n \n+/**\n+ * Class: OpenLayers.Protocol.Response\n+ * Protocols return Response objects to their users.\n+ */\n+OpenLayers.Protocol.Response = OpenLayers.Class({\n /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n- * root for all layers this control is configured with (if an array of\n- * layers was passed to the constructor), or the vector layer the control\n- * was configured with (if a single layer was passed to the constructor).\n+ * Property: code\n+ * {Number} - OpenLayers.Protocol.Response.SUCCESS or\n+ * OpenLayers.Protocol.Response.FAILURE\n */\n- layer: null,\n+ code: null,\n \n /**\n- * Property: layers\n- * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n- * or null if the control was configured with a single layer\n+ * Property: requestType\n+ * {String} The type of request this response corresponds to. Either\n+ * \"create\", \"read\", \"update\" or \"delete\".\n */\n- layers: null,\n+ requestType: null,\n \n /**\n- * APIProperty: callbacks\n- * {Object} The functions that are sent to the handlers.feature for callback\n+ * Property: last\n+ * {Boolean} - true if this is the last response expected in a commit,\n+ * false otherwise, defaults to true.\n */\n- callbacks: null,\n+ last: true,\n+\n+ /**\n+ * Property: features\n+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n+ * The features returned in the response by the server. Depending on the \n+ * protocol's read payload, either features or data will be populated.\n+ */\n+ features: null,\n+\n+ /**\n+ * Property: data\n+ * {Object}\n+ * The data returned in the response by the server. Depending on the \n+ * protocol's read payload, either features or data will be populated.\n+ */\n+ data: null,\n \n /**\n- * APIProperty: selectStyle \n- * {Object} Hash of styles\n+ * Property: reqFeatures\n+ * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n+ * The features provided by the user and placed in the request by the\n+ * protocol.\n */\n- selectStyle: null,\n+ reqFeatures: null,\n \n /**\n- * Property: renderIntent\n- * {String} key used to retrieve the select style from the layer's\n- * style map.\n+ * Property: priv\n */\n- renderIntent: \"select\",\n+ priv: null,\n \n /**\n- * Property: handlers\n- * {Object} Object with references to multiple <OpenLayers.Handler>\n- * instances.\n+ * Property: error\n+ * {Object} The error object in case a service exception was encountered.\n */\n- handlers: null,\n+ error: null,\n \n /**\n- * Constructor: OpenLayers.Control.SelectFeature\n- * Create a new control for selecting features.\n+ * Constructor: OpenLayers.Protocol.Response\n *\n * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n- * layer(s) this control will select features from.\n- * options - {Object} \n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- initialize: function(layers, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n-\n- if (this.scope === null) {\n- this.scope = this;\n- }\n- this.initLayer(layers);\n- var callbacks = {\n- click: this.clickFeature,\n- clickout: this.clickoutFeature\n- };\n- if (this.hover) {\n- callbacks.over = this.overFeature;\n- callbacks.out = this.outFeature;\n- }\n-\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlers = {\n- feature: new OpenLayers.Handler.Feature(\n- this, this.layer, this.callbacks, {\n- geometryTypes: this.geometryTypes\n- }\n- )\n- };\n-\n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(\n- this, {\n- done: this.selectBox\n- }, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- }\n- );\n- }\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * Method: initLayer\n- * Assign the layer property. If layers is an array, we need to use\n- * a RootContainer.\n+ * Method: success\n *\n- * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n+ * Returns:\n+ * {Boolean} - true on success, false otherwise\n */\n- initLayer: function(layers) {\n- if (OpenLayers.Util.isArray(layers)) {\n- this.layers = layers;\n- this.layer = new OpenLayers.Layer.Vector.RootContainer(\n- this.id + \"_container\", {\n- layers: layers\n- }\n- );\n- } else {\n- this.layer = layers;\n- }\n+ success: function() {\n+ return this.code > 0;\n },\n \n+ CLASS_NAME: \"OpenLayers.Protocol.Response\"\n+});\n+\n+OpenLayers.Protocol.Response.SUCCESS = 1;\n+OpenLayers.Protocol.Response.FAILURE = 0;\n+/* ======================================================================\n+ OpenLayers/Request.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n+ */\n+\n+/**\n+ * TODO: deprecate me\n+ * Use OpenLayers.Request.proxy instead.\n+ */\n+OpenLayers.ProxyHost = \"\";\n+\n+/**\n+ * Namespace: OpenLayers.Request\n+ * The OpenLayers.Request namespace contains convenience methods for working\n+ * with XMLHttpRequests. These methods work with a cross-browser\n+ * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.\n+ */\n+if (!OpenLayers.Request) {\n /**\n- * Method: destroy\n+ * This allows for OpenLayers/Request/XMLHttpRequest.js to be included\n+ * before or after this script.\n */\n- destroy: function() {\n- if (this.active && this.layers) {\n- this.map.removeLayer(this.layer);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.layers) {\n- this.layer.destroy();\n- }\n- },\n+ OpenLayers.Request = {};\n+}\n+OpenLayers.Util.extend(OpenLayers.Request, {\n \n /**\n- * Method: activate\n- * Activates the control.\n- * \n- * Returns:\n- * {Boolean} The control was effectively activated.\n+ * Constant: DEFAULT_CONFIG\n+ * {Object} Default configuration for all requests.\n */\n- activate: function() {\n- if (!this.active) {\n- if (this.layers) {\n- this.map.addLayer(this.layer);\n- }\n- this.handlers.feature.activate();\n- if (this.box && this.handlers.box) {\n- this.handlers.box.activate();\n- }\n- }\n- return OpenLayers.Control.prototype.activate.apply(\n- this, arguments\n- );\n+ DEFAULT_CONFIG: {\n+ method: \"GET\",\n+ url: window.location.href,\n+ async: true,\n+ user: undefined,\n+ password: undefined,\n+ params: null,\n+ proxy: OpenLayers.ProxyHost,\n+ headers: {},\n+ data: null,\n+ callback: function() {},\n+ success: null,\n+ failure: null,\n+ scope: null\n },\n \n /**\n- * Method: deactivate\n- * Deactivates the control.\n+ * Constant: URL_SPLIT_REGEX\n+ */\n+ URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n+\n+ /**\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} An events object that handles all \n+ * events on the {<OpenLayers.Request>} object.\n+ *\n+ * All event listeners will receive an event object with three properties:\n+ * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.\n+ * config - {Object} The config object sent to the specific request method.\n+ * requestUrl - {String} The request url.\n * \n- * Returns:\n- * {Boolean} The control was effectively deactivated.\n+ * Supported event types:\n+ * complete - Triggered when we have a response from the request, if a\n+ * listener returns false, no further response processing will take\n+ * place.\n+ * success - Triggered when the HTTP response has a success code (200-299).\n+ * failure - Triggered when the HTTP response does not have a success code.\n */\n- deactivate: function() {\n- if (this.active) {\n- this.handlers.feature.deactivate();\n- if (this.handlers.box) {\n- this.handlers.box.deactivate();\n- }\n- if (this.layers) {\n- this.map.removeLayer(this.layer);\n- }\n- }\n- return OpenLayers.Control.prototype.deactivate.apply(\n- this, arguments\n- );\n- },\n+ events: new OpenLayers.Events(this),\n \n /**\n- * Method: unselectAll\n- * Unselect all selected features. To unselect all except for a single\n- * feature, set the options.except property to the feature.\n+ * Method: makeSameOrigin\n+ * Using the specified proxy, returns a same origin url of the provided url.\n *\n * Parameters:\n- * options - {Object} Optional configuration object.\n+ * url - {String} An arbitrary url\n+ * proxy {String|Function} The proxy to use to make the provided url a\n+ * same origin url.\n+ *\n+ * Returns\n+ * {String} the same origin url. If no proxy is provided, the returned url\n+ * will be the same as the provided url.\n */\n- unselectAll: function(options) {\n- // we'll want an option to supress notification here\n- var layers = this.layers || [this.layer],\n- layer, feature, l, numExcept;\n- for (l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- numExcept = 0;\n- //layer.selectedFeatures is null when layer is destroyed and \n- //one of it's preremovelayer listener calls setLayer \n- //with another layer on this control\n- if (layer.selectedFeatures != null) {\n- while (layer.selectedFeatures.length > numExcept) {\n- feature = layer.selectedFeatures[numExcept];\n- if (!options || options.except != feature) {\n- this.unselect(feature);\n- } else {\n- ++numExcept;\n- }\n+ makeSameOrigin: function(url, proxy) {\n+ var sameOrigin = url.indexOf(\"http\") !== 0;\n+ var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n+ if (urlParts) {\n+ var location = window.location;\n+ sameOrigin =\n+ urlParts[1] == location.protocol &&\n+ urlParts[3] == location.hostname;\n+ var uPort = urlParts[4],\n+ lPort = location.port;\n+ if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n+ sameOrigin = sameOrigin && uPort == lPort;\n+ }\n+ }\n+ if (!sameOrigin) {\n+ if (proxy) {\n+ if (typeof proxy == \"function\") {\n+ url = proxy(url);\n+ } else {\n+ url = proxy + encodeURIComponent(url);\n }\n }\n }\n+ return url;\n },\n \n /**\n- * Method: clickFeature\n- * Called on click in a feature\n- * Only responds if this.hover is false.\n+ * APIMethod: issue\n+ * Create a new XMLHttpRequest object, open it, set any headers, bind\n+ * a callback to done state, and send any data. It is recommended that\n+ * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.\n+ * This method is only documented to provide detail on the configuration\n+ * options available to all request methods.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * config - {Object} Object containing properties for configuring the\n+ * request. Allowed configuration properties are described below.\n+ * This object is modified and should not be reused.\n+ *\n+ * Allowed config properties:\n+ * method - {String} One of GET, POST, PUT, DELETE, HEAD, or\n+ * OPTIONS. Default is GET.\n+ * url - {String} URL for the request.\n+ * async - {Boolean} Open an asynchronous request. Default is true.\n+ * user - {String} User for relevant authentication scheme. Set\n+ * to null to clear current user.\n+ * password - {String} Password for relevant authentication scheme.\n+ * Set to null to clear current password.\n+ * proxy - {String} Optional proxy. Defaults to\n+ * <OpenLayers.ProxyHost>.\n+ * params - {Object} Any key:value pairs to be appended to the\n+ * url as a query string. Assumes url doesn't already include a query\n+ * string or hash. Typically, this is only appropriate for <GET>\n+ * requests where the query string will be appended to the url.\n+ * Parameter values that are arrays will be\n+ * concatenated with a comma (note that this goes against form-encoding)\n+ * as is done with <OpenLayers.Util.getParameterString>.\n+ * headers - {Object} Object with header:value pairs to be set on\n+ * the request.\n+ * data - {String | Document} Optional data to send with the request.\n+ * Typically, this is only used with <POST> and <PUT> requests.\n+ * Make sure to provide the appropriate \"Content-Type\" header for your\n+ * data. For <POST> and <PUT> requests, the content type defaults to\n+ * \"application-xml\". If your data is a different content type, or\n+ * if you are using a different HTTP method, set the \"Content-Type\"\n+ * header to match your data type.\n+ * callback - {Function} Function to call when request is done.\n+ * To determine if the request failed, check request.status (200\n+ * indicates success).\n+ * success - {Function} Optional function to call if request status is in\n+ * the 200s. This will be called in addition to callback above and\n+ * would typically only be used as an alternative.\n+ * failure - {Function} Optional function to call if request status is not\n+ * in the 200s. This will be called in addition to callback above and\n+ * would typically only be used as an alternative.\n+ * scope - {Object} If callback is a public method on some object,\n+ * set the scope to that object.\n+ *\n+ * Returns:\n+ * {XMLHttpRequest} Request object. To abort the request before a response\n+ * is received, call abort() on the request object.\n */\n- clickFeature: function(feature) {\n- if (!this.hover) {\n- var selected = (OpenLayers.Util.indexOf(\n- feature.layer.selectedFeatures, feature) > -1);\n- if (selected) {\n- if (this.toggleSelect()) {\n- this.unselect(feature);\n- } else if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- });\n+ issue: function(config) {\n+ // apply default config - proxy host may have changed\n+ var defaultConfig = OpenLayers.Util.extend(\n+ this.DEFAULT_CONFIG, {\n+ proxy: OpenLayers.ProxyHost\n+ }\n+ );\n+ config = config || {};\n+ config.headers = config.headers || {};\n+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n+ config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n+ // Always set the \"X-Requested-With\" header to signal that this request\n+ // was issued through the XHR-object. Since header keys are case \n+ // insensitive and we want to allow overriding of the \"X-Requested-With\"\n+ // header through the user we cannot use applyDefaults, but have to \n+ // check manually whether we were called with a \"X-Requested-With\"\n+ // header.\n+ var customRequestedWithHeader = false,\n+ headerKey;\n+ for (headerKey in config.headers) {\n+ if (config.headers.hasOwnProperty(headerKey)) {\n+ if (headerKey.toLowerCase() === 'x-requested-with') {\n+ customRequestedWithHeader = true;\n }\n- } else {\n- if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n+ }\n+ }\n+ if (customRequestedWithHeader === false) {\n+ // we did not have a custom \"X-Requested-With\" header\n+ config.headers['X-Requested-With'] = 'XMLHttpRequest';\n+ }\n+\n+ // create request, open, and set headers\n+ var request = new OpenLayers.Request.XMLHttpRequest();\n+ var url = OpenLayers.Util.urlAppend(config.url,\n+ OpenLayers.Util.getParameterString(config.params || {}));\n+ url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n+ request.open(\n+ config.method, url, config.async, config.user, config.password\n+ );\n+ for (var header in config.headers) {\n+ request.setRequestHeader(header, config.headers[header]);\n+ }\n+\n+ var events = this.events;\n+\n+ // we want to execute runCallbacks with \"this\" as the\n+ // execution scope\n+ var self = this;\n+\n+ request.onreadystatechange = function() {\n+ if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n+ var proceed = events.triggerEvent(\n+ \"complete\", {\n+ request: request,\n+ config: config,\n+ requestUrl: url\n+ }\n+ );\n+ if (proceed !== false) {\n+ self.runCallbacks({\n+ request: request,\n+ config: config,\n+ requestUrl: url\n });\n }\n- this.select(feature);\n }\n+ };\n+\n+ // send request (optionally with data) and return\n+ // call in a timeout for asynchronous requests so the return is\n+ // available before readyState == 4 for cached docs\n+ if (config.async === false) {\n+ request.send(config.data);\n+ } else {\n+ window.setTimeout(function() {\n+ if (request.readyState !== 0) { // W3C: 0-UNSENT\n+ request.send(config.data);\n+ }\n+ }, 0);\n }\n+ return request;\n },\n \n /**\n- * Method: multipleSelect\n- * Allow for multiple selected features based on <multiple> property and\n- * <multipleKey> event modifier.\n+ * Method: runCallbacks\n+ * Calls the complete, success and failure callbacks. Application\n+ * can listen to the \"complete\" event, have the listener \n+ * display a confirm window and always return false, and\n+ * execute OpenLayers.Request.runCallbacks if the user\n+ * hits \"yes\" in the confirm window.\n *\n- * Returns:\n- * {Boolean} Allow for multiple selected features.\n+ * Parameters:\n+ * options - {Object} Hash containing request, config and requestUrl keys\n */\n- multipleSelect: function() {\n- return this.multiple || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.multipleKey]);\n- },\n+ runCallbacks: function(options) {\n+ var request = options.request;\n+ var config = options.config;\n \n- /**\n- * Method: toggleSelect\n- * Event should toggle the selected state of a feature based on <toggle>\n- * property and <toggleKey> event modifier.\n- *\n- * Returns:\n- * {Boolean} Toggle the selected state of a feature.\n- */\n- toggleSelect: function() {\n- return this.toggle || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.toggleKey]);\n- },\n+ // bind callbacks to readyState 4 (done)\n+ var complete = (config.scope) ?\n+ OpenLayers.Function.bind(config.callback, config.scope) :\n+ config.callback;\n \n- /**\n- * Method: clickoutFeature\n- * Called on click outside a previously clicked (selected) feature.\n- * Only responds if this.hover is false.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Vector.Feature>} \n- */\n- clickoutFeature: function(feature) {\n- if (!this.hover && this.clickout) {\n- this.unselectAll();\n+ // optional success callback\n+ var success;\n+ if (config.success) {\n+ success = (config.scope) ?\n+ OpenLayers.Function.bind(config.success, config.scope) :\n+ config.success;\n }\n- },\n \n- /**\n- * Method: overFeature\n- * Called on over a feature.\n- * Only responds if this.hover is true.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- overFeature: function(feature) {\n- var layer = feature.layer;\n- if (this.hover) {\n- if (this.highlightOnly) {\n- this.highlight(feature);\n- } else if (OpenLayers.Util.indexOf(\n- layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n+ // optional failure callback\n+ var failure;\n+ if (config.failure) {\n+ failure = (config.scope) ?\n+ OpenLayers.Function.bind(config.failure, config.scope) :\n+ config.failure;\n+ }\n+\n+ if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" &&\n+ request.responseText) {\n+ request.status = 200;\n+ }\n+ complete(request);\n+\n+ if (!request.status || (request.status >= 200 && request.status < 300)) {\n+ this.events.triggerEvent(\"success\", options);\n+ if (success) {\n+ success(request);\n+ }\n+ }\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"failure\", options);\n+ if (failure) {\n+ failure(request);\n }\n }\n },\n \n /**\n- * Method: outFeature\n- * Called on out of a selected feature.\n- * Only responds if this.hover is true.\n+ * APIMethod: GET\n+ * Send an HTTP GET request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to GET.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- outFeature: function(feature) {\n- if (this.hover) {\n- if (this.highlightOnly) {\n- // we do nothing if we're not the last highlighter of the\n- // feature\n- if (feature._lastHighlighter == this.id) {\n- // if another select control had highlighted the feature before\n- // we did it ourself then we use that control to highlight the\n- // feature as it was before we highlighted it, else we just\n- // unhighlight it\n- if (feature._prevHighlighter &&\n- feature._prevHighlighter != this.id) {\n- delete feature._lastHighlighter;\n- var control = this.map.getControl(\n- feature._prevHighlighter);\n- if (control) {\n- control.highlight(feature);\n- }\n- } else {\n- this.unhighlight(feature);\n- }\n- }\n- } else {\n- this.unselect(feature);\n- }\n- }\n+ GET: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"GET\"\n+ });\n+ return OpenLayers.Request.issue(config);\n },\n \n /**\n- * Method: highlight\n- * Redraw feature with the select style.\n+ * APIMethod: POST\n+ * Send a POST request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to POST and \"Content-Type\" header set to \"application/xml\".\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties. The\n+ * default \"Content-Type\" header will be set to \"application-xml\" if\n+ * none is provided. This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- highlight: function(feature) {\n- var layer = feature.layer;\n- var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n- feature: feature\n+ POST: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"POST\"\n });\n- if (cont !== false) {\n- feature._prevHighlighter = feature._lastHighlighter;\n- feature._lastHighlighter = this.id;\n- var style = this.selectStyle || this.renderIntent;\n- layer.drawFeature(feature, style);\n- this.events.triggerEvent(\"featurehighlighted\", {\n- feature: feature\n- });\n+ // set content type to application/xml if it isn't already set\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\";\n }\n+ return OpenLayers.Request.issue(config);\n },\n \n /**\n- * Method: unhighlight\n- * Redraw feature with the \"default\" style\n+ * APIMethod: PUT\n+ * Send an HTTP PUT request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to PUT and \"Content-Type\" header set to \"application/xml\".\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties. The\n+ * default \"Content-Type\" header will be set to \"application-xml\" if\n+ * none is provided. This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- unhighlight: function(feature) {\n- var layer = feature.layer;\n- // three cases:\n- // 1. there's no other highlighter, in that case _prev is undefined,\n- // and we just need to undef _last\n- // 2. another control highlighted the feature after we did it, in\n- // that case _last references this other control, and we just\n- // need to undef _prev\n- // 3. another control highlighted the feature before we did it, in\n- // that case _prev references this other control, and we need to\n- // set _last to _prev and undef _prev\n- if (feature._prevHighlighter == undefined) {\n- delete feature._lastHighlighter;\n- } else if (feature._prevHighlighter == this.id) {\n- delete feature._prevHighlighter;\n- } else {\n- feature._lastHighlighter = feature._prevHighlighter;\n- delete feature._prevHighlighter;\n- }\n- layer.drawFeature(feature, feature.style || feature.layer.style ||\n- \"default\");\n- this.events.triggerEvent(\"featureunhighlighted\", {\n- feature: feature\n+ PUT: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"PUT\"\n });\n+ // set content type to application/xml if it isn't already set\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\";\n+ }\n+ return OpenLayers.Request.issue(config);\n },\n \n /**\n- * Method: select\n- * Add feature to the layer's selectedFeature array, render the feature as\n- * selected, and call the onSelect function.\n- * \n+ * APIMethod: DELETE\n+ * Send an HTTP DELETE request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to DELETE.\n+ *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- select: function(feature) {\n- var cont = this.onBeforeSelect.call(this.scope, feature);\n- var layer = feature.layer;\n- if (cont !== false) {\n- cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- layer.selectedFeatures.push(feature);\n- this.highlight(feature);\n- // if the feature handler isn't involved in the feature\n- // selection (because the box handler is used or the\n- // feature is selected programatically) we fake the\n- // feature handler to allow unselecting on click\n- if (!this.handlers.feature.lastFeature) {\n- this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n- }\n- layer.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- });\n- this.onSelect.call(this.scope, feature);\n- }\n- }\n+ DELETE: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"DELETE\"\n+ });\n+ return OpenLayers.Request.issue(config);\n },\n \n /**\n- * Method: unselect\n- * Remove feature from the layer's selectedFeature array, render the feature as\n- * normal, and call the onUnselect function.\n+ * APIMethod: HEAD\n+ * Send an HTTP HEAD request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to HEAD.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- unselect: function(feature) {\n- var layer = feature.layer;\n- // Store feature style for restoration later\n- this.unhighlight(feature);\n- OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n- layer.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n+ HEAD: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"HEAD\"\n });\n- this.onUnselect.call(this.scope, feature);\n+ return OpenLayers.Request.issue(config);\n },\n \n /**\n- * Method: selectBox\n- * Callback from the handlers.box set up when <box> selection is true\n- * on.\n+ * APIMethod: OPTIONS\n+ * Send an HTTP OPTIONS request. Additional configuration properties are\n+ * documented in the <issue> method, with the method property set\n+ * to OPTIONS.\n *\n * Parameters:\n- * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n+ * config - {Object} Object with properties for configuring the request.\n+ * See the <issue> method for documentation of allowed properties.\n+ * This object is modified and should not be reused.\n+ * \n+ * Returns:\n+ * {XMLHttpRequest} Request object.\n */\n- selectBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- var bounds = new OpenLayers.Bounds(\n- minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n- );\n+ OPTIONS: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"OPTIONS\"\n+ });\n+ return OpenLayers.Request.issue(config);\n+ }\n \n- // if multiple is false, first deselect currently selected features\n- if (!this.multipleSelect()) {\n- this.unselectAll();\n- }\n+});\n+/* ======================================================================\n+ OpenLayers/Request/XMLHttpRequest.js\n+ ====================================================================== */\n \n- // because we're using a box, we consider we want multiple selection\n- var prevMultiple = this.multiple;\n- this.multiple = true;\n- var layers = this.layers || [this.layer];\n- this.events.triggerEvent(\"boxselectionstart\", {\n- layers: layers\n- });\n- var layer;\n- for (var l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- for (var i = 0, len = layer.features.length; i < len; ++i) {\n- var feature = layer.features[i];\n- // check if the feature is displayed\n- if (!feature.getVisibility()) {\n- continue;\n- }\n+// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+// http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n \n- if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n- this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n- if (bounds.toGeometry().intersects(feature.geometry)) {\n- if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n- }\n- }\n- }\n+/**\n+ * @requires OpenLayers/Request.js\n+ */\n+\n+(function() {\n+\n+ // Save reference to earlier defined object implementation (if any)\n+ var oXMLHttpRequest = window.XMLHttpRequest;\n+\n+ // Define on browser type\n+ var bGecko = !!window.controllers,\n+ bIE = window.document.all && !window.opera,\n+ bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n+\n+ // Enables \"XMLHttpRequest()\" call next to \"new XMLHttpReques()\"\n+ function fXMLHttpRequest() {\n+ this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n+ this._listeners = [];\n+ };\n+\n+ // Constructor\n+ function cXMLHttpRequest() {\n+ return new fXMLHttpRequest;\n+ };\n+ cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n+\n+ // BUGFIX: Firefox with Firebug installed would break pages if not executed\n+ if (bGecko && oXMLHttpRequest.wrapped)\n+ cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n+\n+ // Constants\n+ cXMLHttpRequest.UNSENT = 0;\n+ cXMLHttpRequest.OPENED = 1;\n+ cXMLHttpRequest.HEADERS_RECEIVED = 2;\n+ cXMLHttpRequest.LOADING = 3;\n+ cXMLHttpRequest.DONE = 4;\n+\n+ // Public Properties\n+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n+ cXMLHttpRequest.prototype.responseText = '';\n+ cXMLHttpRequest.prototype.responseXML = null;\n+ cXMLHttpRequest.prototype.status = 0;\n+ cXMLHttpRequest.prototype.statusText = '';\n+\n+ // Priority proposal\n+ cXMLHttpRequest.prototype.priority = \"NORMAL\";\n+\n+ // Instance-level Events Handlers\n+ cXMLHttpRequest.prototype.onreadystatechange = null;\n+\n+ // Class-level Events Handlers\n+ cXMLHttpRequest.onreadystatechange = null;\n+ cXMLHttpRequest.onopen = null;\n+ cXMLHttpRequest.onsend = null;\n+ cXMLHttpRequest.onabort = null;\n+\n+ // Public Methods\n+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n+ // Delete headers, required when object is reused\n+ delete this._headers;\n+\n+ // When bAsync parameter value is omitted, use true as default\n+ if (arguments.length < 3)\n+ bAsync = true;\n+\n+ // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests\n+ this._async = bAsync;\n+\n+ // Set the onreadystatechange handler\n+ var oRequest = this,\n+ nState = this.readyState,\n+ fOnUnload;\n+\n+ // BUGFIX: IE - memory leak on page unload (inter-page leak)\n+ if (bIE && bAsync) {\n+ fOnUnload = function() {\n+ if (nState != cXMLHttpRequest.DONE) {\n+ fCleanTransport(oRequest);\n+ // Safe to abort here since onreadystatechange handler removed\n+ oRequest.abort();\n }\n+ };\n+ window.attachEvent(\"onunload\", fOnUnload);\n+ }\n+\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onopen)\n+ cXMLHttpRequest.onopen.apply(this, arguments);\n+\n+ if (arguments.length > 4)\n+ this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else\n+ if (arguments.length > 3)\n+ this._object.open(sMethod, sUrl, bAsync, sUser);\n+ else\n+ this._object.open(sMethod, sUrl, bAsync);\n+\n+ this.readyState = cXMLHttpRequest.OPENED;\n+ fReadyStateChange(this);\n+\n+ this._object.onreadystatechange = function() {\n+ if (bGecko && !bAsync)\n+ return;\n+\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\n+\n+ //\n+ fSynchronizeValues(oRequest);\n+\n+ // BUGFIX: Firefox fires unnecessary DONE when aborting\n+ if (oRequest._aborted) {\n+ // Reset readyState to UNSENT\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+\n+ // Return now\n+ return;\n }\n- this.multiple = prevMultiple;\n- this.events.triggerEvent(\"boxselectionend\", {\n- layers: layers\n- });\n+\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ // Free up queue\n+ delete oRequest._data;\n+ /* if (bAsync)\n+ fQueue_remove(oRequest);*/\n+ //\n+ fCleanTransport(oRequest);\n+ // Uncomment this block if you need a fix for IE cache\n+ /*\n+ // BUGFIX: IE - cache issue\n+ if (!oRequest._object.getResponseHeader(\"Date\")) {\n+ // Save object to cache\n+ oRequest._cached = oRequest._object;\n+\n+ // Instantiate a new transport object\n+ cXMLHttpRequest.call(oRequest);\n+\n+ // Re-send request\n+ if (sUser) {\n+ if (sPassword)\n+ oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else\n+ oRequest._object.open(sMethod, sUrl, bAsync, sUser);\n+ }\n+ else\n+ oRequest._object.open(sMethod, sUrl, bAsync);\n+ oRequest._object.setRequestHeader(\"If-Modified-Since\", oRequest._cached.getResponseHeader(\"Last-Modified\") || new window.Date(0));\n+ // Copy headers set\n+ if (oRequest._headers)\n+ for (var sHeader in oRequest._headers)\n+ if (typeof oRequest._headers[sHeader] == \"string\") // Some frameworks prototype objects with functions\n+ oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);\n+\n+ oRequest._object.onreadystatechange = function() {\n+ // Synchronize state\n+ oRequest.readyState = oRequest._object.readyState;\n+\n+ if (oRequest._aborted) {\n+ //\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+\n+ // Return\n+ return;\n+ }\n+\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ // Clean Object\n+ fCleanTransport(oRequest);\n+\n+ // get cached request\n+ if (oRequest.status == 304)\n+ oRequest._object = oRequest._cached;\n+\n+ //\n+ delete oRequest._cached;\n+\n+ //\n+ fSynchronizeValues(oRequest);\n+\n+ //\n+ fReadyStateChange(oRequest);\n+\n+ // BUGFIX: IE - memory leak in interrupted\n+ if (bIE && bAsync)\n+ window.detachEvent(\"onunload\", fOnUnload);\n+ }\n+ };\n+ oRequest._object.send(null);\n+\n+ // Return now - wait until re-sent request is finished\n+ return;\n+ };\n+ */\n+ // BUGFIX: IE - memory leak in interrupted\n+ if (bIE && bAsync)\n+ window.detachEvent(\"onunload\", fOnUnload);\n+ }\n+\n+ // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice\n+ if (nState != oRequest.readyState)\n+ fReadyStateChange(oRequest);\n+\n+ nState = oRequest.readyState;\n }\n- },\n+ };\n \n- /** \n- * Method: setMap\n- * Set the map property for the control. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- this.handlers.feature.setMap(map);\n- if (this.box) {\n- this.handlers.box.setMap(map);\n+ function fXMLHttpRequest_send(oRequest) {\n+ oRequest._object.send(oRequest._data);\n+\n+ // BUGFIX: Gecko - missing readystatechange calls in synchronous requests\n+ if (bGecko && !oRequest._async) {\n+ oRequest.readyState = cXMLHttpRequest.OPENED;\n+\n+ // Synchronize state\n+ fSynchronizeValues(oRequest);\n+\n+ // Simulate missing states\n+ while (oRequest.readyState < cXMLHttpRequest.DONE) {\n+ oRequest.readyState++;\n+ fReadyStateChange(oRequest);\n+ // Check if we are aborted\n+ if (oRequest._aborted)\n+ return;\n+ }\n }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- },\n+ };\n+ cXMLHttpRequest.prototype.send = function(vData) {\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onsend)\n+ cXMLHttpRequest.onsend.apply(this, arguments);\n \n- /**\n- * APIMethod: setLayer\n- * Attach a new layer to the control, overriding any existing layers.\n- *\n- * Parameters:\n- * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n- * {<OpenLayers.Layer.Vector>}\n- */\n- setLayer: function(layers) {\n- var isActive = this.active;\n- this.unselectAll();\n- this.deactivate();\n- if (this.layers) {\n- this.layer.destroy();\n- this.layers = null;\n+ if (!arguments.length)\n+ vData = null;\n+\n+ // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required\n+ // BUGFIX: IE - rewrites any custom mime-type to \"text/xml\" in case an XMLNode is sent\n+ // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)\n+ if (vData && vData.nodeType) {\n+ vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;\n+ if (!this._headers[\"Content-Type\"])\n+ this._object.setRequestHeader(\"Content-Type\", \"application/xml\");\n }\n- this.initLayer(layers);\n- this.handlers.feature.layer = this.layer;\n- if (isActive) {\n- this.activate();\n+\n+ this._data = vData;\n+ /*\n+ // Add to queue\n+ if (this._async)\n+ fQueue_add(this);\n+ else*/\n+ fXMLHttpRequest_send(this);\n+ };\n+ cXMLHttpRequest.prototype.abort = function() {\n+ // Add method sniffer\n+ if (cXMLHttpRequest.onabort)\n+ cXMLHttpRequest.onabort.apply(this, arguments);\n+\n+ // BUGFIX: Gecko - unnecessary DONE when aborting\n+ if (this.readyState > cXMLHttpRequest.UNSENT)\n+ this._aborted = true;\n+\n+ this._object.abort();\n+\n+ // BUGFIX: IE - memory leak\n+ fCleanTransport(this);\n+\n+ this.readyState = cXMLHttpRequest.UNSENT;\n+\n+ delete this._data;\n+ /* if (this._async)\n+ fQueue_remove(this);*/\n+ };\n+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n+ return this._object.getAllResponseHeaders();\n+ };\n+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n+ return this._object.getResponseHeader(sName);\n+ };\n+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n+ // BUGFIX: IE - cache issue\n+ if (!this._headers)\n+ this._headers = {};\n+ this._headers[sName] = sValue;\n+\n+ return this._object.setRequestHeader(sName, sValue);\n+ };\n+\n+ // EventTarget interface implementation\n+ cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n+ return;\n+ // Add listener\n+ this._listeners.push([sName, fHandler, bUseCapture]);\n+ };\n+\n+ cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n+ break;\n+ // Remove listener\n+ if (oListener)\n+ this._listeners.splice(nIndex, 1);\n+ };\n+\n+ cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n+ var oEventPseudo = {\n+ 'type': oEvent.type,\n+ 'target': this,\n+ 'currentTarget': this,\n+ 'eventPhase': 2,\n+ 'bubbles': oEvent.bubbles,\n+ 'cancelable': oEvent.cancelable,\n+ 'timeStamp': oEvent.timeStamp,\n+ 'stopPropagation': function() {}, // There is no flow\n+ 'preventDefault': function() {}, // There is no default action\n+ 'initEvent': function() {} // Original event object should be initialized\n+ };\n+\n+ // Execute onreadystatechange\n+ if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)\n+ (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n+\n+ // Execute listeners\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == oEventPseudo.type && !oListener[2])\n+ (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);\n+ };\n+\n+ //\n+ cXMLHttpRequest.prototype.toString = function() {\n+ return '[' + \"object\" + ' ' + \"XMLHttpRequest\" + ']';\n+ };\n+\n+ cXMLHttpRequest.toString = function() {\n+ return '[' + \"XMLHttpRequest\" + ']';\n+ };\n+\n+ // Helper function\n+ function fReadyStateChange(oRequest) {\n+ // Sniffing code\n+ if (cXMLHttpRequest.onreadystatechange)\n+ cXMLHttpRequest.onreadystatechange.apply(oRequest);\n+\n+ // Fake event\n+ oRequest.dispatchEvent({\n+ 'type': \"readystatechange\",\n+ 'bubbles': false,\n+ 'cancelable': false,\n+ 'timeStamp': new Date + 0\n+ });\n+ };\n+\n+ function fGetDocument(oRequest) {\n+ var oDocument = oRequest.responseXML,\n+ sResponse = oRequest.responseText;\n+ // Try parsing responseText\n+ if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n+ oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n+ oDocument.async = false;\n+ oDocument.validateOnParse = false;\n+ oDocument.loadXML(sResponse);\n }\n- },\n+ // Check if there is no error in document\n+ if (oDocument)\n+ if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\"))\n+ return null;\n+ return oDocument;\n+ };\n \n- CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n-});\n+ function fSynchronizeValues(oRequest) {\n+ try {\n+ oRequest.responseText = oRequest._object.responseText;\n+ } catch (e) {}\n+ try {\n+ oRequest.responseXML = fGetDocument(oRequest._object);\n+ } catch (e) {}\n+ try {\n+ oRequest.status = oRequest._object.status;\n+ } catch (e) {}\n+ try {\n+ oRequest.statusText = oRequest._object.statusText;\n+ } catch (e) {}\n+ };\n+\n+ function fCleanTransport(oRequest) {\n+ // BUGFIX: IE - memory leak (on-page leak)\n+ oRequest._object.onreadystatechange = new window.Function;\n+ };\n+ /*\n+ // Queue manager\n+ var oQueuePending = {\"CRITICAL\":[],\"HIGH\":[],\"NORMAL\":[],\"LOW\":[],\"LOWEST\":[]},\n+ aQueueRunning = [];\n+ function fQueue_add(oRequest) {\n+ oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : \"NORMAL\"].push(oRequest);\n+ //\n+ setTimeout(fQueue_process);\n+ };\n+\n+ function fQueue_remove(oRequest) {\n+ for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)\n+ if (bFound)\n+ aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];\n+ else\n+ if (aQueueRunning[nIndex] == oRequest)\n+ bFound = true;\n+ if (bFound)\n+ aQueueRunning.length--;\n+ //\n+ setTimeout(fQueue_process);\n+ };\n+\n+ function fQueue_process() {\n+ if (aQueueRunning.length < 6) {\n+ for (var sPriority in oQueuePending) {\n+ if (oQueuePending[sPriority].length) {\n+ var oRequest = oQueuePending[sPriority][0];\n+ oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);\n+ //\n+ aQueueRunning.push(oRequest);\n+ // Send request\n+ fXMLHttpRequest_send(oRequest);\n+ break;\n+ }\n+ }\n+ }\n+ };\n+ */\n+ // Internet Explorer 5.0 (missing apply)\n+ if (!window.Function.prototype.apply) {\n+ window.Function.prototype.apply = function(oRequest, oArguments) {\n+ if (!oArguments)\n+ oArguments = [];\n+ oRequest.__func = this;\n+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n+ delete oRequest.__func;\n+ };\n+ };\n+\n+ // Register new object with window\n+ /**\n+ * Class: OpenLayers.Request.XMLHttpRequest\n+ * Standard-compliant (W3C) cross-browser implementation of the\n+ * XMLHttpRequest object. From\n+ * http://code.google.com/p/xmlhttprequest/.\n+ */\n+ if (!OpenLayers.Request) {\n+ /**\n+ * This allows for OpenLayers/Request.js to be included\n+ * before or after this script.\n+ */\n+ OpenLayers.Request = {};\n+ }\n+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;\n+})();\n /* ======================================================================\n- OpenLayers/Control/LayerSwitcher.js\n+ OpenLayers/Protocol/HTTP.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Lang.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Events/buttonclick.js\n+ * @requires OpenLayers/Protocol.js\n+ * @requires OpenLayers/Request/XMLHttpRequest.js\n */\n \n /**\n- * Class: OpenLayers.Control.LayerSwitcher\n- * The LayerSwitcher control displays a table of contents for the map. This\n- * allows the user interface to switch between BaseLasyers and to show or hide\n- * Overlays. By default the switcher is shown minimized on the right edge of\n- * the map, the user may expand it by clicking on the handle.\n- *\n- * To create the LayerSwitcher outside of the map, pass the Id of a html div\n- * as the first argument to the constructor.\n+ * if application uses the query string, for example, for BBOX parameters,\n+ * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.HTTP\n+ * A basic HTTP protocol for vector layers. Create a new instance with the\n+ * <OpenLayers.Protocol.HTTP> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Control>\n+ * - <OpenLayers.Protocol>\n */\n-OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n \n- /** \n- * Property: layerStates \n- * {Array(Object)} Basically a copy of the \"state\" of the map's layers \n- * the last time the control was drawn. We have this in order to avoid\n- * unnecessarily redrawing the control.\n+ /**\n+ * Property: url\n+ * {String} Service URL, read-only, set through the options\n+ * passed to constructor.\n */\n- layerStates: null,\n-\n- // DOM Elements\n+ url: null,\n \n /**\n- * Property: layersDiv\n- * {DOMElement}\n+ * Property: headers\n+ * {Object} HTTP request headers, read-only, set through the options\n+ * passed to the constructor,\n+ * Example: {'Content-Type': 'plain/text'}\n */\n- layersDiv: null,\n+ headers: null,\n \n /**\n- * Property: baseLayersDiv\n- * {DOMElement}\n+ * Property: params\n+ * {Object} Parameters of GET requests, read-only, set through the options\n+ * passed to the constructor,\n+ * Example: {'bbox': '5,5,5,5'}\n */\n- baseLayersDiv: null,\n+ params: null,\n \n /**\n- * Property: baseLayers\n- * {Array(Object)}\n+ * Property: callback\n+ * {Object} Function to be called when the <read>, <create>,\n+ * <update>, <delete> or <commit> operation completes, read-only,\n+ * set through the options passed to the constructor.\n */\n- baseLayers: null,\n-\n+ callback: null,\n \n /**\n- * Property: dataLbl\n- * {DOMElement}\n+ * Property: scope\n+ * {Object} Callback execution scope, read-only, set through the\n+ * options passed to the constructor.\n */\n- dataLbl: null,\n+ scope: null,\n \n /**\n- * Property: dataLayersDiv\n- * {DOMElement}\n+ * APIProperty: readWithPOST\n+ * {Boolean} true if read operations are done with POST requests\n+ * instead of GET, defaults to false.\n */\n- dataLayersDiv: null,\n+ readWithPOST: false,\n \n /**\n- * Property: dataLayers\n- * {Array(Object)}\n+ * APIProperty: updateWithPOST\n+ * {Boolean} true if update operations are done with POST requests\n+ * defaults to false.\n */\n- dataLayers: null,\n-\n+ updateWithPOST: false,\n \n /**\n- * Property: minimizeDiv\n- * {DOMElement}\n+ * APIProperty: deleteWithPOST\n+ * {Boolean} true if delete operations are done with POST requests\n+ * defaults to false.\n+ * if true, POST data is set to output of format.write().\n */\n- minimizeDiv: null,\n+ deleteWithPOST: false,\n \n /**\n- * Property: maximizeDiv\n- * {DOMElement}\n+ * Property: wildcarded.\n+ * {Boolean} If true percent signs are added around values\n+ * read from LIKE filters, for example if the protocol\n+ * read method is passed a LIKE filter whose property\n+ * is \"foo\" and whose value is \"bar\" the string\n+ * \"foo__ilike=%bar%\" will be sent in the query string;\n+ * defaults to false.\n */\n- maximizeDiv: null,\n+ wildcarded: false,\n \n /**\n- * APIProperty: ascending\n- * {Boolean}\n+ * APIProperty: srsInBBOX\n+ * {Boolean} Include the SRS identifier in BBOX query string parameter. \n+ * Default is false. If true and the layer has a projection object set,\n+ * any BBOX filter will be serialized with a fifth item identifying the\n+ * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n */\n- ascending: true,\n+ srsInBBOX: false,\n \n /**\n- * Constructor: OpenLayers.Control.LayerSwitcher\n+ * Constructor: OpenLayers.Protocol.HTTP\n+ * A class for giving layers generic HTTP protocol.\n *\n * Parameters:\n- * options - {Object}\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options include:\n+ * url - {String}\n+ * headers - {Object} \n+ * params - {Object} URL parameters for GET requests\n+ * format - {<OpenLayers.Format>}\n+ * callback - {Function}\n+ * scope - {Object}\n */\n initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- this.layerStates = [];\n+ options = options || {};\n+ this.params = {};\n+ this.headers = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n+\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ wildcarded: this.wildcarded,\n+ srsInBBOX: this.srsInBBOX\n+ });\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params);\n+ };\n+ }\n },\n \n /**\n * APIMethod: destroy\n+ * Clean up the protocol.\n */\n destroy: function() {\n-\n- //clear out layers info and unregister their events\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n-\n- this.map.events.un({\n- buttonclick: this.onButtonClick,\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n- });\n- this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n-\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ this.params = null;\n+ this.headers = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this);\n },\n \n /**\n- * Method: setMap\n+ * APIMethod: filterToParams\n+ * Optional method to translate an <OpenLayers.Filter> object into an object\n+ * that can be serialized as request query string provided. If a custom\n+ * method is not provided, the filter will be serialized using the \n+ * <OpenLayers.Format.QueryStringFilter> class.\n *\n- * Properties:\n- * map - {<OpenLayers.Map>}\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} filter to convert.\n+ * params - {Object} The parameters object.\n+ *\n+ * Returns:\n+ * {Object} The resulting parameters object.\n */\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n \n- this.map.events.on({\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n+ /**\n+ * APIMethod: read\n+ * Construct a request for reading new features.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Valid options:\n+ * url - {String} Url for the request.\n+ * params - {Object} Parameters to get serialized as a query string.\n+ * headers - {Object} Headers to be set on the request.\n+ * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n+ * query string.\n+ * readWithPOST - {Boolean} If the request should be done with POST.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n+ * references the HTTP request, this object is also passed to the\n+ * callback function when the request completes, its \"features\" property\n+ * is then populated with the features received from the server.\n+ */\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = options || {};\n+ options.params = OpenLayers.Util.applyDefaults(\n+ options.params, this.options.params);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(\n+ options.filter, options.params\n+ );\n+ }\n+ var readWithPOST = (options.readWithPOST !== undefined) ?\n+ options.readWithPOST : this.readWithPOST;\n+ var resp = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n });\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ if (readWithPOST) {\n+ var headers = options.headers || {};\n+ headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ data: OpenLayers.Util.getParameterString(options.params),\n+ headers: headers\n+ });\n } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ resp.priv = OpenLayers.Request.GET({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ params: options.params,\n+ headers: options.headers\n+ });\n }\n+ return resp;\n },\n \n /**\n- * Method: draw\n+ * Method: handleRead\n+ * Individual callbacks are created for read, create and update, should\n+ * a subclass need to override each one separately.\n *\n- * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the\n- * switcher tabs.\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * the user callback.\n+ * options - {Object} The user options passed to the read call.\n */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this);\n+ handleRead: function(resp, options) {\n+ this.handleResponse(resp, options);\n+ },\n \n- // create layout divs\n- this.loadContents();\n+ /**\n+ * APIMethod: create\n+ * Construct a request for writing newly created features.\n+ *\n+ * Parameters:\n+ * features - {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes, its \"features\" property is then populated with the\n+ * the features received from the server.\n+ */\n+ create: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n \n- // set mode to minimize\n- if (!this.outsideViewport) {\n- this.minimizeControl();\n- }\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: features,\n+ requestType: \"create\"\n+ });\n \n- // populate div with current info\n- this.redraw();\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleCreate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(features)\n+ });\n \n- return this.div;\n+ return resp;\n },\n \n /**\n- * Method: onButtonClick\n+ * Method: handleCreate\n+ * Called the the request issued by <create> is complete. May be overridden\n+ * by subclasses.\n *\n * Parameters:\n- * evt - {Event}\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the create call.\n */\n- onButtonClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.minimizeDiv) {\n- this.minimizeControl();\n- } else if (button === this.maximizeDiv) {\n- this.maximizeControl();\n- } else if (button._layerSwitcher === this.id) {\n- if (button[\"for\"]) {\n- button = document.getElementById(button[\"for\"]);\n- }\n- if (!button.disabled) {\n- if (button.type == \"radio\") {\n- button.checked = true;\n- this.map.setBaseLayer(this.map.getLayer(button._layer));\n- } else {\n- button.checked = !button.checked;\n- this.updateMap();\n- }\n- }\n- }\n+ handleCreate: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: clearLayersArray\n- * User specifies either \"base\" or \"data\". we then clear all the\n- * corresponding listeners, the div, and reinitialize a new array.\n+ * APIMethod: update\n+ * Construct a request updating modified feature.\n *\n * Parameters:\n- * layersType - {String}\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes, its \"features\" property is then populated with the\n+ * the feature received from the server.\n */\n- clearLayersArray: function(layersType) {\n- this[layersType + \"LayersDiv\"].innerHTML = \"\";\n- this[layersType + \"Layers\"] = [];\n- },\n+ update: function(feature, options) {\n+ options = options || {};\n+ var url = options.url ||\n+ feature.url ||\n+ this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"update\"\n+ });\n+\n+ var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n+ resp.priv = OpenLayers.Request[method]({\n+ url: url,\n+ callback: this.createCallback(this.handleUpdate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(feature)\n+ });\n \n+ return resp;\n+ },\n \n /**\n- * Method: checkRedraw\n- * Checks if the layer state has changed since the last redraw() call.\n+ * Method: handleUpdate\n+ * Called the the request issued by <update> is complete. May be overridden\n+ * by subclasses.\n *\n- * Returns:\n- * {Boolean} The layer state changed since the last redraw() call.\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the update call.\n */\n- checkRedraw: function() {\n- if (!this.layerStates.length ||\n- (this.map.layers.length != this.layerStates.length)) {\n- return true;\n- }\n-\n- for (var i = 0, len = this.layerStates.length; i < len; i++) {\n- var layerState = this.layerStates[i];\n- var layer = this.map.layers[i];\n- if ((layerState.name != layer.name) ||\n- (layerState.inRange != layer.inRange) ||\n- (layerState.id != layer.id) ||\n- (layerState.visibility != layer.visibility)) {\n- return true;\n- }\n- }\n-\n- return false;\n+ handleUpdate: function(resp, options) {\n+ this.handleResponse(resp, options);\n },\n \n /**\n- * Method: redraw\n- * Goes through and takes the current state of the Map and rebuilds the\n- * control to display that state. Groups base layers into a\n- * radio-button group and lists each data layer with a checkbox.\n+ * APIMethod: delete\n+ * Construct a request deleting a removed feature.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ * options - {Object} Optional object for configuring the request.\n+ * This object is modified and should not be reused.\n *\n * Returns:\n- * {DOMElement} A reference to the DIV DOMElement containing the control\n+ * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n+ * object, whose \"priv\" property references the HTTP request, this \n+ * object is also passed to the callback function when the request\n+ * completes.\n */\n- redraw: function() {\n- //if the state hasn't changed since last redraw, no need\n- // to do anything. Just return the existing div.\n- if (!this.checkRedraw()) {\n- return this.div;\n- }\n-\n- //clear out previous layers\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n-\n- var containsOverlays = false;\n- var containsBaseLayers = false;\n-\n- // Save state -- for checking layer if the map state changed.\n- // We save this before redrawing, because in the process of redrawing\n- // we will trigger more visibility changes, and we want to not redraw\n- // and enter an infinite loop.\n- var len = this.map.layers.length;\n- this.layerStates = new Array(len);\n- for (var i = 0; i < len; i++) {\n- var layer = this.map.layers[i];\n- this.layerStates[i] = {\n- 'name': layer.name,\n- 'visibility': layer.visibility,\n- 'inRange': layer.inRange,\n- 'id': layer.id\n- };\n- }\n-\n- var layers = this.map.layers.slice();\n- if (!this.ascending) {\n- layers.reverse();\n- }\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var baseLayer = layer.isBaseLayer;\n-\n- if (layer.displayInLayerSwitcher) {\n-\n- if (baseLayer) {\n- containsBaseLayers = true;\n- } else {\n- containsOverlays = true;\n- }\n-\n- // only check a baselayer if it is *the* baselayer, check data\n- // layers if they are visible\n- var checked = (baseLayer) ? (layer == this.map.baseLayer) :\n- layer.getVisibility();\n-\n- // create input element\n- var inputElem = document.createElement(\"input\"),\n- // The input shall have an id attribute so we can use\n- // labels to interact with them.\n- inputId = OpenLayers.Util.createUniqueID(\n- this.id + \"_input_\"\n- );\n-\n- inputElem.id = inputId;\n- inputElem.name = (baseLayer) ? this.id + \"_baseLayers\" : layer.name;\n- inputElem.type = (baseLayer) ? \"radio\" : \"checkbox\";\n- inputElem.value = layer.name;\n- inputElem.checked = checked;\n- inputElem.defaultChecked = checked;\n- inputElem.className = \"olButton\";\n- inputElem._layer = layer.id;\n- inputElem._layerSwitcher = this.id;\n-\n- if (!baseLayer && !layer.inRange) {\n- inputElem.disabled = true;\n- }\n-\n- // create span\n- var labelSpan = document.createElement(\"label\");\n- // this isn't the DOM attribute 'for', but an arbitrary name we\n- // use to find the appropriate input element in <onButtonClick>\n- labelSpan[\"for\"] = inputElem.id;\n- OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n- labelSpan._layer = layer.id;\n- labelSpan._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- labelSpan.style.color = \"gray\";\n- }\n- labelSpan.innerHTML = layer.name;\n- labelSpan.style.verticalAlign = (baseLayer) ? \"bottom\" :\n- \"baseline\";\n- // create line break\n- var br = document.createElement(\"br\");\n-\n-\n- var groupArray = (baseLayer) ? this.baseLayers :\n- this.dataLayers;\n- groupArray.push({\n- 'layer': layer,\n- 'inputElem': inputElem,\n- 'labelSpan': labelSpan\n- });\n+ \"delete\": function(feature, options) {\n+ options = options || {};\n+ var url = options.url ||\n+ feature.url ||\n+ this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n \n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"delete\"\n+ });\n \n- var groupDiv = (baseLayer) ? this.baseLayersDiv :\n- this.dataLayersDiv;\n- groupDiv.appendChild(inputElem);\n- groupDiv.appendChild(labelSpan);\n- groupDiv.appendChild(br);\n- }\n+ var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n+ var requestOptions = {\n+ url: url,\n+ callback: this.createCallback(this.handleDelete, resp, options),\n+ headers: options.headers\n+ };\n+ if (this.deleteWithPOST) {\n+ requestOptions.data = this.format.write(feature);\n }\n+ resp.priv = OpenLayers.Request[method](requestOptions);\n \n- // if no overlays, dont display the overlay label\n- this.dataLbl.style.display = (containsOverlays) ? \"\" : \"none\";\n-\n- // if no baselayers, dont display the baselayer label\n- this.baseLbl.style.display = (containsBaseLayers) ? \"\" : \"none\";\n-\n- return this.div;\n+ return resp;\n },\n \n /**\n- * Method: updateMap\n- * Cycles through the loaded data and base layer input arrays and makes\n- * the necessary calls to the Map object such that that the map's\n- * visual state corresponds to what the user has selected in\n- * the control.\n+ * Method: handleDelete\n+ * Called the the request issued by <delete> is complete. May be overridden\n+ * by subclasses.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the delete call.\n */\n- updateMap: function() {\n+ handleDelete: function(resp, options) {\n+ this.handleResponse(resp, options);\n+ },\n \n- // set the newly selected base layer\n- for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n- var layerEntry = this.baseLayers[i];\n- if (layerEntry.inputElem.checked) {\n- this.map.setBaseLayer(layerEntry.layer, false);\n+ /**\n+ * Method: handleResponse\n+ * Called by CRUD specific handlers.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n+ * any user callback.\n+ * options - {Object} The user options passed to the create, read, update,\n+ * or delete call.\n+ */\n+ handleResponse: function(resp, options) {\n+ var request = resp.priv;\n+ if (options.callback) {\n+ if (request.status >= 200 && request.status < 300) {\n+ // success\n+ if (resp.requestType != \"delete\") {\n+ resp.features = this.parseFeatures(request);\n+ }\n+ resp.code = OpenLayers.Protocol.Response.SUCCESS;\n+ } else {\n+ // failure\n+ resp.code = OpenLayers.Protocol.Response.FAILURE;\n }\n+ options.callback.call(options.scope, resp);\n }\n-\n- // set the correct visibilities for the overlays\n- for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n- var layerEntry = this.dataLayers[i];\n- layerEntry.layer.setVisibility(layerEntry.inputElem.checked);\n- }\n-\n },\n \n /**\n- * Method: maximizeControl\n- * Set up the labels and divs for the control\n+ * Method: parseFeatures\n+ * Read HTTP response body and return features.\n *\n * Parameters:\n- * e - {Event}\n+ * request - {XMLHttpRequest} The request object\n+ *\n+ * Returns:\n+ * {Array({<OpenLayers.Feature.Vector>})} or\n+ * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n */\n- maximizeControl: function(e) {\n-\n- // set the div's width and height to empty values, so\n- // the div dimensions can be controlled by CSS\n- this.div.style.width = \"\";\n- this.div.style.height = \"\";\n-\n- this.showControls(false);\n-\n- if (e != null) {\n- OpenLayers.Event.stop(e);\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText;\n+ }\n+ if (!doc || doc.length <= 0) {\n+ return null;\n }\n+ return this.format.read(doc);\n },\n \n /**\n- * Method: minimizeControl\n- * Hide all the contents of the control, shrink the size,\n- * add the maximize icon\n+ * APIMethod: commit\n+ * Iterate over each feature and take action based on the feature state.\n+ * Possible actions are create, update and delete.\n *\n * Parameters:\n- * e - {Event}\n+ * features - {Array({<OpenLayers.Feature.Vector>})}\n+ * options - {Object} Optional object for setting up intermediate commit\n+ * callbacks.\n+ *\n+ * Valid options:\n+ * create - {Object} Optional object to be passed to the <create> method.\n+ * update - {Object} Optional object to be passed to the <update> method.\n+ * delete - {Object} Optional object to be passed to the <delete> method.\n+ * callback - {Function} Optional function to be called when the commit\n+ * is complete.\n+ * scope - {Object} Optional object to be set as the scope of the callback.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,\n+ * one per request made to the server, each object's \"priv\" property\n+ * references the corresponding HTTP request.\n */\n- minimizeControl: function(e) {\n+ commit: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = [],\n+ nResponses = 0;\n \n- // to minimize the control we set its div's width\n- // and height to 0px, we cannot just set \"display\"\n- // to \"none\" because it would hide the maximize\n- // div\n- this.div.style.width = \"0px\";\n- this.div.style.height = \"0px\";\n+ // Divide up features before issuing any requests. This properly\n+ // counts requests in the event that any responses come in before\n+ // all requests have been issued.\n+ var types = {};\n+ types[OpenLayers.State.INSERT] = [];\n+ types[OpenLayers.State.UPDATE] = [];\n+ types[OpenLayers.State.DELETE] = [];\n+ var feature, list, requestFeatures = [];\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ list = types[feature.state];\n+ if (list) {\n+ list.push(feature);\n+ requestFeatures.push(feature);\n+ }\n+ }\n+ // tally up number of requests\n+ var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +\n+ types[OpenLayers.State.UPDATE].length +\n+ types[OpenLayers.State.DELETE].length;\n \n- this.showControls(true);\n+ // This response will be sent to the final callback after all the others\n+ // have been fired.\n+ var success = true;\n+ var finalResponse = new OpenLayers.Protocol.Response({\n+ reqFeatures: requestFeatures\n+ });\n \n- if (e != null) {\n- OpenLayers.Event.stop(e);\n+ function insertCallback(response) {\n+ var len = response.features ? response.features.length : 0;\n+ var fids = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ fids[i] = response.features[i].fid;\n+ }\n+ finalResponse.insertIds = fids;\n+ callback.apply(this, [response]);\n+ }\n+\n+ function callback(response) {\n+ this.callUserCallback(response, options);\n+ success = success && response.success();\n+ nResponses++;\n+ if (nResponses >= nRequests) {\n+ if (options.callback) {\n+ finalResponse.code = success ?\n+ OpenLayers.Protocol.Response.SUCCESS :\n+ OpenLayers.Protocol.Response.FAILURE;\n+ options.callback.apply(options.scope, [finalResponse]);\n+ }\n+ }\n+ }\n+\n+ // start issuing requests\n+ var queue = types[OpenLayers.State.INSERT];\n+ if (queue.length > 0) {\n+ resp.push(this.create(\n+ queue, OpenLayers.Util.applyDefaults({\n+ callback: insertCallback,\n+ scope: this\n+ }, options.create)\n+ ));\n+ }\n+ queue = types[OpenLayers.State.UPDATE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this.update(\n+ queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options.update)));\n+ }\n+ queue = types[OpenLayers.State.DELETE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this[\"delete\"](\n+ queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options[\"delete\"])));\n }\n+ return resp;\n },\n \n /**\n- * Method: showControls\n- * Hide/Show all LayerSwitcher controls depending on whether we are\n- * minimized or not\n+ * APIMethod: abort\n+ * Abort an ongoing request, the response object passed to\n+ * this method must come from this HTTP protocol (as a result\n+ * of a create, read, update, delete or commit operation).\n *\n * Parameters:\n- * minimize - {Boolean}\n+ * response - {<OpenLayers.Protocol.Response>}\n */\n- showControls: function(minimize) {\n-\n- this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n- this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n-\n- this.layersDiv.style.display = minimize ? \"none\" : \"\";\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort();\n+ }\n },\n \n /**\n- * Method: loadContents\n- * Set up the labels and divs for the control\n+ * Method: callUserCallback\n+ * This method is used from within the commit method each time an\n+ * an HTTP response is received from the server, it is responsible\n+ * for calling the user-supplied callbacks.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>}\n+ * options - {Object} The map of options passed to the commit call.\n */\n- loadContents: function() {\n-\n- // layers list div\n- this.layersDiv = document.createElement(\"div\");\n- this.layersDiv.id = this.id + \"_layersDiv\";\n- OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n-\n- this.baseLbl = document.createElement(\"div\");\n- this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n- OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n-\n- this.baseLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n-\n- this.dataLbl = document.createElement(\"div\");\n- this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n- OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n-\n- this.dataLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n-\n- if (this.ascending) {\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- } else {\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n+ callUserCallback: function(resp, options) {\n+ var opt = options[resp.requestType];\n+ if (opt && opt.callback) {\n+ opt.callback.call(opt.scope, resp);\n }\n-\n- this.div.appendChild(this.layersDiv);\n-\n- // maximize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png');\n- this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- \"OpenLayers_Control_MaximizeDiv\",\n- null,\n- null,\n- img,\n- \"absolute\");\n- OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n- this.maximizeDiv.style.display = \"none\";\n-\n- this.div.appendChild(this.maximizeDiv);\n-\n- // minimize button div\n- var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png');\n- this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\n- \"OpenLayers_Control_MinimizeDiv\",\n- null,\n- null,\n- img,\n- \"absolute\");\n- OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n- this.minimizeDiv.style.display = \"none\";\n-\n- this.div.appendChild(this.minimizeDiv);\n },\n \n- CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+ CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n });\n /* ======================================================================\n OpenLayers/Renderer/Elements.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -35712,1807 +36821,698 @@\n * Used to prevent default events (especially opening images in a new tab on\n * ctrl-click) from being executed for externalGraphic symbols\n */\n OpenLayers.Renderer.SVG.preventDefault = function(e) {\n OpenLayers.Event.preventDefault(e);\n };\n /* ======================================================================\n- OpenLayers/Protocol.js\n+ OpenLayers/Strategy.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Protocol\n- * Abstract vector layer protocol class. Not to be instantiated directly. Use\n- * one of the protocol subclasses instead.\n+ * Class: OpenLayers.Strategy\n+ * Abstract vector layer strategy class. Not to be instantiated directly. Use\n+ * one of the strategy subclasses instead.\n */\n-OpenLayers.Protocol = OpenLayers.Class({\n+OpenLayers.Strategy = OpenLayers.Class({\n \n /**\n- * Property: format\n- * {<OpenLayers.Format>} The format used by this protocol.\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n */\n- format: null,\n+ layer: null,\n \n /**\n * Property: options\n * {Object} Any options sent to the constructor.\n */\n options: null,\n \n+ /** \n+ * Property: active \n+ * {Boolean} The control is active.\n+ */\n+ active: null,\n+\n /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the protocol can set autoDestroy to false\n- * to fully control when the protocol is destroyed. Defaults to\n- * true.\n+ * Property: autoActivate\n+ * {Boolean} The creator of the strategy can set autoActivate to false\n+ * to fully control when the protocol is activated and deactivated.\n+ * Defaults to true.\n */\n- autoDestroy: true,\n+ autoActivate: true,\n \n /**\n- * Property: defaultFilter\n- * {<OpenLayers.Filter>} Optional default filter to read requests\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the strategy can set autoDestroy to false\n+ * to fully control when the strategy is destroyed. Defaults to\n+ * true.\n */\n- defaultFilter: null,\n+ autoDestroy: true,\n \n /**\n- * Constructor: OpenLayers.Protocol\n- * Abstract class for vector protocols. Create instances of a subclass.\n+ * Constructor: OpenLayers.Strategy\n+ * Abstract class for vector strategies. Create instances of a subclass.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n */\n initialize: function(options) {\n- options = options || {};\n OpenLayers.Util.extend(this, options);\n this.options = options;\n- },\n-\n- /**\n- * Method: mergeWithDefaultFilter\n- * Merge filter passed to the read method with the default one\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>}\n- */\n- mergeWithDefaultFilter: function(filter) {\n- var merged;\n- if (filter && this.defaultFilter) {\n- merged = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.defaultFilter, filter]\n- });\n- } else {\n- merged = filter || this.defaultFilter || undefined;\n- }\n- return merged;\n+ // set the active property here, so that user cannot override it\n+ this.active = false;\n },\n \n /**\n * APIMethod: destroy\n- * Clean up the protocol.\n+ * Clean up the strategy.\n */\n destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n this.options = null;\n- this.format = null;\n },\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n+ * Method: setLayer\n+ * Called to set the <layer> property.\n *\n * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * layer - {<OpenLayers.Layer.Vector>}\n */\n- read: function(options) {\n- options = options || {};\n- options.filter = this.mergeWithDefaultFilter(options.filter);\n+ setLayer: function(layer) {\n+ this.layer = layer;\n },\n \n-\n- /**\n- * APIMethod: create\n- * Construct a request for writing newly created features.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- create: function() {},\n-\n- /**\n- * APIMethod: update\n- * Construct a request updating modified features.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n- */\n- update: function() {},\n-\n /**\n- * APIMethod: delete\n- * Construct a request deleting a removed feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n+ * Method: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n *\n * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, the same object will be passed to the callback function passed\n- * if one exists in the options object.\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- \"delete\": function() {},\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true;\n+ }\n+ return false;\n+ },\n \n /**\n- * APIMethod: commit\n- * Go over the features and for each take action\n- * based on the feature state. Possible actions are create,\n- * update and delete.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})}\n- * options - {Object} Object whose possible keys are \"create\", \"update\",\n- * \"delete\", \"callback\" and \"scope\", the values referenced by the\n- * first three are objects as passed to the \"create\", \"update\", and\n- * \"delete\" methods, the value referenced by the \"callback\" key is\n- * a function which is called when the commit operation is complete\n- * using the scope referenced by the \"scope\" key.\n+ * Method: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n *\n * Returns:\n- * {Array({<OpenLayers.Protocol.Response>})} An array of\n- * <OpenLayers.Protocol.Response> objects.\n- */\n- commit: function() {},\n-\n- /**\n- * Method: abort\n- * Abort an ongoing request.\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>}\n- */\n- abort: function(response) {},\n-\n- /**\n- * Method: createCallback\n- * Returns a function that applies the given public method with resp and\n- * options arguments.\n- *\n- * Parameters:\n- * method - {Function} The method to be applied by the callback.\n- * response - {<OpenLayers.Protocol.Response>} The protocol response object.\n- * options - {Object} Options sent to the protocol method\n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n */\n- createCallback: function(method, response, options) {\n- return OpenLayers.Function.bind(function() {\n- method.apply(this, [response, options]);\n- }, this);\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true;\n+ }\n+ return false;\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol\"\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n });\n+/* ======================================================================\n+ OpenLayers/Strategy/Fixed.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n /**\n- * Class: OpenLayers.Protocol.Response\n- * Protocols return Response objects to their users.\n+ * @requires OpenLayers/Strategy.js\n */\n-OpenLayers.Protocol.Response = OpenLayers.Class({\n- /**\n- * Property: code\n- * {Number} - OpenLayers.Protocol.Response.SUCCESS or\n- * OpenLayers.Protocol.Response.FAILURE\n- */\n- code: null,\n-\n- /**\n- * Property: requestType\n- * {String} The type of request this response corresponds to. Either\n- * \"create\", \"read\", \"update\" or \"delete\".\n- */\n- requestType: null,\n-\n- /**\n- * Property: last\n- * {Boolean} - true if this is the last response expected in a commit,\n- * false otherwise, defaults to true.\n- */\n- last: true,\n \n- /**\n- * Property: features\n- * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n- * The features returned in the response by the server. Depending on the \n- * protocol's read payload, either features or data will be populated.\n- */\n- features: null,\n+/**\n+ * Class: OpenLayers.Strategy.Fixed\n+ * A simple strategy that requests features once and never requests new data.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Strategy>\n+ */\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * Property: data\n- * {Object}\n- * The data returned in the response by the server. Depending on the \n- * protocol's read payload, either features or data will be populated.\n+ * APIProperty: preload\n+ * {Boolean} Load data before layer made visible. Enabling this may result\n+ * in considerable overhead if your application loads many data layers\n+ * that are not visible by default. Default is false.\n */\n- data: null,\n+ preload: false,\n \n /**\n- * Property: reqFeatures\n- * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}\n- * The features provided by the user and placed in the request by the\n- * protocol.\n+ * Constructor: OpenLayers.Strategy.Fixed\n+ * Create a new Fixed strategy.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- reqFeatures: null,\n \n /**\n- * Property: priv\n+ * Method: activate\n+ * Activate the strategy: load data or add listener to load when visible\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- priv: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"refresh\": this.load,\n+ scope: this\n+ });\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load();\n+ } else {\n+ this.layer.events.on({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ }\n+ return activated;\n+ },\n \n /**\n- * Property: error\n- * {Object} The error object in case a service exception was encountered.\n+ * Method: deactivate\n+ * Deactivate the strategy. Undo what is done in <activate>.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- error: null,\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ \"refresh\": this.load,\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n+ },\n \n /**\n- * Constructor: OpenLayers.Protocol.Response\n+ * Method: load\n+ * Tells protocol to load data and unhooks the visibilitychanged event\n *\n * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n+ * options - {Object} options to pass to protocol read.\n */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n+ });\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: success\n+ * Method: merge\n+ * Add all features to the layer.\n+ * If the layer projection differs from the map projection, features\n+ * will be transformed from the layer projection to the map projection.\n *\n- * Returns:\n- * {Boolean} - true on success, false otherwise\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object passed\n+ * by the protocol.\n */\n- success: function() {\n- return this.code > 0;\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local);\n+ }\n+ }\n+ }\n+ layer.addFeatures(features);\n+ }\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ });\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.Response\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n });\n-\n-OpenLayers.Protocol.Response.SUCCESS = 1;\n-OpenLayers.Protocol.Response.FAILURE = 0;\n /* ======================================================================\n- OpenLayers/Request.js\n+ OpenLayers/Filter/Spatial.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * TODO: deprecate me\n- * Use OpenLayers.Request.proxy instead.\n+ * @requires OpenLayers/Filter.js\n */\n-OpenLayers.ProxyHost = \"\";\n \n /**\n- * Namespace: OpenLayers.Request\n- * The OpenLayers.Request namespace contains convenience methods for working\n- * with XMLHttpRequests. These methods work with a cross-browser\n- * W3C compliant <OpenLayers.Request.XMLHttpRequest> class.\n+ * Class: OpenLayers.Filter.Spatial\n+ * This class represents a spatial filter.\n+ * Currently implemented: BBOX, DWithin and Intersects\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Filter>\n */\n-if (!OpenLayers.Request) {\n- /**\n- * This allows for OpenLayers/Request/XMLHttpRequest.js to be included\n- * before or after this script.\n- */\n- OpenLayers.Request = {};\n-}\n-OpenLayers.Util.extend(OpenLayers.Request, {\n-\n- /**\n- * Constant: DEFAULT_CONFIG\n- * {Object} Default configuration for all requests.\n- */\n- DEFAULT_CONFIG: {\n- method: \"GET\",\n- url: window.location.href,\n- async: true,\n- user: undefined,\n- password: undefined,\n- params: null,\n- proxy: OpenLayers.ProxyHost,\n- headers: {},\n- data: null,\n- callback: function() {},\n- success: null,\n- failure: null,\n- scope: null\n- },\n-\n- /**\n- * Constant: URL_SPLIT_REGEX\n- */\n- URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n+OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n \n /**\n- * APIProperty: events\n- * {<OpenLayers.Events>} An events object that handles all \n- * events on the {<OpenLayers.Request>} object.\n+ * APIProperty: type\n+ * {String} Type of spatial filter.\n *\n- * All event listeners will receive an event object with three properties:\n- * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.\n- * config - {Object} The config object sent to the specific request method.\n- * requestUrl - {String} The request url.\n- * \n- * Supported event types:\n- * complete - Triggered when we have a response from the request, if a\n- * listener returns false, no further response processing will take\n- * place.\n- * success - Triggered when the HTTP response has a success code (200-299).\n- * failure - Triggered when the HTTP response does not have a success code.\n+ * The type should be one of:\n+ * - OpenLayers.Filter.Spatial.BBOX\n+ * - OpenLayers.Filter.Spatial.INTERSECTS\n+ * - OpenLayers.Filter.Spatial.DWITHIN\n+ * - OpenLayers.Filter.Spatial.WITHIN\n+ * - OpenLayers.Filter.Spatial.CONTAINS\n */\n- events: new OpenLayers.Events(this),\n+ type: null,\n \n /**\n- * Method: makeSameOrigin\n- * Using the specified proxy, returns a same origin url of the provided url.\n- *\n- * Parameters:\n- * url - {String} An arbitrary url\n- * proxy {String|Function} The proxy to use to make the provided url a\n- * same origin url.\n- *\n- * Returns\n- * {String} the same origin url. If no proxy is provided, the returned url\n- * will be the same as the provided url.\n+ * APIProperty: property\n+ * {String} Name of the context property to compare.\n */\n- makeSameOrigin: function(url, proxy) {\n- var sameOrigin = url.indexOf(\"http\") !== 0;\n- var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n- if (urlParts) {\n- var location = window.location;\n- sameOrigin =\n- urlParts[1] == location.protocol &&\n- urlParts[3] == location.hostname;\n- var uPort = urlParts[4],\n- lPort = location.port;\n- if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n- sameOrigin = sameOrigin && uPort == lPort;\n- }\n- }\n- if (!sameOrigin) {\n- if (proxy) {\n- if (typeof proxy == \"function\") {\n- url = proxy(url);\n- } else {\n- url = proxy + encodeURIComponent(url);\n- }\n- }\n- }\n- return url;\n- },\n+ property: null,\n \n /**\n- * APIMethod: issue\n- * Create a new XMLHttpRequest object, open it, set any headers, bind\n- * a callback to done state, and send any data. It is recommended that\n- * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.\n- * This method is only documented to provide detail on the configuration\n- * options available to all request methods.\n- *\n- * Parameters:\n- * config - {Object} Object containing properties for configuring the\n- * request. Allowed configuration properties are described below.\n- * This object is modified and should not be reused.\n- *\n- * Allowed config properties:\n- * method - {String} One of GET, POST, PUT, DELETE, HEAD, or\n- * OPTIONS. Default is GET.\n- * url - {String} URL for the request.\n- * async - {Boolean} Open an asynchronous request. Default is true.\n- * user - {String} User for relevant authentication scheme. Set\n- * to null to clear current user.\n- * password - {String} Password for relevant authentication scheme.\n- * Set to null to clear current password.\n- * proxy - {String} Optional proxy. Defaults to\n- * <OpenLayers.ProxyHost>.\n- * params - {Object} Any key:value pairs to be appended to the\n- * url as a query string. Assumes url doesn't already include a query\n- * string or hash. Typically, this is only appropriate for <GET>\n- * requests where the query string will be appended to the url.\n- * Parameter values that are arrays will be\n- * concatenated with a comma (note that this goes against form-encoding)\n- * as is done with <OpenLayers.Util.getParameterString>.\n- * headers - {Object} Object with header:value pairs to be set on\n- * the request.\n- * data - {String | Document} Optional data to send with the request.\n- * Typically, this is only used with <POST> and <PUT> requests.\n- * Make sure to provide the appropriate \"Content-Type\" header for your\n- * data. For <POST> and <PUT> requests, the content type defaults to\n- * \"application-xml\". If your data is a different content type, or\n- * if you are using a different HTTP method, set the \"Content-Type\"\n- * header to match your data type.\n- * callback - {Function} Function to call when request is done.\n- * To determine if the request failed, check request.status (200\n- * indicates success).\n- * success - {Function} Optional function to call if request status is in\n- * the 200s. This will be called in addition to callback above and\n- * would typically only be used as an alternative.\n- * failure - {Function} Optional function to call if request status is not\n- * in the 200s. This will be called in addition to callback above and\n- * would typically only be used as an alternative.\n- * scope - {Object} If callback is a public method on some object,\n- * set the scope to that object.\n- *\n- * Returns:\n- * {XMLHttpRequest} Request object. To abort the request before a response\n- * is received, call abort() on the request object.\n+ * APIProperty: value\n+ * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry\n+ * to be used by the filter. Use bounds for BBOX filters and geometry\n+ * for INTERSECTS or DWITHIN filters.\n */\n- issue: function(config) {\n- // apply default config - proxy host may have changed\n- var defaultConfig = OpenLayers.Util.extend(\n- this.DEFAULT_CONFIG, {\n- proxy: OpenLayers.ProxyHost\n- }\n- );\n- config = config || {};\n- config.headers = config.headers || {};\n- config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n- config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n- // Always set the \"X-Requested-With\" header to signal that this request\n- // was issued through the XHR-object. Since header keys are case \n- // insensitive and we want to allow overriding of the \"X-Requested-With\"\n- // header through the user we cannot use applyDefaults, but have to \n- // check manually whether we were called with a \"X-Requested-With\"\n- // header.\n- var customRequestedWithHeader = false,\n- headerKey;\n- for (headerKey in config.headers) {\n- if (config.headers.hasOwnProperty(headerKey)) {\n- if (headerKey.toLowerCase() === 'x-requested-with') {\n- customRequestedWithHeader = true;\n- }\n- }\n- }\n- if (customRequestedWithHeader === false) {\n- // we did not have a custom \"X-Requested-With\" header\n- config.headers['X-Requested-With'] = 'XMLHttpRequest';\n- }\n-\n- // create request, open, and set headers\n- var request = new OpenLayers.Request.XMLHttpRequest();\n- var url = OpenLayers.Util.urlAppend(config.url,\n- OpenLayers.Util.getParameterString(config.params || {}));\n- url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n- request.open(\n- config.method, url, config.async, config.user, config.password\n- );\n- for (var header in config.headers) {\n- request.setRequestHeader(header, config.headers[header]);\n- }\n-\n- var events = this.events;\n-\n- // we want to execute runCallbacks with \"this\" as the\n- // execution scope\n- var self = this;\n-\n- request.onreadystatechange = function() {\n- if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n- var proceed = events.triggerEvent(\n- \"complete\", {\n- request: request,\n- config: config,\n- requestUrl: url\n- }\n- );\n- if (proceed !== false) {\n- self.runCallbacks({\n- request: request,\n- config: config,\n- requestUrl: url\n- });\n- }\n- }\n- };\n-\n- // send request (optionally with data) and return\n- // call in a timeout for asynchronous requests so the return is\n- // available before readyState == 4 for cached docs\n- if (config.async === false) {\n- request.send(config.data);\n- } else {\n- window.setTimeout(function() {\n- if (request.readyState !== 0) { // W3C: 0-UNSENT\n- request.send(config.data);\n- }\n- }, 0);\n- }\n- return request;\n- },\n+ value: null,\n \n /**\n- * Method: runCallbacks\n- * Calls the complete, success and failure callbacks. Application\n- * can listen to the \"complete\" event, have the listener \n- * display a confirm window and always return false, and\n- * execute OpenLayers.Request.runCallbacks if the user\n- * hits \"yes\" in the confirm window.\n- *\n- * Parameters:\n- * options - {Object} Hash containing request, config and requestUrl keys\n+ * APIProperty: distance\n+ * {Number} The distance to use in a DWithin spatial filter.\n */\n- runCallbacks: function(options) {\n- var request = options.request;\n- var config = options.config;\n-\n- // bind callbacks to readyState 4 (done)\n- var complete = (config.scope) ?\n- OpenLayers.Function.bind(config.callback, config.scope) :\n- config.callback;\n-\n- // optional success callback\n- var success;\n- if (config.success) {\n- success = (config.scope) ?\n- OpenLayers.Function.bind(config.success, config.scope) :\n- config.success;\n- }\n-\n- // optional failure callback\n- var failure;\n- if (config.failure) {\n- failure = (config.scope) ?\n- OpenLayers.Function.bind(config.failure, config.scope) :\n- config.failure;\n- }\n-\n- if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" &&\n- request.responseText) {\n- request.status = 200;\n- }\n- complete(request);\n-\n- if (!request.status || (request.status >= 200 && request.status < 300)) {\n- this.events.triggerEvent(\"success\", options);\n- if (success) {\n- success(request);\n- }\n- }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"failure\", options);\n- if (failure) {\n- failure(request);\n- }\n- }\n- },\n+ distance: null,\n \n /**\n- * APIMethod: GET\n- * Send an HTTP GET request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to GET.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n+ * APIProperty: distanceUnits\n+ * {String} The units to use for the distance, e.g. 'm'.\n */\n- GET: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"GET\"\n- });\n- return OpenLayers.Request.issue(config);\n- },\n+ distanceUnits: null,\n \n- /**\n- * APIMethod: POST\n- * Send a POST request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to POST and \"Content-Type\" header set to \"application/xml\".\n+ /** \n+ * Constructor: OpenLayers.Filter.Spatial\n+ * Creates a spatial filter.\n *\n * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties. The\n- * default \"Content-Type\" header will be set to \"application-xml\" if\n- * none is provided. This object is modified and should not be reused.\n+ * options - {Object} An optional object with properties to set on the\n+ * filter.\n * \n * Returns:\n- * {XMLHttpRequest} Request object.\n+ * {<OpenLayers.Filter.Spatial>}\n */\n- POST: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"POST\"\n- });\n- // set content type to application/xml if it isn't already set\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\";\n- }\n- return OpenLayers.Request.issue(config);\n- },\n \n /**\n- * APIMethod: PUT\n- * Send an HTTP PUT request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to PUT and \"Content-Type\" header set to \"application/xml\".\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties. The\n- * default \"Content-Type\" header will be set to \"application-xml\" if\n- * none is provided. This object is modified and should not be reused.\n+ * Method: evaluate\n+ * Evaluates this filter for a specific feature.\n * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- PUT: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"PUT\"\n- });\n- // set content type to application/xml if it isn't already set\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\";\n- }\n- return OpenLayers.Request.issue(config);\n- },\n-\n- /**\n- * APIMethod: DELETE\n- * Send an HTTP DELETE request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to DELETE.\n- *\n * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n+ * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.\n * \n * Returns:\n- * {XMLHttpRequest} Request object.\n+ * {Boolean} The feature meets filter criteria.\n */\n- DELETE: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"DELETE\"\n- });\n- return OpenLayers.Request.issue(config);\n+ evaluate: function(feature) {\n+ var intersect = false;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ case OpenLayers.Filter.Spatial.INTERSECTS:\n+ if (feature.geometry) {\n+ var geom = this.value;\n+ if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n+ geom = this.value.toGeometry();\n+ }\n+ if (feature.geometry.intersects(geom)) {\n+ intersect = true;\n+ }\n+ }\n+ break;\n+ default:\n+ throw new Error('evaluate is not implemented for this filter type.');\n+ }\n+ return intersect;\n },\n \n /**\n- * APIMethod: HEAD\n- * Send an HTTP HEAD request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to HEAD.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n+ * APIMethod: clone\n+ * Clones this filter.\n * \n * Returns:\n- * {XMLHttpRequest} Request object.\n+ * {<OpenLayers.Filter.Spatial>} Clone of this filter.\n */\n- HEAD: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"HEAD\"\n- });\n- return OpenLayers.Request.issue(config);\n+ clone: function() {\n+ var options = OpenLayers.Util.applyDefaults({\n+ value: this.value && this.value.clone && this.value.clone()\n+ }, this);\n+ return new OpenLayers.Filter.Spatial(options);\n },\n-\n- /**\n- * APIMethod: OPTIONS\n- * Send an HTTP OPTIONS request. Additional configuration properties are\n- * documented in the <issue> method, with the method property set\n- * to OPTIONS.\n- *\n- * Parameters:\n- * config - {Object} Object with properties for configuring the request.\n- * See the <issue> method for documentation of allowed properties.\n- * This object is modified and should not be reused.\n- * \n- * Returns:\n- * {XMLHttpRequest} Request object.\n- */\n- OPTIONS: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"OPTIONS\"\n- });\n- return OpenLayers.Request.issue(config);\n- }\n-\n+ CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n });\n-/* ======================================================================\n- OpenLayers/Request/XMLHttpRequest.js\n- ====================================================================== */\n-\n-// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)\n-//\n-// Licensed under the Apache License, Version 2.0 (the \"License\");\n-// you may not use this file except in compliance with the License.\n-// You may obtain a copy of the License at\n-//\n-// http://www.apache.org/licenses/LICENSE-2.0\n-//\n-// Unless required by applicable law or agreed to in writing, software\n-// distributed under the License is distributed on an \"AS IS\" BASIS,\n-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n-// See the License for the specific language governing permissions and\n-// limitations under the License.\n-\n-/**\n- * @requires OpenLayers/Request.js\n- */\n-\n-(function() {\n-\n- // Save reference to earlier defined object implementation (if any)\n- var oXMLHttpRequest = window.XMLHttpRequest;\n-\n- // Define on browser type\n- var bGecko = !!window.controllers,\n- bIE = window.document.all && !window.opera,\n- bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n-\n- // Enables \"XMLHttpRequest()\" call next to \"new XMLHttpReques()\"\n- function fXMLHttpRequest() {\n- this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n- this._listeners = [];\n- };\n-\n- // Constructor\n- function cXMLHttpRequest() {\n- return new fXMLHttpRequest;\n- };\n- cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n-\n- // BUGFIX: Firefox with Firebug installed would break pages if not executed\n- if (bGecko && oXMLHttpRequest.wrapped)\n- cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n-\n- // Constants\n- cXMLHttpRequest.UNSENT = 0;\n- cXMLHttpRequest.OPENED = 1;\n- cXMLHttpRequest.HEADERS_RECEIVED = 2;\n- cXMLHttpRequest.LOADING = 3;\n- cXMLHttpRequest.DONE = 4;\n-\n- // Public Properties\n- cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n- cXMLHttpRequest.prototype.responseText = '';\n- cXMLHttpRequest.prototype.responseXML = null;\n- cXMLHttpRequest.prototype.status = 0;\n- cXMLHttpRequest.prototype.statusText = '';\n-\n- // Priority proposal\n- cXMLHttpRequest.prototype.priority = \"NORMAL\";\n-\n- // Instance-level Events Handlers\n- cXMLHttpRequest.prototype.onreadystatechange = null;\n-\n- // Class-level Events Handlers\n- cXMLHttpRequest.onreadystatechange = null;\n- cXMLHttpRequest.onopen = null;\n- cXMLHttpRequest.onsend = null;\n- cXMLHttpRequest.onabort = null;\n-\n- // Public Methods\n- cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n- // Delete headers, required when object is reused\n- delete this._headers;\n-\n- // When bAsync parameter value is omitted, use true as default\n- if (arguments.length < 3)\n- bAsync = true;\n-\n- // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests\n- this._async = bAsync;\n-\n- // Set the onreadystatechange handler\n- var oRequest = this,\n- nState = this.readyState,\n- fOnUnload;\n-\n- // BUGFIX: IE - memory leak on page unload (inter-page leak)\n- if (bIE && bAsync) {\n- fOnUnload = function() {\n- if (nState != cXMLHttpRequest.DONE) {\n- fCleanTransport(oRequest);\n- // Safe to abort here since onreadystatechange handler removed\n- oRequest.abort();\n- }\n- };\n- window.attachEvent(\"onunload\", fOnUnload);\n- }\n-\n- // Add method sniffer\n- if (cXMLHttpRequest.onopen)\n- cXMLHttpRequest.onopen.apply(this, arguments);\n-\n- if (arguments.length > 4)\n- this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else\n- if (arguments.length > 3)\n- this._object.open(sMethod, sUrl, bAsync, sUser);\n- else\n- this._object.open(sMethod, sUrl, bAsync);\n-\n- this.readyState = cXMLHttpRequest.OPENED;\n- fReadyStateChange(this);\n-\n- this._object.onreadystatechange = function() {\n- if (bGecko && !bAsync)\n- return;\n-\n- // Synchronize state\n- oRequest.readyState = oRequest._object.readyState;\n-\n- //\n- fSynchronizeValues(oRequest);\n-\n- // BUGFIX: Firefox fires unnecessary DONE when aborting\n- if (oRequest._aborted) {\n- // Reset readyState to UNSENT\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n-\n- // Return now\n- return;\n- }\n-\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- // Free up queue\n- delete oRequest._data;\n- /* if (bAsync)\n- fQueue_remove(oRequest);*/\n- //\n- fCleanTransport(oRequest);\n- // Uncomment this block if you need a fix for IE cache\n- /*\n- // BUGFIX: IE - cache issue\n- if (!oRequest._object.getResponseHeader(\"Date\")) {\n- // Save object to cache\n- oRequest._cached = oRequest._object;\n-\n- // Instantiate a new transport object\n- cXMLHttpRequest.call(oRequest);\n-\n- // Re-send request\n- if (sUser) {\n- if (sPassword)\n- oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else\n- oRequest._object.open(sMethod, sUrl, bAsync, sUser);\n- }\n- else\n- oRequest._object.open(sMethod, sUrl, bAsync);\n- oRequest._object.setRequestHeader(\"If-Modified-Since\", oRequest._cached.getResponseHeader(\"Last-Modified\") || new window.Date(0));\n- // Copy headers set\n- if (oRequest._headers)\n- for (var sHeader in oRequest._headers)\n- if (typeof oRequest._headers[sHeader] == \"string\") // Some frameworks prototype objects with functions\n- oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);\n-\n- oRequest._object.onreadystatechange = function() {\n- // Synchronize state\n- oRequest.readyState = oRequest._object.readyState;\n-\n- if (oRequest._aborted) {\n- //\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n-\n- // Return\n- return;\n- }\n-\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- // Clean Object\n- fCleanTransport(oRequest);\n-\n- // get cached request\n- if (oRequest.status == 304)\n- oRequest._object = oRequest._cached;\n-\n- //\n- delete oRequest._cached;\n-\n- //\n- fSynchronizeValues(oRequest);\n-\n- //\n- fReadyStateChange(oRequest);\n-\n- // BUGFIX: IE - memory leak in interrupted\n- if (bIE && bAsync)\n- window.detachEvent(\"onunload\", fOnUnload);\n- }\n- };\n- oRequest._object.send(null);\n-\n- // Return now - wait until re-sent request is finished\n- return;\n- };\n- */\n- // BUGFIX: IE - memory leak in interrupted\n- if (bIE && bAsync)\n- window.detachEvent(\"onunload\", fOnUnload);\n- }\n-\n- // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice\n- if (nState != oRequest.readyState)\n- fReadyStateChange(oRequest);\n-\n- nState = oRequest.readyState;\n- }\n- };\n-\n- function fXMLHttpRequest_send(oRequest) {\n- oRequest._object.send(oRequest._data);\n-\n- // BUGFIX: Gecko - missing readystatechange calls in synchronous requests\n- if (bGecko && !oRequest._async) {\n- oRequest.readyState = cXMLHttpRequest.OPENED;\n-\n- // Synchronize state\n- fSynchronizeValues(oRequest);\n-\n- // Simulate missing states\n- while (oRequest.readyState < cXMLHttpRequest.DONE) {\n- oRequest.readyState++;\n- fReadyStateChange(oRequest);\n- // Check if we are aborted\n- if (oRequest._aborted)\n- return;\n- }\n- }\n- };\n- cXMLHttpRequest.prototype.send = function(vData) {\n- // Add method sniffer\n- if (cXMLHttpRequest.onsend)\n- cXMLHttpRequest.onsend.apply(this, arguments);\n-\n- if (!arguments.length)\n- vData = null;\n-\n- // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required\n- // BUGFIX: IE - rewrites any custom mime-type to \"text/xml\" in case an XMLNode is sent\n- // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)\n- if (vData && vData.nodeType) {\n- vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;\n- if (!this._headers[\"Content-Type\"])\n- this._object.setRequestHeader(\"Content-Type\", \"application/xml\");\n- }\n-\n- this._data = vData;\n- /*\n- // Add to queue\n- if (this._async)\n- fQueue_add(this);\n- else*/\n- fXMLHttpRequest_send(this);\n- };\n- cXMLHttpRequest.prototype.abort = function() {\n- // Add method sniffer\n- if (cXMLHttpRequest.onabort)\n- cXMLHttpRequest.onabort.apply(this, arguments);\n-\n- // BUGFIX: Gecko - unnecessary DONE when aborting\n- if (this.readyState > cXMLHttpRequest.UNSENT)\n- this._aborted = true;\n-\n- this._object.abort();\n-\n- // BUGFIX: IE - memory leak\n- fCleanTransport(this);\n-\n- this.readyState = cXMLHttpRequest.UNSENT;\n-\n- delete this._data;\n- /* if (this._async)\n- fQueue_remove(this);*/\n- };\n- cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n- return this._object.getAllResponseHeaders();\n- };\n- cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n- return this._object.getResponseHeader(sName);\n- };\n- cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n- // BUGFIX: IE - cache issue\n- if (!this._headers)\n- this._headers = {};\n- this._headers[sName] = sValue;\n-\n- return this._object.setRequestHeader(sName, sValue);\n- };\n-\n- // EventTarget interface implementation\n- cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n- return;\n- // Add listener\n- this._listeners.push([sName, fHandler, bUseCapture]);\n- };\n-\n- cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)\n- break;\n- // Remove listener\n- if (oListener)\n- this._listeners.splice(nIndex, 1);\n- };\n-\n- cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n- var oEventPseudo = {\n- 'type': oEvent.type,\n- 'target': this,\n- 'currentTarget': this,\n- 'eventPhase': 2,\n- 'bubbles': oEvent.bubbles,\n- 'cancelable': oEvent.cancelable,\n- 'timeStamp': oEvent.timeStamp,\n- 'stopPropagation': function() {}, // There is no flow\n- 'preventDefault': function() {}, // There is no default action\n- 'initEvent': function() {} // Original event object should be initialized\n- };\n-\n- // Execute onreadystatechange\n- if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)\n- (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n-\n- // Execute listeners\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == oEventPseudo.type && !oListener[2])\n- (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);\n- };\n-\n- //\n- cXMLHttpRequest.prototype.toString = function() {\n- return '[' + \"object\" + ' ' + \"XMLHttpRequest\" + ']';\n- };\n-\n- cXMLHttpRequest.toString = function() {\n- return '[' + \"XMLHttpRequest\" + ']';\n- };\n-\n- // Helper function\n- function fReadyStateChange(oRequest) {\n- // Sniffing code\n- if (cXMLHttpRequest.onreadystatechange)\n- cXMLHttpRequest.onreadystatechange.apply(oRequest);\n-\n- // Fake event\n- oRequest.dispatchEvent({\n- 'type': \"readystatechange\",\n- 'bubbles': false,\n- 'cancelable': false,\n- 'timeStamp': new Date + 0\n- });\n- };\n-\n- function fGetDocument(oRequest) {\n- var oDocument = oRequest.responseXML,\n- sResponse = oRequest.responseText;\n- // Try parsing responseText\n- if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n- oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n- oDocument.async = false;\n- oDocument.validateOnParse = false;\n- oDocument.loadXML(sResponse);\n- }\n- // Check if there is no error in document\n- if (oDocument)\n- if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\"))\n- return null;\n- return oDocument;\n- };\n-\n- function fSynchronizeValues(oRequest) {\n- try {\n- oRequest.responseText = oRequest._object.responseText;\n- } catch (e) {}\n- try {\n- oRequest.responseXML = fGetDocument(oRequest._object);\n- } catch (e) {}\n- try {\n- oRequest.status = oRequest._object.status;\n- } catch (e) {}\n- try {\n- oRequest.statusText = oRequest._object.statusText;\n- } catch (e) {}\n- };\n-\n- function fCleanTransport(oRequest) {\n- // BUGFIX: IE - memory leak (on-page leak)\n- oRequest._object.onreadystatechange = new window.Function;\n- };\n- /*\n- // Queue manager\n- var oQueuePending = {\"CRITICAL\":[],\"HIGH\":[],\"NORMAL\":[],\"LOW\":[],\"LOWEST\":[]},\n- aQueueRunning = [];\n- function fQueue_add(oRequest) {\n- oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : \"NORMAL\"].push(oRequest);\n- //\n- setTimeout(fQueue_process);\n- };\n-\n- function fQueue_remove(oRequest) {\n- for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)\n- if (bFound)\n- aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];\n- else\n- if (aQueueRunning[nIndex] == oRequest)\n- bFound = true;\n- if (bFound)\n- aQueueRunning.length--;\n- //\n- setTimeout(fQueue_process);\n- };\n \n- function fQueue_process() {\n- if (aQueueRunning.length < 6) {\n- for (var sPriority in oQueuePending) {\n- if (oQueuePending[sPriority].length) {\n- var oRequest = oQueuePending[sPriority][0];\n- oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);\n- //\n- aQueueRunning.push(oRequest);\n- // Send request\n- fXMLHttpRequest_send(oRequest);\n- break;\n- }\n- }\n- }\n- };\n- */\n- // Internet Explorer 5.0 (missing apply)\n- if (!window.Function.prototype.apply) {\n- window.Function.prototype.apply = function(oRequest, oArguments) {\n- if (!oArguments)\n- oArguments = [];\n- oRequest.__func = this;\n- oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n- delete oRequest.__func;\n- };\n- };\n-\n- // Register new object with window\n- /**\n- * Class: OpenLayers.Request.XMLHttpRequest\n- * Standard-compliant (W3C) cross-browser implementation of the\n- * XMLHttpRequest object. From\n- * http://code.google.com/p/xmlhttprequest/.\n- */\n- if (!OpenLayers.Request) {\n- /**\n- * This allows for OpenLayers/Request.js to be included\n- * before or after this script.\n- */\n- OpenLayers.Request = {};\n- }\n- OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;\n-})();\n+OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n+OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n+OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n+OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n+OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n /* ======================================================================\n- OpenLayers/Protocol/HTTP.js\n+ OpenLayers/Strategy/BBOX.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Protocol.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * if application uses the query string, for example, for BBOX parameters,\n- * OpenLayers/Format/QueryStringFilter.js should be included in the build config file\n+ * @requires OpenLayers/Strategy.js\n+ * @requires OpenLayers/Filter/Spatial.js\n */\n \n /**\n- * Class: OpenLayers.Protocol.HTTP\n- * A basic HTTP protocol for vector layers. Create a new instance with the\n- * <OpenLayers.Protocol.HTTP> constructor.\n+ * Class: OpenLayers.Strategy.BBOX\n+ * A simple strategy that reads new features when the viewport invalidates\n+ * some bounds.\n *\n * Inherits from:\n- * - <OpenLayers.Protocol>\n+ * - <OpenLayers.Strategy>\n */\n-OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n-\n- /**\n- * Property: url\n- * {String} Service URL, read-only, set through the options\n- * passed to constructor.\n- */\n- url: null,\n-\n- /**\n- * Property: headers\n- * {Object} HTTP request headers, read-only, set through the options\n- * passed to the constructor,\n- * Example: {'Content-Type': 'plain/text'}\n- */\n- headers: null,\n-\n- /**\n- * Property: params\n- * {Object} Parameters of GET requests, read-only, set through the options\n- * passed to the constructor,\n- * Example: {'bbox': '5,5,5,5'}\n- */\n- params: null,\n-\n- /**\n- * Property: callback\n- * {Object} Function to be called when the <read>, <create>,\n- * <update>, <delete> or <commit> operation completes, read-only,\n- * set through the options passed to the constructor.\n- */\n- callback: null,\n-\n- /**\n- * Property: scope\n- * {Object} Callback execution scope, read-only, set through the\n- * options passed to the constructor.\n- */\n- scope: null,\n+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n \n /**\n- * APIProperty: readWithPOST\n- * {Boolean} true if read operations are done with POST requests\n- * instead of GET, defaults to false.\n+ * Property: bounds\n+ * {<OpenLayers.Bounds>} The current data bounds (in the same projection\n+ * as the layer - not always the same projection as the map).\n */\n- readWithPOST: false,\n+ bounds: null,\n \n- /**\n- * APIProperty: updateWithPOST\n- * {Boolean} true if update operations are done with POST requests\n- * defaults to false.\n+ /** \n+ * Property: resolution \n+ * {Float} The current data resolution. \n */\n- updateWithPOST: false,\n+ resolution: null,\n \n /**\n- * APIProperty: deleteWithPOST\n- * {Boolean} true if delete operations are done with POST requests\n- * defaults to false.\n- * if true, POST data is set to output of format.write().\n+ * APIProperty: ratio\n+ * {Float} The ratio of the data bounds to the viewport bounds (in each\n+ * dimension). Default is 2.\n */\n- deleteWithPOST: false,\n+ ratio: 2,\n \n- /**\n- * Property: wildcarded.\n- * {Boolean} If true percent signs are added around values\n- * read from LIKE filters, for example if the protocol\n- * read method is passed a LIKE filter whose property\n- * is \"foo\" and whose value is \"bar\" the string\n- * \"foo__ilike=%bar%\" will be sent in the query string;\n- * defaults to false.\n+ /** \n+ * Property: resFactor \n+ * {Float} Optional factor used to determine when previously requested \n+ * features are invalid. If set, the resFactor will be compared to the\n+ * resolution of the previous request to the current map resolution.\n+ * If resFactor > (old / new) and 1/resFactor < (old / new). If you\n+ * set a resFactor of 1, data will be requested every time the\n+ * resolution changes. If you set a resFactor of 3, data will be\n+ * requested if the old resolution is 3 times the new, or if the new is\n+ * 3 times the old. If the old bounds do not contain the new bounds\n+ * new data will always be requested (with or without considering\n+ * resFactor). \n */\n- wildcarded: false,\n+ resFactor: null,\n \n /**\n- * APIProperty: srsInBBOX\n- * {Boolean} Include the SRS identifier in BBOX query string parameter. \n- * Default is false. If true and the layer has a projection object set,\n- * any BBOX filter will be serialized with a fifth item identifying the\n- * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n+ * Property: response\n+ * {<OpenLayers.Protocol.Response>} The protocol response object returned\n+ * by the layer protocol.\n */\n- srsInBBOX: false,\n+ response: null,\n \n /**\n- * Constructor: OpenLayers.Protocol.HTTP\n- * A class for giving layers generic HTTP protocol.\n+ * Constructor: OpenLayers.Strategy.BBOX\n+ * Create a new BBOX strategy.\n *\n * Parameters:\n * options - {Object} Optional object whose properties will be set on the\n * instance.\n- *\n- * Valid options include:\n- * url - {String}\n- * headers - {Object} \n- * params - {Object} URL parameters for GET requests\n- * format - {<OpenLayers.Format>}\n- * callback - {Function}\n- * scope - {Object}\n- */\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.headers = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n-\n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- wildcarded: this.wildcarded,\n- srsInBBOX: this.srsInBBOX\n- });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params);\n- };\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up the protocol.\n- */\n- destroy: function() {\n- this.params = null;\n- this.headers = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this);\n- },\n-\n- /**\n- * APIMethod: filterToParams\n- * Optional method to translate an <OpenLayers.Filter> object into an object\n- * that can be serialized as request query string provided. If a custom\n- * method is not provided, the filter will be serialized using the \n- * <OpenLayers.Format.QueryStringFilter> class.\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>} filter to convert.\n- * params - {Object} The parameters object.\n- *\n- * Returns:\n- * {Object} The resulting parameters object.\n */\n \n /**\n- * APIMethod: read\n- * Construct a request for reading new features.\n- *\n- * Parameters:\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n- * Valid options:\n- * url - {String} Url for the request.\n- * params - {Object} Parameters to get serialized as a query string.\n- * headers - {Object} Headers to be set on the request.\n- * filter - {<OpenLayers.Filter>} Filter to get serialized as a\n- * query string.\n- * readWithPOST - {Boolean} If the request should be done with POST.\n- *\n+ * Method: activate\n+ * Set up strategy with regard to reading new batches of remote data.\n+ * \n * Returns:\n- * {<OpenLayers.Protocol.Response>} A response object, whose \"priv\" property\n- * references the HTTP request, this object is also passed to the\n- * callback function when the request completes, its \"features\" property\n- * is then populated with the features received from the server.\n+ * {Boolean} The strategy was successfully activated.\n */\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = options || {};\n- options.params = OpenLayers.Util.applyDefaults(\n- options.params, this.options.params);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(\n- options.filter, options.params\n- );\n- }\n- var readWithPOST = (options.readWithPOST !== undefined) ?\n- options.readWithPOST : this.readWithPOST;\n- var resp = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- if (readWithPOST) {\n- var headers = options.headers || {};\n- headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- data: OpenLayers.Util.getParameterString(options.params),\n- headers: headers\n- });\n- } else {\n- resp.priv = OpenLayers.Request.GET({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- params: options.params,\n- headers: options.headers\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"moveend\": this.update,\n+ \"refresh\": this.update,\n+ \"visibilitychanged\": this.update,\n+ scope: this\n });\n+ this.update();\n }\n- return resp;\n- },\n-\n- /**\n- * Method: handleRead\n- * Individual callbacks are created for read, create and update, should\n- * a subclass need to override each one separately.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * the user callback.\n- * options - {Object} The user options passed to the read call.\n- */\n- handleRead: function(resp, options) {\n- this.handleResponse(resp, options);\n+ return activated;\n },\n \n /**\n- * APIMethod: create\n- * Construct a request for writing newly created features.\n- *\n- * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n+ * Method: deactivate\n+ * Tear down strategy with regard to reading new batches of remote data.\n+ * \n * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes, its \"features\" property is then populated with the\n- * the features received from the server.\n- */\n- create: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: features,\n- requestType: \"create\"\n- });\n-\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleCreate, resp, options),\n- headers: options.headers,\n- data: this.format.write(features)\n- });\n-\n- return resp;\n- },\n-\n- /**\n- * Method: handleCreate\n- * Called the the request issued by <create> is complete. May be overridden\n- * by subclasses.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the create call.\n+ * {Boolean} The strategy was successfully deactivated.\n */\n- handleCreate: function(resp, options) {\n- this.handleResponse(resp, options);\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ \"moveend\": this.update,\n+ \"refresh\": this.update,\n+ \"visibilitychanged\": this.update,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n },\n \n /**\n- * APIMethod: update\n- * Construct a request updating modified feature.\n+ * Method: update\n+ * Callback function called on \"moveend\" or \"refresh\" layer events.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes, its \"features\" property is then populated with the\n- * the feature received from the server.\n- */\n- update: function(feature, options) {\n- options = options || {};\n- var url = options.url ||\n- feature.url ||\n- this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"update\"\n- });\n-\n- var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n- resp.priv = OpenLayers.Request[method]({\n- url: url,\n- callback: this.createCallback(this.handleUpdate, resp, options),\n- headers: options.headers,\n- data: this.format.write(feature)\n- });\n-\n- return resp;\n- },\n-\n- /**\n- * Method: handleUpdate\n- * Called the the request issued by <update> is complete. May be overridden\n- * by subclasses.\n+ * options - {Object} Optional object whose properties will determine\n+ * the behaviour of this Strategy\n *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the update call.\n+ * Valid options include:\n+ * force - {Boolean} if true, new data must be unconditionally read.\n+ * noAbort - {Boolean} if true, do not abort previous requests.\n */\n- handleUpdate: function(resp, options) {\n- this.handleResponse(resp, options);\n+ update: function(options) {\n+ var mapBounds = this.getMapBounds();\n+ if (mapBounds !== null && ((options && options.force) ||\n+ (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {\n+ this.calculateBounds(mapBounds);\n+ this.resolution = this.layer.map.getResolution();\n+ this.triggerRead(options);\n+ }\n },\n \n /**\n- * APIMethod: delete\n- * Construct a request deleting a removed feature.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- * options - {Object} Optional object for configuring the request.\n- * This object is modified and should not be reused.\n+ * Method: getMapBounds\n+ * Get the map bounds expressed in the same projection as this layer.\n *\n * Returns:\n- * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>\n- * object, whose \"priv\" property references the HTTP request, this \n- * object is also passed to the callback function when the request\n- * completes.\n+ * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.\n */\n- \"delete\": function(feature, options) {\n- options = options || {};\n- var url = options.url ||\n- feature.url ||\n- this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n-\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"delete\"\n- });\n-\n- var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n- var requestOptions = {\n- url: url,\n- callback: this.createCallback(this.handleDelete, resp, options),\n- headers: options.headers\n- };\n- if (this.deleteWithPOST) {\n- requestOptions.data = this.format.write(feature);\n+ getMapBounds: function() {\n+ if (this.layer.map === null) {\n+ return null;\n }\n- resp.priv = OpenLayers.Request[method](requestOptions);\n-\n- return resp;\n+ var bounds = this.layer.map.getExtent();\n+ if (bounds && !this.layer.projection.equals(\n+ this.layer.map.getProjectionObject())) {\n+ bounds = bounds.clone().transform(\n+ this.layer.map.getProjectionObject(), this.layer.projection\n+ );\n+ }\n+ return bounds;\n },\n \n /**\n- * Method: handleDelete\n- * Called the the request issued by <delete> is complete. May be overridden\n- * by subclasses.\n+ * Method: invalidBounds\n+ * Determine whether the previously requested set of features is invalid. \n+ * This occurs when the new map bounds do not contain the previously \n+ * requested bounds. In addition, if <resFactor> is set, it will be \n+ * considered.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the delete call.\n- */\n- handleDelete: function(resp, options) {\n- this.handleResponse(resp, options);\n- },\n-\n- /**\n- * Method: handleResponse\n- * Called by CRUD specific handlers.\n+ * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n+ * retrieved from the map object if not provided\n *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object to pass to\n- * any user callback.\n- * options - {Object} The user options passed to the create, read, update,\n- * or delete call.\n+ * Returns:\n+ * {Boolean} \n */\n- handleResponse: function(resp, options) {\n- var request = resp.priv;\n- if (options.callback) {\n- if (request.status >= 200 && request.status < 300) {\n- // success\n- if (resp.requestType != \"delete\") {\n- resp.features = this.parseFeatures(request);\n- }\n- resp.code = OpenLayers.Protocol.Response.SUCCESS;\n- } else {\n- // failure\n- resp.code = OpenLayers.Protocol.Response.FAILURE;\n- }\n- options.callback.call(options.scope, resp);\n+ invalidBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds();\n+ }\n+ var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n+ if (!invalid && this.resFactor) {\n+ var ratio = this.resolution / this.layer.map.getResolution();\n+ invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));\n }\n+ return invalid;\n },\n \n /**\n- * Method: parseFeatures\n- * Read HTTP response body and return features.\n+ * Method: calculateBounds\n *\n * Parameters:\n- * request - {XMLHttpRequest} The request object\n- *\n- * Returns:\n- * {Array({<OpenLayers.Feature.Vector>})} or\n- * {<OpenLayers.Feature.Vector>} Array of features or a single feature.\n+ * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be\n+ * retrieved from the map object if not provided\n */\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText;\n- }\n- if (!doc || doc.length <= 0) {\n- return null;\n+ calculateBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds();\n }\n- return this.format.read(doc);\n+ var center = mapBounds.getCenterLonLat();\n+ var dataWidth = mapBounds.getWidth() * this.ratio;\n+ var dataHeight = mapBounds.getHeight() * this.ratio;\n+ this.bounds = new OpenLayers.Bounds(\n+ center.lon - (dataWidth / 2),\n+ center.lat - (dataHeight / 2),\n+ center.lon + (dataWidth / 2),\n+ center.lat + (dataHeight / 2)\n+ );\n },\n \n /**\n- * APIMethod: commit\n- * Iterate over each feature and take action based on the feature state.\n- * Possible actions are create, update and delete.\n+ * Method: triggerRead\n *\n * Parameters:\n- * features - {Array({<OpenLayers.Feature.Vector>})}\n- * options - {Object} Optional object for setting up intermediate commit\n- * callbacks.\n- *\n- * Valid options:\n- * create - {Object} Optional object to be passed to the <create> method.\n- * update - {Object} Optional object to be passed to the <update> method.\n- * delete - {Object} Optional object to be passed to the <delete> method.\n- * callback - {Function} Optional function to be called when the commit\n- * is complete.\n- * scope - {Object} Optional object to be set as the scope of the callback.\n+ * options - {Object} Additional options for the protocol's read method \n+ * (optional)\n *\n * Returns:\n- * {Array(<OpenLayers.Protocol.Response>)} An array of response objects,\n- * one per request made to the server, each object's \"priv\" property\n- * references the corresponding HTTP request.\n+ * {<OpenLayers.Protocol.Response>} The protocol response object\n+ * returned by the layer protocol.\n */\n- commit: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = [],\n- nResponses = 0;\n-\n- // Divide up features before issuing any requests. This properly\n- // counts requests in the event that any responses come in before\n- // all requests have been issued.\n- var types = {};\n- types[OpenLayers.State.INSERT] = [];\n- types[OpenLayers.State.UPDATE] = [];\n- types[OpenLayers.State.DELETE] = [];\n- var feature, list, requestFeatures = [];\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- list = types[feature.state];\n- if (list) {\n- list.push(feature);\n- requestFeatures.push(feature);\n- }\n- }\n- // tally up number of requests\n- var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +\n- types[OpenLayers.State.UPDATE].length +\n- types[OpenLayers.State.DELETE].length;\n-\n- // This response will be sent to the final callback after all the others\n- // have been fired.\n- var success = true;\n- var finalResponse = new OpenLayers.Protocol.Response({\n- reqFeatures: requestFeatures\n- });\n-\n- function insertCallback(response) {\n- var len = response.features ? response.features.length : 0;\n- var fids = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- fids[i] = response.features[i].fid;\n- }\n- finalResponse.insertIds = fids;\n- callback.apply(this, [response]);\n- }\n-\n- function callback(response) {\n- this.callUserCallback(response, options);\n- success = success && response.success();\n- nResponses++;\n- if (nResponses >= nRequests) {\n- if (options.callback) {\n- finalResponse.code = success ?\n- OpenLayers.Protocol.Response.SUCCESS :\n- OpenLayers.Protocol.Response.FAILURE;\n- options.callback.apply(options.scope, [finalResponse]);\n- }\n- }\n- }\n-\n- // start issuing requests\n- var queue = types[OpenLayers.State.INSERT];\n- if (queue.length > 0) {\n- resp.push(this.create(\n- queue, OpenLayers.Util.applyDefaults({\n- callback: insertCallback,\n- scope: this\n- }, options.create)\n- ));\n- }\n- queue = types[OpenLayers.State.UPDATE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this.update(\n- queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options.update)));\n- }\n- queue = types[OpenLayers.State.DELETE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this[\"delete\"](\n- queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options[\"delete\"])));\n+ triggerRead: function(options) {\n+ if (this.response && !(options && options.noAbort === true)) {\n+ this.layer.protocol.abort(this.response);\n+ this.layer.events.triggerEvent(\"loadend\");\n }\n- return resp;\n+ var evt = {\n+ filter: this.createFilter()\n+ };\n+ this.layer.events.triggerEvent(\"loadstart\", evt);\n+ this.response = this.layer.protocol.read(\n+ OpenLayers.Util.applyDefaults({\n+ filter: evt.filter,\n+ callback: this.merge,\n+ scope: this\n+ }, options));\n },\n \n /**\n- * APIMethod: abort\n- * Abort an ongoing request, the response object passed to\n- * this method must come from this HTTP protocol (as a result\n- * of a create, read, update, delete or commit operation).\n- *\n- * Parameters:\n- * response - {<OpenLayers.Protocol.Response>}\n+ * Method: createFilter\n+ * Creates a spatial BBOX filter. If the layer that this strategy belongs\n+ * to has a filter property, this filter will be combined with the BBOX \n+ * filter.\n+ * \n+ * Returns\n+ * {<OpenLayers.Filter>} The filter object.\n */\n- abort: function(response) {\n- if (response) {\n- response.priv.abort();\n+ createFilter: function() {\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ value: this.bounds,\n+ projection: this.layer.projection\n+ });\n+ if (this.layer.filter) {\n+ filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.layer.filter, filter]\n+ });\n }\n+ return filter;\n },\n \n /**\n- * Method: callUserCallback\n- * This method is used from within the commit method each time an\n- * an HTTP response is received from the server, it is responsible\n- * for calling the user-supplied callbacks.\n+ * Method: merge\n+ * Given a list of features, determine which ones to add to the layer.\n+ * If the layer projection differs from the map projection, features\n+ * will be transformed from the layer projection to the map projection.\n *\n * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>}\n- * options - {Object} The map of options passed to the commit call.\n+ * resp - {<OpenLayers.Protocol.Response>} The response object passed\n+ * by the protocol.\n */\n- callUserCallback: function(resp, options) {\n- var opt = options[resp.requestType];\n- if (opt && opt.callback) {\n- opt.callback.call(opt.scope, resp);\n+ merge: function(resp) {\n+ this.layer.destroyFeatures();\n+ if (resp.success()) {\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local);\n+ }\n+ }\n+ }\n+ this.layer.addFeatures(features);\n+ }\n+ } else {\n+ this.bounds = null;\n }\n+ this.response = null;\n+ this.layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ });\n },\n \n- CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n+ CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n });\n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.light.min.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.light.min.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -62,204 +62,14 @@\n var sourceIsEvt = typeof window.Event == \"function\" && source instanceof window.Event;\n if (!sourceIsEvt && source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n destination.toString = source.toString\n }\n }\n return destination\n };\n-OpenLayers.Util = OpenLayers.Util || {};\n-OpenLayers.Util.vendorPrefix = function() {\n- \"use strict\";\n- var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n- divStyle = document.createElement(\"div\").style,\n- cssCache = {},\n- jsCache = {};\n-\n- function domToCss(prefixedDom) {\n- if (!prefixedDom) {\n- return null\n- }\n- return prefixedDom.replace(/([A-Z])/g, function(c) {\n- return \"-\" + c.toLowerCase()\n- }).replace(/^ms-/, \"-ms-\")\n- }\n-\n- function css(property) {\n- if (cssCache[property] === undefined) {\n- var domProperty = property.replace(/(-[\\s\\S])/g, function(c) {\n- return c.charAt(1).toUpperCase()\n- });\n- var prefixedDom = style(domProperty);\n- cssCache[property] = domToCss(prefixedDom)\n- }\n- return cssCache[property]\n- }\n-\n- function js(obj, property) {\n- if (jsCache[property] === undefined) {\n- var tmpProp, i = 0,\n- l = VENDOR_PREFIXES.length,\n- prefix, isStyleObj = typeof obj.cssText !== \"undefined\";\n- jsCache[property] = null;\n- for (; i < l; i++) {\n- prefix = VENDOR_PREFIXES[i];\n- if (prefix) {\n- if (!isStyleObj) {\n- prefix = prefix.toLowerCase()\n- }\n- tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1)\n- } else {\n- tmpProp = property\n- }\n- if (obj[tmpProp] !== undefined) {\n- jsCache[property] = tmpProp;\n- break\n- }\n- }\n- }\n- return jsCache[property]\n- }\n-\n- function style(property) {\n- return js(divStyle, property)\n- }\n- return {\n- css: css,\n- js: js,\n- style: style,\n- cssCache: cssCache,\n- jsCache: jsCache\n- }\n-}();\n-OpenLayers.Animation = function(window) {\n- var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n- var isNative = !!requestAnimationFrame;\n- var requestFrame = function() {\n- var request = window[requestAnimationFrame] || function(callback, element) {\n- window.setTimeout(callback, 16)\n- };\n- return function(callback, element) {\n- request.apply(window, [callback, element])\n- }\n- }();\n- var counter = 0;\n- var loops = {};\n-\n- function start(callback, duration, element) {\n- duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n- var id = ++counter;\n- var start = +new Date;\n- loops[id] = function() {\n- if (loops[id] && +new Date - start <= duration) {\n- callback();\n- if (loops[id]) {\n- requestFrame(loops[id], element)\n- }\n- } else {\n- delete loops[id]\n- }\n- };\n- requestFrame(loops[id], element);\n- return id\n- }\n-\n- function stop(id) {\n- delete loops[id]\n- }\n- return {\n- isNative: isNative,\n- requestFrame: requestFrame,\n- start: start,\n- stop: stop\n- }\n-}(window);\n-OpenLayers.Kinetic = OpenLayers.Class({\n- threshold: 0,\n- deceleration: .0035,\n- nbPoints: 100,\n- delay: 200,\n- points: undefined,\n- timerId: undefined,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = []\n- },\n- update: function(xy) {\n- this.points.unshift({\n- xy: xy,\n- tick: (new Date).getTime()\n- });\n- if (this.points.length > this.nbPoints) {\n- this.points.pop()\n- }\n- },\n- end: function(xy) {\n- var last, now = (new Date).getTime();\n- for (var i = 0, l = this.points.length, point; i < l; i++) {\n- point = this.points[i];\n- if (now - point.tick > this.delay) {\n- break\n- }\n- last = point\n- }\n- if (!last) {\n- return\n- }\n- var time = (new Date).getTime() - last.tick;\n- var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2));\n- var speed = dist / time;\n- if (speed == 0 || speed < this.threshold) {\n- return\n- }\n- var theta = Math.asin((xy.y - last.xy.y) / dist);\n- if (last.xy.x <= xy.x) {\n- theta = Math.PI - theta\n- }\n- return {\n- speed: speed,\n- theta: theta\n- }\n- },\n- move: function(info, callback) {\n- var v0 = info.speed;\n- var fx = Math.cos(info.theta);\n- var fy = -Math.sin(info.theta);\n- var initialTime = (new Date).getTime();\n- var lastX = 0;\n- var lastY = 0;\n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return\n- }\n- var t = (new Date).getTime() - initialTime;\n- var p = -this.deceleration * Math.pow(t, 2) / 2 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\n- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true\n- }\n- args.x = x - lastX;\n- args.y = y - lastY;\n- lastX = x;\n- lastY = y;\n- callback(args.x, args.y, args.end)\n- };\n- this.timerId = OpenLayers.Animation.start(OpenLayers.Function.bind(timerCallback, this))\n- },\n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n OpenLayers.String = {\n startsWith: function(str, sub) {\n return str.indexOf(sub) == 0\n },\n contains: function(str, sub) {\n return str.indexOf(sub) != -1\n },\n@@ -1795,14 +1605,942 @@\n if (axis == \"lon\") {\n str += coordinate < 0 ? OpenLayers.i18n(\"W\") : OpenLayers.i18n(\"E\")\n } else {\n str += coordinate < 0 ? OpenLayers.i18n(\"S\") : OpenLayers.i18n(\"N\")\n }\n return str\n };\n+OpenLayers.Feature = OpenLayers.Class({\n+ layer: null,\n+ id: null,\n+ lonlat: null,\n+ data: null,\n+ marker: null,\n+ popupClass: null,\n+ popup: null,\n+ initialize: function(layer, lonlat, data) {\n+ this.layer = layer;\n+ this.lonlat = lonlat;\n+ this.data = data != null ? data : {};\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ },\n+ destroy: function() {\n+ if (this.layer != null && this.layer.map != null) {\n+ if (this.popup != null) {\n+ this.layer.map.removePopup(this.popup)\n+ }\n+ }\n+ if (this.layer != null && this.marker != null) {\n+ this.layer.removeMarker(this.marker)\n+ }\n+ this.layer = null;\n+ this.id = null;\n+ this.lonlat = null;\n+ this.data = null;\n+ if (this.marker != null) {\n+ this.destroyMarker(this.marker);\n+ this.marker = null\n+ }\n+ if (this.popup != null) {\n+ this.destroyPopup(this.popup);\n+ this.popup = null\n+ }\n+ },\n+ onScreen: function() {\n+ var onScreen = false;\n+ if (this.layer != null && this.layer.map != null) {\n+ var screenBounds = this.layer.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat)\n+ }\n+ return onScreen\n+ },\n+ createMarker: function() {\n+ if (this.lonlat != null) {\n+ this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon)\n+ }\n+ return this.marker\n+ },\n+ destroyMarker: function() {\n+ this.marker.destroy()\n+ },\n+ createPopup: function(closeBox) {\n+ if (this.lonlat != null) {\n+ if (!this.popup) {\n+ var anchor = this.marker ? this.marker.icon : null;\n+ var popupClass = this.popupClass ? this.popupClass : OpenLayers.Popup.Anchored;\n+ this.popup = new popupClass(this.id + \"_popup\", this.lonlat, this.data.popupSize, this.data.popupContentHTML, anchor, closeBox)\n+ }\n+ if (this.data.overflow != null) {\n+ this.popup.contentDiv.style.overflow = this.data.overflow\n+ }\n+ this.popup.feature = this\n+ }\n+ return this.popup\n+ },\n+ destroyPopup: function() {\n+ if (this.popup) {\n+ this.popup.feature = null;\n+ this.popup.destroy();\n+ this.popup = null\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Feature\"\n+});\n+OpenLayers.State = {\n+ UNKNOWN: \"Unknown\",\n+ INSERT: \"Insert\",\n+ UPDATE: \"Update\",\n+ DELETE: \"Delete\"\n+};\n+OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n+ fid: null,\n+ geometry: null,\n+ attributes: null,\n+ bounds: null,\n+ state: null,\n+ style: null,\n+ url: null,\n+ renderIntent: \"default\",\n+ modified: null,\n+ initialize: function(geometry, attributes, style) {\n+ OpenLayers.Feature.prototype.initialize.apply(this, [null, null, attributes]);\n+ this.lonlat = null;\n+ this.geometry = geometry ? geometry : null;\n+ this.state = null;\n+ this.attributes = {};\n+ if (attributes) {\n+ this.attributes = OpenLayers.Util.extend(this.attributes, attributes)\n+ }\n+ this.style = style ? style : null\n+ },\n+ destroy: function() {\n+ if (this.layer) {\n+ this.layer.removeFeatures(this);\n+ this.layer = null\n+ }\n+ this.geometry = null;\n+ this.modified = null;\n+ OpenLayers.Feature.prototype.destroy.apply(this, arguments)\n+ },\n+ clone: function() {\n+ return new OpenLayers.Feature.Vector(this.geometry ? this.geometry.clone() : null, this.attributes, this.style)\n+ },\n+ onScreen: function(boundsOnly) {\n+ var onScreen = false;\n+ if (this.layer && this.layer.map) {\n+ var screenBounds = this.layer.map.getExtent();\n+ if (boundsOnly) {\n+ var featureBounds = this.geometry.getBounds();\n+ onScreen = screenBounds.intersectsBounds(featureBounds)\n+ } else {\n+ var screenPoly = screenBounds.toGeometry();\n+ onScreen = screenPoly.intersects(this.geometry)\n+ }\n+ }\n+ return onScreen\n+ },\n+ getVisibility: function() {\n+ return !(this.style && this.style.display == \"none\" || !this.layer || this.layer && this.layer.styleMap && this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == \"none\" || this.layer && !this.layer.getVisibility())\n+ },\n+ createMarker: function() {\n+ return null\n+ },\n+ destroyMarker: function() {},\n+ createPopup: function() {\n+ return null\n+ },\n+ atPoint: function(lonlat, toleranceLon, toleranceLat) {\n+ var atPoint = false;\n+ if (this.geometry) {\n+ atPoint = this.geometry.atPoint(lonlat, toleranceLon, toleranceLat)\n+ }\n+ return atPoint\n+ },\n+ destroyPopup: function() {},\n+ move: function(location) {\n+ if (!this.layer || !this.geometry.move) {\n+ return undefined\n+ }\n+ var pixel;\n+ if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n+ pixel = this.layer.getViewPortPxFromLonLat(location)\n+ } else {\n+ pixel = location\n+ }\n+ var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n+ var res = this.layer.map.getResolution();\n+ this.geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y));\n+ this.layer.drawFeature(this);\n+ return lastPixel\n+ },\n+ toState: function(state) {\n+ if (state == OpenLayers.State.UPDATE) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.DELETE:\n+ this.state = state;\n+ break;\n+ case OpenLayers.State.UPDATE:\n+ case OpenLayers.State.INSERT:\n+ break\n+ }\n+ } else if (state == OpenLayers.State.INSERT) {\n+ switch (this.state) {\n+ case OpenLayers.State.UNKNOWN:\n+ break;\n+ default:\n+ this.state = state;\n+ break\n+ }\n+ } else if (state == OpenLayers.State.DELETE) {\n+ switch (this.state) {\n+ case OpenLayers.State.INSERT:\n+ break;\n+ case OpenLayers.State.DELETE:\n+ break;\n+ case OpenLayers.State.UNKNOWN:\n+ case OpenLayers.State.UPDATE:\n+ this.state = state;\n+ break\n+ }\n+ } else if (state == OpenLayers.State.UNKNOWN) {\n+ this.state = state\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Feature.Vector\"\n+});\n+OpenLayers.Feature.Vector.style = {\n+ default: {\n+ fillColor: \"#ee9900\",\n+ fillOpacity: .4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: .8,\n+ strokeColor: \"#ee9900\",\n+ strokeOpacity: 1,\n+ strokeWidth: 1,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: .2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+ },\n+ select: {\n+ fillColor: \"blue\",\n+ fillOpacity: .4,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: .8,\n+ strokeColor: \"blue\",\n+ strokeOpacity: 1,\n+ strokeWidth: 2,\n+ strokeLinecap: \"round\",\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: .2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"pointer\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+ },\n+ temporary: {\n+ fillColor: \"#66cccc\",\n+ fillOpacity: .2,\n+ hoverFillColor: \"white\",\n+ hoverFillOpacity: .8,\n+ strokeColor: \"#66cccc\",\n+ strokeOpacity: 1,\n+ strokeLinecap: \"round\",\n+ strokeWidth: 2,\n+ strokeDashstyle: \"solid\",\n+ hoverStrokeColor: \"red\",\n+ hoverStrokeOpacity: 1,\n+ hoverStrokeWidth: .2,\n+ pointRadius: 6,\n+ hoverPointRadius: 1,\n+ hoverPointUnit: \"%\",\n+ pointerEvents: \"visiblePainted\",\n+ cursor: \"inherit\",\n+ fontColor: \"#000000\",\n+ labelAlign: \"cm\",\n+ labelOutlineColor: \"white\",\n+ labelOutlineWidth: 3\n+ },\n+ delete: {\n+ display: \"none\"\n+ }\n+};\n+OpenLayers.Style = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ title: null,\n+ description: null,\n+ layerName: null,\n+ isDefault: false,\n+ rules: null,\n+ context: null,\n+ defaultStyle: null,\n+ defaultsPerSymbolizer: false,\n+ propertyStyles: null,\n+ initialize: function(style, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules)\n+ }\n+ this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ },\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null\n+ },\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+ var rules = this.rules;\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ var applies = rule.evaluate(feature);\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule)\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature)\n+ }\n+ }\n+ }\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature)\n+ }\n+ }\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\"\n+ }\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label)\n+ }\n+ return style\n+ },\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ })\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ })\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ })\n+ }\n+ }\n+ return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n+ },\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n+ }\n+ return style\n+ },\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ this.addPropertyStyles(propertyStyles, value)\n+ } else {\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break\n+ }\n+ }\n+ }\n+ return propertyStyles\n+ },\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true\n+ }\n+ }\n+ return propertyStyles\n+ },\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles()\n+ },\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles()\n+ },\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i]\n+ }\n+ }\n+ },\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone())\n+ }\n+ }\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options)\n+ },\n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = isNaN(value) || !value ? value : parseFloat(value)\n+ }\n+ return value\n+};\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n+OpenLayers.StyleMap = OpenLayers.Class({\n+ styles: null,\n+ extendDefault: true,\n+ initialize: function(style, options) {\n+ this.styles = {\n+ default: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"default\"]),\n+ select: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"select\"]),\n+ temporary: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"temporary\"]),\n+ delete: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"delete\"])\n+ };\n+ if (style instanceof OpenLayers.Style) {\n+ this.styles[\"default\"] = style;\n+ this.styles[\"select\"] = style;\n+ this.styles[\"temporary\"] = style;\n+ this.styles[\"delete\"] = style\n+ } else if (typeof style == \"object\") {\n+ for (var key in style) {\n+ if (style[key] instanceof OpenLayers.Style) {\n+ this.styles[key] = style[key]\n+ } else if (typeof style[key] == \"object\") {\n+ this.styles[key] = new OpenLayers.Style(style[key])\n+ } else {\n+ this.styles[\"default\"] = new OpenLayers.Style(style);\n+ this.styles[\"select\"] = new OpenLayers.Style(style);\n+ this.styles[\"temporary\"] = new OpenLayers.Style(style);\n+ this.styles[\"delete\"] = new OpenLayers.Style(style);\n+ break\n+ }\n+ }\n+ }\n+ OpenLayers.Util.extend(this, options)\n+ },\n+ destroy: function() {\n+ for (var key in this.styles) {\n+ this.styles[key].destroy()\n+ }\n+ this.styles = null\n+ },\n+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector\n+ }\n+ if (!this.styles[intent]) {\n+ intent = \"default\"\n+ }\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature)\n+ }\n+ return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature))\n+ },\n+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n+ var rules = [];\n+ for (var value in symbolizers) {\n+ rules.push(new OpenLayers.Rule({\n+ symbolizer: symbolizers[value],\n+ context: context,\n+ filter: new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ property: property,\n+ value: value\n+ })\n+ }))\n+ }\n+ this.styles[renderIntent].addRules(rules)\n+ },\n+ CLASS_NAME: \"OpenLayers.StyleMap\"\n+});\n+OpenLayers.Util = OpenLayers.Util || {};\n+OpenLayers.Util.vendorPrefix = function() {\n+ \"use strict\";\n+ var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n+ divStyle = document.createElement(\"div\").style,\n+ cssCache = {},\n+ jsCache = {};\n+\n+ function domToCss(prefixedDom) {\n+ if (!prefixedDom) {\n+ return null\n+ }\n+ return prefixedDom.replace(/([A-Z])/g, function(c) {\n+ return \"-\" + c.toLowerCase()\n+ }).replace(/^ms-/, \"-ms-\")\n+ }\n+\n+ function css(property) {\n+ if (cssCache[property] === undefined) {\n+ var domProperty = property.replace(/(-[\\s\\S])/g, function(c) {\n+ return c.charAt(1).toUpperCase()\n+ });\n+ var prefixedDom = style(domProperty);\n+ cssCache[property] = domToCss(prefixedDom)\n+ }\n+ return cssCache[property]\n+ }\n+\n+ function js(obj, property) {\n+ if (jsCache[property] === undefined) {\n+ var tmpProp, i = 0,\n+ l = VENDOR_PREFIXES.length,\n+ prefix, isStyleObj = typeof obj.cssText !== \"undefined\";\n+ jsCache[property] = null;\n+ for (; i < l; i++) {\n+ prefix = VENDOR_PREFIXES[i];\n+ if (prefix) {\n+ if (!isStyleObj) {\n+ prefix = prefix.toLowerCase()\n+ }\n+ tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1)\n+ } else {\n+ tmpProp = property\n+ }\n+ if (obj[tmpProp] !== undefined) {\n+ jsCache[property] = tmpProp;\n+ break\n+ }\n+ }\n+ }\n+ return jsCache[property]\n+ }\n+\n+ function style(property) {\n+ return js(divStyle, property)\n+ }\n+ return {\n+ css: css,\n+ js: js,\n+ style: style,\n+ cssCache: cssCache,\n+ jsCache: jsCache\n+ }\n+}();\n+OpenLayers.Animation = function(window) {\n+ var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n+ var isNative = !!requestAnimationFrame;\n+ var requestFrame = function() {\n+ var request = window[requestAnimationFrame] || function(callback, element) {\n+ window.setTimeout(callback, 16)\n+ };\n+ return function(callback, element) {\n+ request.apply(window, [callback, element])\n+ }\n+ }();\n+ var counter = 0;\n+ var loops = {};\n+\n+ function start(callback, duration, element) {\n+ duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n+ var id = ++counter;\n+ var start = +new Date;\n+ loops[id] = function() {\n+ if (loops[id] && +new Date - start <= duration) {\n+ callback();\n+ if (loops[id]) {\n+ requestFrame(loops[id], element)\n+ }\n+ } else {\n+ delete loops[id]\n+ }\n+ };\n+ requestFrame(loops[id], element);\n+ return id\n+ }\n+\n+ function stop(id) {\n+ delete loops[id]\n+ }\n+ return {\n+ isNative: isNative,\n+ requestFrame: requestFrame,\n+ start: start,\n+ stop: stop\n+ }\n+}(window);\n+OpenLayers.Kinetic = OpenLayers.Class({\n+ threshold: 0,\n+ deceleration: .0035,\n+ nbPoints: 100,\n+ delay: 200,\n+ points: undefined,\n+ timerId: undefined,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n+ },\n+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = []\n+ },\n+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: (new Date).getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop()\n+ }\n+ },\n+ end: function(xy) {\n+ var last, now = (new Date).getTime();\n+ for (var i = 0, l = this.points.length, point; i < l; i++) {\n+ point = this.points[i];\n+ if (now - point.tick > this.delay) {\n+ break\n+ }\n+ last = point\n+ }\n+ if (!last) {\n+ return\n+ }\n+ var time = (new Date).getTime() - last.tick;\n+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2));\n+ var speed = dist / time;\n+ if (speed == 0 || speed < this.threshold) {\n+ return\n+ }\n+ var theta = Math.asin((xy.y - last.xy.y) / dist);\n+ if (last.xy.x <= xy.x) {\n+ theta = Math.PI - theta\n+ }\n+ return {\n+ speed: speed,\n+ theta: theta\n+ }\n+ },\n+ move: function(info, callback) {\n+ var v0 = info.speed;\n+ var fx = Math.cos(info.theta);\n+ var fy = -Math.sin(info.theta);\n+ var initialTime = (new Date).getTime();\n+ var lastX = 0;\n+ var lastY = 0;\n+ var timerCallback = function() {\n+ if (this.timerId == null) {\n+ return\n+ }\n+ var t = (new Date).getTime() - initialTime;\n+ var p = -this.deceleration * Math.pow(t, 2) / 2 + v0 * t;\n+ var x = p * fx;\n+ var y = p * fy;\n+ var args = {};\n+ args.end = false;\n+ var v = -this.deceleration * t + v0;\n+ if (v <= 0) {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = null;\n+ args.end = true\n+ }\n+ args.x = x - lastX;\n+ args.y = y - lastY;\n+ lastX = x;\n+ lastY = y;\n+ callback(args.x, args.y, args.end)\n+ };\n+ this.timerId = OpenLayers.Animation.start(OpenLayers.Function.bind(timerCallback, this))\n+ },\n+ CLASS_NAME: \"OpenLayers.Kinetic\"\n+});\n+OpenLayers.Projection = OpenLayers.Class({\n+ proj: null,\n+ projCode: null,\n+ titleRegEx: /\\+title=[^\\+]*/,\n+ initialize: function(projCode, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.projCode = projCode;\n+ if (typeof Proj4js == \"object\") {\n+ this.proj = new Proj4js.Proj(projCode)\n+ }\n+ },\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode\n+ },\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null\n+ },\n+ toString: function() {\n+ return this.getCode()\n+ },\n+ equals: function(projection) {\n+ var p = projection,\n+ equals = false;\n+ if (p) {\n+ if (!(p instanceof OpenLayers.Projection)) {\n+ p = new OpenLayers.Projection(p)\n+ }\n+ if (typeof Proj4js == \"object\" && this.proj.defData && p.proj.defData) {\n+ equals = this.proj.defData.replace(this.titleRegEx, \"\") == p.proj.defData.replace(this.titleRegEx, \"\")\n+ } else if (p.getCode) {\n+ var source = this.getCode(),\n+ target = p.getCode();\n+ equals = source == target || !!OpenLayers.Projection.transforms[source] && OpenLayers.Projection.transforms[source][target] === OpenLayers.Projection.nullTransform\n+ }\n+ }\n+ return equals\n+ },\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode\n+ },\n+ CLASS_NAME: \"OpenLayers.Projection\"\n+});\n+OpenLayers.Projection.transforms = {};\n+OpenLayers.Projection.defaults = {\n+ \"EPSG:4326\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90],\n+ yx: true\n+ },\n+ \"CRS:84\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90]\n+ },\n+ \"EPSG:900913\": {\n+ units: \"m\",\n+ maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n+ }\n+};\n+OpenLayers.Projection.addTransform = function(from, to, method) {\n+ if (method === OpenLayers.Projection.nullTransform) {\n+ var defaults = OpenLayers.Projection.defaults[from];\n+ if (defaults && !OpenLayers.Projection.defaults[to]) {\n+ OpenLayers.Projection.defaults[to] = defaults\n+ }\n+ }\n+ if (!OpenLayers.Projection.transforms[from]) {\n+ OpenLayers.Projection.transforms[from] = {}\n+ }\n+ OpenLayers.Projection.transforms[from][to] = method\n+};\n+OpenLayers.Projection.transform = function(point, source, dest) {\n+ if (source && dest) {\n+ if (!(source instanceof OpenLayers.Projection)) {\n+ source = new OpenLayers.Projection(source)\n+ }\n+ if (!(dest instanceof OpenLayers.Projection)) {\n+ dest = new OpenLayers.Projection(dest)\n+ }\n+ if (source.proj && dest.proj) {\n+ point = Proj4js.transform(source.proj, dest.proj, point)\n+ } else {\n+ var sourceCode = source.getCode();\n+ var destCode = dest.getCode();\n+ var transforms = OpenLayers.Projection.transforms;\n+ if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n+ transforms[sourceCode][destCode](point)\n+ }\n+ }\n+ }\n+ return point\n+};\n+OpenLayers.Projection.nullTransform = function(point) {\n+ return point\n+};\n+(function() {\n+ var pole = 20037508.34;\n+\n+ function inverseMercator(xy) {\n+ xy.x = 180 * xy.x / pole;\n+ xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2);\n+ return xy\n+ }\n+\n+ function forwardMercator(xy) {\n+ xy.x = xy.x * pole / 180;\n+ var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n+ xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n+ return xy\n+ }\n+\n+ function map(base, codes) {\n+ var add = OpenLayers.Projection.addTransform;\n+ var same = OpenLayers.Projection.nullTransform;\n+ var i, len, code, other, j;\n+ for (i = 0, len = codes.length; i < len; ++i) {\n+ code = codes[i];\n+ add(base, code, forwardMercator);\n+ add(code, base, inverseMercator);\n+ for (j = i + 1; j < len; ++j) {\n+ other = codes[j];\n+ add(code, other, same);\n+ add(other, code, same)\n+ }\n+ }\n+ }\n+ var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n+ geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n+ i;\n+ for (i = mercator.length - 1; i >= 0; --i) {\n+ map(mercator[i], geographic)\n+ }\n+ for (i = geographic.length - 1; i >= 0; --i) {\n+ map(geographic[i], mercator)\n+ }\n+})();\n+OpenLayers.Rule = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ title: null,\n+ description: null,\n+ context: null,\n+ filter: null,\n+ elseFilter: false,\n+ symbolizer: null,\n+ symbolizers: null,\n+ minScaleDenominator: null,\n+ maxScaleDenominator: null,\n+ initialize: function(options) {\n+ this.symbolizer = {};\n+ OpenLayers.Util.extend(this, options);\n+ if (this.symbolizers) {\n+ delete this.symbolizer\n+ }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ },\n+ destroy: function() {\n+ for (var i in this.symbolizer) {\n+ this.symbolizer[i] = null\n+ }\n+ this.symbolizer = null;\n+ delete this.symbolizers\n+ },\n+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale()\n+ }\n+ if (this.minScaleDenominator) {\n+ applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context)\n+ }\n+ if (applies && this.maxScaleDenominator) {\n+ applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context)\n+ }\n+ if (applies && this.filter) {\n+ if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n+ applies = this.filter.evaluate(feature)\n+ } else {\n+ applies = this.filter.evaluate(context)\n+ }\n+ }\n+ return applies\n+ },\n+ getContext: function(feature) {\n+ var context = this.context;\n+ if (!context) {\n+ context = feature.attributes || feature.data\n+ }\n+ if (typeof this.context == \"function\") {\n+ context = this.context(feature)\n+ }\n+ return context\n+ },\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.symbolizers) {\n+ var len = this.symbolizers.length;\n+ options.symbolizers = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ options.symbolizers[i] = this.symbolizers[i].clone()\n+ }\n+ } else {\n+ options.symbolizer = {};\n+ var value, type;\n+ for (var key in this.symbolizer) {\n+ value = this.symbolizer[key];\n+ type = typeof value;\n+ if (type === \"object\") {\n+ options.symbolizer[key] = OpenLayers.Util.extend({}, value)\n+ } else if (type === \"string\") {\n+ options.symbolizer[key] = value\n+ }\n+ }\n+ }\n+ options.filter = this.filter && this.filter.clone();\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ return new OpenLayers.Rule(options)\n+ },\n+ CLASS_NAME: \"OpenLayers.Rule\"\n+});\n OpenLayers.Event = {\n observers: false,\n KEY_SPACE: 32,\n KEY_BACKSPACE: 8,\n KEY_TAB: 9,\n KEY_RETURN: 13,\n KEY_ESC: 27,\n@@ -2360,150 +3098,14 @@\n },\n easeInOut: function(t, b, c, d) {\n if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n return -c / 2 * (--t * (t - 2) - 1) + b\n },\n CLASS_NAME: \"OpenLayers.Easing.Quad\"\n };\n-OpenLayers.Projection = OpenLayers.Class({\n- proj: null,\n- projCode: null,\n- titleRegEx: /\\+title=[^\\+]*/,\n- initialize: function(projCode, options) {\n- OpenLayers.Util.extend(this, options);\n- this.projCode = projCode;\n- if (typeof Proj4js == \"object\") {\n- this.proj = new Proj4js.Proj(projCode)\n- }\n- },\n- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode\n- },\n- getUnits: function() {\n- return this.proj ? this.proj.units : null\n- },\n- toString: function() {\n- return this.getCode()\n- },\n- equals: function(projection) {\n- var p = projection,\n- equals = false;\n- if (p) {\n- if (!(p instanceof OpenLayers.Projection)) {\n- p = new OpenLayers.Projection(p)\n- }\n- if (typeof Proj4js == \"object\" && this.proj.defData && p.proj.defData) {\n- equals = this.proj.defData.replace(this.titleRegEx, \"\") == p.proj.defData.replace(this.titleRegEx, \"\")\n- } else if (p.getCode) {\n- var source = this.getCode(),\n- target = p.getCode();\n- equals = source == target || !!OpenLayers.Projection.transforms[source] && OpenLayers.Projection.transforms[source][target] === OpenLayers.Projection.nullTransform\n- }\n- }\n- return equals\n- },\n- destroy: function() {\n- delete this.proj;\n- delete this.projCode\n- },\n- CLASS_NAME: \"OpenLayers.Projection\"\n-});\n-OpenLayers.Projection.transforms = {};\n-OpenLayers.Projection.defaults = {\n- \"EPSG:4326\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90],\n- yx: true\n- },\n- \"CRS:84\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90]\n- },\n- \"EPSG:900913\": {\n- units: \"m\",\n- maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n- }\n-};\n-OpenLayers.Projection.addTransform = function(from, to, method) {\n- if (method === OpenLayers.Projection.nullTransform) {\n- var defaults = OpenLayers.Projection.defaults[from];\n- if (defaults && !OpenLayers.Projection.defaults[to]) {\n- OpenLayers.Projection.defaults[to] = defaults\n- }\n- }\n- if (!OpenLayers.Projection.transforms[from]) {\n- OpenLayers.Projection.transforms[from] = {}\n- }\n- OpenLayers.Projection.transforms[from][to] = method\n-};\n-OpenLayers.Projection.transform = function(point, source, dest) {\n- if (source && dest) {\n- if (!(source instanceof OpenLayers.Projection)) {\n- source = new OpenLayers.Projection(source)\n- }\n- if (!(dest instanceof OpenLayers.Projection)) {\n- dest = new OpenLayers.Projection(dest)\n- }\n- if (source.proj && dest.proj) {\n- point = Proj4js.transform(source.proj, dest.proj, point)\n- } else {\n- var sourceCode = source.getCode();\n- var destCode = dest.getCode();\n- var transforms = OpenLayers.Projection.transforms;\n- if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n- transforms[sourceCode][destCode](point)\n- }\n- }\n- }\n- return point\n-};\n-OpenLayers.Projection.nullTransform = function(point) {\n- return point\n-};\n-(function() {\n- var pole = 20037508.34;\n-\n- function inverseMercator(xy) {\n- xy.x = 180 * xy.x / pole;\n- xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2);\n- return xy\n- }\n-\n- function forwardMercator(xy) {\n- xy.x = xy.x * pole / 180;\n- var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n- xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n- return xy\n- }\n-\n- function map(base, codes) {\n- var add = OpenLayers.Projection.addTransform;\n- var same = OpenLayers.Projection.nullTransform;\n- var i, len, code, other, j;\n- for (i = 0, len = codes.length; i < len; ++i) {\n- code = codes[i];\n- add(base, code, forwardMercator);\n- add(code, base, inverseMercator);\n- for (j = i + 1; j < len; ++j) {\n- other = codes[j];\n- add(code, other, same);\n- add(other, code, same)\n- }\n- }\n- }\n- var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n- geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n- i;\n- for (i = mercator.length - 1; i >= 0; --i) {\n- map(mercator[i], geographic)\n- }\n- for (i = geographic.length - 1; i >= 0; --i) {\n- map(geographic[i], mercator)\n- }\n-})();\n OpenLayers.Map = OpenLayers.Class({\n Z_INDEX_BASE: {\n BaseLayer: 100,\n Overlay: 325,\n Feature: 725,\n Popup: 750,\n Control: 1e3\n@@ -3681,2358 +4283,4568 @@\n }\n }\n },\n CLASS_NAME: \"OpenLayers.Map\"\n });\n OpenLayers.Map.TILE_WIDTH = 256;\n OpenLayers.Map.TILE_HEIGHT = 256;\n-OpenLayers.Feature = OpenLayers.Class({\n- layer: null,\n- id: null,\n- lonlat: null,\n+OpenLayers.Format = OpenLayers.Class({\n+ options: null,\n+ externalProjection: null,\n+ internalProjection: null,\n data: null,\n- marker: null,\n- popupClass: null,\n- popup: null,\n- initialize: function(layer, lonlat, data) {\n- this.layer = layer;\n- this.lonlat = lonlat;\n- this.data = data != null ? data : {};\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ keepData: false,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options\n },\n- destroy: function() {\n- if (this.layer != null && this.layer.map != null) {\n- if (this.popup != null) {\n- this.layer.map.removePopup(this.popup)\n+ destroy: function() {},\n+ read: function(data) {\n+ throw new Error(\"Read not implemented.\")\n+ },\n+ write: function(object) {\n+ throw new Error(\"Write not implemented.\")\n+ },\n+ CLASS_NAME: \"OpenLayers.Format\"\n+});\n+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n+ indent: \" \",\n+ space: \" \",\n+ newline: \"\\n\",\n+ level: 0,\n+ pretty: false,\n+ nativeJSON: function() {\n+ return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\")\n+ }(),\n+ read: function(json, filter) {\n+ var object;\n+ if (this.nativeJSON) {\n+ object = JSON.parse(json, filter)\n+ } else try {\n+ if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, \"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, \"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g, \"\"))) {\n+ object = eval(\"(\" + json + \")\");\n+ if (typeof filter === \"function\") {\n+ function walk(k, v) {\n+ if (v && typeof v === \"object\") {\n+ for (var i in v) {\n+ if (v.hasOwnProperty(i)) {\n+ v[i] = walk(i, v[i])\n+ }\n+ }\n+ }\n+ return filter(k, v)\n+ }\n+ object = walk(\"\", object)\n+ }\n }\n+ } catch (e) {}\n+ if (this.keepData) {\n+ this.data = object\n }\n- if (this.layer != null && this.marker != null) {\n- this.layer.removeMarker(this.marker)\n- }\n- this.layer = null;\n- this.id = null;\n- this.lonlat = null;\n- this.data = null;\n- if (this.marker != null) {\n- this.destroyMarker(this.marker);\n- this.marker = null\n- }\n- if (this.popup != null) {\n- this.destroyPopup(this.popup);\n- this.popup = null\n- }\n+ return object\n },\n- onScreen: function() {\n- var onScreen = false;\n- if (this.layer != null && this.layer.map != null) {\n- var screenBounds = this.layer.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat)\n+ write: function(value, pretty) {\n+ this.pretty = !!pretty;\n+ var json = null;\n+ var type = typeof value;\n+ if (this.serialize[type]) {\n+ try {\n+ json = !this.pretty && this.nativeJSON ? JSON.stringify(value) : this.serialize[type].apply(this, [value])\n+ } catch (err) {\n+ OpenLayers.Console.error(\"Trouble serializing: \" + err)\n+ }\n }\n- return onScreen\n+ return json\n },\n- createMarker: function() {\n- if (this.lonlat != null) {\n- this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon)\n+ writeIndent: function() {\n+ var pieces = [];\n+ if (this.pretty) {\n+ for (var i = 0; i < this.level; ++i) {\n+ pieces.push(this.indent)\n+ }\n }\n- return this.marker\n+ return pieces.join(\"\")\n },\n- destroyMarker: function() {\n- this.marker.destroy()\n+ writeNewline: function() {\n+ return this.pretty ? this.newline : \"\"\n },\n- createPopup: function(closeBox) {\n- if (this.lonlat != null) {\n- if (!this.popup) {\n- var anchor = this.marker ? this.marker.icon : null;\n- var popupClass = this.popupClass ? this.popupClass : OpenLayers.Popup.Anchored;\n- this.popup = new popupClass(this.id + \"_popup\", this.lonlat, this.data.popupSize, this.data.popupContentHTML, anchor, closeBox)\n+ writeSpace: function() {\n+ return this.pretty ? this.space : \"\"\n+ },\n+ serialize: {\n+ object: function(object) {\n+ if (object == null) {\n+ return \"null\"\n }\n- if (this.data.overflow != null) {\n- this.popup.contentDiv.style.overflow = this.data.overflow\n+ if (object.constructor == Date) {\n+ return this.serialize.date.apply(this, [object])\n }\n- this.popup.feature = this\n- }\n- return this.popup\n- },\n- destroyPopup: function() {\n- if (this.popup) {\n- this.popup.feature = null;\n- this.popup.destroy();\n- this.popup = null\n+ if (object.constructor == Array) {\n+ return this.serialize.array.apply(this, [object])\n+ }\n+ var pieces = [\"{\"];\n+ this.level += 1;\n+ var key, keyJSON, valueJSON;\n+ var addComma = false;\n+ for (key in object) {\n+ if (object.hasOwnProperty(key)) {\n+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [key, this.pretty]);\n+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [object[key], this.pretty]);\n+ if (keyJSON != null && valueJSON != null) {\n+ if (addComma) {\n+ pieces.push(\",\")\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), keyJSON, \":\", this.writeSpace(), valueJSON);\n+ addComma = true\n+ }\n+ }\n+ }\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), \"}\");\n+ return pieces.join(\"\")\n+ },\n+ array: function(array) {\n+ var json;\n+ var pieces = [\"[\"];\n+ this.level += 1;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ json = OpenLayers.Format.JSON.prototype.write.apply(this, [array[i], this.pretty]);\n+ if (json != null) {\n+ if (i > 0) {\n+ pieces.push(\",\")\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), json)\n+ }\n+ }\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), \"]\");\n+ return pieces.join(\"\")\n+ },\n+ string: function(string) {\n+ var m = {\n+ \"\\b\": \"\\\\b\",\n+ \"\\t\": \"\\\\t\",\n+ \"\\n\": \"\\\\n\",\n+ \"\\f\": \"\\\\f\",\n+ \"\\r\": \"\\\\r\",\n+ '\"': '\\\\\"',\n+ \"\\\\\": \"\\\\\\\\\"\n+ };\n+ if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n+ return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n+ var c = m[b];\n+ if (c) {\n+ return c\n+ }\n+ c = b.charCodeAt();\n+ return \"\\\\u00\" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)\n+ }) + '\"'\n+ }\n+ return '\"' + string + '\"'\n+ },\n+ number: function(number) {\n+ return isFinite(number) ? String(number) : \"null\"\n+ },\n+ boolean: function(bool) {\n+ return String(bool)\n+ },\n+ date: function(date) {\n+ function format(number) {\n+ return number < 10 ? \"0\" + number : number\n+ }\n+ return '\"' + date.getFullYear() + \"-\" + format(date.getMonth() + 1) + \"-\" + format(date.getDate()) + \"T\" + format(date.getHours()) + \":\" + format(date.getMinutes()) + \":\" + format(date.getSeconds()) + '\"'\n }\n },\n- CLASS_NAME: \"OpenLayers.Feature\"\n+ CLASS_NAME: \"OpenLayers.Format.JSON\"\n });\n-OpenLayers.State = {\n- UNKNOWN: \"Unknown\",\n- INSERT: \"Insert\",\n- UPDATE: \"Update\",\n- DELETE: \"Delete\"\n-};\n-OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {\n- fid: null,\n- geometry: null,\n- attributes: null,\n+OpenLayers.Geometry = OpenLayers.Class({\n+ id: null,\n+ parent: null,\n bounds: null,\n- state: null,\n- style: null,\n- url: null,\n- renderIntent: \"default\",\n- modified: null,\n- initialize: function(geometry, attributes, style) {\n- OpenLayers.Feature.prototype.initialize.apply(this, [null, null, attributes]);\n- this.lonlat = null;\n- this.geometry = geometry ? geometry : null;\n- this.state = null;\n- this.attributes = {};\n- if (attributes) {\n- this.attributes = OpenLayers.Util.extend(this.attributes, attributes)\n- }\n- this.style = style ? style : null\n+ initialize: function() {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n destroy: function() {\n- if (this.layer) {\n- this.layer.removeFeatures(this);\n- this.layer = null\n- }\n- this.geometry = null;\n- this.modified = null;\n- OpenLayers.Feature.prototype.destroy.apply(this, arguments)\n+ this.id = null;\n+ this.bounds = null\n },\n clone: function() {\n- return new OpenLayers.Feature.Vector(this.geometry ? this.geometry.clone() : null, this.attributes, this.style)\n+ return new OpenLayers.Geometry\n },\n- onScreen: function(boundsOnly) {\n- var onScreen = false;\n- if (this.layer && this.layer.map) {\n- var screenBounds = this.layer.map.getExtent();\n- if (boundsOnly) {\n- var featureBounds = this.geometry.getBounds();\n- onScreen = screenBounds.intersectsBounds(featureBounds)\n- } else {\n- var screenPoly = screenBounds.toGeometry();\n- onScreen = screenPoly.intersects(this.geometry)\n- }\n+ setBounds: function(bounds) {\n+ if (bounds) {\n+ this.bounds = bounds.clone()\n }\n- return onScreen\n },\n- getVisibility: function() {\n- return !(this.style && this.style.display == \"none\" || !this.layer || this.layer && this.layer.styleMap && this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == \"none\" || this.layer && !this.layer.getVisibility())\n+ clearBounds: function() {\n+ this.bounds = null;\n+ if (this.parent) {\n+ this.parent.clearBounds()\n+ }\n },\n- createMarker: function() {\n- return null\n+ extendBounds: function(newBounds) {\n+ var bounds = this.getBounds();\n+ if (!bounds) {\n+ this.setBounds(newBounds)\n+ } else {\n+ this.bounds.extend(newBounds)\n+ }\n },\n- destroyMarker: function() {},\n- createPopup: function() {\n- return null\n+ getBounds: function() {\n+ if (this.bounds == null) {\n+ this.calculateBounds()\n+ }\n+ return this.bounds\n },\n+ calculateBounds: function() {},\n+ distanceTo: function(geometry, options) {},\n+ getVertices: function(nodes) {},\n atPoint: function(lonlat, toleranceLon, toleranceLat) {\n var atPoint = false;\n- if (this.geometry) {\n- atPoint = this.geometry.atPoint(lonlat, toleranceLon, toleranceLat)\n+ var bounds = this.getBounds();\n+ if (bounds != null && lonlat != null) {\n+ var dX = toleranceLon != null ? toleranceLon : 0;\n+ var dY = toleranceLat != null ? toleranceLat : 0;\n+ var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY);\n+ atPoint = toleranceBounds.containsLonLat(lonlat)\n }\n return atPoint\n },\n- destroyPopup: function() {},\n- move: function(location) {\n- if (!this.layer || !this.geometry.move) {\n- return undefined\n- }\n- var pixel;\n- if (location.CLASS_NAME == \"OpenLayers.LonLat\") {\n- pixel = this.layer.getViewPortPxFromLonLat(location)\n+ getLength: function() {\n+ return 0\n+ },\n+ getArea: function() {\n+ return 0\n+ },\n+ getCentroid: function() {\n+ return null\n+ },\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ string = OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this))\n } else {\n- pixel = location\n+ string = Object.prototype.toString.call(this)\n }\n- var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());\n- var res = this.layer.map.getResolution();\n- this.geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y));\n- this.layer.drawFeature(this);\n- return lastPixel\n+ return string\n },\n- toState: function(state) {\n- if (state == OpenLayers.State.UPDATE) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.DELETE:\n- this.state = state;\n- break;\n- case OpenLayers.State.UPDATE:\n- case OpenLayers.State.INSERT:\n- break\n- }\n- } else if (state == OpenLayers.State.INSERT) {\n- switch (this.state) {\n- case OpenLayers.State.UNKNOWN:\n- break;\n- default:\n- this.state = state;\n- break\n- }\n- } else if (state == OpenLayers.State.DELETE) {\n- switch (this.state) {\n- case OpenLayers.State.INSERT:\n- break;\n- case OpenLayers.State.DELETE:\n- break;\n- case OpenLayers.State.UNKNOWN:\n- case OpenLayers.State.UPDATE:\n- this.state = state;\n- break\n+ CLASS_NAME: \"OpenLayers.Geometry\"\n+});\n+OpenLayers.Geometry.fromWKT = function(wkt) {\n+ var geom;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ var format = OpenLayers.Geometry.fromWKT.format;\n+ if (!format) {\n+ format = new OpenLayers.Format.WKT;\n+ OpenLayers.Geometry.fromWKT.format = format\n+ }\n+ var result = format.read(wkt);\n+ if (result instanceof OpenLayers.Feature.Vector) {\n+ geom = result.geometry\n+ } else if (OpenLayers.Util.isArray(result)) {\n+ var len = result.length;\n+ var components = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ components[i] = result[i].geometry\n }\n- } else if (state == OpenLayers.State.UNKNOWN) {\n- this.state = state\n+ geom = new OpenLayers.Geometry.Collection(components)\n }\n- },\n- CLASS_NAME: \"OpenLayers.Feature.Vector\"\n-});\n-OpenLayers.Feature.Vector.style = {\n- default: {\n- fillColor: \"#ee9900\",\n- fillOpacity: .4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: .8,\n- strokeColor: \"#ee9900\",\n- strokeOpacity: 1,\n- strokeWidth: 1,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: .2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n- },\n- select: {\n- fillColor: \"blue\",\n- fillOpacity: .4,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: .8,\n- strokeColor: \"blue\",\n- strokeOpacity: 1,\n- strokeWidth: 2,\n- strokeLinecap: \"round\",\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: .2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"pointer\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n- },\n- temporary: {\n- fillColor: \"#66cccc\",\n- fillOpacity: .2,\n- hoverFillColor: \"white\",\n- hoverFillOpacity: .8,\n- strokeColor: \"#66cccc\",\n- strokeOpacity: 1,\n- strokeLinecap: \"round\",\n- strokeWidth: 2,\n- strokeDashstyle: \"solid\",\n- hoverStrokeColor: \"red\",\n- hoverStrokeOpacity: 1,\n- hoverStrokeWidth: .2,\n- pointRadius: 6,\n- hoverPointRadius: 1,\n- hoverPointUnit: \"%\",\n- pointerEvents: \"visiblePainted\",\n- cursor: \"inherit\",\n- fontColor: \"#000000\",\n- labelAlign: \"cm\",\n- labelOutlineColor: \"white\",\n- labelOutlineWidth: 3\n- },\n- delete: {\n- display: \"none\"\n }\n+ return geom\n };\n-OpenLayers.Style = OpenLayers.Class({\n- id: null,\n- name: null,\n- title: null,\n- description: null,\n- layerName: null,\n- isDefault: false,\n- rules: null,\n- context: null,\n- defaultStyle: null,\n- defaultsPerSymbolizer: false,\n- propertyStyles: null,\n- initialize: function(style, options) {\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules)\n+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n+ var point = options && options.point;\n+ var tolerance = options && options.tolerance;\n+ var intersection = false;\n+ var x11_21 = seg1.x1 - seg2.x1;\n+ var y11_21 = seg1.y1 - seg2.y1;\n+ var x12_11 = seg1.x2 - seg1.x1;\n+ var y12_11 = seg1.y2 - seg1.y1;\n+ var y22_21 = seg2.y2 - seg2.y1;\n+ var x22_21 = seg2.x2 - seg2.x1;\n+ var d = y22_21 * x12_11 - x22_21 * y12_11;\n+ var n1 = x22_21 * y11_21 - y22_21 * x11_21;\n+ var n2 = x12_11 * y11_21 - y12_11 * x11_21;\n+ if (d == 0) {\n+ if (n1 == 0 && n2 == 0) {\n+ intersection = true\n }\n- this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null\n+ } else {\n+ var along1 = n1 / d;\n+ var along2 = n2 / d;\n+ if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n+ if (!point) {\n+ intersection = true\n+ } else {\n+ var x = seg1.x1 + along1 * x12_11;\n+ var y = seg1.y1 + along1 * y12_11;\n+ intersection = new OpenLayers.Geometry.Point(x, y)\n+ }\n }\n- this.rules = null;\n- this.defaultStyle = null\n- },\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n- var rules = this.rules;\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- var applies = rule.evaluate(feature);\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule)\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature)\n+ }\n+ if (tolerance) {\n+ var dist;\n+ if (intersection) {\n+ if (point) {\n+ var segs = [seg1, seg2];\n+ var seg, x, y;\n+ outer: for (var i = 0; i < 2; ++i) {\n+ seg = segs[i];\n+ for (var j = 1; j < 3; ++j) {\n+ x = seg[\"x\" + j];\n+ y = seg[\"y\" + j];\n+ dist = Math.sqrt(Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2));\n+ if (dist < tolerance) {\n+ intersection.x = x;\n+ intersection.y = y;\n+ break outer\n+ }\n+ }\n }\n }\n- }\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature)\n+ } else {\n+ var segs = [seg1, seg2];\n+ var source, target, x, y, p, result;\n+ outer: for (var i = 0; i < 2; ++i) {\n+ source = segs[i];\n+ target = segs[(i + 1) % 2];\n+ for (var j = 1; j < 3; ++j) {\n+ p = {\n+ x: source[\"x\" + j],\n+ y: source[\"y\" + j]\n+ };\n+ result = OpenLayers.Geometry.distanceToSegment(p, target);\n+ if (result.distance < tolerance) {\n+ if (point) {\n+ intersection = new OpenLayers.Geometry.Point(p.x, p.y)\n+ } else {\n+ intersection = true\n+ }\n+ break outer\n+ }\n+ }\n }\n }\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\"\n- }\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label)\n- }\n- return style\n+ }\n+ return intersection\n+};\n+OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n+ var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n+ result.distance = Math.sqrt(result.distance);\n+ return result\n+};\n+OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n+ var x0 = point.x;\n+ var y0 = point.y;\n+ var x1 = segment.x1;\n+ var y1 = segment.y1;\n+ var x2 = segment.x2;\n+ var y2 = segment.y2;\n+ var dx = x2 - x1;\n+ var dy = y2 - y1;\n+ var along = (dx * (x0 - x1) + dy * (y0 - y1)) / (Math.pow(dx, 2) + Math.pow(dy, 2));\n+ var x, y;\n+ if (along <= 0) {\n+ x = x1;\n+ y = y1\n+ } else if (along >= 1) {\n+ x = x2;\n+ y = y2\n+ } else {\n+ x = x1 + along * dx;\n+ y = y1 + along * dy\n+ }\n+ return {\n+ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n+ x: x,\n+ y: y,\n+ along: along\n+ }\n+};\n+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n+ x: null,\n+ y: null,\n+ initialize: function(x, y) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+ this.x = parseFloat(x);\n+ this.y = parseFloat(y)\n },\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- })\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- })\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- })\n- }\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Geometry.Point(this.x, this.y)\n }\n- return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n+ OpenLayers.Util.applyDefaults(obj, this);\n+ return obj\n },\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n- }\n- return style\n+ calculateBounds: function() {\n+ this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x, this.y)\n },\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- this.addPropertyStyles(propertyStyles, value)\n- } else {\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var distance, x0, y0, x1, y1, result;\n+ if (geometry instanceof OpenLayers.Geometry.Point) {\n+ x0 = this.x;\n+ y0 = this.y;\n+ x1 = geometry.x;\n+ y1 = geometry.y;\n+ distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));\n+ result = !details ? distance : {\n+ x0: x0,\n+ y0: y0,\n+ x1: x1,\n+ y1: y1,\n+ distance: distance\n+ }\n+ } else {\n+ result = geometry.distanceTo(this, options);\n+ if (details) {\n+ result = {\n+ x0: result.x1,\n+ y0: result.y1,\n+ x1: result.x0,\n+ y1: result.y0,\n+ distance: result.distance\n }\n }\n }\n- return propertyStyles\n+ return result\n },\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true\n- }\n+ equals: function(geom) {\n+ var equals = false;\n+ if (geom != null) {\n+ equals = this.x == geom.x && this.y == geom.y || isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)\n }\n- return propertyStyles\n+ return equals\n },\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles()\n+ toShortString: function() {\n+ return this.x + \", \" + this.y\n },\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles()\n+ move: function(x, y) {\n+ this.x = this.x + x;\n+ this.y = this.y + y;\n+ this.clearBounds()\n },\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i]\n- }\n+ rotate: function(angle, origin) {\n+ angle *= Math.PI / 180;\n+ var radius = this.distanceTo(origin);\n+ var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);\n+ this.x = origin.x + radius * Math.cos(theta);\n+ this.y = origin.y + radius * Math.sin(theta);\n+ this.clearBounds()\n+ },\n+ getCentroid: function() {\n+ return new OpenLayers.Geometry.Point(this.x, this.y)\n+ },\n+ resize: function(scale, origin, ratio) {\n+ ratio = ratio == undefined ? 1 : ratio;\n+ this.x = origin.x + scale * ratio * (this.x - origin.x);\n+ this.y = origin.y + scale * (this.y - origin.y);\n+ this.clearBounds();\n+ return this\n+ },\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.equals(geometry)\n+ } else {\n+ intersect = geometry.intersects(this)\n }\n+ return intersect\n },\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone())\n- }\n+ transform: function(source, dest) {\n+ if (source && dest) {\n+ OpenLayers.Projection.transform(this, source, dest);\n+ this.bounds = null\n }\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options)\n+ return this\n },\n- CLASS_NAME: \"OpenLayers.Style\"\n+ getVertices: function(nodes) {\n+ return [this]\n+ },\n+ CLASS_NAME: \"OpenLayers.Geometry.Point\"\n });\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = isNaN(value) || !value ? value : parseFloat(value)\n- }\n- return value\n-};\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n-OpenLayers.Rule = OpenLayers.Class({\n- id: null,\n- name: null,\n- title: null,\n- description: null,\n- context: null,\n- filter: null,\n- elseFilter: false,\n- symbolizer: null,\n- symbolizers: null,\n- minScaleDenominator: null,\n- maxScaleDenominator: null,\n- initialize: function(options) {\n- this.symbolizer = {};\n- OpenLayers.Util.extend(this, options);\n- if (this.symbolizers) {\n- delete this.symbolizer\n+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n+ components: null,\n+ componentTypes: null,\n+ initialize: function(components) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+ this.components = [];\n+ if (components != null) {\n+ this.addComponents(components)\n }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null\n- }\n- this.symbolizer = null;\n- delete this.symbolizers\n+ this.components.length = 0;\n+ this.components = null;\n+ OpenLayers.Geometry.prototype.destroy.apply(this, arguments)\n },\n- evaluate: function(feature) {\n- var context = this.getContext(feature);\n- var applies = true;\n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale()\n- }\n- if (this.minScaleDenominator) {\n- applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context)\n- }\n- if (applies && this.maxScaleDenominator) {\n- applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context)\n+ clone: function() {\n+ var geometry = eval(\"new \" + this.CLASS_NAME + \"()\");\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ geometry.addComponent(this.components[i].clone())\n }\n- if (applies && this.filter) {\n- if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n- applies = this.filter.evaluate(feature)\n- } else {\n- applies = this.filter.evaluate(context)\n- }\n+ OpenLayers.Util.applyDefaults(geometry, this);\n+ return geometry\n+ },\n+ getComponentsString: function() {\n+ var strings = [];\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ strings.push(this.components[i].toShortString())\n }\n- return applies\n+ return strings.join(\",\")\n },\n- getContext: function(feature) {\n- var context = this.context;\n- if (!context) {\n- context = feature.attributes || feature.data\n+ calculateBounds: function() {\n+ this.bounds = null;\n+ var bounds = new OpenLayers.Bounds;\n+ var components = this.components;\n+ if (components) {\n+ for (var i = 0, len = components.length; i < len; i++) {\n+ bounds.extend(components[i].getBounds())\n+ }\n }\n- if (typeof this.context == \"function\") {\n- context = this.context(feature)\n+ if (bounds.left != null && bounds.bottom != null && bounds.right != null && bounds.top != null) {\n+ this.setBounds(bounds)\n }\n- return context\n },\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.symbolizers) {\n- var len = this.symbolizers.length;\n- options.symbolizers = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- options.symbolizers[i] = this.symbolizers[i].clone()\n- }\n- } else {\n- options.symbolizer = {};\n- var value, type;\n- for (var key in this.symbolizer) {\n- value = this.symbolizer[key];\n- type = typeof value;\n- if (type === \"object\") {\n- options.symbolizer[key] = OpenLayers.Util.extend({}, value)\n- } else if (type === \"string\") {\n- options.symbolizer[key] = value\n- }\n- }\n+ addComponents: function(components) {\n+ if (!OpenLayers.Util.isArray(components)) {\n+ components = [components]\n+ }\n+ for (var i = 0, len = components.length; i < len; i++) {\n+ this.addComponent(components[i])\n }\n- options.filter = this.filter && this.filter.clone();\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- return new OpenLayers.Rule(options)\n },\n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n-OpenLayers.StyleMap = OpenLayers.Class({\n- styles: null,\n- extendDefault: true,\n- initialize: function(style, options) {\n- this.styles = {\n- default: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"default\"]),\n- select: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"select\"]),\n- temporary: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"temporary\"]),\n- delete: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"delete\"])\n- };\n- if (style instanceof OpenLayers.Style) {\n- this.styles[\"default\"] = style;\n- this.styles[\"select\"] = style;\n- this.styles[\"temporary\"] = style;\n- this.styles[\"delete\"] = style\n- } else if (typeof style == \"object\") {\n- for (var key in style) {\n- if (style[key] instanceof OpenLayers.Style) {\n- this.styles[key] = style[key]\n- } else if (typeof style[key] == \"object\") {\n- this.styles[key] = new OpenLayers.Style(style[key])\n+ addComponent: function(component, index) {\n+ var added = false;\n+ if (component) {\n+ if (this.componentTypes == null || OpenLayers.Util.indexOf(this.componentTypes, component.CLASS_NAME) > -1) {\n+ if (index != null && index < this.components.length) {\n+ var components1 = this.components.slice(0, index);\n+ var components2 = this.components.slice(index, this.components.length);\n+ components1.push(component);\n+ this.components = components1.concat(components2)\n } else {\n- this.styles[\"default\"] = new OpenLayers.Style(style);\n- this.styles[\"select\"] = new OpenLayers.Style(style);\n- this.styles[\"temporary\"] = new OpenLayers.Style(style);\n- this.styles[\"delete\"] = new OpenLayers.Style(style);\n- break\n+ this.components.push(component)\n }\n+ component.parent = this;\n+ this.clearBounds();\n+ added = true\n }\n }\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy()\n- }\n- this.styles = null\n+ return added\n },\n- createSymbolizer: function(feature, intent) {\n- if (!feature) {\n- feature = new OpenLayers.Feature.Vector\n- }\n- if (!this.styles[intent]) {\n- intent = \"default\"\n+ removeComponents: function(components) {\n+ var removed = false;\n+ if (!OpenLayers.Util.isArray(components)) {\n+ components = [components]\n }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature)\n+ for (var i = components.length - 1; i >= 0; --i) {\n+ removed = this.removeComponent(components[i]) || removed\n }\n- return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature))\n+ return removed\n },\n- addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n- var rules = [];\n- for (var value in symbolizers) {\n- rules.push(new OpenLayers.Rule({\n- symbolizer: symbolizers[value],\n- context: context,\n- filter: new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- property: property,\n- value: value\n- })\n- }))\n+ removeComponent: function(component) {\n+ OpenLayers.Util.removeItem(this.components, component);\n+ this.clearBounds();\n+ return true\n+ },\n+ getLength: function() {\n+ var length = 0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ length += this.components[i].getLength()\n }\n- this.styles[renderIntent].addRules(rules)\n+ return length\n },\n- CLASS_NAME: \"OpenLayers.StyleMap\"\n-});\n-OpenLayers.Layer = OpenLayers.Class({\n- id: null,\n- name: null,\n- div: null,\n- opacity: 1,\n- alwaysInRange: null,\n- RESOLUTION_PROPERTIES: [\"scales\", \"resolutions\", \"maxScale\", \"minScale\", \"maxResolution\", \"minResolution\", \"numZoomLevels\", \"maxZoomLevel\"],\n- events: null,\n- map: null,\n- isBaseLayer: false,\n- alpha: false,\n- displayInLayerSwitcher: true,\n- visibility: true,\n- attribution: null,\n- inRange: false,\n- imageSize: null,\n- options: null,\n- eventListeners: null,\n- gutter: 0,\n- projection: null,\n- units: null,\n- scales: null,\n- resolutions: null,\n- maxExtent: null,\n- minExtent: null,\n- maxResolution: null,\n- minResolution: null,\n- numZoomLevels: null,\n- minScale: null,\n- maxScale: null,\n- displayOutsideMaxExtent: false,\n- wrapDateLine: false,\n- metadata: null,\n- initialize: function(name, options) {\n- this.metadata = {};\n- options = OpenLayers.Util.extend({}, options);\n- if (this.alwaysInRange != null) {\n- options.alwaysInRange = this.alwaysInRange\n+ getArea: function() {\n+ var area = 0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ area += this.components[i].getArea()\n }\n- this.addOptions(options);\n- this.name = name;\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.style.width = \"100%\";\n- this.div.style.height = \"100%\";\n- this.div.dir = \"ltr\";\n- this.events = new OpenLayers.Events(this, this.div);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n+ return area\n+ },\n+ getGeodesicArea: function(projection) {\n+ var area = 0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ area += this.components[i].getGeodesicArea(projection)\n }\n+ return area\n },\n- destroy: function(setNewBaseLayer) {\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true\n+ getCentroid: function(weighted) {\n+ if (!weighted) {\n+ return this.components.length && this.components[0].getCentroid()\n }\n- if (this.map != null) {\n- this.map.removeLayer(this, setNewBaseLayer)\n+ var len = this.components.length;\n+ if (!len) {\n+ return false\n }\n- this.projection = null;\n- this.map = null;\n- this.name = null;\n- this.div = null;\n- this.options = null;\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n+ var areas = [];\n+ var centroids = [];\n+ var areaSum = 0;\n+ var minArea = Number.MAX_VALUE;\n+ var component;\n+ for (var i = 0; i < len; ++i) {\n+ component = this.components[i];\n+ var area = component.getArea();\n+ var centroid = component.getCentroid(true);\n+ if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {\n+ continue\n }\n- this.events.destroy()\n+ areas.push(area);\n+ areaSum += area;\n+ minArea = area < minArea && area > 0 ? area : minArea;\n+ centroids.push(centroid)\n }\n- this.eventListeners = null;\n- this.events = null\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer(this.name, this.getOptions())\n+ len = areas.length;\n+ if (areaSum === 0) {\n+ for (var i = 0; i < len; ++i) {\n+ areas[i] = 1\n+ }\n+ areaSum = areas.length\n+ } else {\n+ for (var i = 0; i < len; ++i) {\n+ areas[i] /= minArea\n+ }\n+ areaSum /= minArea\n }\n- OpenLayers.Util.applyDefaults(obj, this);\n- obj.map = null;\n- return obj\n- },\n- getOptions: function() {\n- var options = {};\n- for (var o in this.options) {\n- options[o] = this[o]\n+ var xSum = 0,\n+ ySum = 0,\n+ centroid, area;\n+ for (var i = 0; i < len; ++i) {\n+ centroid = centroids[i];\n+ area = areas[i];\n+ xSum += centroid.x * area;\n+ ySum += centroid.y * area\n }\n- return options\n+ return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum)\n },\n- setName: function(newName) {\n- if (newName != this.name) {\n- this.name = newName;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"name\"\n- })\n- }\n+ getGeodesicLength: function(projection) {\n+ var length = 0;\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ length += this.components[i].getGeodesicLength(projection)\n }\n+ return length\n },\n- addOptions: function(newOptions, reinitialize) {\n- if (this.options == null) {\n- this.options = {}\n+ move: function(x, y) {\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ this.components[i].move(x, y)\n }\n- if (newOptions) {\n- if (typeof newOptions.projection == \"string\") {\n- newOptions.projection = new OpenLayers.Projection(newOptions.projection)\n- }\n- if (newOptions.projection) {\n- OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()])\n- }\n- if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n- newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent)\n- }\n- if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n- newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent)\n- }\n+ },\n+ rotate: function(angle, origin) {\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ this.components[i].rotate(angle, origin)\n }\n- OpenLayers.Util.extend(this.options, newOptions);\n- OpenLayers.Util.extend(this, newOptions);\n- if (this.projection && this.projection.getUnits()) {\n- this.units = this.projection.getUnits()\n+ },\n+ resize: function(scale, origin, ratio) {\n+ for (var i = 0; i < this.components.length; ++i) {\n+ this.components[i].resize(scale, origin, ratio)\n }\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- var properties = this.RESOLUTION_PROPERTIES.concat([\"projection\", \"units\", \"minExtent\", \"maxExtent\"]);\n- for (var o in newOptions) {\n- if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) {\n- this.initResolutions();\n- if (reinitialize && this.map.baseLayer === this) {\n- this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true);\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- })\n- }\n+ return this\n+ },\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var result, best, distance;\n+ var min = Number.POSITIVE_INFINITY;\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ result = this.components[i].distanceTo(geometry, options);\n+ distance = details ? result.distance : result;\n+ if (distance < min) {\n+ min = distance;\n+ best = result;\n+ if (min == 0) {\n break\n }\n }\n }\n+ return best\n },\n- onMapResize: function() {},\n- redraw: function() {\n- var redrawn = false;\n- if (this.map) {\n- this.inRange = this.calculateInRange();\n- var extent = this.getExtent();\n- if (extent && this.inRange && this.visibility) {\n- var zoomChanged = true;\n- this.moveTo(extent, zoomChanged, false);\n- this.events.triggerEvent(\"moveend\", {\n- zoomChanged: zoomChanged\n- });\n- redrawn = true\n+ equals: function(geometry) {\n+ var equivalent = true;\n+ if (!geometry || !geometry.CLASS_NAME || this.CLASS_NAME != geometry.CLASS_NAME) {\n+ equivalent = false\n+ } else if (!OpenLayers.Util.isArray(geometry.components) || geometry.components.length != this.components.length) {\n+ equivalent = false\n+ } else {\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ if (!this.components[i].equals(geometry.components[i])) {\n+ equivalent = false;\n+ break\n+ }\n }\n }\n- return redrawn\n+ return equivalent\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- var display = this.visibility;\n- if (!this.isBaseLayer) {\n- display = display && this.inRange\n+ transform: function(source, dest) {\n+ if (source && dest) {\n+ for (var i = 0, len = this.components.length; i < len; i++) {\n+ var component = this.components[i];\n+ component.transform(source, dest)\n+ }\n+ this.bounds = null\n }\n- this.display(display)\n+ return this\n },\n- moveByPx: function(dx, dy) {},\n- setMap: function(map) {\n- if (this.map == null) {\n- this.map = map;\n- this.maxExtent = this.maxExtent || this.map.maxExtent;\n- this.minExtent = this.minExtent || this.map.minExtent;\n- this.projection = this.projection || this.map.projection;\n- if (typeof this.projection == \"string\") {\n- this.projection = new OpenLayers.Projection(this.projection)\n- }\n- this.units = this.projection.getUnits() || this.units || this.map.units;\n- this.initResolutions();\n- if (!this.isBaseLayer) {\n- this.inRange = this.calculateInRange();\n- var show = this.visibility && this.inRange;\n- this.div.style.display = show ? \"\" : \"none\"\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ intersect = geometry.intersects(this.components[i]);\n+ if (intersect) {\n+ break\n }\n- this.setTileSize()\n }\n+ return intersect\n },\n- afterAdd: function() {},\n- removeMap: function(map) {},\n- getImageSize: function(bounds) {\n- return this.imageSize || this.tileSize\n- },\n- setTileSize: function(size) {\n- var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize();\n- this.tileSize = tileSize;\n- if (this.gutter) {\n- this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter)\n+ getVertices: function(nodes) {\n+ var vertices = [];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ Array.prototype.push.apply(vertices, this.components[i].getVertices(nodes))\n }\n+ return vertices\n },\n- getVisibility: function() {\n- return this.visibility\n+ CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n+});\n+OpenLayers.Geometry.MultiPoint = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n+ addPoint: function(point, index) {\n+ this.addComponent(point, index)\n },\n- setVisibility: function(visibility) {\n- if (visibility != this.visibility) {\n- this.visibility = visibility;\n- this.display(visibility);\n- this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"visibility\"\n- })\n- }\n- this.events.triggerEvent(\"visibilitychanged\")\n- }\n+ removePoint: function(point) {\n+ this.removeComponent(point)\n },\n- display: function(display) {\n- if (display != (this.div.style.display != \"none\")) {\n- this.div.style.display = display && this.calculateInRange() ? \"block\" : \"none\"\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n+});\n+OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n+ getLength: function() {\n+ var length = 0;\n+ if (this.components && this.components.length > 1) {\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ length += this.components[i - 1].distanceTo(this.components[i])\n+ }\n }\n+ return length\n },\n- calculateInRange: function() {\n- var inRange = false;\n- if (this.alwaysInRange) {\n- inRange = true\n- } else {\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- inRange = resolution >= this.minResolution && resolution <= this.maxResolution\n+ getGeodesicLength: function(projection) {\n+ var geom = this;\n+ if (projection) {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ if (!gg.equals(projection)) {\n+ geom = this.clone().transform(projection, gg)\n }\n }\n- return inRange\n- },\n- setIsBaseLayer: function(isBaseLayer) {\n- if (isBaseLayer != this.isBaseLayer) {\n- this.isBaseLayer = isBaseLayer;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n+ var length = 0;\n+ if (geom.components && geom.components.length > 1) {\n+ var p1, p2;\n+ for (var i = 1, len = geom.components.length; i < len; i++) {\n+ p1 = geom.components[i - 1];\n+ p2 = geom.components[i];\n+ length += OpenLayers.Util.distVincenty({\n+ lon: p1.x,\n+ lat: p1.y\n+ }, {\n+ lon: p2.x,\n+ lat: p2.y\n })\n }\n }\n+ return length * 1e3\n },\n- initResolutions: function() {\n- var i, len, p;\n- var props = {},\n- alwaysInRange = true;\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p];\n- if (alwaysInRange && this.options[p]) {\n- alwaysInRange = false\n- }\n- }\n- if (this.options.alwaysInRange == null) {\n- this.alwaysInRange = alwaysInRange\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales)\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props)\n- }\n- if (props.resolutions == null) {\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p] != null ? this.options[p] : this.map[p]\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales)\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props)\n- }\n- }\n- var maxResolution;\n- if (this.options.maxResolution && this.options.maxResolution !== \"auto\") {\n- maxResolution = this.options.maxResolution\n- }\n- if (this.options.minScale) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(this.options.minScale, this.units)\n- }\n- var minResolution;\n- if (this.options.minResolution && this.options.minResolution !== \"auto\") {\n- minResolution = this.options.minResolution\n- }\n- if (this.options.maxScale) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units)\n+ CLASS_NAME: \"OpenLayers.Geometry.Curve\"\n+});\n+OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {\n+ removeComponent: function(point) {\n+ var removed = this.components && this.components.length > 2;\n+ if (removed) {\n+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments)\n }\n- if (props.resolutions) {\n- props.resolutions.sort(function(a, b) {\n- return b - a\n- });\n- if (!maxResolution) {\n- maxResolution = props.resolutions[0]\n- }\n- if (!minResolution) {\n- var lastIdx = props.resolutions.length - 1;\n- minResolution = props.resolutions[lastIdx]\n+ return removed\n+ },\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ var type = geometry.CLASS_NAME;\n+ if (type == \"OpenLayers.Geometry.LineString\" || type == \"OpenLayers.Geometry.LinearRing\" || type == \"OpenLayers.Geometry.Point\") {\n+ var segs1 = this.getSortedSegments();\n+ var segs2;\n+ if (type == \"OpenLayers.Geometry.Point\") {\n+ segs2 = [{\n+ x1: geometry.x,\n+ y1: geometry.y,\n+ x2: geometry.x,\n+ y2: geometry.y\n+ }]\n+ } else {\n+ segs2 = geometry.getSortedSegments()\n }\n- }\n- this.resolutions = props.resolutions;\n- if (this.resolutions) {\n- len = this.resolutions.length;\n- this.scales = new Array(len);\n- for (i = 0; i < len; i++) {\n- this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units)\n+ var seg1, seg1x1, seg1x2, seg1y1, seg1y2, seg2, seg2y1, seg2y2;\n+ outer: for (var i = 0, len = segs1.length; i < len; ++i) {\n+ seg1 = segs1[i];\n+ seg1x1 = seg1.x1;\n+ seg1x2 = seg1.x2;\n+ seg1y1 = seg1.y1;\n+ seg1y2 = seg1.y2;\n+ inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {\n+ seg2 = segs2[j];\n+ if (seg2.x1 > seg1x2) {\n+ break\n+ }\n+ if (seg2.x2 < seg1x1) {\n+ continue\n+ }\n+ seg2y1 = seg2.y1;\n+ seg2y2 = seg2.y2;\n+ if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {\n+ continue\n+ }\n+ if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {\n+ continue\n+ }\n+ if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {\n+ intersect = true;\n+ break outer\n+ }\n+ }\n }\n- this.numZoomLevels = len\n- }\n- this.minResolution = minResolution;\n- if (minResolution) {\n- this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units)\n- }\n- this.maxResolution = maxResolution;\n- if (maxResolution) {\n- this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units)\n+ } else {\n+ intersect = geometry.intersects(this)\n }\n+ return intersect\n },\n- resolutionsFromScales: function(scales) {\n- if (scales == null) {\n- return\n+ getSortedSegments: function() {\n+ var numSeg = this.components.length - 1;\n+ var segments = new Array(numSeg),\n+ point1, point2;\n+ for (var i = 0; i < numSeg; ++i) {\n+ point1 = this.components[i];\n+ point2 = this.components[i + 1];\n+ if (point1.x < point2.x) {\n+ segments[i] = {\n+ x1: point1.x,\n+ y1: point1.y,\n+ x2: point2.x,\n+ y2: point2.y\n+ }\n+ } else {\n+ segments[i] = {\n+ x1: point2.x,\n+ y1: point2.y,\n+ x2: point1.x,\n+ y2: point1.y\n+ }\n+ }\n }\n- var resolutions, i, len;\n- len = scales.length;\n- resolutions = new Array(len);\n- for (i = 0; i < len; i++) {\n- resolutions[i] = OpenLayers.Util.getResolutionFromScale(scales[i], this.units)\n+\n+ function byX1(seg1, seg2) {\n+ return seg1.x1 - seg2.x1\n }\n- return resolutions\n+ return segments.sort(byX1)\n },\n- calculateResolutions: function(props) {\n- var viewSize, wRes, hRes;\n- var maxResolution = props.maxResolution;\n- if (props.minScale != null) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(props.minScale, this.units)\n- } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.maxExtent.getWidth() / viewSize.w;\n- hRes = this.maxExtent.getHeight() / viewSize.h;\n- maxResolution = Math.max(wRes, hRes)\n- }\n- var minResolution = props.minResolution;\n- if (props.maxScale != null) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units)\n- } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.minExtent.getWidth() / viewSize.w;\n- hRes = this.minExtent.getHeight() / viewSize.h;\n- minResolution = Math.max(wRes, hRes)\n+ splitWithSegment: function(seg, options) {\n+ var edge = !(options && options.edge === false);\n+ var tolerance = options && options.tolerance;\n+ var lines = [];\n+ var verts = this.getVertices();\n+ var points = [];\n+ var intersections = [];\n+ var split = false;\n+ var vert1, vert2, point;\n+ var node, vertex, target;\n+ var interOptions = {\n+ point: true,\n+ tolerance: tolerance\n+ };\n+ var result = null;\n+ for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n+ vert1 = verts[i];\n+ points.push(vert1.clone());\n+ vert2 = verts[i + 1];\n+ target = {\n+ x1: vert1.x,\n+ y1: vert1.y,\n+ x2: vert2.x,\n+ y2: vert2.y\n+ };\n+ point = OpenLayers.Geometry.segmentsIntersect(seg, target, interOptions);\n+ if (point instanceof OpenLayers.Geometry.Point) {\n+ if (point.x === seg.x1 && point.y === seg.y1 || point.x === seg.x2 && point.y === seg.y2 || point.equals(vert1) || point.equals(vert2)) {\n+ vertex = true\n+ } else {\n+ vertex = false\n+ }\n+ if (vertex || edge) {\n+ if (!point.equals(intersections[intersections.length - 1])) {\n+ intersections.push(point.clone())\n+ }\n+ if (i === 0) {\n+ if (point.equals(vert1)) {\n+ continue\n+ }\n+ }\n+ if (point.equals(vert2)) {\n+ continue\n+ }\n+ split = true;\n+ if (!point.equals(vert1)) {\n+ points.push(point)\n+ }\n+ lines.push(new OpenLayers.Geometry.LineString(points));\n+ points = [point.clone()]\n+ }\n+ }\n }\n- if (typeof maxResolution !== \"number\" && typeof minResolution !== \"number\" && this.maxExtent != null) {\n- var tileSize = this.map.getTileSize();\n- maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h)\n+ if (split) {\n+ points.push(vert2.clone());\n+ lines.push(new OpenLayers.Geometry.LineString(points))\n }\n- var maxZoomLevel = props.maxZoomLevel;\n- var numZoomLevels = props.numZoomLevels;\n- if (typeof minResolution === \"number\" && typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n- var ratio = maxResolution / minResolution;\n- numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1\n- } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n- numZoomLevels = maxZoomLevel + 1\n+ if (intersections.length > 0) {\n+ var xDir = seg.x1 < seg.x2 ? 1 : -1;\n+ var yDir = seg.y1 < seg.y2 ? 1 : -1;\n+ result = {\n+ lines: lines,\n+ points: intersections.sort(function(p1, p2) {\n+ return xDir * p1.x - xDir * p2.x || yDir * p1.y - yDir * p2.y\n+ })\n+ }\n }\n- if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 || typeof maxResolution !== \"number\" && typeof minResolution !== \"number\") {\n- return\n+ return result\n+ },\n+ split: function(target, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var sourceSplit, targetSplit, sourceParts, targetParts;\n+ if (target instanceof OpenLayers.Geometry.LineString) {\n+ var verts = this.getVertices();\n+ var vert1, vert2, seg, splits, lines, point;\n+ var points = [];\n+ sourceParts = [];\n+ for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n+ vert1 = verts[i];\n+ vert2 = verts[i + 1];\n+ seg = {\n+ x1: vert1.x,\n+ y1: vert1.y,\n+ x2: vert2.x,\n+ y2: vert2.y\n+ };\n+ targetParts = targetParts || [target];\n+ if (mutual) {\n+ points.push(vert1.clone())\n+ }\n+ for (var j = 0; j < targetParts.length; ++j) {\n+ splits = targetParts[j].splitWithSegment(seg, options);\n+ if (splits) {\n+ lines = splits.lines;\n+ if (lines.length > 0) {\n+ lines.unshift(j, 1);\n+ Array.prototype.splice.apply(targetParts, lines);\n+ j += lines.length - 2\n+ }\n+ if (mutual) {\n+ for (var k = 0, len = splits.points.length; k < len; ++k) {\n+ point = splits.points[k];\n+ if (!point.equals(vert1)) {\n+ points.push(point);\n+ sourceParts.push(new OpenLayers.Geometry.LineString(points));\n+ if (point.equals(vert2)) {\n+ points = []\n+ } else {\n+ points = [point.clone()]\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ if (mutual && sourceParts.length > 0 && points.length > 0) {\n+ points.push(vert2.clone());\n+ sourceParts.push(new OpenLayers.Geometry.LineString(points))\n+ }\n+ } else {\n+ results = target.splitWith(this, options)\n }\n- var resolutions = new Array(numZoomLevels);\n- var base = 2;\n- if (typeof minResolution == \"number\" && typeof maxResolution == \"number\") {\n- base = Math.pow(maxResolution / minResolution, 1 / (numZoomLevels - 1))\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true\n+ } else {\n+ targetParts = []\n }\n- var i;\n- if (typeof maxResolution === \"number\") {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[i] = maxResolution / Math.pow(base, i)\n- }\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true\n } else {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[numZoomLevels - 1 - i] = minResolution * Math.pow(base, i)\n+ sourceParts = []\n+ }\n+ if (targetSplit || sourceSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts]\n+ } else {\n+ results = targetParts\n }\n }\n- return resolutions\n- },\n- getResolution: function() {\n- var zoom = this.map.getZoom();\n- return this.getResolutionForZoom(zoom)\n- },\n- getExtent: function() {\n- return this.map.calculateBounds()\n+ return results\n },\n- getZoomForExtent: function(extent, closest) {\n- var viewSize = this.map.getSize();\n- var idealResolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);\n- return this.getZoomForResolution(idealResolution, closest)\n+ splitWith: function(geometry, options) {\n+ return geometry.split(this, options)\n },\n- getDataExtent: function() {},\n- getResolutionForZoom: function(zoom) {\n- zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n- var resolution;\n- if (this.map.fractionalZoom) {\n- var low = Math.floor(zoom);\n- var high = Math.ceil(zoom);\n- resolution = this.resolutions[low] - (zoom - low) * (this.resolutions[low] - this.resolutions[high])\n+ getVertices: function(nodes) {\n+ var vertices;\n+ if (nodes === true) {\n+ vertices = [this.components[0], this.components[this.components.length - 1]]\n+ } else if (nodes === false) {\n+ vertices = this.components.slice(1, this.components.length - 1)\n } else {\n- resolution = this.resolutions[Math.round(zoom)]\n+ vertices = this.components.slice()\n }\n- return resolution\n+ return vertices\n },\n- getZoomForResolution: function(resolution, closest) {\n- var zoom, i, len;\n- if (this.map.fractionalZoom) {\n- var lowZoom = 0;\n- var highZoom = this.resolutions.length - 1;\n- var highRes = this.resolutions[lowZoom];\n- var lowRes = this.resolutions[highZoom];\n- var res;\n- for (i = 0, len = this.resolutions.length; i < len; ++i) {\n- res = this.resolutions[i];\n- if (res >= resolution) {\n- highRes = res;\n- lowZoom = i\n- }\n- if (res <= resolution) {\n- lowRes = res;\n- highZoom = i;\n- break\n- }\n- }\n- var dRes = highRes - lowRes;\n- if (dRes > 0) {\n- zoom = lowZoom + (highRes - resolution) / dRes\n- } else {\n- zoom = lowZoom\n- }\n- } else {\n- var diff;\n- var minDiff = Number.POSITIVE_INFINITY;\n- for (i = 0, len = this.resolutions.length; i < len; i++) {\n- if (closest) {\n- diff = Math.abs(this.resolutions[i] - resolution);\n- if (diff > minDiff) {\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var details = edge && options && options.details;\n+ var result, best = {};\n+ var min = Number.POSITIVE_INFINITY;\n+ if (geometry instanceof OpenLayers.Geometry.Point) {\n+ var segs = this.getSortedSegments();\n+ var x = geometry.x;\n+ var y = geometry.y;\n+ var seg;\n+ for (var i = 0, len = segs.length; i < len; ++i) {\n+ seg = segs[i];\n+ result = OpenLayers.Geometry.distanceToSegment(geometry, seg);\n+ if (result.distance < min) {\n+ min = result.distance;\n+ best = result;\n+ if (min === 0) {\n break\n }\n- minDiff = diff\n } else {\n- if (this.resolutions[i] < resolution) {\n+ if (seg.x2 > x && (y > seg.y1 && y < seg.y2 || y < seg.y1 && y > seg.y2)) {\n break\n }\n }\n }\n- zoom = Math.max(0, i - 1)\n- }\n- return zoom\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- var map = this.map;\n- if (viewPortPx != null && map.minPx) {\n- var res = map.getResolution();\n- var maxExtent = map.getMaxExtent({\n- restricted: true\n- });\n- var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n- var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n- lonlat = new OpenLayers.LonLat(lon, lat);\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent)\n+ if (details) {\n+ best = {\n+ distance: best.distance,\n+ x0: best.x,\n+ y0: best.y,\n+ x1: x,\n+ y1: y\n+ }\n+ } else {\n+ best = best.distance\n }\n- }\n- return lonlat\n- },\n- getViewPortPxFromLonLat: function(lonlat, resolution) {\n- var px = null;\n- if (lonlat != null) {\n- resolution = resolution || this.map.getResolution();\n- var extent = this.map.calculateBounds(null, resolution);\n- px = new OpenLayers.Pixel(1 / resolution * (lonlat.lon - extent.left), 1 / resolution * (extent.top - lonlat.lat))\n- }\n- return px\n- },\n- setOpacity: function(opacity) {\n- if (opacity != this.opacity) {\n- this.opacity = opacity;\n- var childNodes = this.div.childNodes;\n- for (var i = 0, len = childNodes.length; i < len; ++i) {\n- var element = childNodes[i].firstChild || childNodes[i];\n- var lastChild = childNodes[i].lastChild;\n- if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n- element = lastChild.parentNode\n+ } else if (geometry instanceof OpenLayers.Geometry.LineString) {\n+ var segs0 = this.getSortedSegments();\n+ var segs1 = geometry.getSortedSegments();\n+ var seg0, seg1, intersection, x0, y0;\n+ var len1 = segs1.length;\n+ var interOptions = {\n+ point: true\n+ };\n+ outer: for (var i = 0, len = segs0.length; i < len; ++i) {\n+ seg0 = segs0[i];\n+ x0 = seg0.x1;\n+ y0 = seg0.y1;\n+ for (var j = 0; j < len1; ++j) {\n+ seg1 = segs1[j];\n+ intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);\n+ if (intersection) {\n+ min = 0;\n+ best = {\n+ distance: 0,\n+ x0: intersection.x,\n+ y0: intersection.y,\n+ x1: intersection.x,\n+ y1: intersection.y\n+ };\n+ break outer\n+ } else {\n+ result = OpenLayers.Geometry.distanceToSegment({\n+ x: x0,\n+ y: y0\n+ }, seg1);\n+ if (result.distance < min) {\n+ min = result.distance;\n+ best = {\n+ distance: min,\n+ x0: x0,\n+ y0: y0,\n+ x1: result.x,\n+ y1: result.y\n+ }\n+ }\n+ }\n }\n- OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity)\n }\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- })\n+ if (!details) {\n+ best = best.distance\n+ }\n+ if (min !== 0) {\n+ if (seg0) {\n+ result = geometry.distanceTo(new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), options);\n+ var dist = details ? result.distance : result;\n+ if (dist < min) {\n+ if (details) {\n+ best = {\n+ distance: min,\n+ x0: result.x1,\n+ y0: result.y1,\n+ x1: result.x0,\n+ y1: result.y0\n+ }\n+ } else {\n+ best = dist\n+ }\n+ }\n+ }\n+ }\n+ } else {\n+ best = geometry.distanceTo(this, options);\n+ if (details) {\n+ best = {\n+ distance: best.distance,\n+ x0: best.x1,\n+ y0: best.y1,\n+ x1: best.x0,\n+ y1: best.y0\n+ }\n }\n }\n+ return best\n },\n- getZIndex: function() {\n- return this.div.style.zIndex\n- },\n- setZIndex: function(zIndex) {\n- this.div.style.zIndex = zIndex\n- },\n- adjustBounds: function(bounds) {\n- if (this.gutter) {\n- var mapGutter = this.gutter * this.map.getResolution();\n- bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter)\n- }\n- if (this.wrapDateLine) {\n- var wrappingOptions = {\n- rightTolerance: this.getResolution(),\n- leftTolerance: this.getResolution()\n+ simplify: function(tolerance) {\n+ if (this && this !== null) {\n+ var points = this.getVertices();\n+ if (points.length < 3) {\n+ return this\n+ }\n+ var compareNumbers = function(a, b) {\n+ return a - b\n };\n- bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions)\n+ var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) {\n+ var maxDistance = 0;\n+ var indexFarthest = 0;\n+ for (var index = firstPoint, distance; index < lastPoint; index++) {\n+ distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);\n+ if (distance > maxDistance) {\n+ maxDistance = distance;\n+ indexFarthest = index\n+ }\n+ }\n+ if (maxDistance > tolerance && indexFarthest != firstPoint) {\n+ pointIndexsToKeep.push(indexFarthest);\n+ douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);\n+ douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance)\n+ }\n+ };\n+ var perpendicularDistance = function(point1, point2, point) {\n+ var area = Math.abs(.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));\n+ var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));\n+ var height = area / bottom * 2;\n+ return height\n+ };\n+ var firstPoint = 0;\n+ var lastPoint = points.length - 1;\n+ var pointIndexsToKeep = [];\n+ pointIndexsToKeep.push(firstPoint);\n+ pointIndexsToKeep.push(lastPoint);\n+ while (points[firstPoint].equals(points[lastPoint])) {\n+ lastPoint--;\n+ pointIndexsToKeep.push(lastPoint)\n+ }\n+ douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);\n+ var returnPoints = [];\n+ pointIndexsToKeep.sort(compareNumbers);\n+ for (var index = 0; index < pointIndexsToKeep.length; index++) {\n+ returnPoints.push(points[pointIndexsToKeep[index]])\n+ }\n+ return new OpenLayers.Geometry.LineString(returnPoints)\n+ } else {\n+ return this\n }\n- return bounds\n },\n- CLASS_NAME: \"OpenLayers.Layer\"\n+ CLASS_NAME: \"OpenLayers.Geometry.LineString\"\n });\n-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n- URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n- url: null,\n- params: null,\n- reproject: false,\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.url = url;\n- if (!this.params) {\n- this.params = OpenLayers.Util.extend({}, params)\n- }\n- },\n- destroy: function() {\n- this.url = null;\n- this.params = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- setUrl: function(newUrl) {\n- this.url = newUrl\n- },\n- mergeNewParams: function(newParams) {\n- this.params = OpenLayers.Util.extend(this.params, newParams);\n- var ret = this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"params\"\n- })\n+OpenLayers.Geometry.MultiLineString = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n+ componentTypes: [\"OpenLayers.Geometry.LineString\"],\n+ split: function(geometry, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var splits, sourceLine, sourceLines, sourceSplit, targetSplit;\n+ var sourceParts = [];\n+ var targetParts = [geometry];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ sourceLine = this.components[i];\n+ sourceSplit = false;\n+ for (var j = 0; j < targetParts.length; ++j) {\n+ splits = sourceLine.split(targetParts[j], options);\n+ if (splits) {\n+ if (mutual) {\n+ sourceLines = splits[0];\n+ for (var k = 0, klen = sourceLines.length; k < klen; ++k) {\n+ if (k === 0 && sourceParts.length) {\n+ sourceParts[sourceParts.length - 1].addComponent(sourceLines[k])\n+ } else {\n+ sourceParts.push(new OpenLayers.Geometry.MultiLineString([sourceLines[k]]))\n+ }\n+ }\n+ sourceSplit = true;\n+ splits = splits[1]\n+ }\n+ if (splits.length) {\n+ splits.unshift(j, 1);\n+ Array.prototype.splice.apply(targetParts, splits);\n+ break\n+ }\n+ }\n+ }\n+ if (!sourceSplit) {\n+ if (sourceParts.length) {\n+ sourceParts[sourceParts.length - 1].addComponent(sourceLine.clone())\n+ } else {\n+ sourceParts = [new OpenLayers.Geometry.MultiLineString(sourceLine.clone())]\n+ }\n+ }\n }\n- return ret\n- },\n- redraw: function(force) {\n- if (force) {\n- return this.mergeNewParams({\n- _olSalt: Math.random()\n- })\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true\n } else {\n- return OpenLayers.Layer.prototype.redraw.apply(this, [])\n- }\n- },\n- selectUrl: function(paramString, urls) {\n- var product = 1;\n- for (var i = 0, len = paramString.length; i < len; i++) {\n- product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n- product -= Math.floor(product)\n+ sourceParts = []\n }\n- return urls[Math.floor(product * urls.length)]\n- },\n- getFullRequestString: function(newParams, altUrl) {\n- var url = altUrl || this.url;\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url)\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true\n+ } else {\n+ targetParts = []\n }\n- var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key]\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts]\n+ } else {\n+ results = targetParts\n }\n }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n- return OpenLayers.Util.urlAppend(url, paramsString)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n-});\n-OpenLayers.Tile = OpenLayers.Class({\n- events: null,\n- eventListeners: null,\n- id: null,\n- layer: null,\n- url: null,\n- bounds: null,\n- size: null,\n- position: null,\n- isLoading: false,\n- initialize: function(layer, position, bounds, url, size, options) {\n- this.layer = layer;\n- this.position = position.clone();\n- this.setBounds(bounds);\n- this.url = url;\n- if (size) {\n- this.size = size.clone()\n- }\n- this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n- OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- },\n- unload: function() {\n- if (this.isLoading) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"unload\")\n- }\n+ return results\n },\n- destroy: function() {\n- this.layer = null;\n- this.bounds = null;\n- this.size = null;\n- this.position = null;\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n+ splitWith: function(geometry, options) {\n+ var results = null;\n+ var mutual = options && options.mutual;\n+ var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n+ if (geometry instanceof OpenLayers.Geometry.LineString) {\n+ targetParts = [];\n+ sourceParts = [geometry];\n+ for (var i = 0, len = this.components.length; i < len; ++i) {\n+ targetSplit = false;\n+ targetLine = this.components[i];\n+ for (var j = 0; j < sourceParts.length; ++j) {\n+ splits = sourceParts[j].split(targetLine, options);\n+ if (splits) {\n+ if (mutual) {\n+ sourceLines = splits[0];\n+ if (sourceLines.length) {\n+ sourceLines.unshift(j, 1);\n+ Array.prototype.splice.apply(sourceParts, sourceLines);\n+ j += sourceLines.length - 2\n+ }\n+ splits = splits[1];\n+ if (splits.length === 0) {\n+ splits = [targetLine.clone()]\n+ }\n+ }\n+ for (var k = 0, klen = splits.length; k < klen; ++k) {\n+ if (k === 0 && targetParts.length) {\n+ targetParts[targetParts.length - 1].addComponent(splits[k])\n+ } else {\n+ targetParts.push(new OpenLayers.Geometry.MultiLineString([splits[k]]))\n+ }\n+ }\n+ targetSplit = true\n+ }\n+ }\n+ if (!targetSplit) {\n+ if (targetParts.length) {\n+ targetParts[targetParts.length - 1].addComponent(targetLine.clone())\n+ } else {\n+ targetParts = [new OpenLayers.Geometry.MultiLineString([targetLine.clone()])]\n+ }\n+ }\n+ }\n+ } else {\n+ results = geometry.split(this)\n }\n- this.events.destroy();\n- this.eventListeners = null;\n- this.events = null\n- },\n- draw: function(force) {\n- if (!force) {\n- this.clear()\n+ if (sourceParts && sourceParts.length > 1) {\n+ sourceSplit = true\n+ } else {\n+ sourceParts = []\n }\n- var draw = this.shouldDraw();\n- if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n- draw = null\n+ if (targetParts && targetParts.length > 1) {\n+ targetSplit = true\n+ } else {\n+ targetParts = []\n }\n- return draw\n- },\n- shouldDraw: function() {\n- var withinMaxExtent = false,\n- maxExtent = this.layer.maxExtent;\n- if (maxExtent) {\n- var map = this.layer.map;\n- var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n- if (this.bounds.intersectsBounds(maxExtent, {\n- inclusive: false,\n- worldBounds: worldBounds\n- })) {\n- withinMaxExtent = true\n+ if (sourceSplit || targetSplit) {\n+ if (mutual) {\n+ results = [sourceParts, targetParts]\n+ } else {\n+ results = targetParts\n }\n }\n- return withinMaxExtent || this.layer.displayOutsideMaxExtent\n+ return results\n },\n- setBounds: function(bounds) {\n- bounds = bounds.clone();\n- if (this.layer.map.baseLayer.wrapDateLine) {\n- var worldExtent = this.layer.map.getMaxExtent(),\n- tolerance = this.layer.map.getResolution();\n- bounds = bounds.wrapDateLine(worldExtent, {\n- leftTolerance: tolerance,\n- rightTolerance: tolerance\n- })\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n+});\n+OpenLayers.Geometry.LinearRing = OpenLayers.Class(OpenLayers.Geometry.LineString, {\n+ componentTypes: [\"OpenLayers.Geometry.Point\"],\n+ addComponent: function(point, index) {\n+ var added = false;\n+ var lastPoint = this.components.pop();\n+ if (index != null || !point.equals(lastPoint)) {\n+ added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, arguments)\n }\n- this.bounds = bounds\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint]);\n+ return added\n },\n- moveTo: function(bounds, position, redraw) {\n- if (redraw == null) {\n- redraw = true\n- }\n- this.setBounds(bounds);\n- this.position = position.clone();\n- if (redraw) {\n- this.draw()\n+ removeComponent: function(point) {\n+ var removed = this.components && this.components.length > 3;\n+ if (removed) {\n+ this.components.pop();\n+ OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments);\n+ var firstPoint = this.components[0];\n+ OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint])\n }\n+ return removed\n },\n- clear: function(draw) {},\n- CLASS_NAME: \"OpenLayers.Tile\"\n-});\n-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n- url: null,\n- imgDiv: null,\n- frame: null,\n- imageReloadAttempts: null,\n- layerAlphaHack: null,\n- asyncRequestId: null,\n- maxGetUrlLength: null,\n- canvasContext: null,\n- crossOriginKeyword: null,\n- initialize: function(layer, position, bounds, url, size, options) {\n- OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n- this.url = url;\n- this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n- if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n- this.frame = document.createElement(\"div\");\n- this.frame.style.position = \"absolute\";\n- this.frame.style.overflow = \"hidden\"\n+ move: function(x, y) {\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ this.components[i].move(x, y)\n }\n- if (this.maxGetUrlLength != null) {\n- OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame)\n+ },\n+ rotate: function(angle, origin) {\n+ for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n+ this.components[i].rotate(angle, origin)\n }\n },\n- destroy: function() {\n- if (this.imgDiv) {\n- this.clear();\n- this.imgDiv = null;\n- this.frame = null\n+ resize: function(scale, origin, ratio) {\n+ for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n+ this.components[i].resize(scale, origin, ratio)\n }\n- this.asyncRequestId = null;\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n+ return this\n },\n- draw: function() {\n- var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (shouldDraw) {\n- if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n- this.bounds = this.getBoundsFromBaseLayer(this.position)\n- }\n- if (this.isLoading) {\n- this._loadEvent = \"reload\"\n- } else {\n- this.isLoading = true;\n- this._loadEvent = \"loadstart\"\n+ transform: function(source, dest) {\n+ if (source && dest) {\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ var component = this.components[i];\n+ component.transform(source, dest)\n }\n- this.renderTile();\n- this.positionTile()\n- } else if (shouldDraw === false) {\n- this.unload()\n+ this.bounds = null\n }\n- return shouldDraw\n+ return this\n },\n- renderTile: function() {\n- if (this.layer.async) {\n- var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n- this.layer.getURLasync(this.bounds, function(url) {\n- if (id == this.asyncRequestId) {\n- this.url = url;\n- this.initImage()\n+ getCentroid: function() {\n+ if (this.components) {\n+ var len = this.components.length;\n+ if (len > 0 && len <= 2) {\n+ return this.components[0].clone()\n+ } else if (len > 2) {\n+ var sumX = 0;\n+ var sumY = 0;\n+ var x0 = this.components[0].x;\n+ var y0 = this.components[0].y;\n+ var area = -1 * this.getArea();\n+ if (area != 0) {\n+ for (var i = 0; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n+ sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0))\n+ }\n+ var x = x0 + sumX / (6 * area);\n+ var y = y0 + sumY / (6 * area)\n+ } else {\n+ for (var i = 0; i < len - 1; i++) {\n+ sumX += this.components[i].x;\n+ sumY += this.components[i].y\n+ }\n+ var x = sumX / (len - 1);\n+ var y = sumY / (len - 1)\n }\n- }, this)\n- } else {\n- this.url = this.layer.getURL(this.bounds);\n- this.initImage()\n- }\n- },\n- positionTile: function() {\n- var style = this.getTile().style,\n- size = this.frame ? this.size : this.layer.getImageSize(this.bounds),\n- ratio = 1;\n- if (this.layer instanceof OpenLayers.Layer.Grid) {\n- ratio = this.layer.getServerResolution() / this.layer.map.getResolution()\n+ return new OpenLayers.Geometry.Point(x, y)\n+ } else {\n+ return null\n+ }\n }\n- style.left = this.position.x + \"px\";\n- style.top = this.position.y + \"px\";\n- style.width = Math.round(ratio * size.w) + \"px\";\n- style.height = Math.round(ratio * size.h) + \"px\"\n },\n- clear: function() {\n- OpenLayers.Tile.prototype.clear.apply(this, arguments);\n- var img = this.imgDiv;\n- if (img) {\n- var tile = this.getTile();\n- if (tile.parentNode === this.layer.div) {\n- this.layer.div.removeChild(tile)\n- }\n- this.setImgSrc();\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"\"\n+ getArea: function() {\n+ var area = 0;\n+ if (this.components && this.components.length > 2) {\n+ var sum = 0;\n+ for (var i = 0, len = this.components.length; i < len - 1; i++) {\n+ var b = this.components[i];\n+ var c = this.components[i + 1];\n+ sum += (b.x + c.x) * (c.y - b.y)\n }\n- OpenLayers.Element.removeClass(img, \"olImageLoadError\")\n+ area = -sum / 2\n }\n- this.canvasContext = null\n+ return area\n },\n- getImage: function() {\n- if (!this.imgDiv) {\n- this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n- var style = this.imgDiv.style;\n- if (this.frame) {\n- var left = 0,\n- top = 0;\n- if (this.layer.gutter) {\n- left = this.layer.gutter / this.layer.tileSize.w * 100;\n- top = this.layer.gutter / this.layer.tileSize.h * 100\n- }\n- style.left = -left + \"%\";\n- style.top = -top + \"%\";\n- style.width = 2 * left + 100 + \"%\";\n- style.height = 2 * top + 100 + \"%\"\n- }\n- style.visibility = \"hidden\";\n- style.opacity = 0;\n- if (this.layer.opacity < 1) {\n- style.filter = \"alpha(opacity=\" + this.layer.opacity * 100 + \")\"\n- }\n- style.position = \"absolute\";\n- if (this.layerAlphaHack) {\n- style.paddingTop = style.height;\n- style.height = \"0\";\n- style.width = \"100%\"\n+ getGeodesicArea: function(projection) {\n+ var ring = this;\n+ if (projection) {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ if (!gg.equals(projection)) {\n+ ring = this.clone().transform(projection, gg)\n }\n- if (this.frame) {\n- this.frame.appendChild(this.imgDiv)\n+ }\n+ var area = 0;\n+ var len = ring.components && ring.components.length;\n+ if (len > 2) {\n+ var p1, p2;\n+ for (var i = 0; i < len - 1; i++) {\n+ p1 = ring.components[i];\n+ p2 = ring.components[i + 1];\n+ area += OpenLayers.Util.rad(p2.x - p1.x) * (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + Math.sin(OpenLayers.Util.rad(p2.y)))\n }\n+ area = area * 6378137 * 6378137 / 2\n }\n- return this.imgDiv\n- },\n- setImage: function(img) {\n- this.imgDiv = img\n+ return area\n },\n- initImage: function() {\n- if (!this.url && !this.imgDiv) {\n- this.isLoading = false;\n- return\n+ containsPoint: function(point) {\n+ var approx = OpenLayers.Number.limitSigDigs;\n+ var digs = 14;\n+ var px = approx(point.x, digs);\n+ var py = approx(point.y, digs);\n+\n+ function getX(y, x1, y1, x2, y2) {\n+ return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2\n }\n- this.events.triggerEvent(\"beforeload\");\n- this.layer.div.appendChild(this.getTile());\n- this.events.triggerEvent(this._loadEvent);\n- var img = this.getImage();\n- var src = img.getAttribute(\"src\") || \"\";\n- if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n- this._loadTimeout = window.setTimeout(OpenLayers.Function.bind(this.onImageLoad, this), 0)\n- } else {\n- this.stopLoading();\n- if (this.crossOriginKeyword) {\n- img.removeAttribute(\"crossorigin\")\n+ var numSeg = this.components.length - 1;\n+ var start, end, x1, y1, x2, y2, cx, cy;\n+ var crosses = 0;\n+ for (var i = 0; i < numSeg; ++i) {\n+ start = this.components[i];\n+ x1 = approx(start.x, digs);\n+ y1 = approx(start.y, digs);\n+ end = this.components[i + 1];\n+ x2 = approx(end.x, digs);\n+ y2 = approx(end.y, digs);\n+ if (y1 == y2) {\n+ if (py == y1) {\n+ if (x1 <= x2 && (px >= x1 && px <= x2) || x1 >= x2 && (px <= x1 && px >= x2)) {\n+ crosses = -1;\n+ break\n+ }\n+ }\n+ continue\n }\n- OpenLayers.Event.observe(img, \"load\", OpenLayers.Function.bind(this.onImageLoad, this));\n- OpenLayers.Event.observe(img, \"error\", OpenLayers.Function.bind(this.onImageError, this));\n- this.imageReloadAttempts = 0;\n- this.setImgSrc(this.url)\n- }\n- },\n- setImgSrc: function(url) {\n- var img = this.imgDiv;\n- if (url) {\n- img.style.visibility = \"hidden\";\n- img.style.opacity = 0;\n- if (this.crossOriginKeyword) {\n- if (url.substr(0, 5) !== \"data:\") {\n- img.setAttribute(\"crossorigin\", this.crossOriginKeyword)\n- } else {\n- img.removeAttribute(\"crossorigin\")\n+ cx = approx(getX(py, x1, y1, x2, y2), digs);\n+ if (cx == px) {\n+ if (y1 < y2 && (py >= y1 && py <= y2) || y1 > y2 && (py <= y1 && py >= y2)) {\n+ crosses = -1;\n+ break\n }\n }\n- img.src = url\n- } else {\n- this.stopLoading();\n- this.imgDiv = null;\n- if (img.parentNode) {\n- img.parentNode.removeChild(img)\n+ if (cx <= px) {\n+ continue\n+ }\n+ if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n+ continue\n+ }\n+ if (y1 < y2 && (py >= y1 && py < y2) || y1 > y2 && (py < y1 && py >= y2)) {\n+ ++crosses\n }\n }\n+ var contained = crosses == -1 ? 1 : !!(crosses & 1);\n+ return contained\n },\n- getTile: function() {\n- return this.frame ? this.frame : this.getImage()\n- },\n- createBackBuffer: function() {\n- if (!this.imgDiv || this.isLoading) {\n- return\n- }\n- var backBuffer;\n- if (this.frame) {\n- backBuffer = this.frame.cloneNode(false);\n- backBuffer.appendChild(this.imgDiv)\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry)\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n+ intersect = geometry.intersects(this)\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(this, [geometry])\n } else {\n- backBuffer = this.imgDiv\n- }\n- this.imgDiv = null;\n- return backBuffer\n- },\n- onImageLoad: function() {\n- var img = this.imgDiv;\n- this.stopLoading();\n- img.style.visibility = \"inherit\";\n- img.style.opacity = this.layer.opacity;\n- this.isLoading = false;\n- this.canvasContext = null;\n- this.events.triggerEvent(\"loadend\");\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"', sizingMethod='scale')\"\n- }\n- },\n- onImageError: function() {\n- var img = this.imgDiv;\n- if (img.src != null) {\n- this.imageReloadAttempts++;\n- if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n- this.setImgSrc(this.layer.getURL(this.bounds))\n- } else {\n- OpenLayers.Element.addClass(img, \"olImageLoadError\");\n- this.events.triggerEvent(\"loaderror\");\n- this.onImageLoad()\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = geometry.components[i].intersects(this);\n+ if (intersect) {\n+ break\n+ }\n }\n }\n+ return intersect\n },\n- stopLoading: function() {\n- OpenLayers.Event.stopObservingElement(this.imgDiv);\n- window.clearTimeout(this._loadTimeout);\n- delete this._loadTimeout\n- },\n- getCanvasContext: function() {\n- if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n- if (!this.canvasContext) {\n- var canvas = document.createElement(\"canvas\");\n- canvas.width = this.size.w;\n- canvas.height = this.size.h;\n- this.canvasContext = canvas.getContext(\"2d\");\n- this.canvasContext.drawImage(this.imgDiv, 0, 0)\n- }\n- return this.canvasContext\n- }\n+ getVertices: function(nodes) {\n+ return nodes === true ? [] : this.components.slice(0, this.components.length - 1)\n },\n- CLASS_NAME: \"OpenLayers.Tile.Image\"\n+ CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n });\n-OpenLayers.Tile.Image.IMAGE = function() {\n- var img = new Image;\n- img.className = \"olTileImage\";\n- img.galleryImg = \"no\";\n- return img\n-}();\n-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n- tileSize: null,\n- tileOriginCorner: \"bl\",\n- tileOrigin: null,\n- tileOptions: null,\n- tileClass: OpenLayers.Tile.Image,\n- grid: null,\n- singleTile: false,\n- ratio: 1.5,\n- buffer: 0,\n- transitionEffect: \"resize\",\n- numLoadingTiles: 0,\n- serverResolutions: null,\n- loading: false,\n- backBuffer: null,\n- gridResolution: null,\n- backBufferResolution: null,\n- backBufferLonLat: null,\n- backBufferTimerId: null,\n- removeBackBufferDelay: null,\n- className: null,\n- gridLayout: null,\n- rowSign: null,\n- transitionendEvents: [\"transitionend\", \"webkitTransitionEnd\", \"otransitionend\", \"oTransitionEnd\"],\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments);\n- this.grid = [];\n- this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n- this.initProperties();\n- this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1\n- },\n- initProperties: function() {\n- if (this.options.removeBackBufferDelay === undefined) {\n- this.removeBackBufferDelay = this.singleTile ? 0 : 2500\n- }\n- if (this.options.className === undefined) {\n- this.className = this.singleTile ? \"olLayerGridSingleTile\" : \"olLayerGrid\"\n- }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n- OpenLayers.Element.addClass(this.div, this.className)\n- },\n- removeMap: function(map) {\n- this.removeBackBuffer()\n- },\n- destroy: function() {\n- this.removeBackBuffer();\n- this.clearGrid();\n- this.grid = null;\n- this.tileSize = null;\n- OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments)\n- },\n- clearGrid: function() {\n- if (this.grid) {\n- for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n- var row = this.grid[iRow];\n- for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n- var tile = row[iCol];\n- this.destroyTile(tile)\n- }\n+OpenLayers.Geometry.Polygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n+ componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n+ getArea: function() {\n+ var area = 0;\n+ if (this.components && this.components.length > 0) {\n+ area += Math.abs(this.components[0].getArea());\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getArea())\n }\n- this.grid = [];\n- this.gridResolution = null;\n- this.gridLayout = null\n- }\n- },\n- addOptions: function(newOptions, reinitialize) {\n- var singleTileChanged = newOptions.singleTile !== undefined && newOptions.singleTile !== this.singleTile;\n- OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n- if (this.map && singleTileChanged) {\n- this.initProperties();\n- this.clearGrid();\n- this.tileSize = this.options.tileSize;\n- this.setTileSize();\n- this.moveTo(null, true)\n }\n+ return area\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone()\n+ getGeodesicArea: function(projection) {\n+ var area = 0;\n+ if (this.components && this.components.length > 0) {\n+ area += Math.abs(this.components[0].getGeodesicArea(projection));\n+ for (var i = 1, len = this.components.length; i < len; i++) {\n+ area -= Math.abs(this.components[i].getGeodesicArea(projection))\n+ }\n }\n- obj.grid = [];\n- obj.gridResolution = null;\n- obj.backBuffer = null;\n- obj.backBufferTimerId = null;\n- obj.loading = false;\n- obj.numLoadingTiles = 0;\n- return obj\n+ return area\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n- bounds = bounds || this.map.getExtent();\n- if (bounds != null) {\n- var forceReTile = !this.grid.length || zoomChanged;\n- var tilesBounds = this.getTilesBounds();\n- var resolution = this.map.getResolution();\n- var serverResolution = this.getServerResolution(resolution);\n- if (this.singleTile) {\n- if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) {\n- if (zoomChanged && this.transitionEffect !== \"resize\") {\n- this.removeBackBuffer()\n- }\n- if (!zoomChanged || this.transitionEffect === \"resize\") {\n- this.applyBackBuffer(resolution)\n- }\n- this.initSingleTile(bounds)\n- }\n- } else {\n- forceReTile = forceReTile || !tilesBounds.intersectsBounds(bounds, {\n- worldBounds: this.map.baseLayer.wrapDateLine && this.map.getMaxExtent()\n- });\n- if (forceReTile) {\n- if (zoomChanged && (this.transitionEffect === \"resize\" || this.gridResolution === resolution)) {\n- this.applyBackBuffer(resolution)\n+ containsPoint: function(point) {\n+ var numRings = this.components.length;\n+ var contained = false;\n+ if (numRings > 0) {\n+ contained = this.components[0].containsPoint(point);\n+ if (contained !== 1) {\n+ if (contained && numRings > 1) {\n+ var hole;\n+ for (var i = 1; i < numRings; ++i) {\n+ hole = this.components[i].containsPoint(point);\n+ if (hole) {\n+ if (hole === 1) {\n+ contained = 1\n+ } else {\n+ contained = false\n+ }\n+ break\n+ }\n }\n- this.initGriddedTiles(bounds)\n- } else {\n- this.moveGriddedTiles()\n }\n }\n }\n+ return contained\n },\n- getTileData: function(loc) {\n- var data = null,\n- x = loc.lon,\n- y = loc.lat,\n- numRows = this.grid.length;\n- if (this.map && numRows) {\n- var res = this.map.getResolution(),\n- tileWidth = this.tileSize.w,\n- tileHeight = this.tileSize.h,\n- bounds = this.grid[0][0].bounds,\n- left = bounds.left,\n- top = bounds.top;\n- if (x < left) {\n- if (this.map.baseLayer.wrapDateLine) {\n- var worldWidth = this.map.getMaxExtent().getWidth();\n- var worldsAway = Math.ceil((left - x) / worldWidth);\n- x += worldWidth * worldsAway\n+ intersects: function(geometry) {\n+ var intersect = false;\n+ var i, len;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ intersect = this.containsPoint(geometry)\n+ } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ for (i = 0, len = this.components.length; i < len; ++i) {\n+ intersect = geometry.intersects(this.components[i]);\n+ if (intersect) {\n+ break\n }\n }\n- var dtx = (x - left) / (res * tileWidth);\n- var dty = (top - y) / (res * tileHeight);\n- var col = Math.floor(dtx);\n- var row = Math.floor(dty);\n- if (row >= 0 && row < numRows) {\n- var tile = this.grid[row][col];\n- if (tile) {\n- data = {\n- tile: tile,\n- i: Math.floor((dtx - col) * tileWidth),\n- j: Math.floor((dty - row) * tileHeight)\n+ if (!intersect) {\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.containsPoint(geometry.components[i]);\n+ if (intersect) {\n+ break\n }\n }\n }\n+ } else {\n+ for (i = 0, len = geometry.components.length; i < len; ++i) {\n+ intersect = this.intersects(geometry.components[i]);\n+ if (intersect) {\n+ break\n+ }\n+ }\n }\n- return data\n- },\n- destroyTile: function(tile) {\n- this.removeTileMonitoringHooks(tile);\n- tile.destroy()\n- },\n- getServerResolution: function(resolution) {\n- var distance = Number.POSITIVE_INFINITY;\n- resolution = resolution || this.map.getResolution();\n- if (this.serverResolutions && OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n- var i, newDistance, newResolution, serverResolution;\n- for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n- newResolution = this.serverResolutions[i];\n- newDistance = Math.abs(newResolution - resolution);\n- if (newDistance > distance) {\n+ if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n+ var ring = this.components[0];\n+ for (i = 0, len = ring.components.length; i < len; ++i) {\n+ intersect = geometry.containsPoint(ring.components[i]);\n+ if (intersect) {\n break\n }\n- distance = newDistance;\n- serverResolution = newResolution\n }\n- resolution = serverResolution\n }\n- return resolution\n- },\n- getServerZoom: function() {\n- var resolution = this.getServerResolution();\n- return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0)\n+ return intersect\n },\n- applyBackBuffer: function(resolution) {\n- if (this.backBufferTimerId !== null) {\n- this.removeBackBuffer()\n+ distanceTo: function(geometry, options) {\n+ var edge = !(options && options.edge === false);\n+ var result;\n+ if (!edge && this.intersects(geometry)) {\n+ result = 0\n+ } else {\n+ result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(this, [geometry, options])\n }\n- var backBuffer = this.backBuffer;\n- if (!backBuffer) {\n- backBuffer = this.createBackBuffer();\n- if (!backBuffer) {\n- return\n+ return result\n+ },\n+ CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n+});\n+OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n+ var angle = Math.PI * (1 / sides - 1 / 2);\n+ if (rotation) {\n+ angle += rotation / 180 * Math.PI\n+ }\n+ var rotatedAngle, x, y;\n+ var points = [];\n+ for (var i = 0; i < sides; ++i) {\n+ rotatedAngle = angle + i * 2 * Math.PI / sides;\n+ x = origin.x + radius * Math.cos(rotatedAngle);\n+ y = origin.y + radius * Math.sin(rotatedAngle);\n+ points.push(new OpenLayers.Geometry.Point(x, y))\n+ }\n+ var ring = new OpenLayers.Geometry.LinearRing(points);\n+ return new OpenLayers.Geometry.Polygon([ring])\n+};\n+OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n+ componentTypes: [\"OpenLayers.Geometry.Polygon\"],\n+ CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n+});\n+OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n+ ignoreExtraDims: false,\n+ read: function(json, type, filter) {\n+ type = type ? type : \"FeatureCollection\";\n+ var results = null;\n+ var obj = null;\n+ if (typeof json == \"string\") {\n+ obj = OpenLayers.Format.JSON.prototype.read.apply(this, [json, filter])\n+ } else {\n+ obj = json\n+ }\n+ if (!obj) {\n+ OpenLayers.Console.error(\"Bad JSON: \" + json)\n+ } else if (typeof obj.type != \"string\") {\n+ OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json)\n+ } else if (this.isValidType(obj, type)) {\n+ switch (type) {\n+ case \"Geometry\":\n+ try {\n+ results = this.parseGeometry(obj)\n+ } catch (err) {\n+ OpenLayers.Console.error(err)\n+ }\n+ break;\n+ case \"Feature\":\n+ try {\n+ results = this.parseFeature(obj);\n+ results.type = \"Feature\"\n+ } catch (err) {\n+ OpenLayers.Console.error(err)\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ results = [];\n+ switch (obj.type) {\n+ case \"Feature\":\n+ try {\n+ results.push(this.parseFeature(obj))\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err)\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ for (var i = 0, len = obj.features.length; i < len; ++i) {\n+ try {\n+ results.push(this.parseFeature(obj.features[i]))\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err)\n+ }\n+ }\n+ break;\n+ default:\n+ try {\n+ var geom = this.parseGeometry(obj);\n+ results.push(new OpenLayers.Feature.Vector(geom))\n+ } catch (err) {\n+ results = null;\n+ OpenLayers.Console.error(err)\n+ }\n+ }\n+ break\n }\n- if (resolution === this.gridResolution) {\n- this.div.insertBefore(backBuffer, this.div.firstChild)\n+ }\n+ return results\n+ },\n+ isValidType: function(obj, type) {\n+ var valid = false;\n+ switch (type) {\n+ case \"Geometry\":\n+ if (OpenLayers.Util.indexOf([\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\", \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"], obj.type) == -1) {\n+ OpenLayers.Console.error(\"Unsupported geometry type: \" + obj.type)\n+ } else {\n+ valid = true\n+ }\n+ break;\n+ case \"FeatureCollection\":\n+ valid = true;\n+ break;\n+ default:\n+ if (obj.type == type) {\n+ valid = true\n+ } else {\n+ OpenLayers.Console.error(\"Cannot convert types from \" + obj.type + \" to \" + type)\n+ }\n+ }\n+ return valid\n+ },\n+ parseFeature: function(obj) {\n+ var feature, geometry, attributes, bbox;\n+ attributes = obj.properties ? obj.properties : {};\n+ bbox = obj.geometry && obj.geometry.bbox || obj.bbox;\n+ try {\n+ geometry = this.parseGeometry(obj.geometry)\n+ } catch (err) {\n+ throw err\n+ }\n+ feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+ if (bbox) {\n+ feature.bounds = OpenLayers.Bounds.fromArray(bbox)\n+ }\n+ if (obj.id) {\n+ feature.fid = obj.id\n+ }\n+ return feature\n+ },\n+ parseGeometry: function(obj) {\n+ if (obj == null) {\n+ return null\n+ }\n+ var geometry, collection = false;\n+ if (obj.type == \"GeometryCollection\") {\n+ if (!OpenLayers.Util.isArray(obj.geometries)) {\n+ throw \"GeometryCollection must have geometries array: \" + obj\n+ }\n+ var numGeom = obj.geometries.length;\n+ var components = new Array(numGeom);\n+ for (var i = 0; i < numGeom; ++i) {\n+ components[i] = this.parseGeometry.apply(this, [obj.geometries[i]])\n+ }\n+ geometry = new OpenLayers.Geometry.Collection(components);\n+ collection = true\n+ } else {\n+ if (!OpenLayers.Util.isArray(obj.coordinates)) {\n+ throw \"Geometry must have coordinates array: \" + obj\n+ }\n+ if (!this.parseCoords[obj.type.toLowerCase()]) {\n+ throw \"Unsupported geometry type: \" + obj.type\n+ }\n+ try {\n+ geometry = this.parseCoords[obj.type.toLowerCase()].apply(this, [obj.coordinates])\n+ } catch (err) {\n+ throw err\n+ }\n+ }\n+ if (this.internalProjection && this.externalProjection && !collection) {\n+ geometry.transform(this.externalProjection, this.internalProjection)\n+ }\n+ return geometry\n+ },\n+ parseCoords: {\n+ point: function(array) {\n+ if (this.ignoreExtraDims == false && array.length != 2) {\n+ throw \"Only 2D points are supported: \" + array\n+ }\n+ return new OpenLayers.Geometry.Point(array[0], array[1])\n+ },\n+ multipoint: function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]])\n+ } catch (err) {\n+ throw err\n+ }\n+ points.push(p)\n+ }\n+ return new OpenLayers.Geometry.MultiPoint(points)\n+ },\n+ linestring: function(array) {\n+ var points = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"point\"].apply(this, [array[i]])\n+ } catch (err) {\n+ throw err\n+ }\n+ points.push(p)\n+ }\n+ return new OpenLayers.Geometry.LineString(points)\n+ },\n+ multilinestring: function(array) {\n+ var lines = [];\n+ var l = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]])\n+ } catch (err) {\n+ throw err\n+ }\n+ lines.push(l)\n+ }\n+ return new OpenLayers.Geometry.MultiLineString(lines)\n+ },\n+ polygon: function(array) {\n+ var rings = [];\n+ var r, l;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ l = this.parseCoords[\"linestring\"].apply(this, [array[i]])\n+ } catch (err) {\n+ throw err\n+ }\n+ r = new OpenLayers.Geometry.LinearRing(l.components);\n+ rings.push(r)\n+ }\n+ return new OpenLayers.Geometry.Polygon(rings)\n+ },\n+ multipolygon: function(array) {\n+ var polys = [];\n+ var p = null;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ try {\n+ p = this.parseCoords[\"polygon\"].apply(this, [array[i]])\n+ } catch (err) {\n+ throw err\n+ }\n+ polys.push(p)\n+ }\n+ return new OpenLayers.Geometry.MultiPolygon(polys)\n+ },\n+ box: function(array) {\n+ if (array.length != 2) {\n+ throw \"GeoJSON box coordinates must have 2 elements\"\n+ }\n+ return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(array[0][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[0][1])])])\n+ }\n+ },\n+ write: function(obj, pretty) {\n+ var geojson = {\n+ type: null\n+ };\n+ if (OpenLayers.Util.isArray(obj)) {\n+ geojson.type = \"FeatureCollection\";\n+ var numFeatures = obj.length;\n+ geojson.features = new Array(numFeatures);\n+ for (var i = 0; i < numFeatures; ++i) {\n+ var element = obj[i];\n+ if (!element instanceof OpenLayers.Feature.Vector) {\n+ var msg = \"FeatureCollection only supports collections \" + \"of features: \" + element;\n+ throw msg\n+ }\n+ geojson.features[i] = this.extract.feature.apply(this, [element])\n+ }\n+ } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n+ geojson = this.extract.geometry.apply(this, [obj])\n+ } else if (obj instanceof OpenLayers.Feature.Vector) {\n+ geojson = this.extract.feature.apply(this, [obj]);\n+ if (obj.layer && obj.layer.projection) {\n+ geojson.crs = this.createCRSObject(obj)\n+ }\n+ }\n+ return OpenLayers.Format.JSON.prototype.write.apply(this, [geojson, pretty])\n+ },\n+ createCRSObject: function(object) {\n+ var proj = object.layer.projection.toString();\n+ var crs = {};\n+ if (proj.match(/epsg:/i)) {\n+ var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n+ if (code == 4326) {\n+ crs = {\n+ type: \"name\",\n+ properties: {\n+ name: \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n+ }\n+ }\n } else {\n- this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div)\n+ crs = {\n+ type: \"name\",\n+ properties: {\n+ name: \"EPSG:\" + code\n+ }\n+ }\n }\n- this.backBuffer = backBuffer;\n- var topLeftTileBounds = this.grid[0][0].bounds;\n- this.backBufferLonLat = {\n- lon: topLeftTileBounds.left,\n- lat: topLeftTileBounds.top\n+ }\n+ return crs\n+ },\n+ extract: {\n+ feature: function(feature) {\n+ var geom = this.extract.geometry.apply(this, [feature.geometry]);\n+ var json = {\n+ type: \"Feature\",\n+ properties: feature.attributes,\n+ geometry: geom\n };\n- this.backBufferResolution = this.gridResolution\n+ if (feature.fid != null) {\n+ json.id = feature.fid\n+ }\n+ return json\n+ },\n+ geometry: function(geometry) {\n+ if (geometry == null) {\n+ return null\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection, this.externalProjection)\n+ }\n+ var geometryType = geometry.CLASS_NAME.split(\".\")[2];\n+ var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n+ var json;\n+ if (geometryType == \"Collection\") {\n+ json = {\n+ type: \"GeometryCollection\",\n+ geometries: data\n+ }\n+ } else {\n+ json = {\n+ type: geometryType,\n+ coordinates: data\n+ }\n+ }\n+ return json\n+ },\n+ point: function(point) {\n+ return [point.x, point.y]\n+ },\n+ multipoint: function(multipoint) {\n+ var array = [];\n+ for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [multipoint.components[i]]))\n+ }\n+ return array\n+ },\n+ linestring: function(linestring) {\n+ var array = [];\n+ for (var i = 0, len = linestring.components.length; i < len; ++i) {\n+ array.push(this.extract.point.apply(this, [linestring.components[i]]))\n+ }\n+ return array\n+ },\n+ multilinestring: function(multilinestring) {\n+ var array = [];\n+ for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]))\n+ }\n+ return array\n+ },\n+ polygon: function(polygon) {\n+ var array = [];\n+ for (var i = 0, len = polygon.components.length; i < len; ++i) {\n+ array.push(this.extract.linestring.apply(this, [polygon.components[i]]))\n+ }\n+ return array\n+ },\n+ multipolygon: function(multipolygon) {\n+ var array = [];\n+ for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n+ array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]))\n+ }\n+ return array\n+ },\n+ collection: function(collection) {\n+ var len = collection.components.length;\n+ var array = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ array[i] = this.extract.geometry.apply(this, [collection.components[i]])\n+ }\n+ return array\n }\n- var ratio = this.backBufferResolution / resolution;\n- var tiles = backBuffer.childNodes,\n- tile;\n- for (var i = tiles.length - 1; i >= 0; --i) {\n- tile = tiles[i];\n- tile.style.top = (ratio * tile._i * tile._h | 0) + \"px\";\n- tile.style.left = (ratio * tile._j * tile._w | 0) + \"px\";\n- tile.style.width = Math.round(ratio * tile._w) + \"px\";\n- tile.style.height = Math.round(ratio * tile._h) + \"px\"\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\n+});\n+OpenLayers.Filter = OpenLayers.Class({\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n+ },\n+ destroy: function() {},\n+ evaluate: function(context) {\n+ return true\n+ },\n+ clone: function() {\n+ return null\n+ },\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this)\n+ } else {\n+ string = Object.prototype.toString.call(this)\n }\n- var position = this.getViewPortPxFromLonLat(this.backBufferLonLat, resolution);\n- var leftOffset = this.map.layerContainerOriginPx.x;\n- var topOffset = this.map.layerContainerOriginPx.y;\n- backBuffer.style.left = Math.round(position.x - leftOffset) + \"px\";\n- backBuffer.style.top = Math.round(position.y - topOffset) + \"px\"\n+ return string\n },\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.grid.length > 0) {\n- backBuffer = document.createElement(\"div\");\n- backBuffer.id = this.div.id + \"_bb\";\n- backBuffer.className = \"olBackBuffer\";\n- backBuffer.style.position = \"absolute\";\n- var map = this.map;\n- backBuffer.style.zIndex = this.transitionEffect === \"resize\" ? this.getZIndex() - 1 : map.Z_INDEX_BASE.BaseLayer - (map.getNumLayers() - map.getLayerIndex(this));\n- for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n- for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n- var tile = this.grid[i][j],\n- markup = this.grid[i][j].createBackBuffer();\n- if (markup) {\n- markup._i = i;\n- markup._j = j;\n- markup._w = tile.size.w;\n- markup._h = tile.size.h;\n- markup.id = tile.id + \"_bb\";\n- backBuffer.appendChild(markup)\n+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n+OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\n+ type: null,\n+ property: null,\n+ value: null,\n+ matchCase: true,\n+ lowerBoundary: null,\n+ upperBoundary: null,\n+ initialize: function(options) {\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n+ if (this.type === OpenLayers.Filter.Comparison.LIKE && options.matchCase === undefined) {\n+ this.matchCase = null\n+ }\n+ },\n+ evaluate: function(context) {\n+ if (context instanceof OpenLayers.Feature.Vector) {\n+ context = context.attributes\n+ }\n+ var result = false;\n+ var got = context[this.property];\n+ var exp;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Comparison.EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase && typeof got == \"string\" && typeof exp == \"string\") {\n+ result = got.toUpperCase() == exp.toUpperCase()\n+ } else {\n+ result = got == exp\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n+ exp = this.value;\n+ if (!this.matchCase && typeof got == \"string\" && typeof exp == \"string\") {\n+ result = got.toUpperCase() != exp.toUpperCase()\n+ } else {\n+ result = got != exp\n+ }\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN:\n+ result = got < this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN:\n+ result = got > this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n+ result = got <= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n+ result = got >= this.value;\n+ break;\n+ case OpenLayers.Filter.Comparison.BETWEEN:\n+ result = got >= this.lowerBoundary && got <= this.upperBoundary;\n+ break;\n+ case OpenLayers.Filter.Comparison.LIKE:\n+ var regexp = new RegExp(this.value, \"gi\");\n+ result = regexp.test(got);\n+ break;\n+ case OpenLayers.Filter.Comparison.IS_NULL:\n+ result = got === null;\n+ break\n+ }\n+ return result\n+ },\n+ value2regex: function(wildCard, singleChar, escapeChar) {\n+ if (wildCard == \".\") {\n+ throw new Error(\"'.' is an unsupported wildCard character for \" + \"OpenLayers.Filter.Comparison\")\n+ }\n+ wildCard = wildCard ? wildCard : \"*\";\n+ singleChar = singleChar ? singleChar : \".\";\n+ escapeChar = escapeChar ? escapeChar : \"!\";\n+ this.value = this.value.replace(new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n+ this.value = this.value.replace(new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n+ this.value = this.value.replace(new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n+ this.value = this.value.replace(new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n+ this.value = this.value.replace(new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n+ return this.value\n+ },\n+ regex2value: function() {\n+ var value = this.value;\n+ value = value.replace(/!/g, \"!!\");\n+ value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n+ return $1 ? $0 : \"!.\"\n+ });\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"!*\"\n+ });\n+ value = value.replace(/\\\\\\\\/g, \"\\\\\");\n+ value = value.replace(/\\.\\*/g, \"*\");\n+ return value\n+ },\n+ clone: function() {\n+ return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison, this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n+});\n+OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n+OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n+OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n+OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n+OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n+OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n+OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n+OpenLayers.Filter.Comparison.LIKE = \"~\";\n+OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n+OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\n+ filters: null,\n+ type: null,\n+ initialize: function(options) {\n+ this.filters = [];\n+ OpenLayers.Filter.prototype.initialize.apply(this, [options])\n+ },\n+ destroy: function() {\n+ this.filters = null;\n+ OpenLayers.Filter.prototype.destroy.apply(this)\n+ },\n+ evaluate: function(context) {\n+ var i, len;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Logical.AND:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == false) {\n+ return false\n }\n }\n+ return true;\n+ case OpenLayers.Filter.Logical.OR:\n+ for (i = 0, len = this.filters.length; i < len; i++) {\n+ if (this.filters[i].evaluate(context) == true) {\n+ return true\n+ }\n+ }\n+ return false;\n+ case OpenLayers.Filter.Logical.NOT:\n+ return !this.filters[0].evaluate(context)\n+ }\n+ return undefined\n+ },\n+ clone: function() {\n+ var filters = [];\n+ for (var i = 0, len = this.filters.length; i < len; ++i) {\n+ filters.push(this.filters[i].clone())\n+ }\n+ return new OpenLayers.Filter.Logical({\n+ type: this.type,\n+ filters: filters\n+ })\n+ },\n+ CLASS_NAME: \"OpenLayers.Filter.Logical\"\n+});\n+OpenLayers.Filter.Logical.AND = \"&&\";\n+OpenLayers.Filter.Logical.OR = \"||\";\n+OpenLayers.Filter.Logical.NOT = \"!\";\n+OpenLayers.Control = OpenLayers.Class({\n+ id: null,\n+ map: null,\n+ div: null,\n+ type: null,\n+ allowSelection: false,\n+ displayClass: \"\",\n+ title: \"\",\n+ autoActivate: false,\n+ active: null,\n+ handlerOptions: null,\n+ handler: null,\n+ eventListeners: null,\n+ events: null,\n+ initialize: function(options) {\n+ this.displayClass = this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n+ }\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ }\n+ },\n+ destroy: function() {\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n+ }\n+ this.events.destroy();\n+ this.events = null\n+ }\n+ this.eventListeners = null;\n+ if (this.handler) {\n+ this.handler.destroy();\n+ this.handler = null\n+ }\n+ if (this.handlers) {\n+ for (var key in this.handlers) {\n+ if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == \"function\") {\n+ this.handlers[key].destroy()\n+ }\n }\n+ this.handlers = null\n }\n- return backBuffer\n+ if (this.map) {\n+ this.map.removeControl(this);\n+ this.map = null\n+ }\n+ this.div = null\n },\n- removeBackBuffer: function() {\n- if (this._transitionElement) {\n- for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.stopObserving(this._transitionElement, this.transitionendEvents[i], this._removeBackBuffer)\n+ setMap: function(map) {\n+ this.map = map;\n+ if (this.handler) {\n+ this.handler.setMap(map)\n+ }\n+ },\n+ draw: function(px) {\n+ if (this.div == null) {\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.className = this.displayClass;\n+ if (!this.allowSelection) {\n+ this.div.className += \" olControlNoSelect\";\n+ this.div.setAttribute(\"unselectable\", \"on\", 0);\n+ this.div.onselectstart = OpenLayers.Function.False\n+ }\n+ if (this.title != \"\") {\n+ this.div.title = this.title\n }\n- delete this._transitionElement\n }\n- if (this.backBuffer) {\n- if (this.backBuffer.parentNode) {\n- this.backBuffer.parentNode.removeChild(this.backBuffer)\n+ if (px != null) {\n+ this.position = px.clone()\n+ }\n+ this.moveTo(this.position);\n+ return this.div\n+ },\n+ moveTo: function(px) {\n+ if (px != null && this.div != null) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\"\n+ }\n+ },\n+ activate: function() {\n+ if (this.active) {\n+ return false\n+ }\n+ if (this.handler) {\n+ this.handler.activate()\n+ }\n+ this.active = true;\n+ if (this.map) {\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n+ }\n+ this.events.triggerEvent(\"activate\");\n+ return true\n+ },\n+ deactivate: function() {\n+ if (this.active) {\n+ if (this.handler) {\n+ this.handler.deactivate()\n }\n- this.backBuffer = null;\n- this.backBufferResolution = null;\n- if (this.backBufferTimerId !== null) {\n- window.clearTimeout(this.backBufferTimerId);\n- this.backBufferTimerId = null\n+ this.active = false;\n+ if (this.map) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n }\n+ this.events.triggerEvent(\"deactivate\");\n+ return true\n }\n+ return false\n },\n- moveByPx: function(dx, dy) {\n- if (!this.singleTile) {\n- this.moveGriddedTiles()\n+ CLASS_NAME: \"OpenLayers.Control\"\n+});\n+OpenLayers.Control.TYPE_BUTTON = 1;\n+OpenLayers.Control.TYPE_TOGGLE = 2;\n+OpenLayers.Control.TYPE_TOOL = 3;\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n+ target: null,\n+ events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n+ startRegEx: /^mousedown|touchstart$/,\n+ cancelRegEx: /^touchmove$/,\n+ completeRegEx: /^mouseup|touchend$/,\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ })\n }\n },\n- setTileSize: function(size) {\n- if (this.singleTile) {\n- size = this.map.getSize();\n- size.h = parseInt(size.h * this.ratio, 10);\n- size.w = parseInt(size.w * this.ratio, 10)\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick)\n }\n- OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size])\n+ delete this.target\n },\n- getTilesBounds: function() {\n- var bounds = null;\n- var length = this.grid.length;\n- if (length) {\n- var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n- width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n- height = this.grid.length * bottomLeftTileBounds.getHeight();\n- bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height)\n+ getPressedButton: function(element) {\n+ var depth = 3,\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ button = element;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return button\n+ },\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === \"a\") {\n+ ignore = true;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return ignore\n+ },\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ })\n+ }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n+ }\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt\n+ }\n }\n- return bounds\n+ return propagate\n+ }\n+});\n+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n+ controls: null,\n+ autoActivate: true,\n+ defaultControl: null,\n+ saveState: false,\n+ allowDepress: false,\n+ activeState: null,\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.controls = [];\n+ this.activeState = {}\n },\n- initSingleTile: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n- var center = bounds.getCenterLonLat();\n- var tileWidth = bounds.getWidth() * this.ratio;\n- var tileHeight = bounds.getHeight() * this.ratio;\n- var tileBounds = new OpenLayers.Bounds(center.lon - tileWidth / 2, center.lat - tileHeight / 2, center.lon + tileWidth / 2, center.lat + tileHeight / 2);\n- var px = this.map.getLayerPxFromLonLat({\n- lon: tileBounds.left,\n- lat: tileBounds.top\n- });\n- if (!this.grid.length) {\n- this.grid[0] = []\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick)\n }\n- var tile = this.grid[0][0];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- tile.draw();\n- this.grid[0][0] = tile\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n+ ctl = this.controls[i];\n+ if (ctl.events) {\n+ ctl.events.un({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ })\n+ }\n+ ctl.panel_div = null\n+ }\n+ this.activeState = null\n+ },\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ if (control === this.defaultControl || this.saveState && this.activeState[control.id]) {\n+ control.activate()\n+ }\n+ }\n+ if (this.saveState === true) {\n+ this.defaultControl = null\n+ }\n+ this.redraw();\n+ return true\n } else {\n- tile.moveTo(tileBounds, px)\n+ return false\n }\n- this.removeExcessTiles(1, 1);\n- this.gridResolution = this.getServerResolution()\n },\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n- var offsetlon = bounds.left - origin.lon;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n- var rowSign = this.rowSign;\n- var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n- var tilerow = Math[~rowSign ? \"floor\" : \"ceil\"](offsetlat / tilelat) - this.buffer * rowSign;\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ this.activeState[control.id] = control.deactivate()\n+ }\n+ this.redraw();\n+ return true\n+ } else {\n+ return false\n }\n },\n- getTileOrigin: function() {\n- var origin = this.tileOrigin;\n- if (!origin) {\n- var extent = this.getMaxExtent();\n- var edges = {\n- tl: [\"left\", \"top\"],\n- tr: [\"right\", \"top\"],\n- bl: [\"left\", \"bottom\"],\n- br: [\"right\", \"bottom\"]\n- } [this.tileOriginCorner];\n- origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]])\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick)\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n }\n- return origin\n+ this.addControlsToMap(this.controls);\n+ return this.div\n },\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var startcol = tileLayout.startcol;\n- var startrow = tileLayout.startrow;\n- var rowSign = this.rowSign;\n- return new OpenLayers.Bounds(origin.lon + (startcol + col) * tilelon, origin.lat - (startrow + row * rowSign) * tilelat * rowSign, origin.lon + (startcol + col + 1) * tilelon, origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign)\n+ redraw: function() {\n+ for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n+ this.div.removeChild(this.div.childNodes[i])\n+ }\n+ this.div.innerHTML = \"\";\n+ if (this.active) {\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.div.appendChild(this.controls[i].panel_div)\n+ }\n+ }\n },\n- initGriddedTiles: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n- var viewSize = this.map.getSize();\n- var origin = this.getTileOrigin();\n- var resolution = this.map.getResolution(),\n- serverResolution = this.getServerResolution(),\n- ratio = resolution / serverResolution,\n- tileSize = {\n- w: this.tileSize.w / ratio,\n- h: this.tileSize.h / ratio\n+ activateControl: function(control) {\n+ if (!this.active) {\n+ return false\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n+ control.trigger();\n+ return\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n+ if (control.active) {\n+ control.deactivate()\n+ } else {\n+ control.activate()\n+ }\n+ return\n+ }\n+ if (this.allowDepress && control.active) {\n+ control.deactivate()\n+ } else {\n+ var c;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ c = this.controls[i];\n+ if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n+ c.deactivate()\n+ }\n+ }\n+ control.activate()\n+ }\n+ },\n+ addControls: function(controls) {\n+ if (!OpenLayers.Util.isArray(controls)) {\n+ controls = [controls]\n+ }\n+ this.controls = this.controls.concat(controls);\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var control = controls[i],\n+ element = this.createControlMarkup(control);\n+ OpenLayers.Element.addClass(element, control.displayClass + \"ItemInactive\");\n+ OpenLayers.Element.addClass(element, \"olButton\");\n+ if (control.title != \"\" && !element.title) {\n+ element.title = control.title\n+ }\n+ control.panel_div = element\n+ }\n+ if (this.map) {\n+ this.addControlsToMap(controls);\n+ this.redraw()\n+ }\n+ },\n+ createControlMarkup: function(control) {\n+ return document.createElement(\"div\")\n+ },\n+ addControlsToMap: function(controls) {\n+ var control;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ control = controls[i];\n+ if (control.autoActivate === true) {\n+ control.autoActivate = false;\n+ this.map.addControl(control);\n+ control.autoActivate = true\n+ } else {\n+ this.map.addControl(control);\n+ control.deactivate()\n+ }\n+ control.events.on({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ })\n+ }\n+ },\n+ iconOn: function() {\n+ var d = this.panel_div;\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n+ d.className = d.className.replace(re, \"$1Active\")\n+ },\n+ iconOff: function() {\n+ var d = this.panel_div;\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n+ d.className = d.className.replace(re, \"$1Inactive\")\n+ },\n+ onButtonClick: function(evt) {\n+ var controls = this.controls,\n+ button = evt.buttonElement;\n+ for (var i = controls.length - 1; i >= 0; --i) {\n+ if (controls[i].panel_div === button) {\n+ this.activateControl(controls[i]);\n+ break\n+ }\n+ }\n+ },\n+ getControlsBy: function(property, match) {\n+ var test = typeof match.test == \"function\";\n+ var found = OpenLayers.Array.filter(this.controls, function(item) {\n+ return item[property] == match || test && match.test(item[property])\n+ });\n+ return found\n+ },\n+ getControlsByName: function(match) {\n+ return this.getControlsBy(\"name\", match)\n+ },\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Panel\"\n+});\n+OpenLayers.Handler = OpenLayers.Class({\n+ id: null,\n+ control: null,\n+ map: null,\n+ keyMask: null,\n+ active: false,\n+ evt: null,\n+ touch: false,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.control = control;\n+ this.callbacks = callbacks;\n+ var map = this.map || control.map;\n+ if (map) {\n+ this.setMap(map)\n+ }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ },\n+ setMap: function(map) {\n+ this.map = map\n+ },\n+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true\n+ }\n+ var keyModifiers = (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n+ return keyModifiers == this.keyMask\n+ },\n+ activate: function() {\n+ if (this.active) {\n+ return false\n+ }\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.register(events[i], this[events[i]])\n+ }\n+ }\n+ this.active = true;\n+ return true\n+ },\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false\n+ }\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]])\n+ }\n+ }\n+ this.touch = false;\n+ this.active = false;\n+ return true\n+ },\n+ startTouch: function() {\n+ if (!this.touch) {\n+ this.touch = true;\n+ var events = [\"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"mouseout\"];\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]])\n+ }\n+ }\n+ }\n+ },\n+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args)\n+ }\n+ },\n+ register: function(name, method) {\n+ this.map.events.registerPriority(name, this, method);\n+ this.map.events.registerPriority(name, this, this.setEvent)\n+ },\n+ unregister: function(name, method) {\n+ this.map.events.unregister(name, this, method);\n+ this.map.events.unregister(name, this, this.setEvent)\n+ },\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true\n+ },\n+ destroy: function() {\n+ this.deactivate();\n+ this.control = this.map = null\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler\"\n+});\n+OpenLayers.Handler.MOD_NONE = 0;\n+OpenLayers.Handler.MOD_SHIFT = 1;\n+OpenLayers.Handler.MOD_CTRL = 2;\n+OpenLayers.Handler.MOD_ALT = 4;\n+OpenLayers.Handler.MOD_META = 8;\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+ started: false,\n+ stopDown: true,\n+ dragging: false,\n+ last: null,\n+ start: null,\n+ lastMoveEvt: null,\n+ oldOnselectstart: null,\n+ interval: 0,\n+ timeoutId: null,\n+ documentDrag: false,\n+ documentEvents: null,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ if (this.documentDrag === true) {\n+ var me = this;\n+ this._docMove = function(evt) {\n+ me.mousemove({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ },\n+ element: document\n+ })\n };\n- var minRows = Math.ceil(viewSize.h / tileSize.h) + 2 * this.buffer + 1;\n- var minCols = Math.ceil(viewSize.w / tileSize.w) + 2 * this.buffer + 1;\n- var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n- this.gridLayout = tileLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n- var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n- var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n- var startPx = this.map.getViewPortPxFromLonLat(new OpenLayers.LonLat(tileBounds.left, tileBounds.top));\n- startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n- startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n- var tileData = [],\n- center = this.map.getCenter();\n- var rowidx = 0;\n- do {\n- var row = this.grid[rowidx];\n- if (!row) {\n- row = [];\n- this.grid.push(row)\n+ this._docUp = function(evt) {\n+ me.mouseup({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ }\n+ })\n }\n- var colidx = 0;\n- do {\n- tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n- var px = startPx.clone();\n- px.x = px.x + colidx * Math.round(tileSize.w);\n- px.y = px.y + rowidx * Math.round(tileSize.h);\n- var tile = row[colidx];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- row.push(tile)\n+ }\n+ },\n+ dragstart: function(evt) {\n+ var propagate = true;\n+ this.dragging = false;\n+ if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) {\n+ this.started = true;\n+ this.start = evt.xy;\n+ this.last = evt.xy;\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.down(evt);\n+ this.callback(\"down\", [evt.xy]);\n+ OpenLayers.Event.preventDefault(evt);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True\n+ }\n+ document.onselectstart = OpenLayers.Function.False;\n+ propagate = !this.stopDown\n+ } else {\n+ this.started = false;\n+ this.start = null;\n+ this.last = null\n+ }\n+ return propagate\n+ },\n+ dragmove: function(evt) {\n+ this.lastMoveEvt = evt;\n+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ if (evt.element === document) {\n+ this.adjustXY(evt);\n+ this.setEvent(evt)\n } else {\n- tile.moveTo(tileBounds, px, false)\n+ this.removeDocumentEvents()\n }\n- var tileCenter = tileBounds.getCenterLonLat();\n- tileData.push({\n- tile: tile,\n- distance: Math.pow(tileCenter.lon - center.lon, 2) + Math.pow(tileCenter.lat - center.lat, 2)\n- });\n- colidx += 1\n- } while (tileBounds.right <= bounds.right + tilelon * this.buffer || colidx < minCols);\n- rowidx += 1\n- } while (tileBounds.bottom >= bounds.bottom - tilelat * this.buffer || rowidx < minRows);\n- this.removeExcessTiles(rowidx, colidx);\n- var resolution = this.getServerResolution();\n- this.gridResolution = resolution;\n- tileData.sort(function(a, b) {\n- return a.distance - b.distance\n- });\n- for (var i = 0, ii = tileData.length; i < ii; ++i) {\n- tileData[i].tile.draw()\n+ }\n+ if (this.interval > 0) {\n+ this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval)\n+ }\n+ this.dragging = true;\n+ this.move(evt);\n+ this.callback(\"move\", [evt.xy]);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart;\n+ document.onselectstart = OpenLayers.Function.False\n+ }\n+ this.last = evt.xy\n }\n+ return true\n },\n- getMaxExtent: function() {\n- return this.maxExtent\n+ dragend: function(evt) {\n+ if (this.started) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ this.adjustXY(evt);\n+ this.removeDocumentEvents()\n+ }\n+ var dragged = this.start != this.last;\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.up(evt);\n+ this.callback(\"up\", [evt.xy]);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy])\n+ }\n+ document.onselectstart = this.oldOnselectstart\n+ }\n+ return true\n },\n- addTile: function(bounds, position) {\n- var tile = new this.tileClass(this, position, bounds, null, this.tileSize, this.tileOptions);\n- this.events.triggerEvent(\"addtile\", {\n- tile: tile\n+ down: function(evt) {},\n+ move: function(evt) {},\n+ up: function(evt) {},\n+ out: function(evt) {},\n+ mousedown: function(evt) {\n+ return this.dragstart(evt)\n+ },\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return this.dragstart(evt)\n+ },\n+ mousemove: function(evt) {\n+ return this.dragmove(evt)\n+ },\n+ touchmove: function(evt) {\n+ return this.dragmove(evt)\n+ },\n+ removeTimeout: function() {\n+ this.timeoutId = null;\n+ if (this.dragging) {\n+ this.mousemove(this.lastMoveEvt)\n+ }\n+ },\n+ mouseup: function(evt) {\n+ return this.dragend(evt)\n+ },\n+ touchend: function(evt) {\n+ evt.xy = this.last;\n+ return this.dragend(evt)\n+ },\n+ mouseout: function(evt) {\n+ if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ if (this.documentDrag === true) {\n+ this.addDocumentEvents()\n+ } else {\n+ var dragged = this.start != this.last;\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.out(evt);\n+ this.callback(\"out\", []);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy])\n+ }\n+ if (document.onselectstart) {\n+ document.onselectstart = this.oldOnselectstart\n+ }\n+ }\n+ }\n+ return true\n+ },\n+ click: function(evt) {\n+ return this.start == this.last\n+ },\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragging = false;\n+ activated = true\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.started = false;\n+ this.dragging = false;\n+ this.start = null;\n+ this.last = null;\n+ deactivated = true;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\")\n+ }\n+ return deactivated\n+ },\n+ adjustXY: function(evt) {\n+ var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n+ evt.xy.x -= pos[0];\n+ evt.xy.y -= pos[1]\n+ },\n+ addDocumentEvents: function() {\n+ OpenLayers.Element.addClass(document.body, \"olDragDown\");\n+ this.documentEvents = true;\n+ OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.observe(document, \"mouseup\", this._docUp)\n+ },\n+ removeDocumentEvents: function() {\n+ OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n+ this.documentEvents = false;\n+ OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp)\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+});\n+OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n+ dragHandler: null,\n+ boxDivClassName: \"olHandlerBoxZoomBox\",\n+ boxOffsets: null,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.dragHandler = new OpenLayers.Handler.Drag(this, {\n+ down: this.startBox,\n+ move: this.moveBox,\n+ out: this.removeBox,\n+ up: this.endBox\n+ }, {\n+ keyMask: this.keyMask\n+ })\n+ },\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.destroy();\n+ this.dragHandler = null\n+ }\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n+ if (this.dragHandler) {\n+ this.dragHandler.setMap(map)\n+ }\n+ },\n+ startBox: function(xy) {\n+ this.callback(\"start\", []);\n+ this.zoomBox = OpenLayers.Util.createDiv(\"zoomBox\", {\n+ x: -9999,\n+ y: -9999\n });\n- return tile\n+ this.zoomBox.className = this.boxDivClassName;\n+ this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n+ this.map.viewPortDiv.appendChild(this.zoomBox);\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDrawBox\")\n },\n- addTileMonitoringHooks: function(tile) {\n- var replacingCls = \"olTileReplacing\";\n- tile.onLoadStart = function() {\n- if (this.loading === false) {\n- this.loading = true;\n- this.events.triggerEvent(\"loadstart\")\n+ moveBox: function(xy) {\n+ var startX = this.dragHandler.start.x;\n+ var startY = this.dragHandler.start.y;\n+ var deltaX = Math.abs(startX - xy.x);\n+ var deltaY = Math.abs(startY - xy.y);\n+ var offset = this.getBoxOffsets();\n+ this.zoomBox.style.width = deltaX + offset.width + 1 + \"px\";\n+ this.zoomBox.style.height = deltaY + offset.height + 1 + \"px\";\n+ this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + \"px\";\n+ this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + \"px\"\n+ },\n+ endBox: function(end) {\n+ var result;\n+ if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) {\n+ var start = this.dragHandler.start;\n+ var top = Math.min(start.y, end.y);\n+ var bottom = Math.max(start.y, end.y);\n+ var left = Math.min(start.x, end.x);\n+ var right = Math.max(start.x, end.x);\n+ result = new OpenLayers.Bounds(left, bottom, right, top)\n+ } else {\n+ result = this.dragHandler.start.clone()\n+ }\n+ this.removeBox();\n+ this.callback(\"done\", [result])\n+ },\n+ removeBox: function() {\n+ this.map.viewPortDiv.removeChild(this.zoomBox);\n+ this.zoomBox = null;\n+ this.boxOffsets = null;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDrawBox\")\n+ },\n+ activate: function() {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragHandler.activate();\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ deactivate: function() {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ if (this.dragHandler.deactivate()) {\n+ if (this.zoomBox) {\n+ this.removeBox()\n+ }\n }\n- this.events.triggerEvent(\"tileloadstart\", {\n- tile: tile\n- });\n- this.numLoadingTiles++;\n- if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- OpenLayers.Element.addClass(tile.getTile(), replacingCls)\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ getBoxOffsets: function() {\n+ if (!this.boxOffsets) {\n+ var testDiv = document.createElement(\"div\");\n+ testDiv.style.position = \"absolute\";\n+ testDiv.style.border = \"1px solid black\";\n+ testDiv.style.width = \"3px\";\n+ document.body.appendChild(testDiv);\n+ var w3cBoxModel = testDiv.clientWidth == 3;\n+ document.body.removeChild(testDiv);\n+ var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-left-width\"));\n+ var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-right-width\"));\n+ var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-top-width\"));\n+ var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-bottom-width\"));\n+ this.boxOffsets = {\n+ left: left,\n+ right: right,\n+ top: top,\n+ bottom: bottom,\n+ width: w3cBoxModel === false ? left + right : 0,\n+ height: w3cBoxModel === false ? top + bottom : 0\n }\n- };\n- tile.onLoadEnd = function(evt) {\n- this.numLoadingTiles--;\n- var aborted = evt.type === \"unload\";\n- this.events.triggerEvent(\"tileloaded\", {\n- tile: tile,\n- aborted: aborted\n+ }\n+ return this.boxOffsets\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Box\"\n+});\n+OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_TOOL,\n+ out: false,\n+ keyMask: null,\n+ alwaysZoom: false,\n+ zoomOnClick: true,\n+ draw: function() {\n+ this.handler = new OpenLayers.Handler.Box(this, {\n+ done: this.zoomBox\n+ }, {\n+ keyMask: this.keyMask\n+ })\n+ },\n+ zoomBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var bounds, targetCenterPx = position.getCenterPixel();\n+ if (!this.out) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n+ } else {\n+ var pixWidth = position.right - position.left;\n+ var pixHeight = position.bottom - position.top;\n+ var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth);\n+ var extent = this.map.getExtent();\n+ var center = this.map.getLonLatFromPixel(targetCenterPx);\n+ var xmin = center.lon - extent.getWidth() / 2 * zoomFactor;\n+ var xmax = center.lon + extent.getWidth() / 2 * zoomFactor;\n+ var ymin = center.lat - extent.getHeight() / 2 * zoomFactor;\n+ var ymax = center.lat + extent.getHeight() / 2 * zoomFactor;\n+ bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax)\n+ }\n+ var lastZoom = this.map.getZoom(),\n+ size = this.map.getSize(),\n+ centerPx = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ },\n+ zoom = this.map.getZoomForExtent(bounds),\n+ oldRes = this.map.getResolution(),\n+ newRes = this.map.getResolutionForZoom(zoom);\n+ if (oldRes == newRes) {\n+ this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx))\n+ } else {\n+ var zoomOriginPx = {\n+ x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes),\n+ y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes)\n+ };\n+ this.map.zoomTo(zoom, zoomOriginPx)\n+ }\n+ if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n+ this.map.zoomTo(lastZoom + (this.out ? -1 : 1))\n+ }\n+ } else if (this.zoomOnClick) {\n+ if (!this.out) {\n+ this.map.zoomTo(this.map.getZoom() + 1, position)\n+ } else {\n+ this.map.zoomTo(this.map.getZoom() - 1, position)\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n+});\n+OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_TOOL,\n+ panned: false,\n+ interval: 0,\n+ documentDrag: false,\n+ kinetic: null,\n+ enableKinetic: true,\n+ kineticInterval: 10,\n+ draw: function() {\n+ if (this.enableKinetic && OpenLayers.Kinetic) {\n+ var config = {\n+ interval: this.kineticInterval\n+ };\n+ if (typeof this.enableKinetic === \"object\") {\n+ config = OpenLayers.Util.extend(config, this.enableKinetic)\n+ }\n+ this.kinetic = new OpenLayers.Kinetic(config)\n+ }\n+ this.handler = new OpenLayers.Handler.Drag(this, {\n+ move: this.panMap,\n+ done: this.panMapDone,\n+ down: this.panMapStart\n+ }, {\n+ interval: this.interval,\n+ documentDrag: this.documentDrag\n+ })\n+ },\n+ panMapStart: function() {\n+ if (this.kinetic) {\n+ this.kinetic.begin()\n+ }\n+ },\n+ panMap: function(xy) {\n+ if (this.kinetic) {\n+ this.kinetic.update(xy)\n+ }\n+ this.panned = true;\n+ this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n+ dragging: true,\n+ animate: false\n+ })\n+ },\n+ panMapDone: function(xy) {\n+ if (this.panned) {\n+ var res = null;\n+ if (this.kinetic) {\n+ res = this.kinetic.end(xy)\n+ }\n+ this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n+ dragging: !!res,\n+ animate: false\n });\n- if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- var tileDiv = tile.getTile();\n- if (OpenLayers.Element.getStyle(tileDiv, \"display\") === \"none\") {\n- var bufferTile = document.getElementById(tile.id + \"_bb\");\n- if (bufferTile) {\n- bufferTile.parentNode.removeChild(bufferTile)\n- }\n- }\n- OpenLayers.Element.removeClass(tileDiv, replacingCls)\n+ if (res) {\n+ var self = this;\n+ this.kinetic.move(res, function(x, y, end) {\n+ self.map.pan(x, y, {\n+ dragging: !end,\n+ animate: false\n+ })\n+ })\n }\n- if (this.numLoadingTiles === 0) {\n- if (this.backBuffer) {\n- if (this.backBuffer.childNodes.length === 0) {\n- this.removeBackBuffer()\n+ this.panned = false\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.DragPan\"\n+});\n+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n+ wheelListener: null,\n+ interval: 0,\n+ maxDelta: Number.POSITIVE_INFINITY,\n+ delta: 0,\n+ cumulative: true,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this)\n+ },\n+ destroy: function() {\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ this.wheelListener = null\n+ },\n+ onWheelEvent: function(e) {\n+ if (!this.map || !this.checkModifiers(e)) {\n+ return\n+ }\n+ var overScrollableDiv = false;\n+ var allowScroll = false;\n+ var overMapDiv = false;\n+ var elem = OpenLayers.Event.element(e);\n+ while (elem != null && !overMapDiv && !overScrollableDiv) {\n+ if (!overScrollableDiv) {\n+ try {\n+ var overflow;\n+ if (elem.currentStyle) {\n+ overflow = elem.currentStyle[\"overflow\"]\n } else {\n- this._transitionElement = aborted ? this.div.lastChild : tile.imgDiv;\n- var transitionendEvents = this.transitionendEvents;\n- for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.observe(this._transitionElement, transitionendEvents[i], this._removeBackBuffer)\n+ var style = document.defaultView.getComputedStyle(elem, null);\n+ overflow = style.getPropertyValue(\"overflow\")\n+ }\n+ overScrollableDiv = overflow && overflow == \"auto\" || overflow == \"scroll\"\n+ } catch (err) {}\n+ }\n+ if (!allowScroll) {\n+ allowScroll = OpenLayers.Element.hasClass(elem, \"olScrollable\");\n+ if (!allowScroll) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (elem == layer.div || elem == layer.pane) {\n+ allowScroll = true;\n+ break\n }\n- this.backBufferTimerId = window.setTimeout(this._removeBackBuffer, this.removeBackBufferDelay)\n }\n }\n- this.loading = false;\n- this.events.triggerEvent(\"loadend\")\n }\n+ overMapDiv = elem == this.map.div;\n+ elem = elem.parentNode\n+ }\n+ if (!overScrollableDiv && overMapDiv) {\n+ if (allowScroll) {\n+ var delta = 0;\n+ if (e.wheelDelta) {\n+ delta = e.wheelDelta;\n+ if (delta % 160 === 0) {\n+ delta = delta * .75\n+ }\n+ delta = delta / 120\n+ } else if (e.detail) {\n+ delta = -(e.detail / Math.abs(e.detail))\n+ }\n+ this.delta += delta;\n+ window.clearTimeout(this._timeoutId);\n+ if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n+ var evt = OpenLayers.Util.extend({}, e);\n+ this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() {\n+ this.wheelZoom(evt)\n+ }, this), this.interval)\n+ } else {\n+ this.wheelZoom(e)\n+ }\n+ }\n+ OpenLayers.Event.stop(e)\n+ }\n+ },\n+ wheelZoom: function(e) {\n+ var delta = this.delta;\n+ this.delta = 0;\n+ if (delta) {\n+ e.xy = this.map.events.getMousePosition(e);\n+ if (delta < 0) {\n+ this.callback(\"down\", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1])\n+ } else {\n+ this.callback(\"up\", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1])\n+ }\n+ }\n+ },\n+ activate: function(evt) {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ deactivate: function(evt) {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+});\n+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n+ delay: 300,\n+ single: true,\n+ double: false,\n+ pixelTolerance: 0,\n+ dblclickTolerance: 13,\n+ stopSingle: false,\n+ stopDouble: false,\n+ timerId: null,\n+ down: null,\n+ last: null,\n+ first: null,\n+ rightclickTimerId: null,\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true\n+ },\n+ touchmove: function(evt) {\n+ this.last = this.getEventInfo(evt);\n+ return true\n+ },\n+ touchend: function(evt) {\n+ if (this.down) {\n+ evt.xy = this.last.xy;\n+ evt.lastTouches = this.last.touches;\n+ this.handleSingle(evt);\n+ this.down = null\n+ }\n+ return true\n+ },\n+ mousedown: function(evt) {\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true\n+ },\n+ mouseup: function(evt) {\n+ var propagate = true;\n+ if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) {\n+ propagate = this.rightclick(evt)\n+ }\n+ return propagate\n+ },\n+ rightclick: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.rightclickTimerId != null) {\n+ this.clearTimer();\n+ this.callback(\"dblrightclick\", [evt]);\n+ return !this.stopDouble\n+ } else {\n+ var clickEvent = this[\"double\"] ? OpenLayers.Util.extend({}, evt) : this.callback(\"rightclick\", [evt]);\n+ var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent);\n+ this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay)\n+ }\n+ }\n+ return !this.stopSingle\n+ },\n+ delayedRightCall: function(evt) {\n+ this.rightclickTimerId = null;\n+ if (evt) {\n+ this.callback(\"rightclick\", [evt])\n+ }\n+ },\n+ click: function(evt) {\n+ if (!this.last) {\n+ this.last = this.getEventInfo(evt)\n+ }\n+ this.handleSingle(evt);\n+ return !this.stopSingle\n+ },\n+ dblclick: function(evt) {\n+ this.handleDouble(evt);\n+ return !this.stopDouble\n+ },\n+ handleDouble: function(evt) {\n+ if (this.passesDblclickTolerance(evt)) {\n+ if (this[\"double\"]) {\n+ this.callback(\"dblclick\", [evt])\n+ }\n+ this.clearTimer()\n+ }\n+ },\n+ handleSingle: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.timerId != null) {\n+ if (this.last.touches && this.last.touches.length === 1) {\n+ if (this[\"double\"]) {\n+ OpenLayers.Event.preventDefault(evt)\n+ }\n+ this.handleDouble(evt)\n+ }\n+ if (!this.last.touches || this.last.touches.length !== 2) {\n+ this.clearTimer()\n+ }\n+ } else {\n+ this.first = this.getEventInfo(evt);\n+ var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null;\n+ this.queuePotentialClick(clickEvent)\n+ }\n+ }\n+ },\n+ queuePotentialClick: function(evt) {\n+ this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n+ },\n+ passesTolerance: function(evt) {\n+ var passes = true;\n+ if (this.pixelTolerance != null && this.down && this.down.xy) {\n+ passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n+ if (passes && this.touch && this.down.touches.length === this.last.touches.length) {\n+ for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n+ if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) {\n+ passes = false;\n+ break\n+ }\n+ }\n+ }\n+ }\n+ return passes\n+ },\n+ getTouchDistance: function(from, to) {\n+ return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2))\n+ },\n+ passesDblclickTolerance: function(evt) {\n+ var passes = true;\n+ if (this.down && this.first) {\n+ passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance\n+ }\n+ return passes\n+ },\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null\n+ }\n+ if (this.rightclickTimerId != null) {\n+ window.clearTimeout(this.rightclickTimerId);\n+ this.rightclickTimerId = null\n+ }\n+ },\n+ delayedCall: function(evt) {\n+ this.timerId = null;\n+ if (evt) {\n+ this.callback(\"click\", [evt])\n+ }\n+ },\n+ getEventInfo: function(evt) {\n+ var touches;\n+ if (evt.touches) {\n+ var len = evt.touches.length;\n+ touches = new Array(len);\n+ var touch;\n+ for (var i = 0; i < len; i++) {\n+ touch = evt.touches[i];\n+ touches[i] = {\n+ clientX: touch.olClientX,\n+ clientY: touch.olClientY\n+ }\n+ }\n+ }\n+ return {\n+ xy: evt.xy,\n+ touches: touches\n+ }\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ this.down = null;\n+ this.first = null;\n+ this.last = null;\n+ deactivated = true\n+ }\n+ return deactivated\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Click\"\n+});\n+OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n+ dragPan: null,\n+ dragPanOptions: null,\n+ pinchZoom: null,\n+ pinchZoomOptions: null,\n+ documentDrag: false,\n+ zoomBox: null,\n+ zoomBoxEnabled: true,\n+ zoomWheelEnabled: true,\n+ mouseWheelOptions: null,\n+ handleRightClicks: false,\n+ zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n+ autoActivate: true,\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments)\n+ },\n+ destroy: function() {\n+ this.deactivate();\n+ if (this.dragPan) {\n+ this.dragPan.destroy()\n+ }\n+ this.dragPan = null;\n+ if (this.zoomBox) {\n+ this.zoomBox.destroy()\n+ }\n+ this.zoomBox = null;\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy()\n+ }\n+ this.pinchZoom = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ activate: function() {\n+ this.dragPan.activate();\n+ if (this.zoomWheelEnabled) {\n+ this.handlers.wheel.activate()\n+ }\n+ this.handlers.click.activate();\n+ if (this.zoomBoxEnabled) {\n+ this.zoomBox.activate()\n+ }\n+ if (this.pinchZoom) {\n+ this.pinchZoom.activate()\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments)\n+ },\n+ deactivate: function() {\n+ if (this.pinchZoom) {\n+ this.pinchZoom.deactivate()\n+ }\n+ this.zoomBox.deactivate();\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.handlers.wheel.deactivate();\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n+ },\n+ draw: function() {\n+ if (this.handleRightClicks) {\n+ this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False\n+ }\n+ var clickCallbacks = {\n+ click: this.defaultClick,\n+ dblclick: this.defaultDblClick,\n+ dblrightclick: this.defaultDblRightClick\n };\n- tile.onLoadError = function() {\n- this.events.triggerEvent(\"tileerror\", {\n- tile: tile\n- })\n+ var clickOptions = {\n+ double: true,\n+ stopDouble: true\n };\n- tile.events.on({\n- loadstart: tile.onLoadStart,\n- loadend: tile.onLoadEnd,\n- unload: tile.onLoadEnd,\n- loaderror: tile.onLoadError,\n+ this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n+ this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions));\n+ this.zoomBox = new OpenLayers.Control.ZoomBox({\n+ map: this.map,\n+ keyMask: this.zoomBoxKeyMask\n+ });\n+ this.dragPan.draw();\n+ this.zoomBox.draw();\n+ var wheelOptions = this.map.fractionalZoom ? {} : {\n+ cumulative: false,\n+ interval: 50,\n+ maxDelta: 6\n+ };\n+ this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, {\n+ up: this.wheelUp,\n+ down: this.wheelDown\n+ }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions));\n+ if (OpenLayers.Control.PinchZoom) {\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions))\n+ }\n+ },\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut()\n+ }\n+ },\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy)\n+ },\n+ defaultDblRightClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom - 1, evt.xy)\n+ },\n+ wheelChange: function(evt, deltaZ) {\n+ if (!this.map.fractionalZoom) {\n+ deltaZ = Math.round(deltaZ)\n+ }\n+ var currentZoom = this.map.getZoom(),\n+ newZoom = currentZoom + deltaZ;\n+ newZoom = Math.max(newZoom, 0);\n+ newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n+ if (newZoom === currentZoom) {\n+ return\n+ }\n+ this.map.zoomTo(newZoom, evt.xy)\n+ },\n+ wheelUp: function(evt, delta) {\n+ this.wheelChange(evt, delta || 1)\n+ },\n+ wheelDown: function(evt, delta) {\n+ this.wheelChange(evt, delta || -1)\n+ },\n+ disableZoomBox: function() {\n+ this.zoomBoxEnabled = false;\n+ this.zoomBox.deactivate()\n+ },\n+ enableZoomBox: function() {\n+ this.zoomBoxEnabled = true;\n+ if (this.active) {\n+ this.zoomBox.activate()\n+ }\n+ },\n+ disableZoomWheel: function() {\n+ this.zoomWheelEnabled = false;\n+ this.handlers.wheel.deactivate()\n+ },\n+ enableZoomWheel: function() {\n+ this.zoomWheelEnabled = true;\n+ if (this.active) {\n+ this.handlers.wheel.activate()\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Navigation\"\n+});\n+OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n+ separator: \", \",\n+ template: \"${layers}\",\n+ destroy: function() {\n+ this.map.events.un({\n+ removelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ changebaselayer: this.updateAttribution,\n scope: this\n- })\n+ });\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- loadstart: tile.onLoadStart,\n- loadend: tile.onLoadEnd,\n- unload: tile.onLoadEnd,\n- loaderror: tile.onLoadError,\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ this.map.events.on({\n+ changebaselayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ removelayer: this.updateAttribution,\n scope: this\n- })\n+ });\n+ this.updateAttribution();\n+ return this.div\n },\n- moveGriddedTiles: function() {\n- var buffer = this.buffer + 1;\n- while (true) {\n- var tlTile = this.grid[0][0];\n- var tlViewPort = {\n- x: tlTile.position.x + this.map.layerContainerOriginPx.x,\n- y: tlTile.position.y + this.map.layerContainerOriginPx.y\n- };\n- var ratio = this.getServerResolution() / this.map.getResolution();\n- var tileSize = {\n- w: Math.round(this.tileSize.w * ratio),\n- h: Math.round(this.tileSize.h * ratio)\n- };\n- if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n- this.shiftColumn(true, tileSize)\n- } else if (tlViewPort.x < -tileSize.w * buffer) {\n- this.shiftColumn(false, tileSize)\n- } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n- this.shiftRow(true, tileSize)\n- } else if (tlViewPort.y < -tileSize.h * buffer) {\n- this.shiftRow(false, tileSize)\n- } else {\n- break\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution)\n+ }\n+ }\n }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ })\n }\n },\n- shiftRow: function(prepend, tileSize) {\n- var grid = this.grid;\n- var rowIndex = prepend ? 0 : grid.length - 1;\n- var sign = prepend ? -1 : 1;\n- var rowSign = this.rowSign;\n- var tileLayout = this.gridLayout;\n- tileLayout.startrow += sign * rowSign;\n- var modelRow = grid[rowIndex];\n- var row = grid[prepend ? \"pop\" : \"shift\"]();\n- for (var i = 0, len = row.length; i < len; i++) {\n- var tile = row[i];\n- var position = modelRow[i].position.clone();\n- position.y += tileSize.h * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position)\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+});\n+OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n+ layerStates: null,\n+ layersDiv: null,\n+ baseLayersDiv: null,\n+ baseLayers: null,\n+ dataLbl: null,\n+ dataLayersDiv: null,\n+ dataLayers: null,\n+ minimizeDiv: null,\n+ maximizeDiv: null,\n+ ascending: true,\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ this.layerStates = []\n+ },\n+ destroy: function() {\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ this.map.events.on({\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick)\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n }\n- grid[prepend ? \"unshift\" : \"push\"](row)\n },\n- shiftColumn: function(prepend, tileSize) {\n- var grid = this.grid;\n- var colIndex = prepend ? 0 : grid[0].length - 1;\n- var sign = prepend ? -1 : 1;\n- var tileLayout = this.gridLayout;\n- tileLayout.startcol += sign;\n- for (var i = 0, len = grid.length; i < len; i++) {\n- var row = grid[i];\n- var position = row[colIndex].position.clone();\n- var tile = row[prepend ? \"pop\" : \"shift\"]();\n- position.x += tileSize.w * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n- row[prepend ? \"unshift\" : \"push\"](tile)\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this);\n+ this.loadContents();\n+ if (!this.outsideViewport) {\n+ this.minimizeControl()\n }\n+ this.redraw();\n+ return this.div\n },\n- removeExcessTiles: function(rows, columns) {\n- var i, l;\n- while (this.grid.length > rows) {\n- var row = this.grid.pop();\n- for (i = 0, l = row.length; i < l; i++) {\n- var tile = row[i];\n- this.destroyTile(tile)\n+ onButtonClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.minimizeDiv) {\n+ this.minimizeControl()\n+ } else if (button === this.maximizeDiv) {\n+ this.maximizeControl()\n+ } else if (button._layerSwitcher === this.id) {\n+ if (button[\"for\"]) {\n+ button = document.getElementById(button[\"for\"])\n+ }\n+ if (!button.disabled) {\n+ if (button.type == \"radio\") {\n+ button.checked = true;\n+ this.map.setBaseLayer(this.map.getLayer(button._layer))\n+ } else {\n+ button.checked = !button.checked;\n+ this.updateMap()\n+ }\n }\n }\n- for (i = 0, l = this.grid.length; i < l; i++) {\n- while (this.grid[i].length > columns) {\n- var row = this.grid[i];\n- var tile = row.pop();\n- this.destroyTile(tile)\n+ },\n+ clearLayersArray: function(layersType) {\n+ this[layersType + \"LayersDiv\"].innerHTML = \"\";\n+ this[layersType + \"Layers\"] = []\n+ },\n+ checkRedraw: function() {\n+ if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) {\n+ return true\n+ }\n+ for (var i = 0, len = this.layerStates.length; i < len; i++) {\n+ var layerState = this.layerStates[i];\n+ var layer = this.map.layers[i];\n+ if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) {\n+ return true\n }\n }\n+ return false\n },\n- onMapResize: function() {\n- if (this.singleTile) {\n- this.clearGrid();\n- this.setTileSize()\n+ redraw: function() {\n+ if (!this.checkRedraw()) {\n+ return this.div\n+ }\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+ var containsOverlays = false;\n+ var containsBaseLayers = false;\n+ var len = this.map.layers.length;\n+ this.layerStates = new Array(len);\n+ for (var i = 0; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ this.layerStates[i] = {\n+ name: layer.name,\n+ visibility: layer.visibility,\n+ inRange: layer.inRange,\n+ id: layer.id\n+ }\n+ }\n+ var layers = this.map.layers.slice();\n+ if (!this.ascending) {\n+ layers.reverse()\n+ }\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ var baseLayer = layer.isBaseLayer;\n+ if (layer.displayInLayerSwitcher) {\n+ if (baseLayer) {\n+ containsBaseLayers = true\n+ } else {\n+ containsOverlays = true\n+ }\n+ var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility();\n+ var inputElem = document.createElement(\"input\"),\n+ inputId = OpenLayers.Util.createUniqueID(this.id + \"_input_\");\n+ inputElem.id = inputId;\n+ inputElem.name = baseLayer ? this.id + \"_baseLayers\" : layer.name;\n+ inputElem.type = baseLayer ? \"radio\" : \"checkbox\";\n+ inputElem.value = layer.name;\n+ inputElem.checked = checked;\n+ inputElem.defaultChecked = checked;\n+ inputElem.className = \"olButton\";\n+ inputElem._layer = layer.id;\n+ inputElem._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ inputElem.disabled = true\n+ }\n+ var labelSpan = document.createElement(\"label\");\n+ labelSpan[\"for\"] = inputElem.id;\n+ OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n+ labelSpan._layer = layer.id;\n+ labelSpan._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ labelSpan.style.color = \"gray\"\n+ }\n+ labelSpan.innerHTML = layer.name;\n+ labelSpan.style.verticalAlign = baseLayer ? \"bottom\" : \"baseline\";\n+ var br = document.createElement(\"br\");\n+ var groupArray = baseLayer ? this.baseLayers : this.dataLayers;\n+ groupArray.push({\n+ layer: layer,\n+ inputElem: inputElem,\n+ labelSpan: labelSpan\n+ });\n+ var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv;\n+ groupDiv.appendChild(inputElem);\n+ groupDiv.appendChild(labelSpan);\n+ groupDiv.appendChild(br)\n+ }\n }\n+ this.dataLbl.style.display = containsOverlays ? \"\" : \"none\";\n+ this.baseLbl.style.display = containsBaseLayers ? \"\" : \"none\";\n+ return this.div\n },\n- getTileBounds: function(viewPortPx) {\n- var maxExtent = this.maxExtent;\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth);\n- var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight);\n- return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n+ updateMap: function() {\n+ for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n+ var layerEntry = this.baseLayers[i];\n+ if (layerEntry.inputElem.checked) {\n+ this.map.setBaseLayer(layerEntry.layer, false)\n+ }\n+ }\n+ for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n+ var layerEntry = this.dataLayers[i];\n+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked)\n+ }\n },\n- CLASS_NAME: \"OpenLayers.Layer.Grid\"\n-});\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- sphericalMercator: false,\n- zoomOffset: 0,\n- serverResolutions: null,\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options)\n+ maximizeControl: function(e) {\n+ this.div.style.width = \"\";\n+ this.div.style.height = \"\";\n+ this.showControls(false);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e)\n }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n+ minimizeControl: function(e) {\n+ this.div.style.width = \"0px\";\n+ this.div.style.height = \"0px\";\n+ this.showControls(true);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e)\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n },\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = \"\" + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url)\n+ showControls: function(minimize) {\n+ this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n+ this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n+ this.layersDiv.style.display = minimize ? \"none\" : \"\"\n+ },\n+ loadContents: function() {\n+ this.layersDiv = document.createElement(\"div\");\n+ this.layersDiv.id = this.id + \"_layersDiv\";\n+ OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n+ this.baseLbl = document.createElement(\"div\");\n+ this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n+ OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n+ this.baseLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n+ this.dataLbl = document.createElement(\"div\");\n+ this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n+ OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n+ this.dataLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n+ if (this.ascending) {\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv)\n+ } else {\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv)\n }\n- return OpenLayers.String.format(url, xyz)\n+ this.div.appendChild(this.layersDiv);\n+ var img = OpenLayers.Util.getImageLocation(\"layer-switcher-maximize.png\");\n+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MaximizeDiv\", null, null, img, \"absolute\");\n+ OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n+ this.maximizeDiv.style.display = \"none\";\n+ this.div.appendChild(this.maximizeDiv);\n+ var img = OpenLayers.Util.getImageLocation(\"layer-switcher-minimize.png\");\n+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MinimizeDiv\", null, null, img, \"absolute\");\n+ OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n+ this.minimizeDiv.style.display = \"none\";\n+ this.div.appendChild(this.minimizeDiv)\n },\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = (x % limit + limit) % limit\n+ CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+});\n+OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n+ zoomInText: \"+\",\n+ zoomInId: \"olZoomInLink\",\n+ zoomOutText: \"\u2212\",\n+ zoomOutId: \"olZoomOutLink\",\n+ draw: function() {\n+ var div = OpenLayers.Control.prototype.draw.apply(this),\n+ links = this.getOrCreateLinks(div),\n+ zoomIn = links.zoomIn,\n+ zoomOut = links.zoomOut,\n+ eventsInstance = this.map.events;\n+ if (zoomOut.parentNode !== div) {\n+ eventsInstance = this.events;\n+ eventsInstance.attachToElement(zoomOut.parentNode)\n+ }\n+ eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n+ this.zoomInLink = zoomIn;\n+ this.zoomOutLink = zoomOut;\n+ return div\n+ },\n+ getOrCreateLinks: function(el) {\n+ var zoomIn = document.getElementById(this.zoomInId),\n+ zoomOut = document.getElementById(this.zoomOutId);\n+ if (!zoomIn) {\n+ zoomIn = document.createElement(\"a\");\n+ zoomIn.href = \"#zoomIn\";\n+ zoomIn.appendChild(document.createTextNode(this.zoomInText));\n+ zoomIn.className = \"olControlZoomIn\";\n+ el.appendChild(zoomIn)\n }\n+ OpenLayers.Element.addClass(zoomIn, \"olButton\");\n+ if (!zoomOut) {\n+ zoomOut = document.createElement(\"a\");\n+ zoomOut.href = \"#zoomOut\";\n+ zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n+ zoomOut.className = \"olControlZoomOut\";\n+ el.appendChild(zoomOut)\n+ }\n+ OpenLayers.Element.addClass(zoomOut, \"olButton\");\n return {\n- x: x,\n- y: y,\n- z: z\n+ zoomIn: zoomIn,\n+ zoomOut: zoomOut\n }\n },\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n+ onZoomClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.zoomInLink) {\n+ this.map.zoomIn()\n+ } else if (button === this.zoomOutLink) {\n+ this.map.zoomOut()\n }\n },\n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onZoomClick)\n+ }\n+ delete this.zoomInLink;\n+ delete this.zoomOutLink;\n+ OpenLayers.Control.prototype.destroy.apply(this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Zoom\"\n });\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- name: \"OpenStreetMap\",\n- url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n- sphericalMercator: true,\n- wrapDateLine: true,\n- tileOptions: null,\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options && this.options.tileOptions)\n+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n+ EVENTMAP: {\n+ click: {\n+ in: \"click\",\n+ out: \"clickout\"\n+ },\n+ mousemove: {\n+ in: \"over\",\n+ out: \"out\"\n+ },\n+ dblclick: {\n+ in: \"dblclick\",\n+ out: null\n+ },\n+ mousedown: {\n+ in: null,\n+ out: null\n+ },\n+ mouseup: {\n+ in: null,\n+ out: null\n+ },\n+ touchstart: {\n+ in: \"click\",\n+ out: \"clickout\"\n+ }\n+ },\n+ feature: null,\n+ lastFeature: null,\n+ down: null,\n+ up: null,\n+ clickTolerance: 4,\n+ geometryTypes: null,\n+ stopClick: true,\n+ stopDown: true,\n+ stopUp: false,\n+ initialize: function(control, layer, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.layer = layer\n+ },\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n+ },\n+ touchmove: function(evt) {\n+ OpenLayers.Event.preventDefault(evt)\n+ },\n+ mousedown: function(evt) {\n+ if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n+ this.down = evt.xy\n+ }\n+ return this.handle(evt) ? !this.stopDown : true\n+ },\n+ mouseup: function(evt) {\n+ this.up = evt.xy;\n+ return this.handle(evt) ? !this.stopUp : true\n+ },\n+ click: function(evt) {\n+ return this.handle(evt) ? !this.stopClick : true\n+ },\n+ mousemove: function(evt) {\n+ if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n+ return true\n+ }\n+ this.handle(evt);\n+ return true\n+ },\n+ dblclick: function(evt) {\n+ return !this.handle(evt)\n+ },\n+ geometryTypeMatches: function(feature) {\n+ return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n+ },\n+ handle: function(evt) {\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n+ }\n+ var type = evt.type;\n+ var handled = false;\n+ var previouslyIn = !!this.feature;\n+ var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n+ this.feature = this.layer.getFeatureFromEvent(evt);\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n+ }\n+ if (this.lastFeature && !this.lastFeature.layer) {\n+ this.lastFeature = null\n+ }\n+ if (this.feature) {\n+ if (type === \"touchstart\") {\n+ OpenLayers.Event.preventDefault(evt)\n+ }\n+ var inNew = this.feature != this.lastFeature;\n+ if (this.geometryTypeMatches(this.feature)) {\n+ if (previouslyIn && inNew) {\n+ if (this.lastFeature) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n+ }\n+ this.triggerCallback(type, \"in\", [this.feature])\n+ } else if (!previouslyIn || click) {\n+ this.triggerCallback(type, \"in\", [this.feature])\n+ }\n+ this.lastFeature = this.feature;\n+ handled = true\n+ } else {\n+ if (this.lastFeature && (previouslyIn && inNew || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n+ }\n+ this.feature = null\n+ }\n+ } else if (this.lastFeature && (previouslyIn || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n+ }\n+ return handled\n+ },\n+ triggerCallback: function(type, mode, args) {\n+ var key = this.EVENTMAP[type][mode];\n+ if (key) {\n+ if (type == \"click\" && this.up && this.down) {\n+ var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n+ if (dpx <= this.clickTolerance) {\n+ this.callback(key, args)\n+ }\n+ this.up = this.down = null\n+ } else {\n+ this.callback(key, args)\n+ }\n+ }\n+ },\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n+ scope: this\n+ });\n+ activated = true\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.feature = null;\n+ this.lastFeature = null;\n+ this.down = null;\n+ this.up = null;\n+ this.map.events.un({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n+ scope: this\n+ });\n+ deactivated = true\n+ }\n+ return deactivated\n+ },\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop()\n+ }\n+ },\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index)\n+ },\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n+ this.layer.setZIndex(index)\n+ } else {\n+ this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Feature\"\n+});\n+OpenLayers.Layer = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ div: null,\n+ opacity: 1,\n+ alwaysInRange: null,\n+ RESOLUTION_PROPERTIES: [\"scales\", \"resolutions\", \"maxScale\", \"minScale\", \"maxResolution\", \"minResolution\", \"numZoomLevels\", \"maxZoomLevel\"],\n+ events: null,\n+ map: null,\n+ isBaseLayer: false,\n+ alpha: false,\n+ displayInLayerSwitcher: true,\n+ visibility: true,\n+ attribution: null,\n+ inRange: false,\n+ imageSize: null,\n+ options: null,\n+ eventListeners: null,\n+ gutter: 0,\n+ projection: null,\n+ units: null,\n+ scales: null,\n+ resolutions: null,\n+ maxExtent: null,\n+ minExtent: null,\n+ maxResolution: null,\n+ minResolution: null,\n+ numZoomLevels: null,\n+ minScale: null,\n+ maxScale: null,\n+ displayOutsideMaxExtent: false,\n+ wrapDateLine: false,\n+ metadata: null,\n+ initialize: function(name, options) {\n+ this.metadata = {};\n+ options = OpenLayers.Util.extend({}, options);\n+ if (this.alwaysInRange != null) {\n+ options.alwaysInRange = this.alwaysInRange\n+ }\n+ this.addOptions(options);\n+ this.name = name;\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.style.width = \"100%\";\n+ this.div.style.height = \"100%\";\n+ this.div.dir = \"ltr\";\n+ this.events = new OpenLayers.Events(this, this.div);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n+ }\n+ }\n+ },\n+ destroy: function(setNewBaseLayer) {\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true\n+ }\n+ if (this.map != null) {\n+ this.map.removeLayer(this, setNewBaseLayer)\n+ }\n+ this.projection = null;\n+ this.map = null;\n+ this.name = null;\n+ this.div = null;\n+ this.options = null;\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n+ }\n+ this.events.destroy()\n+ }\n+ this.eventListeners = null;\n+ this.events = null\n },\n clone: function(obj) {\n if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n+ obj = new OpenLayers.Layer(this.name, this.getOptions())\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ OpenLayers.Util.applyDefaults(obj, this);\n+ obj.map = null;\n return obj\n },\n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- key: null,\n- serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n- metadata: null,\n- protocolRegex: /^http:/i,\n- type: \"Road\",\n- culture: \"en-US\",\n- metadataParams: null,\n- tileOptions: null,\n- protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n- initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options.tileOptions);\n- this.loadMetadata()\n+ getOptions: function() {\n+ var options = {};\n+ for (var o in this.options) {\n+ options[o] = this[o]\n+ }\n+ return options\n },\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script)\n+ setName: function(newName) {\n+ if (newName != this.name) {\n+ this.name = newName;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"name\"\n+ })\n+ }\n+ }\n },\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n+ addOptions: function(newOptions, reinitialize) {\n+ if (this.options == null) {\n+ this.options = {}\n }\n- this.addOptions({\n- maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n- numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n- }, true);\n+ if (newOptions) {\n+ if (typeof newOptions.projection == \"string\") {\n+ newOptions.projection = new OpenLayers.Projection(newOptions.projection)\n+ }\n+ if (newOptions.projection) {\n+ OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()])\n+ }\n+ if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent)\n+ }\n+ if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent)\n+ }\n+ }\n+ OpenLayers.Util.extend(this.options, newOptions);\n+ OpenLayers.Util.extend(this, newOptions);\n+ if (this.projection && this.projection.getUnits()) {\n+ this.units = this.projection.getUnits()\n+ }\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ var properties = this.RESOLUTION_PROPERTIES.concat([\"projection\", \"units\", \"minExtent\", \"maxExtent\"]);\n+ for (var o in newOptions) {\n+ if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) {\n+ this.initResolutions();\n+ if (reinitialize && this.map.baseLayer === this) {\n+ this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true);\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ })\n+ }\n+ break\n+ }\n+ }\n+ }\n+ },\n+ onMapResize: function() {},\n+ redraw: function() {\n+ var redrawn = false;\n+ if (this.map) {\n+ this.inRange = this.calculateInRange();\n+ var extent = this.getExtent();\n+ if (extent && this.inRange && this.visibility) {\n+ var zoomChanged = true;\n+ this.moveTo(extent, zoomChanged, false);\n+ this.events.triggerEvent(\"moveend\", {\n+ zoomChanged: zoomChanged\n+ });\n+ redrawn = true\n+ }\n+ }\n+ return redrawn\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ var display = this.visibility;\n if (!this.isBaseLayer) {\n- this.redraw()\n+ display = display && this.inRange\n }\n- this.updateAttribution()\n+ this.display(display)\n },\n- getURL: function(bounds) {\n- if (!this.url) {\n- return\n+ moveByPx: function(dx, dy) {},\n+ setMap: function(map) {\n+ if (this.map == null) {\n+ this.map = map;\n+ this.maxExtent = this.maxExtent || this.map.maxExtent;\n+ this.minExtent = this.minExtent || this.map.minExtent;\n+ this.projection = this.projection || this.map.projection;\n+ if (typeof this.projection == \"string\") {\n+ this.projection = new OpenLayers.Projection(this.projection)\n+ }\n+ this.units = this.projection.getUnits() || this.units || this.map.units;\n+ this.initResolutions();\n+ if (!this.isBaseLayer) {\n+ this.inRange = this.calculateInRange();\n+ var show = this.visibility && this.inRange;\n+ this.div.style.display = show ? \"\" : \"none\"\n+ }\n+ this.setTileSize()\n }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = \"0\";\n- var mask = 1 << i - 1;\n- if ((x & mask) != 0) {\n- digit++\n+ },\n+ afterAdd: function() {},\n+ removeMap: function(map) {},\n+ getImageSize: function(bounds) {\n+ return this.imageSize || this.tileSize\n+ },\n+ setTileSize: function(size) {\n+ var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize();\n+ this.tileSize = tileSize;\n+ if (this.gutter) {\n+ this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter)\n+ }\n+ },\n+ getVisibility: function() {\n+ return this.visibility\n+ },\n+ setVisibility: function(visibility) {\n+ if (visibility != this.visibility) {\n+ this.visibility = visibility;\n+ this.display(visibility);\n+ this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"visibility\"\n+ })\n }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++\n+ this.events.triggerEvent(\"visibilitychanged\")\n+ }\n+ },\n+ display: function(display) {\n+ if (display != (this.div.style.display != \"none\")) {\n+ this.div.style.display = display && this.calculateInRange() ? \"block\" : \"none\"\n+ }\n+ },\n+ calculateInRange: function() {\n+ var inRange = false;\n+ if (this.alwaysInRange) {\n+ inRange = true\n+ } else {\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ inRange = resolution >= this.minResolution && resolution <= this.maxResolution\n }\n- quadDigits.push(digit)\n }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl(\"\" + x + y + z, this.url);\n- return OpenLayers.String.format(url, {\n- quadkey: quadKey\n- })\n+ return inRange\n },\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ setIsBaseLayer: function(isBaseLayer) {\n+ if (isBaseLayer != this.isBaseLayer) {\n+ this.isBaseLayer = isBaseLayer;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ })\n+ }\n+ }\n+ },\n+ initResolutions: function() {\n+ var i, len, p;\n+ var props = {},\n+ alwaysInRange = true;\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p];\n+ if (alwaysInRange && this.options[p]) {\n+ alwaysInRange = false\n+ }\n+ }\n+ if (this.options.alwaysInRange == null) {\n+ this.alwaysInRange = alwaysInRange\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales)\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props)\n+ }\n+ if (props.resolutions == null) {\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p] != null ? this.options[p] : this.map[p]\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales)\n+ }\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props)\n+ }\n+ }\n+ var maxResolution;\n+ if (this.options.maxResolution && this.options.maxResolution !== \"auto\") {\n+ maxResolution = this.options.maxResolution\n+ }\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(this.options.minScale, this.units)\n+ }\n+ var minResolution;\n+ if (this.options.minResolution && this.options.minResolution !== \"auto\") {\n+ minResolution = this.options.minResolution\n+ }\n+ if (this.options.maxScale) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units)\n+ }\n+ if (props.resolutions) {\n+ props.resolutions.sort(function(a, b) {\n+ return b - a\n+ });\n+ if (!maxResolution) {\n+ maxResolution = props.resolutions[0]\n+ }\n+ if (!minResolution) {\n+ var lastIdx = props.resolutions.length - 1;\n+ minResolution = props.resolutions[lastIdx]\n+ }\n+ }\n+ this.resolutions = props.resolutions;\n+ if (this.resolutions) {\n+ len = this.resolutions.length;\n+ this.scales = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units)\n+ }\n+ this.numZoomLevels = len\n+ }\n+ this.minResolution = minResolution;\n+ if (minResolution) {\n+ this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units)\n+ }\n+ this.maxResolution = maxResolution;\n+ if (maxResolution) {\n+ this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units)\n+ }\n+ },\n+ resolutionsFromScales: function(scales) {\n+ if (scales == null) {\n return\n }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \"\n+ var resolutions, i, len;\n+ len = scales.length;\n+ resolutions = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ resolutions[i] = OpenLayers.Util.getResolutionFromScale(scales[i], this.units)\n+ }\n+ return resolutions\n+ },\n+ calculateResolutions: function(props) {\n+ var viewSize, wRes, hRes;\n+ var maxResolution = props.maxResolution;\n+ if (props.minScale != null) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(props.minScale, this.units)\n+ } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.maxExtent.getWidth() / viewSize.w;\n+ hRes = this.maxExtent.getHeight() / viewSize.h;\n+ maxResolution = Math.max(wRes, hRes)\n+ }\n+ var minResolution = props.minResolution;\n+ if (props.maxScale != null) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units)\n+ } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.minExtent.getWidth() / viewSize.w;\n+ hRes = this.minExtent.getHeight() / viewSize.h;\n+ minResolution = Math.max(wRes, hRes)\n+ }\n+ if (typeof maxResolution !== \"number\" && typeof minResolution !== \"number\" && this.maxExtent != null) {\n+ var tileSize = this.map.getTileSize();\n+ maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h)\n+ }\n+ var maxZoomLevel = props.maxZoomLevel;\n+ var numZoomLevels = props.numZoomLevels;\n+ if (typeof minResolution === \"number\" && typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n+ var ratio = maxResolution / minResolution;\n+ numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1\n+ } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n+ numZoomLevels = maxZoomLevel + 1\n+ }\n+ if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 || typeof maxResolution !== \"number\" && typeof minResolution !== \"number\") {\n+ return\n+ }\n+ var resolutions = new Array(numZoomLevels);\n+ var base = 2;\n+ if (typeof minResolution == \"number\" && typeof maxResolution == \"number\") {\n+ base = Math.pow(maxResolution / minResolution, 1 / (numZoomLevels - 1))\n+ }\n+ var i;\n+ if (typeof maxResolution === \"number\") {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[i] = maxResolution / Math.pow(base, i)\n+ }\n+ } else {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[numZoomLevels - 1 - i] = minResolution * Math.pow(base, i)\n+ }\n+ }\n+ return resolutions\n+ },\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom)\n+ },\n+ getExtent: function() {\n+ return this.map.calculateBounds()\n+ },\n+ getZoomForExtent: function(extent, closest) {\n+ var viewSize = this.map.getSize();\n+ var idealResolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);\n+ return this.getZoomForResolution(idealResolution, closest)\n+ },\n+ getDataExtent: function() {},\n+ getResolutionForZoom: function(zoom) {\n+ zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n+ var resolution;\n+ if (this.map.fractionalZoom) {\n+ var low = Math.floor(zoom);\n+ var high = Math.ceil(zoom);\n+ resolution = this.resolutions[low] - (zoom - low) * (this.resolutions[low] - this.resolutions[high])\n+ } else {\n+ resolution = this.resolutions[Math.round(zoom)]\n+ }\n+ return resolution\n+ },\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom, i, len;\n+ if (this.map.fractionalZoom) {\n+ var lowZoom = 0;\n+ var highZoom = this.resolutions.length - 1;\n+ var highRes = this.resolutions[lowZoom];\n+ var lowRes = this.resolutions[highZoom];\n+ var res;\n+ for (i = 0, len = this.resolutions.length; i < len; ++i) {\n+ res = this.resolutions[i];\n+ if (res >= resolution) {\n+ highRes = res;\n+ lowZoom = i\n+ }\n+ if (res <= resolution) {\n+ lowRes = res;\n+ highZoom = i;\n+ break\n+ }\n+ }\n+ var dRes = highRes - lowRes;\n+ if (dRes > 0) {\n+ zoom = lowZoom + (highRes - resolution) / dRes\n+ } else {\n+ zoom = lowZoom\n+ }\n+ } else {\n+ var diff;\n+ var minDiff = Number.POSITIVE_INFINITY;\n+ for (i = 0, len = this.resolutions.length; i < len; i++) {\n+ if (closest) {\n+ diff = Math.abs(this.resolutions[i] - resolution);\n+ if (diff > minDiff) {\n+ break\n+ }\n+ minDiff = diff\n+ } else {\n+ if (this.resolutions[i] < resolution) {\n+ break\n+ }\n }\n }\n+ zoom = Math.max(0, i - 1)\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- })\n+ return zoom\n },\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution)\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ var map = this.map;\n+ if (viewPortPx != null && map.minPx) {\n+ var res = map.getResolution();\n+ var maxExtent = map.getMaxExtent({\n+ restricted: true\n+ });\n+ var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n+ var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n+ lonlat = new OpenLayers.LonLat(lon, lat);\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent)\n+ }\n+ }\n+ return lonlat\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options)\n+ getViewPortPxFromLonLat: function(lonlat, resolution) {\n+ var px = null;\n+ if (lonlat != null) {\n+ resolution = resolution || this.map.getResolution();\n+ var extent = this.map.calculateBounds(null, resolution);\n+ px = new OpenLayers.Pixel(1 / resolution * (lonlat.lon - extent.left), 1 / resolution * (extent.top - lonlat.lat))\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj\n+ return px\n },\n- destroy: function() {\n- this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n+ setOpacity: function(opacity) {\n+ if (opacity != this.opacity) {\n+ this.opacity = opacity;\n+ var childNodes = this.div.childNodes;\n+ for (var i = 0, len = childNodes.length; i < len; ++i) {\n+ var element = childNodes[i].firstChild || childNodes[i];\n+ var lastChild = childNodes[i].lastChild;\n+ if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n+ element = lastChild.parentNode\n+ }\n+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity)\n+ }\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ })\n+ }\n+ }\n },\n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+ getZIndex: function() {\n+ return this.div.style.zIndex\n+ },\n+ setZIndex: function(zIndex) {\n+ this.div.style.zIndex = zIndex\n+ },\n+ adjustBounds: function(bounds) {\n+ if (this.gutter) {\n+ var mapGutter = this.gutter * this.map.getResolution();\n+ bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter)\n+ }\n+ if (this.wrapDateLine) {\n+ var wrappingOptions = {\n+ rightTolerance: this.getResolution(),\n+ leftTolerance: this.getResolution()\n+ };\n+ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions)\n+ }\n+ return bounds\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer\"\n });\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined;\n- delete this._callbackId\n-};\n OpenLayers.Renderer = OpenLayers.Class({\n container: null,\n root: null,\n extent: null,\n locked: false,\n size: null,\n resolution: null,\n@@ -6597,1000 +9409,360 @@\n }\n }\n }\n return maxExtent\n },\n CLASS_NAME: \"OpenLayers.Layer.Vector\"\n });\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n- },\n- isBaseLayer: true,\n- encodeBBOX: false,\n- noMagic: false,\n- yx: {},\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\"\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n- if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n- if (options == null || !options.isBaseLayer) {\n- this.isBaseLayer = false\n- }\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ displayInLayerSwitcher: false,\n+ layers: null,\n+ display: function() {},\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature\n }\n }\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString\n- },\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n- },\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n- var value = projectionCode == \"none\" ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value\n- } else {\n- this.params.SRS = value\n- }\n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n- }\n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n-});\n-OpenLayers.Layer.SphericalMercator = {\n- getExtent: function() {\n- var extent = null;\n- if (this.sphericalMercator) {\n- extent = this.map.calculateBounds()\n- } else {\n- extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)\n- }\n- return extent\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments)\n- },\n- getViewPortPxFromLonLat: function(lonlat) {\n- return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments)\n- },\n- initMercatorParameters: function() {\n- this.RESOLUTIONS = [];\n- var maxResolution = 156543.03390625;\n- for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n- this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom)\n- }\n- this.units = \"m\";\n- this.projection = this.projection || \"EPSG:900913\"\n- },\n- forwardMercator: function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(lon, lat) {\n- var point = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- }, gg, sm);\n- return new OpenLayers.LonLat(point.x, point.y)\n- }\n- }(),\n- inverseMercator: function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(x, y) {\n- var point = OpenLayers.Projection.transform({\n- x: x,\n- y: y\n- }, sm, gg);\n- return new OpenLayers.LonLat(point.x, point.y)\n- }\n- }()\n-};\n-OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n- smoothDragPan: true,\n- isBaseLayer: true,\n- isFixed: true,\n- pane: null,\n- mapObject: null,\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (this.pane == null) {\n- this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\")\n- }\n- },\n- destroy: function() {\n- this.mapObject = null;\n- this.pane = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n- },\n setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n- this.pane.style.display = this.div.style.display;\n- this.pane.style.width = \"100%\";\n- this.pane.style.height = \"100%\";\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.pane.style.background = \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\"\n- }\n- if (this.isFixed) {\n- this.map.viewPortDiv.appendChild(this.pane)\n- } else {\n- this.map.layerContainerDiv.appendChild(this.pane)\n- }\n- this.loadMapObject();\n- if (this.mapObject == null) {\n- this.loadWarningMessage()\n- }\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer)\n },\n removeMap: function(map) {\n- if (this.pane && this.pane.parentNode) {\n- this.pane.parentNode.removeChild(this.pane)\n- }\n- OpenLayers.Layer.prototype.removeMap.apply(this, arguments)\n- },\n- loadWarningMessage: function() {\n- this.div.style.backgroundColor = \"darkblue\";\n- var viewSize = this.map.getSize();\n- var msgW = Math.min(viewSize.w, 300);\n- var msgH = Math.min(viewSize.h, 200);\n- var size = new OpenLayers.Size(msgW, msgH);\n- var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n- var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n- var div = OpenLayers.Util.createDiv(this.name + \"_warning\", topLeft, size, null, null, null, \"auto\");\n- div.style.padding = \"7px\";\n- div.style.backgroundColor = \"yellow\";\n- div.innerHTML = this.getWarningHTML();\n- this.div.appendChild(div)\n- },\n- getWarningHTML: function() {\n- return \"\"\n- },\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- this.pane.style.display = this.div.style.display\n- },\n- setZIndex: function(zIndex) {\n- OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1\n- },\n- moveByPx: function(dx, dy) {\n- OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n- if (this.dragPanMapObject) {\n- this.dragPanMapObject(dx, -dy)\n- } else {\n- this.moveTo(this.map.getCachedCenter())\n- }\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- if (this.mapObject != null) {\n- var newCenter = this.map.getCenter();\n- var newZoom = this.map.getZoom();\n- if (newCenter != null) {\n- var moOldCenter = this.getMapObjectCenter();\n- var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n- var moOldZoom = this.getMapObjectZoom();\n- var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n- if (!newCenter.equals(oldCenter) || newZoom != oldZoom) {\n- if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) {\n- var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n- var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n- this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y)\n- } else {\n- var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n- var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n- this.setMapObjectCenter(center, zoom, dragging)\n- }\n- }\n+ collectRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer)\n }\n }\n },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if (this.mapObject != null && this.getMapObjectCenter() != null) {\n- var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n- var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n- lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat)\n- }\n- return lonlat\n- },\n- getViewPortPxFromLonLat: function(lonlat) {\n- var viewPortPx = null;\n- if (this.mapObject != null && this.getMapObjectCenter() != null) {\n- var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n- var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n- viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel)\n- }\n- return viewPortPx\n- },\n- getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n- var olLonLat = null;\n- if (moLonLat != null) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- olLonLat = new OpenLayers.LonLat(lon, lat)\n- }\n- return olLonLat\n- },\n- getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n- var moLatLng = null;\n- if (olLonLat != null) {\n- moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat)\n- }\n- return moLatLng\n- },\n- getOLPixelFromMapObjectPixel: function(moPixel) {\n- var olPixel = null;\n- if (moPixel != null) {\n- var x = this.getXFromMapObjectPixel(moPixel);\n- var y = this.getYFromMapObjectPixel(moPixel);\n- olPixel = new OpenLayers.Pixel(x, y)\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer)\n+ }\n }\n- return olPixel\n },\n- getMapObjectPixelFromOLPixel: function(olPixel) {\n- var moPixel = null;\n- if (olPixel != null) {\n- moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y)\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots()\n }\n- return moPixel\n },\n- CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n });\n-OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n- initialize: function() {},\n- initResolutions: function() {\n- var props = [\"minZoomLevel\", \"maxZoomLevel\", \"numZoomLevels\"];\n- for (var i = 0, len = props.length; i < len; i++) {\n- var property = props[i];\n- this[property] = this.options[property] != null ? this.options[property] : this.map[property]\n- }\n- if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) {\n- this.minZoomLevel = this.MIN_ZOOM_LEVEL\n- }\n- var desiredZoomLevels;\n- var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n- if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) {\n- desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1\n- } else {\n- desiredZoomLevels = this.numZoomLevels\n- }\n- if (desiredZoomLevels != null) {\n- this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels)\n- } else {\n- this.numZoomLevels = limitZoomLevels\n- }\n- this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n- if (this.RESOLUTIONS != null) {\n- var resolutionsIndex = 0;\n- this.resolutions = [];\n- for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n- this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]\n- }\n- this.maxResolution = this.resolutions[0];\n- this.minResolution = this.resolutions[this.resolutions.length - 1]\n+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n+ multipleKey: null,\n+ toggleKey: null,\n+ multiple: false,\n+ clickout: true,\n+ toggle: false,\n+ hover: false,\n+ highlightOnly: false,\n+ box: false,\n+ onBeforeSelect: function() {},\n+ onSelect: function() {},\n+ onUnselect: function() {},\n+ scope: null,\n+ geometryTypes: null,\n+ layer: null,\n+ layers: null,\n+ callbacks: null,\n+ selectStyle: null,\n+ renderIntent: \"select\",\n+ handlers: null,\n+ initialize: function(layers, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (this.scope === null) {\n+ this.scope = this\n }\n- },\n- getResolution: function() {\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getResolution.apply(this, arguments)\n- } else {\n- var resolution = null;\n- var viewSize = this.map.getSize();\n- var extent = this.getExtent();\n- if (viewSize != null && extent != null) {\n- resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h)\n- }\n- return resolution\n+ this.initLayer(layers);\n+ var callbacks = {\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature\n+ };\n+ if (this.hover) {\n+ callbacks.over = this.overFeature;\n+ callbacks.out = this.outFeature\n }\n- },\n- getExtent: function() {\n- var size = this.map.getSize();\n- var tl = this.getLonLatFromViewPortPx({\n- x: 0,\n- y: 0\n- });\n- var br = this.getLonLatFromViewPortPx({\n- x: size.w,\n- y: size.h\n- });\n- if (tl != null && br != null) {\n- return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat)\n- } else {\n- return null\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlers = {\n+ feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, {\n+ geometryTypes: this.geometryTypes\n+ })\n+ };\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(this, {\n+ done: this.selectBox\n+ }, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ })\n }\n },\n- getZoomForResolution: function(resolution) {\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments)\n+ initLayer: function(layers) {\n+ if (OpenLayers.Util.isArray(layers)) {\n+ this.layers = layers;\n+ this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + \"_container\", {\n+ layers: layers\n+ })\n } else {\n- var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n- return this.getZoomForExtent(extent)\n- }\n- },\n- getOLZoomFromMapObjectZoom: function(moZoom) {\n- var zoom = null;\n- if (moZoom != null) {\n- zoom = moZoom - this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom))\n- }\n- }\n- return zoom\n- },\n- getMapObjectZoomFromOLZoom: function(olZoom) {\n- var zoom = null;\n- if (olZoom != null) {\n- zoom = olZoom + this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom))\n- }\n+ this.layer = layers\n }\n- return zoom\n },\n- CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n-});\n-OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {\n- MIN_ZOOM_LEVEL: 0,\n- MAX_ZOOM_LEVEL: 21,\n- RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7],\n- type: null,\n- wrapDateLine: true,\n- sphericalMercator: false,\n- version: null,\n- initialize: function(name, options) {\n- options = options || {};\n- if (!options.version) {\n- options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\"\n- }\n- var mixin = OpenLayers.Layer.Google[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (mixin) {\n- OpenLayers.Util.applyDefaults(options, mixin)\n- } else {\n- throw \"Unsupported Google Maps API version: \" + options.version\n- }\n- OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n- if (options.maxExtent) {\n- options.maxExtent = options.maxExtent.clone()\n- }\n- OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]);\n- OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]);\n- if (this.sphericalMercator) {\n- OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n- this.initMercatorParameters()\n+ destroy: function() {\n+ if (this.active && this.layers) {\n+ this.map.removeLayer(this.layer)\n }\n- },\n- clone: function() {\n- return new OpenLayers.Layer.Google(this.name, this.getOptions())\n- },\n- setVisibility: function(visible) {\n- var opacity = this.opacity == null ? 1 : this.opacity;\n- OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n- this.setOpacity(opacity)\n- },\n- display: function(visible) {\n- if (!this._dragging) {\n- this.setGMapVisibility(visible)\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.layers) {\n+ this.layer.destroy()\n }\n- OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments)\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- this._dragging = dragging;\n- OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n- delete this._dragging\n- },\n- setOpacity: function(opacity) {\n- if (opacity !== this.opacity) {\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- })\n+ activate: function() {\n+ if (!this.active) {\n+ if (this.layers) {\n+ this.map.addLayer(this.layer)\n }\n- this.opacity = opacity\n- }\n- if (this.getVisibility()) {\n- var container = this.getMapContainer();\n- OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity)\n- }\n- },\n- destroy: function() {\n- if (this.map) {\n- this.setGMapVisibility(false);\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache && cache.count <= 1) {\n- this.removeGMapElements()\n+ this.handlers.feature.activate();\n+ if (this.box && this.handlers.box) {\n+ this.handlers.box.activate()\n }\n }\n- OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments)\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n- removeGMapElements: function() {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject && this.getMapContainer();\n- if (container && container.parentNode) {\n- container.parentNode.removeChild(container)\n- }\n- var termsOfUse = cache.termsOfUse;\n- if (termsOfUse && termsOfUse.parentNode) {\n- termsOfUse.parentNode.removeChild(termsOfUse)\n- }\n- var poweredBy = cache.poweredBy;\n- if (poweredBy && poweredBy.parentNode) {\n- poweredBy.parentNode.removeChild(poweredBy)\n+ deactivate: function() {\n+ if (this.active) {\n+ this.handlers.feature.deactivate();\n+ if (this.handlers.box) {\n+ this.handlers.box.deactivate()\n }\n- if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) {\n- google.maps.event.clearListeners(this.mapObject, \"tilesloaded\")\n+ if (this.layers) {\n+ this.map.removeLayer(this.layer)\n }\n }\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- removeMap: function(map) {\n- if (this.visibility && this.mapObject) {\n- this.setGMapVisibility(false)\n- }\n- var cache = OpenLayers.Layer.Google.cache[map.id];\n- if (cache) {\n- if (cache.count <= 1) {\n- this.removeGMapElements();\n- delete OpenLayers.Layer.Google.cache[map.id]\n- } else {\n- --cache.count\n+ unselectAll: function(options) {\n+ var layers = this.layers || [this.layer],\n+ layer, feature, l, numExcept;\n+ for (l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ numExcept = 0;\n+ if (layer.selectedFeatures != null) {\n+ while (layer.selectedFeatures.length > numExcept) {\n+ feature = layer.selectedFeatures[numExcept];\n+ if (!options || options.except != feature) {\n+ this.unselect(feature)\n+ } else {\n+ ++numExcept\n+ }\n+ }\n }\n }\n- delete this.termsOfUse;\n- delete this.poweredBy;\n- delete this.mapObject;\n- delete this.dragObject;\n- OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments)\n },\n- getOLBoundsFromMapObjectBounds: function(moBounds) {\n- var olBounds = null;\n- if (moBounds != null) {\n- var sw = moBounds.getSouthWest();\n- var ne = moBounds.getNorthEast();\n- if (this.sphericalMercator) {\n- sw = this.forwardMercator(sw.lng(), sw.lat());\n- ne = this.forwardMercator(ne.lng(), ne.lat())\n+ clickFeature: function(feature) {\n+ if (!this.hover) {\n+ var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1;\n+ if (selected) {\n+ if (this.toggleSelect()) {\n+ this.unselect(feature)\n+ } else if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ })\n+ }\n } else {\n- sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n- ne = new OpenLayers.LonLat(ne.lng(), ne.lat())\n+ if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ })\n+ }\n+ this.select(feature)\n }\n- olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat)\n }\n- return olBounds\n- },\n- getWarningHTML: function() {\n- return OpenLayers.i18n(\"googleWarning\")\n- },\n- getMapObjectCenter: function() {\n- return this.mapObject.getCenter()\n- },\n- getMapObjectZoom: function() {\n- return this.mapObject.getZoom()\n },\n- getLongitudeFromMapObjectLonLat: function(moLonLat) {\n- return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng()\n- },\n- getLatitudeFromMapObjectLonLat: function(moLonLat) {\n- var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat();\n- return lat\n- },\n- getXFromMapObjectPixel: function(moPixel) {\n- return moPixel.x\n+ multipleSelect: function() {\n+ return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]\n },\n- getYFromMapObjectPixel: function(moPixel) {\n- return moPixel.y\n+ toggleSelect: function() {\n+ return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]\n },\n- CLASS_NAME: \"OpenLayers.Layer.Google\"\n-});\n-OpenLayers.Layer.Google.cache = {};\n-OpenLayers.Layer.Google.v2 = {\n- termsOfUse: null,\n- poweredBy: null,\n- dragObject: null,\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = G_NORMAL_MAP\n- }\n- var mapObject, termsOfUse, poweredBy;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- mapObject = cache.mapObject;\n- termsOfUse = cache.termsOfUse;\n- poweredBy = cache.poweredBy;\n- ++cache.count\n- } else {\n- var container = this.map.viewPortDiv;\n- var div = document.createElement(\"div\");\n- div.id = this.map.id + \"_GMap2Container\";\n- div.style.position = \"absolute\";\n- div.style.width = \"100%\";\n- div.style.height = \"100%\";\n- container.appendChild(div);\n- try {\n- mapObject = new GMap2(div);\n- termsOfUse = div.lastChild;\n- container.appendChild(termsOfUse);\n- termsOfUse.style.zIndex = \"1100\";\n- termsOfUse.style.right = \"\";\n- termsOfUse.style.bottom = \"\";\n- termsOfUse.className = \"olLayerGoogleCopyright\";\n- poweredBy = div.lastChild;\n- container.appendChild(poweredBy);\n- poweredBy.style.zIndex = \"1100\";\n- poweredBy.style.right = \"\";\n- poweredBy.style.bottom = \"\";\n- poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\"\n- } catch (e) {\n- throw e\n- }\n- OpenLayers.Layer.Google.cache[this.map.id] = {\n- mapObject: mapObject,\n- termsOfUse: termsOfUse,\n- poweredBy: poweredBy,\n- count: 1\n- }\n- }\n- this.mapObject = mapObject;\n- this.termsOfUse = termsOfUse;\n- this.poweredBy = poweredBy;\n- if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) {\n- this.mapObject.addMapType(this.type)\n- }\n- if (typeof mapObject.getDragObject == \"function\") {\n- this.dragObject = mapObject.getDragObject()\n- } else {\n- this.dragPanMapObject = null\n- }\n- if (this.isBaseLayer === false) {\n- this.setGMapVisibility(this.div.style.display !== \"none\")\n+ clickoutFeature: function(feature) {\n+ if (!this.hover && this.clickout) {\n+ this.unselectAll()\n }\n },\n- onMapResize: function() {\n- if (this.visibility && this.mapObject.isLoaded()) {\n- this.mapObject.checkResize()\n- } else {\n- if (!this._resized) {\n- var layer = this;\n- var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n- GEvent.removeListener(handle);\n- delete layer._resized;\n- layer.mapObject.checkResize();\n- layer.moveTo(layer.map.getCenter(), layer.map.getZoom())\n- })\n+ overFeature: function(feature) {\n+ var layer = feature.layer;\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ this.highlight(feature)\n+ } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature)\n }\n- this._resized = true\n }\n },\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject.getContainer();\n- if (visible === true) {\n- this.mapObject.setMapType(this.type);\n- container.style.display = \"\";\n- this.termsOfUse.style.left = \"\";\n- this.termsOfUse.style.display = \"\";\n- this.poweredBy.style.display = \"\";\n- cache.displayed = this.id\n- } else {\n- if (cache.displayed === this.id) {\n- delete cache.displayed\n- }\n- if (!cache.displayed) {\n- container.style.display = \"none\";\n- this.termsOfUse.style.display = \"none\";\n- this.termsOfUse.style.left = \"-9999px\";\n- this.poweredBy.style.display = \"none\"\n+ outFeature: function(feature) {\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ if (feature._lastHighlighter == this.id) {\n+ if (feature._prevHighlighter && feature._prevHighlighter != this.id) {\n+ delete feature._lastHighlighter;\n+ var control = this.map.getControl(feature._prevHighlighter);\n+ if (control) {\n+ control.highlight(feature)\n+ }\n+ } else {\n+ this.unhighlight(feature)\n+ }\n }\n+ } else {\n+ this.unselect(feature)\n }\n }\n },\n- getMapContainer: function() {\n- return this.mapObject.getContainer()\n- },\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon))\n+ highlight: function(feature) {\n+ var layer = feature.layer;\n+ var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ feature._prevHighlighter = feature._lastHighlighter;\n+ feature._lastHighlighter = this.id;\n+ var style = this.selectStyle || this.renderIntent;\n+ layer.drawFeature(feature, style);\n+ this.events.triggerEvent(\"featurehighlighted\", {\n+ feature: feature\n+ })\n }\n- return moBounds\n },\n- setMapObjectCenter: function(center, zoom) {\n- this.mapObject.setCenter(center, zoom)\n- },\n- dragPanMapObject: function(dX, dY) {\n- this.dragObject.moveBy(new GSize(-dX, dY))\n- },\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- return this.mapObject.fromContainerPixelToLatLng(moPixel)\n- },\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- return this.mapObject.fromLatLngToContainerPixel(moLonLat)\n- },\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds)\n- },\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new GLatLng(lonlat.lat, lonlat.lon)\n+ unhighlight: function(feature) {\n+ var layer = feature.layer;\n+ if (feature._prevHighlighter == undefined) {\n+ delete feature._lastHighlighter\n+ } else if (feature._prevHighlighter == this.id) {\n+ delete feature._prevHighlighter\n } else {\n- gLatLng = new GLatLng(lat, lon)\n+ feature._lastHighlighter = feature._prevHighlighter;\n+ delete feature._prevHighlighter\n }\n- return gLatLng\n- },\n- getMapObjectPixelFromXY: function(x, y) {\n- return new GPoint(x, y)\n- }\n-};\n-OpenLayers.Layer.Google.v3 = {\n- DEFAULTS: {\n- sphericalMercator: true,\n- projection: \"EPSG:900913\"\n+ layer.drawFeature(feature, feature.style || feature.layer.style || \"default\");\n+ this.events.triggerEvent(\"featureunhighlighted\", {\n+ feature: feature\n+ })\n },\n- animationEnabled: true,\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = google.maps.MapTypeId.ROADMAP\n- }\n- var mapObject;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- mapObject = cache.mapObject;\n- ++cache.count\n- } else {\n- var center = this.map.getCenter();\n- var container = document.createElement(\"div\");\n- container.className = \"olForeignContainer\";\n- container.style.width = \"100%\";\n- container.style.height = \"100%\";\n- mapObject = new google.maps.Map(container, {\n- center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n- zoom: this.map.getZoom() || 0,\n- mapTypeId: this.type,\n- disableDefaultUI: true,\n- keyboardShortcuts: false,\n- draggable: false,\n- disableDoubleClickZoom: true,\n- scrollwheel: false,\n- streetViewControl: false\n+ select: function(feature) {\n+ var cont = this.onBeforeSelect.call(this.scope, feature);\n+ var layer = feature.layer;\n+ if (cont !== false) {\n+ cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n });\n- var googleControl = document.createElement(\"div\");\n- googleControl.style.width = \"100%\";\n- googleControl.style.height = \"100%\";\n- mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n- cache = {\n- googleControl: googleControl,\n- mapObject: mapObject,\n- count: 1\n- };\n- OpenLayers.Layer.Google.cache[this.map.id] = cache\n- }\n- this.mapObject = mapObject;\n- this.setGMapVisibility(this.visibility)\n- },\n- onMapResize: function() {\n- if (this.visibility) {\n- google.maps.event.trigger(this.mapObject, \"resize\")\n- }\n- },\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- var map = this.map;\n- if (cache) {\n- var type = this.type;\n- var layers = map.layers;\n- var layer;\n- for (var i = layers.length - 1; i >= 0; --i) {\n- layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) {\n- type = layer.type;\n- visible = true;\n- break\n- }\n- }\n- var container = this.mapObject.getDiv();\n- if (visible === true) {\n- if (container.parentNode !== map.div) {\n- if (!cache.rendered) {\n- var me = this;\n- google.maps.event.addListenerOnce(this.mapObject, \"tilesloaded\", function() {\n- cache.rendered = true;\n- me.setGMapVisibility(me.getVisibility());\n- me.moveTo(me.map.getCenter())\n- })\n- } else {\n- map.div.appendChild(container);\n- cache.googleControl.appendChild(map.viewPortDiv);\n- google.maps.event.trigger(this.mapObject, \"resize\")\n- }\n+ if (cont !== false) {\n+ layer.selectedFeatures.push(feature);\n+ this.highlight(feature);\n+ if (!this.handlers.feature.lastFeature) {\n+ this.handlers.feature.lastFeature = layer.selectedFeatures[0]\n }\n- this.mapObject.setMapTypeId(type)\n- } else if (cache.googleControl.hasChildNodes()) {\n- map.div.appendChild(map.viewPortDiv);\n- map.div.removeChild(container)\n+ layer.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ });\n+ this.onSelect.call(this.scope, feature)\n }\n }\n },\n- getMapContainer: function() {\n- return this.mapObject.getDiv()\n- },\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon))\n- }\n- return moBounds\n- },\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- var size = this.map.getSize();\n- var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n- var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n- var res = this.map.getResolution();\n- var delta_x = moPixel.x - size.w / 2;\n- var delta_y = moPixel.y - size.h / 2;\n- var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res);\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent)\n- }\n- return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat)\n- },\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- var res = this.map.getResolution();\n- var extent = this.map.getExtent();\n- return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat))\n+ unselect: function(feature) {\n+ var layer = feature.layer;\n+ this.unhighlight(feature);\n+ OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n+ layer.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n+ });\n+ this.onUnselect.call(this.scope, feature)\n },\n- setMapObjectCenter: function(center, zoom) {\n- if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n- var mapContainer = this.getMapContainer();\n- google.maps.event.addListenerOnce(this.mapObject, \"idle\", function() {\n- mapContainer.style.visibility = \"\"\n+ selectBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n });\n- mapContainer.style.visibility = \"hidden\"\n- }\n- this.mapObject.setOptions({\n- center: center,\n- zoom: zoom\n- })\n- },\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds)\n- },\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon)\n- } else {\n- gLatLng = new google.maps.LatLng(lat, lon)\n- }\n- return gLatLng\n- },\n- getMapObjectPixelFromXY: function(x, y) {\n- return new google.maps.Point(x, y)\n- }\n-};\n-OpenLayers.Filter = OpenLayers.Class({\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {},\n- evaluate: function(context) {\n- return true\n- },\n- clone: function() {\n- return null\n- },\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this)\n- } else {\n- string = Object.prototype.toString.call(this)\n- }\n- return string\n- },\n- CLASS_NAME: \"OpenLayers.Filter\"\n-});\n-OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {\n- filters: null,\n- type: null,\n- initialize: function(options) {\n- this.filters = [];\n- OpenLayers.Filter.prototype.initialize.apply(this, [options])\n- },\n- destroy: function() {\n- this.filters = null;\n- OpenLayers.Filter.prototype.destroy.apply(this)\n- },\n- evaluate: function(context) {\n- var i, len;\n- switch (this.type) {\n- case OpenLayers.Filter.Logical.AND:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == false) {\n- return false\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);\n+ if (!this.multipleSelect()) {\n+ this.unselectAll()\n+ }\n+ var prevMultiple = this.multiple;\n+ this.multiple = true;\n+ var layers = this.layers || [this.layer];\n+ this.events.triggerEvent(\"boxselectionstart\", {\n+ layers: layers\n+ });\n+ var layer;\n+ for (var l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ for (var i = 0, len = layer.features.length; i < len; ++i) {\n+ var feature = layer.features[i];\n+ if (!feature.getVisibility()) {\n+ continue\n }\n- }\n- return true;\n- case OpenLayers.Filter.Logical.OR:\n- for (i = 0, len = this.filters.length; i < len; i++) {\n- if (this.filters[i].evaluate(context) == true) {\n- return true\n+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n+ if (bounds.toGeometry().intersects(feature.geometry)) {\n+ if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature)\n+ }\n+ }\n }\n }\n- return false;\n- case OpenLayers.Filter.Logical.NOT:\n- return !this.filters[0].evaluate(context)\n- }\n- return undefined\n- },\n- clone: function() {\n- var filters = [];\n- for (var i = 0, len = this.filters.length; i < len; ++i) {\n- filters.push(this.filters[i].clone())\n+ }\n+ this.multiple = prevMultiple;\n+ this.events.triggerEvent(\"boxselectionend\", {\n+ layers: layers\n+ })\n }\n- return new OpenLayers.Filter.Logical({\n- type: this.type,\n- filters: filters\n- })\n },\n- CLASS_NAME: \"OpenLayers.Filter.Logical\"\n-});\n-OpenLayers.Filter.Logical.AND = \"&&\";\n-OpenLayers.Filter.Logical.OR = \"||\";\n-OpenLayers.Filter.Logical.NOT = \"!\";\n-OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {\n- type: null,\n- property: null,\n- value: null,\n- matchCase: true,\n- lowerBoundary: null,\n- upperBoundary: null,\n- initialize: function(options) {\n- OpenLayers.Filter.prototype.initialize.apply(this, [options]);\n- if (this.type === OpenLayers.Filter.Comparison.LIKE && options.matchCase === undefined) {\n- this.matchCase = null\n+ setMap: function(map) {\n+ this.handlers.feature.setMap(map);\n+ if (this.box) {\n+ this.handlers.box.setMap(map)\n }\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments)\n },\n- evaluate: function(context) {\n- if (context instanceof OpenLayers.Feature.Vector) {\n- context = context.attributes\n- }\n- var result = false;\n- var got = context[this.property];\n- var exp;\n- switch (this.type) {\n- case OpenLayers.Filter.Comparison.EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase && typeof got == \"string\" && typeof exp == \"string\") {\n- result = got.toUpperCase() == exp.toUpperCase()\n- } else {\n- result = got == exp\n- }\n- break;\n- case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:\n- exp = this.value;\n- if (!this.matchCase && typeof got == \"string\" && typeof exp == \"string\") {\n- result = got.toUpperCase() != exp.toUpperCase()\n- } else {\n- result = got != exp\n- }\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN:\n- result = got < this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN:\n- result = got > this.value;\n- break;\n- case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:\n- result = got <= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:\n- result = got >= this.value;\n- break;\n- case OpenLayers.Filter.Comparison.BETWEEN:\n- result = got >= this.lowerBoundary && got <= this.upperBoundary;\n- break;\n- case OpenLayers.Filter.Comparison.LIKE:\n- var regexp = new RegExp(this.value, \"gi\");\n- result = regexp.test(got);\n- break;\n- case OpenLayers.Filter.Comparison.IS_NULL:\n- result = got === null;\n- break\n+ setLayer: function(layers) {\n+ var isActive = this.active;\n+ this.unselectAll();\n+ this.deactivate();\n+ if (this.layers) {\n+ this.layer.destroy();\n+ this.layers = null\n }\n- return result\n- },\n- value2regex: function(wildCard, singleChar, escapeChar) {\n- if (wildCard == \".\") {\n- throw new Error(\"'.' is an unsupported wildCard character for \" + \"OpenLayers.Filter.Comparison\")\n+ this.initLayer(layers);\n+ this.handlers.feature.layer = this.layer;\n+ if (isActive) {\n+ this.activate()\n }\n- wildCard = wildCard ? wildCard : \"*\";\n- singleChar = singleChar ? singleChar : \".\";\n- escapeChar = escapeChar ? escapeChar : \"!\";\n- this.value = this.value.replace(new RegExp(\"\\\\\" + escapeChar + \"(.|$)\", \"g\"), \"\\\\$1\");\n- this.value = this.value.replace(new RegExp(\"\\\\\" + singleChar, \"g\"), \".\");\n- this.value = this.value.replace(new RegExp(\"\\\\\" + wildCard, \"g\"), \".*\");\n- this.value = this.value.replace(new RegExp(\"\\\\\\\\.\\\\*\", \"g\"), \"\\\\\" + wildCard);\n- this.value = this.value.replace(new RegExp(\"\\\\\\\\\\\\.\", \"g\"), \"\\\\\" + singleChar);\n- return this.value\n- },\n- regex2value: function() {\n- var value = this.value;\n- value = value.replace(/!/g, \"!!\");\n- value = value.replace(/(\\\\)?\\\\\\./g, function($0, $1) {\n- return $1 ? $0 : \"!.\"\n- });\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"!*\"\n- });\n- value = value.replace(/\\\\\\\\/g, \"\\\\\");\n- value = value.replace(/\\.\\*/g, \"*\");\n- return value\n- },\n- clone: function() {\n- return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison, this)\n },\n- CLASS_NAME: \"OpenLayers.Filter.Comparison\"\n+ CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n });\n-OpenLayers.Filter.Comparison.EQUAL_TO = \"==\";\n-OpenLayers.Filter.Comparison.NOT_EQUAL_TO = \"!=\";\n-OpenLayers.Filter.Comparison.LESS_THAN = \"<\";\n-OpenLayers.Filter.Comparison.GREATER_THAN = \">\";\n-OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = \"<=\";\n-OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = \">=\";\n-OpenLayers.Filter.Comparison.BETWEEN = \"..\";\n-OpenLayers.Filter.Comparison.LIKE = \"~\";\n-OpenLayers.Filter.Comparison.IS_NULL = \"NULL\";\n OpenLayers.Popup = OpenLayers.Class({\n events: null,\n id: \"\",\n lonlat: null,\n div: null,\n contentSize: null,\n size: null,\n@@ -8311,4491 +10483,2716 @@\n initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n this.imageSrc = OpenLayers.Util.getImageLocation(\"cloud-popup-relative.png\");\n OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n this.contentDiv.className = this.contentDisplayClass\n },\n CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n });\n-OpenLayers.Format = OpenLayers.Class({\n- options: null,\n- externalProjection: null,\n- internalProjection: null,\n- data: null,\n- keepData: false,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options\n- },\n- destroy: function() {},\n- read: function(data) {\n- throw new Error(\"Read not implemented.\")\n- },\n- write: function(object) {\n- throw new Error(\"Write not implemented.\")\n- },\n- CLASS_NAME: \"OpenLayers.Format\"\n-});\n-OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n- indent: \" \",\n- space: \" \",\n- newline: \"\\n\",\n- level: 0,\n- pretty: false,\n- nativeJSON: function() {\n- return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\")\n- }(),\n- read: function(json, filter) {\n- var object;\n- if (this.nativeJSON) {\n- object = JSON.parse(json, filter)\n- } else try {\n- if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, \"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, \"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g, \"\"))) {\n- object = eval(\"(\" + json + \")\");\n- if (typeof filter === \"function\") {\n- function walk(k, v) {\n- if (v && typeof v === \"object\") {\n- for (var i in v) {\n- if (v.hasOwnProperty(i)) {\n- v[i] = walk(i, v[i])\n- }\n- }\n- }\n- return filter(k, v)\n- }\n- object = walk(\"\", object)\n- }\n- }\n- } catch (e) {}\n- if (this.keepData) {\n- this.data = object\n- }\n- return object\n- },\n- write: function(value, pretty) {\n- this.pretty = !!pretty;\n- var json = null;\n- var type = typeof value;\n- if (this.serialize[type]) {\n- try {\n- json = !this.pretty && this.nativeJSON ? JSON.stringify(value) : this.serialize[type].apply(this, [value])\n- } catch (err) {\n- OpenLayers.Console.error(\"Trouble serializing: \" + err)\n- }\n- }\n- return json\n- },\n- writeIndent: function() {\n- var pieces = [];\n- if (this.pretty) {\n- for (var i = 0; i < this.level; ++i) {\n- pieces.push(this.indent)\n- }\n- }\n- return pieces.join(\"\")\n- },\n- writeNewline: function() {\n- return this.pretty ? this.newline : \"\"\n- },\n- writeSpace: function() {\n- return this.pretty ? this.space : \"\"\n- },\n- serialize: {\n- object: function(object) {\n- if (object == null) {\n- return \"null\"\n- }\n- if (object.constructor == Date) {\n- return this.serialize.date.apply(this, [object])\n- }\n- if (object.constructor == Array) {\n- return this.serialize.array.apply(this, [object])\n- }\n- var pieces = [\"{\"];\n- this.level += 1;\n- var key, keyJSON, valueJSON;\n- var addComma = false;\n- for (key in object) {\n- if (object.hasOwnProperty(key)) {\n- keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [key, this.pretty]);\n- valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [object[key], this.pretty]);\n- if (keyJSON != null && valueJSON != null) {\n- if (addComma) {\n- pieces.push(\",\")\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), keyJSON, \":\", this.writeSpace(), valueJSON);\n- addComma = true\n- }\n- }\n- }\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), \"}\");\n- return pieces.join(\"\")\n- },\n- array: function(array) {\n- var json;\n- var pieces = [\"[\"];\n- this.level += 1;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- json = OpenLayers.Format.JSON.prototype.write.apply(this, [array[i], this.pretty]);\n- if (json != null) {\n- if (i > 0) {\n- pieces.push(\",\")\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), json)\n- }\n- }\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), \"]\");\n- return pieces.join(\"\")\n- },\n- string: function(string) {\n- var m = {\n- \"\\b\": \"\\\\b\",\n- \"\\t\": \"\\\\t\",\n- \"\\n\": \"\\\\n\",\n- \"\\f\": \"\\\\f\",\n- \"\\r\": \"\\\\r\",\n- '\"': '\\\\\"',\n- \"\\\\\": \"\\\\\\\\\"\n- };\n- if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n- return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n- var c = m[b];\n- if (c) {\n- return c\n- }\n- c = b.charCodeAt();\n- return \"\\\\u00\" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)\n- }) + '\"'\n- }\n- return '\"' + string + '\"'\n- },\n- number: function(number) {\n- return isFinite(number) ? String(number) : \"null\"\n- },\n- boolean: function(bool) {\n- return String(bool)\n- },\n- date: function(date) {\n- function format(number) {\n- return number < 10 ? \"0\" + number : number\n- }\n- return '\"' + date.getFullYear() + \"-\" + format(date.getMonth() + 1) + \"-\" + format(date.getDate()) + \"T\" + format(date.getHours()) + \":\" + format(date.getMinutes()) + \":\" + format(date.getSeconds()) + '\"'\n+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n+ URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n+ url: null,\n+ params: null,\n+ reproject: false,\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n+ this.url = url;\n+ if (!this.params) {\n+ this.params = OpenLayers.Util.extend({}, params)\n }\n },\n- CLASS_NAME: \"OpenLayers.Format.JSON\"\n-});\n-OpenLayers.Geometry = OpenLayers.Class({\n- id: null,\n- parent: null,\n- bounds: null,\n- initialize: function() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n destroy: function() {\n- this.id = null;\n- this.bounds = null\n- },\n- clone: function() {\n- return new OpenLayers.Geometry\n- },\n- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone()\n- }\n- },\n- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds()\n- }\n- },\n- extendBounds: function(newBounds) {\n- var bounds = this.getBounds();\n- if (!bounds) {\n- this.setBounds(newBounds)\n- } else {\n- this.bounds.extend(newBounds)\n- }\n- },\n- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds()\n- }\n- return this.bounds\n- },\n- calculateBounds: function() {},\n- distanceTo: function(geometry, options) {},\n- getVertices: function(nodes) {},\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if (bounds != null && lonlat != null) {\n- var dX = toleranceLon != null ? toleranceLon : 0;\n- var dY = toleranceLat != null ? toleranceLat : 0;\n- var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY);\n- atPoint = toleranceBounds.containsLonLat(lonlat)\n- }\n- return atPoint\n- },\n- getLength: function() {\n- return 0\n- },\n- getArea: function() {\n- return 0\n- },\n- getCentroid: function() {\n- return null\n- },\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- string = OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this))\n- } else {\n- string = Object.prototype.toString.call(this)\n- }\n- return string\n- },\n- CLASS_NAME: \"OpenLayers.Geometry\"\n-});\n-OpenLayers.Geometry.fromWKT = function(wkt) {\n- var geom;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- var format = OpenLayers.Geometry.fromWKT.format;\n- if (!format) {\n- format = new OpenLayers.Format.WKT;\n- OpenLayers.Geometry.fromWKT.format = format\n- }\n- var result = format.read(wkt);\n- if (result instanceof OpenLayers.Feature.Vector) {\n- geom = result.geometry\n- } else if (OpenLayers.Util.isArray(result)) {\n- var len = result.length;\n- var components = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- components[i] = result[i].geometry\n- }\n- geom = new OpenLayers.Geometry.Collection(components)\n- }\n- }\n- return geom\n-};\n-OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n- var point = options && options.point;\n- var tolerance = options && options.tolerance;\n- var intersection = false;\n- var x11_21 = seg1.x1 - seg2.x1;\n- var y11_21 = seg1.y1 - seg2.y1;\n- var x12_11 = seg1.x2 - seg1.x1;\n- var y12_11 = seg1.y2 - seg1.y1;\n- var y22_21 = seg2.y2 - seg2.y1;\n- var x22_21 = seg2.x2 - seg2.x1;\n- var d = y22_21 * x12_11 - x22_21 * y12_11;\n- var n1 = x22_21 * y11_21 - y22_21 * x11_21;\n- var n2 = x12_11 * y11_21 - y12_11 * x11_21;\n- if (d == 0) {\n- if (n1 == 0 && n2 == 0) {\n- intersection = true\n- }\n- } else {\n- var along1 = n1 / d;\n- var along2 = n2 / d;\n- if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n- if (!point) {\n- intersection = true\n- } else {\n- var x = seg1.x1 + along1 * x12_11;\n- var y = seg1.y1 + along1 * y12_11;\n- intersection = new OpenLayers.Geometry.Point(x, y)\n- }\n- }\n- }\n- if (tolerance) {\n- var dist;\n- if (intersection) {\n- if (point) {\n- var segs = [seg1, seg2];\n- var seg, x, y;\n- outer: for (var i = 0; i < 2; ++i) {\n- seg = segs[i];\n- for (var j = 1; j < 3; ++j) {\n- x = seg[\"x\" + j];\n- y = seg[\"y\" + j];\n- dist = Math.sqrt(Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2));\n- if (dist < tolerance) {\n- intersection.x = x;\n- intersection.y = y;\n- break outer\n- }\n- }\n- }\n- }\n- } else {\n- var segs = [seg1, seg2];\n- var source, target, x, y, p, result;\n- outer: for (var i = 0; i < 2; ++i) {\n- source = segs[i];\n- target = segs[(i + 1) % 2];\n- for (var j = 1; j < 3; ++j) {\n- p = {\n- x: source[\"x\" + j],\n- y: source[\"y\" + j]\n- };\n- result = OpenLayers.Geometry.distanceToSegment(p, target);\n- if (result.distance < tolerance) {\n- if (point) {\n- intersection = new OpenLayers.Geometry.Point(p.x, p.y)\n- } else {\n- intersection = true\n- }\n- break outer\n- }\n- }\n- }\n- }\n- }\n- return intersection\n-};\n-OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n- var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n- result.distance = Math.sqrt(result.distance);\n- return result\n-};\n-OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n- var x0 = point.x;\n- var y0 = point.y;\n- var x1 = segment.x1;\n- var y1 = segment.y1;\n- var x2 = segment.x2;\n- var y2 = segment.y2;\n- var dx = x2 - x1;\n- var dy = y2 - y1;\n- var along = (dx * (x0 - x1) + dy * (y0 - y1)) / (Math.pow(dx, 2) + Math.pow(dy, 2));\n- var x, y;\n- if (along <= 0) {\n- x = x1;\n- y = y1\n- } else if (along >= 1) {\n- x = x2;\n- y = y2\n- } else {\n- x = x1 + along * dx;\n- y = y1 + along * dy\n- }\n- return {\n- distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n- x: x,\n- y: y,\n- along: along\n- }\n-};\n-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n- x: null,\n- y: null,\n- initialize: function(x, y) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n- this.x = parseFloat(x);\n- this.y = parseFloat(y)\n+ this.url = null;\n+ this.params = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n },\n clone: function(obj) {\n if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y)\n+ obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions())\n }\n- OpenLayers.Util.applyDefaults(obj, this);\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n return obj\n },\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x, this.y)\n- },\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var distance, x0, y0, x1, y1, result;\n- if (geometry instanceof OpenLayers.Geometry.Point) {\n- x0 = this.x;\n- y0 = this.y;\n- x1 = geometry.x;\n- y1 = geometry.y;\n- distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));\n- result = !details ? distance : {\n- x0: x0,\n- y0: y0,\n- x1: x1,\n- y1: y1,\n- distance: distance\n- }\n- } else {\n- result = geometry.distanceTo(this, options);\n- if (details) {\n- result = {\n- x0: result.x1,\n- y0: result.y1,\n- x1: result.x0,\n- y1: result.y0,\n- distance: result.distance\n- }\n- }\n- }\n- return result\n+ setUrl: function(newUrl) {\n+ this.url = newUrl\n },\n- equals: function(geom) {\n- var equals = false;\n- if (geom != null) {\n- equals = this.x == geom.x && this.y == geom.y || isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)\n+ mergeNewParams: function(newParams) {\n+ this.params = OpenLayers.Util.extend(this.params, newParams);\n+ var ret = this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"params\"\n+ })\n }\n- return equals\n- },\n- toShortString: function() {\n- return this.x + \", \" + this.y\n- },\n- move: function(x, y) {\n- this.x = this.x + x;\n- this.y = this.y + y;\n- this.clearBounds()\n- },\n- rotate: function(angle, origin) {\n- angle *= Math.PI / 180;\n- var radius = this.distanceTo(origin);\n- var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);\n- this.x = origin.x + radius * Math.cos(theta);\n- this.y = origin.y + radius * Math.sin(theta);\n- this.clearBounds()\n- },\n- getCentroid: function() {\n- return new OpenLayers.Geometry.Point(this.x, this.y)\n- },\n- resize: function(scale, origin, ratio) {\n- ratio = ratio == undefined ? 1 : ratio;\n- this.x = origin.x + scale * ratio * (this.x - origin.x);\n- this.y = origin.y + scale * (this.y - origin.y);\n- this.clearBounds();\n- return this\n+ return ret\n },\n- intersects: function(geometry) {\n- var intersect = false;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.equals(geometry)\n+ redraw: function(force) {\n+ if (force) {\n+ return this.mergeNewParams({\n+ _olSalt: Math.random()\n+ })\n } else {\n- intersect = geometry.intersects(this)\n- }\n- return intersect\n- },\n- transform: function(source, dest) {\n- if (source && dest) {\n- OpenLayers.Projection.transform(this, source, dest);\n- this.bounds = null\n- }\n- return this\n- },\n- getVertices: function(nodes) {\n- return [this]\n- },\n- CLASS_NAME: \"OpenLayers.Geometry.Point\"\n-});\n-OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {\n- components: null,\n- componentTypes: null,\n- initialize: function(components) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n- this.components = [];\n- if (components != null) {\n- this.addComponents(components)\n+ return OpenLayers.Layer.prototype.redraw.apply(this, [])\n }\n },\n- destroy: function() {\n- this.components.length = 0;\n- this.components = null;\n- OpenLayers.Geometry.prototype.destroy.apply(this, arguments)\n- },\n- clone: function() {\n- var geometry = eval(\"new \" + this.CLASS_NAME + \"()\");\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- geometry.addComponent(this.components[i].clone())\n+ selectUrl: function(paramString, urls) {\n+ var product = 1;\n+ for (var i = 0, len = paramString.length; i < len; i++) {\n+ product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n+ product -= Math.floor(product)\n }\n- OpenLayers.Util.applyDefaults(geometry, this);\n- return geometry\n+ return urls[Math.floor(product * urls.length)]\n },\n- getComponentsString: function() {\n- var strings = [];\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- strings.push(this.components[i].toShortString())\n+ getFullRequestString: function(newParams, altUrl) {\n+ var url = altUrl || this.url;\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url)\n }\n- return strings.join(\",\")\n- },\n- calculateBounds: function() {\n- this.bounds = null;\n- var bounds = new OpenLayers.Bounds;\n- var components = this.components;\n- if (components) {\n- for (var i = 0, len = components.length; i < len; i++) {\n- bounds.extend(components[i].getBounds())\n+ var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key]\n }\n }\n- if (bounds.left != null && bounds.bottom != null && bounds.right != null && bounds.top != null) {\n- this.setBounds(bounds)\n- }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n+ return OpenLayers.Util.urlAppend(url, paramsString)\n },\n- addComponents: function(components) {\n- if (!OpenLayers.Util.isArray(components)) {\n- components = [components]\n- }\n- for (var i = 0, len = components.length; i < len; i++) {\n- this.addComponent(components[i])\n+ CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n+});\n+OpenLayers.Tile = OpenLayers.Class({\n+ events: null,\n+ eventListeners: null,\n+ id: null,\n+ layer: null,\n+ url: null,\n+ bounds: null,\n+ size: null,\n+ position: null,\n+ isLoading: false,\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ this.layer = layer;\n+ this.position = position.clone();\n+ this.setBounds(bounds);\n+ this.url = url;\n+ if (size) {\n+ this.size = size.clone()\n }\n- },\n- addComponent: function(component, index) {\n- var added = false;\n- if (component) {\n- if (this.componentTypes == null || OpenLayers.Util.indexOf(this.componentTypes, component.CLASS_NAME) > -1) {\n- if (index != null && index < this.components.length) {\n- var components1 = this.components.slice(0, index);\n- var components2 = this.components.slice(index, this.components.length);\n- components1.push(component);\n- this.components = components1.concat(components2)\n- } else {\n- this.components.push(component)\n- }\n- component.parent = this;\n- this.clearBounds();\n- added = true\n- }\n+ this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n }\n- return added\n },\n- removeComponents: function(components) {\n- var removed = false;\n- if (!OpenLayers.Util.isArray(components)) {\n- components = [components]\n- }\n- for (var i = components.length - 1; i >= 0; --i) {\n- removed = this.removeComponent(components[i]) || removed\n+ unload: function() {\n+ if (this.isLoading) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"unload\")\n }\n- return removed\n- },\n- removeComponent: function(component) {\n- OpenLayers.Util.removeItem(this.components, component);\n- this.clearBounds();\n- return true\n },\n- getLength: function() {\n- var length = 0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- length += this.components[i].getLength()\n+ destroy: function() {\n+ this.layer = null;\n+ this.bounds = null;\n+ this.size = null;\n+ this.position = null;\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n }\n- return length\n+ this.events.destroy();\n+ this.eventListeners = null;\n+ this.events = null\n },\n- getArea: function() {\n- var area = 0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- area += this.components[i].getArea()\n+ draw: function(force) {\n+ if (!force) {\n+ this.clear()\n }\n- return area\n- },\n- getGeodesicArea: function(projection) {\n- var area = 0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- area += this.components[i].getGeodesicArea(projection)\n+ var draw = this.shouldDraw();\n+ if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n+ draw = null\n }\n- return area\n+ return draw\n },\n- getCentroid: function(weighted) {\n- if (!weighted) {\n- return this.components.length && this.components[0].getCentroid()\n- }\n- var len = this.components.length;\n- if (!len) {\n- return false\n- }\n- var areas = [];\n- var centroids = [];\n- var areaSum = 0;\n- var minArea = Number.MAX_VALUE;\n- var component;\n- for (var i = 0; i < len; ++i) {\n- component = this.components[i];\n- var area = component.getArea();\n- var centroid = component.getCentroid(true);\n- if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {\n- continue\n- }\n- areas.push(area);\n- areaSum += area;\n- minArea = area < minArea && area > 0 ? area : minArea;\n- centroids.push(centroid)\n- }\n- len = areas.length;\n- if (areaSum === 0) {\n- for (var i = 0; i < len; ++i) {\n- areas[i] = 1\n- }\n- areaSum = areas.length\n- } else {\n- for (var i = 0; i < len; ++i) {\n- areas[i] /= minArea\n+ shouldDraw: function() {\n+ var withinMaxExtent = false,\n+ maxExtent = this.layer.maxExtent;\n+ if (maxExtent) {\n+ var map = this.layer.map;\n+ var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n+ if (this.bounds.intersectsBounds(maxExtent, {\n+ inclusive: false,\n+ worldBounds: worldBounds\n+ })) {\n+ withinMaxExtent = true\n }\n- areaSum /= minArea\n- }\n- var xSum = 0,\n- ySum = 0,\n- centroid, area;\n- for (var i = 0; i < len; ++i) {\n- centroid = centroids[i];\n- area = areas[i];\n- xSum += centroid.x * area;\n- ySum += centroid.y * area\n- }\n- return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum)\n- },\n- getGeodesicLength: function(projection) {\n- var length = 0;\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- length += this.components[i].getGeodesicLength(projection)\n- }\n- return length\n- },\n- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- this.components[i].move(x, y)\n- }\n- },\n- rotate: function(angle, origin) {\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- this.components[i].rotate(angle, origin)\n }\n+ return withinMaxExtent || this.layer.displayOutsideMaxExtent\n },\n- resize: function(scale, origin, ratio) {\n- for (var i = 0; i < this.components.length; ++i) {\n- this.components[i].resize(scale, origin, ratio)\n- }\n- return this\n- },\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var result, best, distance;\n- var min = Number.POSITIVE_INFINITY;\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- result = this.components[i].distanceTo(geometry, options);\n- distance = details ? result.distance : result;\n- if (distance < min) {\n- min = distance;\n- best = result;\n- if (min == 0) {\n- break\n- }\n- }\n+ setBounds: function(bounds) {\n+ bounds = bounds.clone();\n+ if (this.layer.map.baseLayer.wrapDateLine) {\n+ var worldExtent = this.layer.map.getMaxExtent(),\n+ tolerance = this.layer.map.getResolution();\n+ bounds = bounds.wrapDateLine(worldExtent, {\n+ leftTolerance: tolerance,\n+ rightTolerance: tolerance\n+ })\n }\n- return best\n+ this.bounds = bounds\n },\n- equals: function(geometry) {\n- var equivalent = true;\n- if (!geometry || !geometry.CLASS_NAME || this.CLASS_NAME != geometry.CLASS_NAME) {\n- equivalent = false\n- } else if (!OpenLayers.Util.isArray(geometry.components) || geometry.components.length != this.components.length) {\n- equivalent = false\n- } else {\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- if (!this.components[i].equals(geometry.components[i])) {\n- equivalent = false;\n- break\n- }\n- }\n+ moveTo: function(bounds, position, redraw) {\n+ if (redraw == null) {\n+ redraw = true\n }\n- return equivalent\n- },\n- transform: function(source, dest) {\n- if (source && dest) {\n- for (var i = 0, len = this.components.length; i < len; i++) {\n- var component = this.components[i];\n- component.transform(source, dest)\n- }\n- this.bounds = null\n+ this.setBounds(bounds);\n+ this.position = position.clone();\n+ if (redraw) {\n+ this.draw()\n }\n- return this\n },\n- intersects: function(geometry) {\n- var intersect = false;\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- intersect = geometry.intersects(this.components[i]);\n- if (intersect) {\n- break\n- }\n+ clear: function(draw) {},\n+ CLASS_NAME: \"OpenLayers.Tile\"\n+});\n+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n+ url: null,\n+ imgDiv: null,\n+ frame: null,\n+ imageReloadAttempts: null,\n+ layerAlphaHack: null,\n+ asyncRequestId: null,\n+ maxGetUrlLength: null,\n+ canvasContext: null,\n+ crossOriginKeyword: null,\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n+ this.url = url;\n+ this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n+ if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n+ this.frame = document.createElement(\"div\");\n+ this.frame.style.position = \"absolute\";\n+ this.frame.style.overflow = \"hidden\"\n }\n- return intersect\n- },\n- getVertices: function(nodes) {\n- var vertices = [];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- Array.prototype.push.apply(vertices, this.components[i].getVertices(nodes))\n+ if (this.maxGetUrlLength != null) {\n+ OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame)\n }\n- return vertices\n },\n- CLASS_NAME: \"OpenLayers.Geometry.Collection\"\n-});\n-OpenLayers.Geometry.MultiPoint = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n- addPoint: function(point, index) {\n- this.addComponent(point, index)\n- },\n- removePoint: function(point) {\n- this.removeComponent(point)\n- },\n- CLASS_NAME: \"OpenLayers.Geometry.MultiPoint\"\n-});\n-OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n- getLength: function() {\n- var length = 0;\n- if (this.components && this.components.length > 1) {\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- length += this.components[i - 1].distanceTo(this.components[i])\n- }\n+ destroy: function() {\n+ if (this.imgDiv) {\n+ this.clear();\n+ this.imgDiv = null;\n+ this.frame = null\n }\n- return length\n+ this.asyncRequestId = null;\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n },\n- getGeodesicLength: function(projection) {\n- var geom = this;\n- if (projection) {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- if (!gg.equals(projection)) {\n- geom = this.clone().transform(projection, gg)\n+ draw: function() {\n+ var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (shouldDraw) {\n+ if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n+ this.bounds = this.getBoundsFromBaseLayer(this.position)\n }\n- }\n- var length = 0;\n- if (geom.components && geom.components.length > 1) {\n- var p1, p2;\n- for (var i = 1, len = geom.components.length; i < len; i++) {\n- p1 = geom.components[i - 1];\n- p2 = geom.components[i];\n- length += OpenLayers.Util.distVincenty({\n- lon: p1.x,\n- lat: p1.y\n- }, {\n- lon: p2.x,\n- lat: p2.y\n- })\n+ if (this.isLoading) {\n+ this._loadEvent = \"reload\"\n+ } else {\n+ this.isLoading = true;\n+ this._loadEvent = \"loadstart\"\n }\n+ this.renderTile();\n+ this.positionTile()\n+ } else if (shouldDraw === false) {\n+ this.unload()\n }\n- return length * 1e3\n- },\n- CLASS_NAME: \"OpenLayers.Geometry.Curve\"\n-});\n-OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {\n- removeComponent: function(point) {\n- var removed = this.components && this.components.length > 2;\n- if (removed) {\n- OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments)\n- }\n- return removed\n+ return shouldDraw\n },\n- intersects: function(geometry) {\n- var intersect = false;\n- var type = geometry.CLASS_NAME;\n- if (type == \"OpenLayers.Geometry.LineString\" || type == \"OpenLayers.Geometry.LinearRing\" || type == \"OpenLayers.Geometry.Point\") {\n- var segs1 = this.getSortedSegments();\n- var segs2;\n- if (type == \"OpenLayers.Geometry.Point\") {\n- segs2 = [{\n- x1: geometry.x,\n- y1: geometry.y,\n- x2: geometry.x,\n- y2: geometry.y\n- }]\n- } else {\n- segs2 = geometry.getSortedSegments()\n- }\n- var seg1, seg1x1, seg1x2, seg1y1, seg1y2, seg2, seg2y1, seg2y2;\n- outer: for (var i = 0, len = segs1.length; i < len; ++i) {\n- seg1 = segs1[i];\n- seg1x1 = seg1.x1;\n- seg1x2 = seg1.x2;\n- seg1y1 = seg1.y1;\n- seg1y2 = seg1.y2;\n- inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {\n- seg2 = segs2[j];\n- if (seg2.x1 > seg1x2) {\n- break\n- }\n- if (seg2.x2 < seg1x1) {\n- continue\n- }\n- seg2y1 = seg2.y1;\n- seg2y2 = seg2.y2;\n- if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {\n- continue\n- }\n- if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {\n- continue\n- }\n- if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {\n- intersect = true;\n- break outer\n- }\n+ renderTile: function() {\n+ if (this.layer.async) {\n+ var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n+ this.layer.getURLasync(this.bounds, function(url) {\n+ if (id == this.asyncRequestId) {\n+ this.url = url;\n+ this.initImage()\n }\n- }\n+ }, this)\n } else {\n- intersect = geometry.intersects(this)\n+ this.url = this.layer.getURL(this.bounds);\n+ this.initImage()\n }\n- return intersect\n },\n- getSortedSegments: function() {\n- var numSeg = this.components.length - 1;\n- var segments = new Array(numSeg),\n- point1, point2;\n- for (var i = 0; i < numSeg; ++i) {\n- point1 = this.components[i];\n- point2 = this.components[i + 1];\n- if (point1.x < point2.x) {\n- segments[i] = {\n- x1: point1.x,\n- y1: point1.y,\n- x2: point2.x,\n- y2: point2.y\n- }\n- } else {\n- segments[i] = {\n- x1: point2.x,\n- y1: point2.y,\n- x2: point1.x,\n- y2: point1.y\n- }\n- }\n- }\n-\n- function byX1(seg1, seg2) {\n- return seg1.x1 - seg2.x1\n+ positionTile: function() {\n+ var style = this.getTile().style,\n+ size = this.frame ? this.size : this.layer.getImageSize(this.bounds),\n+ ratio = 1;\n+ if (this.layer instanceof OpenLayers.Layer.Grid) {\n+ ratio = this.layer.getServerResolution() / this.layer.map.getResolution()\n }\n- return segments.sort(byX1)\n+ style.left = this.position.x + \"px\";\n+ style.top = this.position.y + \"px\";\n+ style.width = Math.round(ratio * size.w) + \"px\";\n+ style.height = Math.round(ratio * size.h) + \"px\"\n },\n- splitWithSegment: function(seg, options) {\n- var edge = !(options && options.edge === false);\n- var tolerance = options && options.tolerance;\n- var lines = [];\n- var verts = this.getVertices();\n- var points = [];\n- var intersections = [];\n- var split = false;\n- var vert1, vert2, point;\n- var node, vertex, target;\n- var interOptions = {\n- point: true,\n- tolerance: tolerance\n- };\n- var result = null;\n- for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n- vert1 = verts[i];\n- points.push(vert1.clone());\n- vert2 = verts[i + 1];\n- target = {\n- x1: vert1.x,\n- y1: vert1.y,\n- x2: vert2.x,\n- y2: vert2.y\n- };\n- point = OpenLayers.Geometry.segmentsIntersect(seg, target, interOptions);\n- if (point instanceof OpenLayers.Geometry.Point) {\n- if (point.x === seg.x1 && point.y === seg.y1 || point.x === seg.x2 && point.y === seg.y2 || point.equals(vert1) || point.equals(vert2)) {\n- vertex = true\n- } else {\n- vertex = false\n- }\n- if (vertex || edge) {\n- if (!point.equals(intersections[intersections.length - 1])) {\n- intersections.push(point.clone())\n- }\n- if (i === 0) {\n- if (point.equals(vert1)) {\n- continue\n- }\n- }\n- if (point.equals(vert2)) {\n- continue\n- }\n- split = true;\n- if (!point.equals(vert1)) {\n- points.push(point)\n- }\n- lines.push(new OpenLayers.Geometry.LineString(points));\n- points = [point.clone()]\n- }\n+ clear: function() {\n+ OpenLayers.Tile.prototype.clear.apply(this, arguments);\n+ var img = this.imgDiv;\n+ if (img) {\n+ var tile = this.getTile();\n+ if (tile.parentNode === this.layer.div) {\n+ this.layer.div.removeChild(tile)\n }\n- }\n- if (split) {\n- points.push(vert2.clone());\n- lines.push(new OpenLayers.Geometry.LineString(points))\n- }\n- if (intersections.length > 0) {\n- var xDir = seg.x1 < seg.x2 ? 1 : -1;\n- var yDir = seg.y1 < seg.y2 ? 1 : -1;\n- result = {\n- lines: lines,\n- points: intersections.sort(function(p1, p2) {\n- return xDir * p1.x - xDir * p2.x || yDir * p1.y - yDir * p2.y\n- })\n+ this.setImgSrc();\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"\"\n }\n+ OpenLayers.Element.removeClass(img, \"olImageLoadError\")\n }\n- return result\n+ this.canvasContext = null\n },\n- split: function(target, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var sourceSplit, targetSplit, sourceParts, targetParts;\n- if (target instanceof OpenLayers.Geometry.LineString) {\n- var verts = this.getVertices();\n- var vert1, vert2, seg, splits, lines, point;\n- var points = [];\n- sourceParts = [];\n- for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {\n- vert1 = verts[i];\n- vert2 = verts[i + 1];\n- seg = {\n- x1: vert1.x,\n- y1: vert1.y,\n- x2: vert2.x,\n- y2: vert2.y\n- };\n- targetParts = targetParts || [target];\n- if (mutual) {\n- points.push(vert1.clone())\n- }\n- for (var j = 0; j < targetParts.length; ++j) {\n- splits = targetParts[j].splitWithSegment(seg, options);\n- if (splits) {\n- lines = splits.lines;\n- if (lines.length > 0) {\n- lines.unshift(j, 1);\n- Array.prototype.splice.apply(targetParts, lines);\n- j += lines.length - 2\n- }\n- if (mutual) {\n- for (var k = 0, len = splits.points.length; k < len; ++k) {\n- point = splits.points[k];\n- if (!point.equals(vert1)) {\n- points.push(point);\n- sourceParts.push(new OpenLayers.Geometry.LineString(points));\n- if (point.equals(vert2)) {\n- points = []\n- } else {\n- points = [point.clone()]\n- }\n- }\n- }\n- }\n- }\n+ getImage: function() {\n+ if (!this.imgDiv) {\n+ this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n+ var style = this.imgDiv.style;\n+ if (this.frame) {\n+ var left = 0,\n+ top = 0;\n+ if (this.layer.gutter) {\n+ left = this.layer.gutter / this.layer.tileSize.w * 100;\n+ top = this.layer.gutter / this.layer.tileSize.h * 100\n }\n+ style.left = -left + \"%\";\n+ style.top = -top + \"%\";\n+ style.width = 2 * left + 100 + \"%\";\n+ style.height = 2 * top + 100 + \"%\"\n }\n- if (mutual && sourceParts.length > 0 && points.length > 0) {\n- points.push(vert2.clone());\n- sourceParts.push(new OpenLayers.Geometry.LineString(points))\n+ style.visibility = \"hidden\";\n+ style.opacity = 0;\n+ if (this.layer.opacity < 1) {\n+ style.filter = \"alpha(opacity=\" + this.layer.opacity * 100 + \")\"\n }\n- } else {\n- results = target.splitWith(this, options)\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true\n- } else {\n- targetParts = []\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true\n- } else {\n- sourceParts = []\n- }\n- if (targetSplit || sourceSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts]\n- } else {\n- results = targetParts\n+ style.position = \"absolute\";\n+ if (this.layerAlphaHack) {\n+ style.paddingTop = style.height;\n+ style.height = \"0\";\n+ style.width = \"100%\"\n+ }\n+ if (this.frame) {\n+ this.frame.appendChild(this.imgDiv)\n }\n }\n- return results\n+ return this.imgDiv\n },\n- splitWith: function(geometry, options) {\n- return geometry.split(this, options)\n+ setImage: function(img) {\n+ this.imgDiv = img\n },\n- getVertices: function(nodes) {\n- var vertices;\n- if (nodes === true) {\n- vertices = [this.components[0], this.components[this.components.length - 1]]\n- } else if (nodes === false) {\n- vertices = this.components.slice(1, this.components.length - 1)\n+ initImage: function() {\n+ if (!this.url && !this.imgDiv) {\n+ this.isLoading = false;\n+ return\n+ }\n+ this.events.triggerEvent(\"beforeload\");\n+ this.layer.div.appendChild(this.getTile());\n+ this.events.triggerEvent(this._loadEvent);\n+ var img = this.getImage();\n+ var src = img.getAttribute(\"src\") || \"\";\n+ if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n+ this._loadTimeout = window.setTimeout(OpenLayers.Function.bind(this.onImageLoad, this), 0)\n } else {\n- vertices = this.components.slice()\n+ this.stopLoading();\n+ if (this.crossOriginKeyword) {\n+ img.removeAttribute(\"crossorigin\")\n+ }\n+ OpenLayers.Event.observe(img, \"load\", OpenLayers.Function.bind(this.onImageLoad, this));\n+ OpenLayers.Event.observe(img, \"error\", OpenLayers.Function.bind(this.onImageError, this));\n+ this.imageReloadAttempts = 0;\n+ this.setImgSrc(this.url)\n }\n- return vertices\n },\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var details = edge && options && options.details;\n- var result, best = {};\n- var min = Number.POSITIVE_INFINITY;\n- if (geometry instanceof OpenLayers.Geometry.Point) {\n- var segs = this.getSortedSegments();\n- var x = geometry.x;\n- var y = geometry.y;\n- var seg;\n- for (var i = 0, len = segs.length; i < len; ++i) {\n- seg = segs[i];\n- result = OpenLayers.Geometry.distanceToSegment(geometry, seg);\n- if (result.distance < min) {\n- min = result.distance;\n- best = result;\n- if (min === 0) {\n- break\n- }\n+ setImgSrc: function(url) {\n+ var img = this.imgDiv;\n+ if (url) {\n+ img.style.visibility = \"hidden\";\n+ img.style.opacity = 0;\n+ if (this.crossOriginKeyword) {\n+ if (url.substr(0, 5) !== \"data:\") {\n+ img.setAttribute(\"crossorigin\", this.crossOriginKeyword)\n } else {\n- if (seg.x2 > x && (y > seg.y1 && y < seg.y2 || y < seg.y1 && y > seg.y2)) {\n- break\n- }\n- }\n- }\n- if (details) {\n- best = {\n- distance: best.distance,\n- x0: best.x,\n- y0: best.y,\n- x1: x,\n- y1: y\n- }\n- } else {\n- best = best.distance\n- }\n- } else if (geometry instanceof OpenLayers.Geometry.LineString) {\n- var segs0 = this.getSortedSegments();\n- var segs1 = geometry.getSortedSegments();\n- var seg0, seg1, intersection, x0, y0;\n- var len1 = segs1.length;\n- var interOptions = {\n- point: true\n- };\n- outer: for (var i = 0, len = segs0.length; i < len; ++i) {\n- seg0 = segs0[i];\n- x0 = seg0.x1;\n- y0 = seg0.y1;\n- for (var j = 0; j < len1; ++j) {\n- seg1 = segs1[j];\n- intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);\n- if (intersection) {\n- min = 0;\n- best = {\n- distance: 0,\n- x0: intersection.x,\n- y0: intersection.y,\n- x1: intersection.x,\n- y1: intersection.y\n- };\n- break outer\n- } else {\n- result = OpenLayers.Geometry.distanceToSegment({\n- x: x0,\n- y: y0\n- }, seg1);\n- if (result.distance < min) {\n- min = result.distance;\n- best = {\n- distance: min,\n- x0: x0,\n- y0: y0,\n- x1: result.x,\n- y1: result.y\n- }\n- }\n- }\n- }\n- }\n- if (!details) {\n- best = best.distance\n- }\n- if (min !== 0) {\n- if (seg0) {\n- result = geometry.distanceTo(new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), options);\n- var dist = details ? result.distance : result;\n- if (dist < min) {\n- if (details) {\n- best = {\n- distance: min,\n- x0: result.x1,\n- y0: result.y1,\n- x1: result.x0,\n- y1: result.y0\n- }\n- } else {\n- best = dist\n- }\n- }\n+ img.removeAttribute(\"crossorigin\")\n }\n }\n+ img.src = url\n } else {\n- best = geometry.distanceTo(this, options);\n- if (details) {\n- best = {\n- distance: best.distance,\n- x0: best.x1,\n- y0: best.y1,\n- x1: best.x0,\n- y1: best.y0\n- }\n+ this.stopLoading();\n+ this.imgDiv = null;\n+ if (img.parentNode) {\n+ img.parentNode.removeChild(img)\n }\n }\n- return best\n },\n- simplify: function(tolerance) {\n- if (this && this !== null) {\n- var points = this.getVertices();\n- if (points.length < 3) {\n- return this\n- }\n- var compareNumbers = function(a, b) {\n- return a - b\n- };\n- var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) {\n- var maxDistance = 0;\n- var indexFarthest = 0;\n- for (var index = firstPoint, distance; index < lastPoint; index++) {\n- distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);\n- if (distance > maxDistance) {\n- maxDistance = distance;\n- indexFarthest = index\n- }\n- }\n- if (maxDistance > tolerance && indexFarthest != firstPoint) {\n- pointIndexsToKeep.push(indexFarthest);\n- douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);\n- douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance)\n- }\n- };\n- var perpendicularDistance = function(point1, point2, point) {\n- var area = Math.abs(.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));\n- var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));\n- var height = area / bottom * 2;\n- return height\n- };\n- var firstPoint = 0;\n- var lastPoint = points.length - 1;\n- var pointIndexsToKeep = [];\n- pointIndexsToKeep.push(firstPoint);\n- pointIndexsToKeep.push(lastPoint);\n- while (points[firstPoint].equals(points[lastPoint])) {\n- lastPoint--;\n- pointIndexsToKeep.push(lastPoint)\n- }\n- douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);\n- var returnPoints = [];\n- pointIndexsToKeep.sort(compareNumbers);\n- for (var index = 0; index < pointIndexsToKeep.length; index++) {\n- returnPoints.push(points[pointIndexsToKeep[index]])\n- }\n- return new OpenLayers.Geometry.LineString(returnPoints)\n- } else {\n- return this\n- }\n+ getTile: function() {\n+ return this.frame ? this.frame : this.getImage()\n },\n- CLASS_NAME: \"OpenLayers.Geometry.LineString\"\n-});\n-OpenLayers.Geometry.MultiLineString = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n- componentTypes: [\"OpenLayers.Geometry.LineString\"],\n- split: function(geometry, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var splits, sourceLine, sourceLines, sourceSplit, targetSplit;\n- var sourceParts = [];\n- var targetParts = [geometry];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- sourceLine = this.components[i];\n- sourceSplit = false;\n- for (var j = 0; j < targetParts.length; ++j) {\n- splits = sourceLine.split(targetParts[j], options);\n- if (splits) {\n- if (mutual) {\n- sourceLines = splits[0];\n- for (var k = 0, klen = sourceLines.length; k < klen; ++k) {\n- if (k === 0 && sourceParts.length) {\n- sourceParts[sourceParts.length - 1].addComponent(sourceLines[k])\n- } else {\n- sourceParts.push(new OpenLayers.Geometry.MultiLineString([sourceLines[k]]))\n- }\n- }\n- sourceSplit = true;\n- splits = splits[1]\n- }\n- if (splits.length) {\n- splits.unshift(j, 1);\n- Array.prototype.splice.apply(targetParts, splits);\n- break\n- }\n- }\n- }\n- if (!sourceSplit) {\n- if (sourceParts.length) {\n- sourceParts[sourceParts.length - 1].addComponent(sourceLine.clone())\n- } else {\n- sourceParts = [new OpenLayers.Geometry.MultiLineString(sourceLine.clone())]\n- }\n- }\n+ createBackBuffer: function() {\n+ if (!this.imgDiv || this.isLoading) {\n+ return\n }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true\n+ var backBuffer;\n+ if (this.frame) {\n+ backBuffer = this.frame.cloneNode(false);\n+ backBuffer.appendChild(this.imgDiv)\n } else {\n- sourceParts = []\n+ backBuffer = this.imgDiv\n }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true\n- } else {\n- targetParts = []\n+ this.imgDiv = null;\n+ return backBuffer\n+ },\n+ onImageLoad: function() {\n+ var img = this.imgDiv;\n+ this.stopLoading();\n+ img.style.visibility = \"inherit\";\n+ img.style.opacity = this.layer.opacity;\n+ this.isLoading = false;\n+ this.canvasContext = null;\n+ this.events.triggerEvent(\"loadend\");\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"', sizingMethod='scale')\"\n }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts]\n+ },\n+ onImageError: function() {\n+ var img = this.imgDiv;\n+ if (img.src != null) {\n+ this.imageReloadAttempts++;\n+ if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n+ this.setImgSrc(this.layer.getURL(this.bounds))\n } else {\n- results = targetParts\n+ OpenLayers.Element.addClass(img, \"olImageLoadError\");\n+ this.events.triggerEvent(\"loaderror\");\n+ this.onImageLoad()\n }\n }\n- return results\n },\n- splitWith: function(geometry, options) {\n- var results = null;\n- var mutual = options && options.mutual;\n- var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;\n- if (geometry instanceof OpenLayers.Geometry.LineString) {\n- targetParts = [];\n- sourceParts = [geometry];\n- for (var i = 0, len = this.components.length; i < len; ++i) {\n- targetSplit = false;\n- targetLine = this.components[i];\n- for (var j = 0; j < sourceParts.length; ++j) {\n- splits = sourceParts[j].split(targetLine, options);\n- if (splits) {\n- if (mutual) {\n- sourceLines = splits[0];\n- if (sourceLines.length) {\n- sourceLines.unshift(j, 1);\n- Array.prototype.splice.apply(sourceParts, sourceLines);\n- j += sourceLines.length - 2\n- }\n- splits = splits[1];\n- if (splits.length === 0) {\n- splits = [targetLine.clone()]\n- }\n- }\n- for (var k = 0, klen = splits.length; k < klen; ++k) {\n- if (k === 0 && targetParts.length) {\n- targetParts[targetParts.length - 1].addComponent(splits[k])\n- } else {\n- targetParts.push(new OpenLayers.Geometry.MultiLineString([splits[k]]))\n- }\n- }\n- targetSplit = true\n- }\n- }\n- if (!targetSplit) {\n- if (targetParts.length) {\n- targetParts[targetParts.length - 1].addComponent(targetLine.clone())\n- } else {\n- targetParts = [new OpenLayers.Geometry.MultiLineString([targetLine.clone()])]\n- }\n- }\n- }\n- } else {\n- results = geometry.split(this)\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- sourceSplit = true\n- } else {\n- sourceParts = []\n- }\n- if (targetParts && targetParts.length > 1) {\n- targetSplit = true\n- } else {\n- targetParts = []\n- }\n- if (sourceSplit || targetSplit) {\n- if (mutual) {\n- results = [sourceParts, targetParts]\n- } else {\n- results = targetParts\n+ stopLoading: function() {\n+ OpenLayers.Event.stopObservingElement(this.imgDiv);\n+ window.clearTimeout(this._loadTimeout);\n+ delete this._loadTimeout\n+ },\n+ getCanvasContext: function() {\n+ if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n+ if (!this.canvasContext) {\n+ var canvas = document.createElement(\"canvas\");\n+ canvas.width = this.size.w;\n+ canvas.height = this.size.h;\n+ this.canvasContext = canvas.getContext(\"2d\");\n+ this.canvasContext.drawImage(this.imgDiv, 0, 0)\n }\n+ return this.canvasContext\n }\n- return results\n },\n- CLASS_NAME: \"OpenLayers.Geometry.MultiLineString\"\n+ CLASS_NAME: \"OpenLayers.Tile.Image\"\n });\n-OpenLayers.Geometry.LinearRing = OpenLayers.Class(OpenLayers.Geometry.LineString, {\n- componentTypes: [\"OpenLayers.Geometry.Point\"],\n- addComponent: function(point, index) {\n- var added = false;\n- var lastPoint = this.components.pop();\n- if (index != null || !point.equals(lastPoint)) {\n- added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, arguments)\n- }\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint]);\n- return added\n+OpenLayers.Tile.Image.IMAGE = function() {\n+ var img = new Image;\n+ img.className = \"olTileImage\";\n+ img.galleryImg = \"no\";\n+ return img\n+}();\n+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n+ tileSize: null,\n+ tileOriginCorner: \"bl\",\n+ tileOrigin: null,\n+ tileOptions: null,\n+ tileClass: OpenLayers.Tile.Image,\n+ grid: null,\n+ singleTile: false,\n+ ratio: 1.5,\n+ buffer: 0,\n+ transitionEffect: \"resize\",\n+ numLoadingTiles: 0,\n+ serverResolutions: null,\n+ loading: false,\n+ backBuffer: null,\n+ gridResolution: null,\n+ backBufferResolution: null,\n+ backBufferLonLat: null,\n+ backBufferTimerId: null,\n+ removeBackBufferDelay: null,\n+ className: null,\n+ gridLayout: null,\n+ rowSign: null,\n+ transitionendEvents: [\"transitionend\", \"webkitTransitionEnd\", \"otransitionend\", \"oTransitionEnd\"],\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments);\n+ this.grid = [];\n+ this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n+ this.initProperties();\n+ this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1\n },\n- removeComponent: function(point) {\n- var removed = this.components && this.components.length > 3;\n- if (removed) {\n- this.components.pop();\n- OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments);\n- var firstPoint = this.components[0];\n- OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint])\n+ initProperties: function() {\n+ if (this.options.removeBackBufferDelay === undefined) {\n+ this.removeBackBufferDelay = this.singleTile ? 0 : 2500\n }\n- return removed\n- },\n- move: function(x, y) {\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- this.components[i].move(x, y)\n+ if (this.options.className === undefined) {\n+ this.className = this.singleTile ? \"olLayerGridSingleTile\" : \"olLayerGrid\"\n }\n },\n- rotate: function(angle, origin) {\n- for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n- this.components[i].rotate(angle, origin)\n- }\n+ setMap: function(map) {\n+ OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n+ OpenLayers.Element.addClass(this.div, this.className)\n },\n- resize: function(scale, origin, ratio) {\n- for (var i = 0, len = this.components.length; i < len - 1; ++i) {\n- this.components[i].resize(scale, origin, ratio)\n- }\n- return this\n+ removeMap: function(map) {\n+ this.removeBackBuffer()\n },\n- transform: function(source, dest) {\n- if (source && dest) {\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- var component = this.components[i];\n- component.transform(source, dest)\n- }\n- this.bounds = null\n- }\n- return this\n+ destroy: function() {\n+ this.removeBackBuffer();\n+ this.clearGrid();\n+ this.grid = null;\n+ this.tileSize = null;\n+ OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments)\n },\n- getCentroid: function() {\n- if (this.components) {\n- var len = this.components.length;\n- if (len > 0 && len <= 2) {\n- return this.components[0].clone()\n- } else if (len > 2) {\n- var sumX = 0;\n- var sumY = 0;\n- var x0 = this.components[0].x;\n- var y0 = this.components[0].y;\n- var area = -1 * this.getArea();\n- if (area != 0) {\n- for (var i = 0; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));\n- sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0))\n- }\n- var x = x0 + sumX / (6 * area);\n- var y = y0 + sumY / (6 * area)\n- } else {\n- for (var i = 0; i < len - 1; i++) {\n- sumX += this.components[i].x;\n- sumY += this.components[i].y\n- }\n- var x = sumX / (len - 1);\n- var y = sumY / (len - 1)\n+ clearGrid: function() {\n+ if (this.grid) {\n+ for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n+ var row = this.grid[iRow];\n+ for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n+ var tile = row[iCol];\n+ this.destroyTile(tile)\n }\n- return new OpenLayers.Geometry.Point(x, y)\n- } else {\n- return null\n }\n+ this.grid = [];\n+ this.gridResolution = null;\n+ this.gridLayout = null\n }\n },\n- getArea: function() {\n- var area = 0;\n- if (this.components && this.components.length > 2) {\n- var sum = 0;\n- for (var i = 0, len = this.components.length; i < len - 1; i++) {\n- var b = this.components[i];\n- var c = this.components[i + 1];\n- sum += (b.x + c.x) * (c.y - b.y)\n- }\n- area = -sum / 2\n+ addOptions: function(newOptions, reinitialize) {\n+ var singleTileChanged = newOptions.singleTile !== undefined && newOptions.singleTile !== this.singleTile;\n+ OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n+ if (this.map && singleTileChanged) {\n+ this.initProperties();\n+ this.clearGrid();\n+ this.tileSize = this.options.tileSize;\n+ this.setTileSize();\n+ this.moveTo(null, true)\n }\n- return area\n },\n- getGeodesicArea: function(projection) {\n- var ring = this;\n- if (projection) {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- if (!gg.equals(projection)) {\n- ring = this.clone().transform(projection, gg)\n- }\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions())\n }\n- var area = 0;\n- var len = ring.components && ring.components.length;\n- if (len > 2) {\n- var p1, p2;\n- for (var i = 0; i < len - 1; i++) {\n- p1 = ring.components[i];\n- p2 = ring.components[i + 1];\n- area += OpenLayers.Util.rad(p2.x - p1.x) * (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + Math.sin(OpenLayers.Util.rad(p2.y)))\n- }\n- area = area * 6378137 * 6378137 / 2\n+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone()\n }\n- return area\n+ obj.grid = [];\n+ obj.gridResolution = null;\n+ obj.backBuffer = null;\n+ obj.backBufferTimerId = null;\n+ obj.loading = false;\n+ obj.numLoadingTiles = 0;\n+ return obj\n },\n- containsPoint: function(point) {\n- var approx = OpenLayers.Number.limitSigDigs;\n- var digs = 14;\n- var px = approx(point.x, digs);\n- var py = approx(point.y, digs);\n-\n- function getX(y, x1, y1, x2, y2) {\n- return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2\n- }\n- var numSeg = this.components.length - 1;\n- var start, end, x1, y1, x2, y2, cx, cy;\n- var crosses = 0;\n- for (var i = 0; i < numSeg; ++i) {\n- start = this.components[i];\n- x1 = approx(start.x, digs);\n- y1 = approx(start.y, digs);\n- end = this.components[i + 1];\n- x2 = approx(end.x, digs);\n- y2 = approx(end.y, digs);\n- if (y1 == y2) {\n- if (py == y1) {\n- if (x1 <= x2 && (px >= x1 && px <= x2) || x1 >= x2 && (px <= x1 && px >= x2)) {\n- crosses = -1;\n- break\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n+ bounds = bounds || this.map.getExtent();\n+ if (bounds != null) {\n+ var forceReTile = !this.grid.length || zoomChanged;\n+ var tilesBounds = this.getTilesBounds();\n+ var resolution = this.map.getResolution();\n+ var serverResolution = this.getServerResolution(resolution);\n+ if (this.singleTile) {\n+ if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) {\n+ if (zoomChanged && this.transitionEffect !== \"resize\") {\n+ this.removeBackBuffer()\n }\n+ if (!zoomChanged || this.transitionEffect === \"resize\") {\n+ this.applyBackBuffer(resolution)\n+ }\n+ this.initSingleTile(bounds)\n }\n- continue\n- }\n- cx = approx(getX(py, x1, y1, x2, y2), digs);\n- if (cx == px) {\n- if (y1 < y2 && (py >= y1 && py <= y2) || y1 > y2 && (py <= y1 && py >= y2)) {\n- crosses = -1;\n- break\n- }\n- }\n- if (cx <= px) {\n- continue\n- }\n- if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {\n- continue\n- }\n- if (y1 < y2 && (py >= y1 && py < y2) || y1 > y2 && (py < y1 && py >= y2)) {\n- ++crosses\n- }\n- }\n- var contained = crosses == -1 ? 1 : !!(crosses & 1);\n- return contained\n- },\n- intersects: function(geometry) {\n- var intersect = false;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry)\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n- intersect = geometry.intersects(this)\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(this, [geometry])\n- } else {\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = geometry.components[i].intersects(this);\n- if (intersect) {\n- break\n- }\n- }\n- }\n- return intersect\n- },\n- getVertices: function(nodes) {\n- return nodes === true ? [] : this.components.slice(0, this.components.length - 1)\n- },\n- CLASS_NAME: \"OpenLayers.Geometry.LinearRing\"\n-});\n-OpenLayers.Geometry.Polygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n- componentTypes: [\"OpenLayers.Geometry.LinearRing\"],\n- getArea: function() {\n- var area = 0;\n- if (this.components && this.components.length > 0) {\n- area += Math.abs(this.components[0].getArea());\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getArea())\n- }\n- }\n- return area\n- },\n- getGeodesicArea: function(projection) {\n- var area = 0;\n- if (this.components && this.components.length > 0) {\n- area += Math.abs(this.components[0].getGeodesicArea(projection));\n- for (var i = 1, len = this.components.length; i < len; i++) {\n- area -= Math.abs(this.components[i].getGeodesicArea(projection))\n- }\n- }\n- return area\n- },\n- containsPoint: function(point) {\n- var numRings = this.components.length;\n- var contained = false;\n- if (numRings > 0) {\n- contained = this.components[0].containsPoint(point);\n- if (contained !== 1) {\n- if (contained && numRings > 1) {\n- var hole;\n- for (var i = 1; i < numRings; ++i) {\n- hole = this.components[i].containsPoint(point);\n- if (hole) {\n- if (hole === 1) {\n- contained = 1\n- } else {\n- contained = false\n- }\n- break\n- }\n+ } else {\n+ forceReTile = forceReTile || !tilesBounds.intersectsBounds(bounds, {\n+ worldBounds: this.map.baseLayer.wrapDateLine && this.map.getMaxExtent()\n+ });\n+ if (forceReTile) {\n+ if (zoomChanged && (this.transitionEffect === \"resize\" || this.gridResolution === resolution)) {\n+ this.applyBackBuffer(resolution)\n }\n+ this.initGriddedTiles(bounds)\n+ } else {\n+ this.moveGriddedTiles()\n }\n }\n }\n- return contained\n },\n- intersects: function(geometry) {\n- var intersect = false;\n- var i, len;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- intersect = this.containsPoint(geometry)\n- } else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- for (i = 0, len = this.components.length; i < len; ++i) {\n- intersect = geometry.intersects(this.components[i]);\n- if (intersect) {\n- break\n+ getTileData: function(loc) {\n+ var data = null,\n+ x = loc.lon,\n+ y = loc.lat,\n+ numRows = this.grid.length;\n+ if (this.map && numRows) {\n+ var res = this.map.getResolution(),\n+ tileWidth = this.tileSize.w,\n+ tileHeight = this.tileSize.h,\n+ bounds = this.grid[0][0].bounds,\n+ left = bounds.left,\n+ top = bounds.top;\n+ if (x < left) {\n+ if (this.map.baseLayer.wrapDateLine) {\n+ var worldWidth = this.map.getMaxExtent().getWidth();\n+ var worldsAway = Math.ceil((left - x) / worldWidth);\n+ x += worldWidth * worldsAway\n }\n }\n- if (!intersect) {\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.containsPoint(geometry.components[i]);\n- if (intersect) {\n- break\n+ var dtx = (x - left) / (res * tileWidth);\n+ var dty = (top - y) / (res * tileHeight);\n+ var col = Math.floor(dtx);\n+ var row = Math.floor(dty);\n+ if (row >= 0 && row < numRows) {\n+ var tile = this.grid[row][col];\n+ if (tile) {\n+ data = {\n+ tile: tile,\n+ i: Math.floor((dtx - col) * tileWidth),\n+ j: Math.floor((dty - row) * tileHeight)\n }\n }\n }\n- } else {\n- for (i = 0, len = geometry.components.length; i < len; ++i) {\n- intersect = this.intersects(geometry.components[i]);\n- if (intersect) {\n- break\n- }\n- }\n- }\n- if (!intersect && geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n- var ring = this.components[0];\n- for (i = 0, len = ring.components.length; i < len; ++i) {\n- intersect = geometry.containsPoint(ring.components[i]);\n- if (intersect) {\n- break\n- }\n- }\n }\n- return intersect\n+ return data\n },\n- distanceTo: function(geometry, options) {\n- var edge = !(options && options.edge === false);\n- var result;\n- if (!edge && this.intersects(geometry)) {\n- result = 0\n- } else {\n- result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(this, [geometry, options])\n- }\n- return result\n+ destroyTile: function(tile) {\n+ this.removeTileMonitoringHooks(tile);\n+ tile.destroy()\n },\n- CLASS_NAME: \"OpenLayers.Geometry.Polygon\"\n-});\n-OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {\n- var angle = Math.PI * (1 / sides - 1 / 2);\n- if (rotation) {\n- angle += rotation / 180 * Math.PI\n- }\n- var rotatedAngle, x, y;\n- var points = [];\n- for (var i = 0; i < sides; ++i) {\n- rotatedAngle = angle + i * 2 * Math.PI / sides;\n- x = origin.x + radius * Math.cos(rotatedAngle);\n- y = origin.y + radius * Math.sin(rotatedAngle);\n- points.push(new OpenLayers.Geometry.Point(x, y))\n- }\n- var ring = new OpenLayers.Geometry.LinearRing(points);\n- return new OpenLayers.Geometry.Polygon([ring])\n-};\n-OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {\n- componentTypes: [\"OpenLayers.Geometry.Polygon\"],\n- CLASS_NAME: \"OpenLayers.Geometry.MultiPolygon\"\n-});\n-OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {\n- ignoreExtraDims: false,\n- read: function(json, type, filter) {\n- type = type ? type : \"FeatureCollection\";\n- var results = null;\n- var obj = null;\n- if (typeof json == \"string\") {\n- obj = OpenLayers.Format.JSON.prototype.read.apply(this, [json, filter])\n- } else {\n- obj = json\n- }\n- if (!obj) {\n- OpenLayers.Console.error(\"Bad JSON: \" + json)\n- } else if (typeof obj.type != \"string\") {\n- OpenLayers.Console.error(\"Bad GeoJSON - no type: \" + json)\n- } else if (this.isValidType(obj, type)) {\n- switch (type) {\n- case \"Geometry\":\n- try {\n- results = this.parseGeometry(obj)\n- } catch (err) {\n- OpenLayers.Console.error(err)\n- }\n- break;\n- case \"Feature\":\n- try {\n- results = this.parseFeature(obj);\n- results.type = \"Feature\"\n- } catch (err) {\n- OpenLayers.Console.error(err)\n- }\n- break;\n- case \"FeatureCollection\":\n- results = [];\n- switch (obj.type) {\n- case \"Feature\":\n- try {\n- results.push(this.parseFeature(obj))\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err)\n- }\n- break;\n- case \"FeatureCollection\":\n- for (var i = 0, len = obj.features.length; i < len; ++i) {\n- try {\n- results.push(this.parseFeature(obj.features[i]))\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err)\n- }\n- }\n- break;\n- default:\n- try {\n- var geom = this.parseGeometry(obj);\n- results.push(new OpenLayers.Feature.Vector(geom))\n- } catch (err) {\n- results = null;\n- OpenLayers.Console.error(err)\n- }\n- }\n+ getServerResolution: function(resolution) {\n+ var distance = Number.POSITIVE_INFINITY;\n+ resolution = resolution || this.map.getResolution();\n+ if (this.serverResolutions && OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n+ var i, newDistance, newResolution, serverResolution;\n+ for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n+ newResolution = this.serverResolutions[i];\n+ newDistance = Math.abs(newResolution - resolution);\n+ if (newDistance > distance) {\n break\n- }\n- }\n- return results\n- },\n- isValidType: function(obj, type) {\n- var valid = false;\n- switch (type) {\n- case \"Geometry\":\n- if (OpenLayers.Util.indexOf([\"Point\", \"MultiPoint\", \"LineString\", \"MultiLineString\", \"Polygon\", \"MultiPolygon\", \"Box\", \"GeometryCollection\"], obj.type) == -1) {\n- OpenLayers.Console.error(\"Unsupported geometry type: \" + obj.type)\n- } else {\n- valid = true\n- }\n- break;\n- case \"FeatureCollection\":\n- valid = true;\n- break;\n- default:\n- if (obj.type == type) {\n- valid = true\n- } else {\n- OpenLayers.Console.error(\"Cannot convert types from \" + obj.type + \" to \" + type)\n }\n+ distance = newDistance;\n+ serverResolution = newResolution\n+ }\n+ resolution = serverResolution\n }\n- return valid\n+ return resolution\n },\n- parseFeature: function(obj) {\n- var feature, geometry, attributes, bbox;\n- attributes = obj.properties ? obj.properties : {};\n- bbox = obj.geometry && obj.geometry.bbox || obj.bbox;\n- try {\n- geometry = this.parseGeometry(obj.geometry)\n- } catch (err) {\n- throw err\n- }\n- feature = new OpenLayers.Feature.Vector(geometry, attributes);\n- if (bbox) {\n- feature.bounds = OpenLayers.Bounds.fromArray(bbox)\n- }\n- if (obj.id) {\n- feature.fid = obj.id\n- }\n- return feature\n+ getServerZoom: function() {\n+ var resolution = this.getServerResolution();\n+ return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0)\n },\n- parseGeometry: function(obj) {\n- if (obj == null) {\n- return null\n- }\n- var geometry, collection = false;\n- if (obj.type == \"GeometryCollection\") {\n- if (!OpenLayers.Util.isArray(obj.geometries)) {\n- throw \"GeometryCollection must have geometries array: \" + obj\n- }\n- var numGeom = obj.geometries.length;\n- var components = new Array(numGeom);\n- for (var i = 0; i < numGeom; ++i) {\n- components[i] = this.parseGeometry.apply(this, [obj.geometries[i]])\n- }\n- geometry = new OpenLayers.Geometry.Collection(components);\n- collection = true\n- } else {\n- if (!OpenLayers.Util.isArray(obj.coordinates)) {\n- throw \"Geometry must have coordinates array: \" + obj\n- }\n- if (!this.parseCoords[obj.type.toLowerCase()]) {\n- throw \"Unsupported geometry type: \" + obj.type\n- }\n- try {\n- geometry = this.parseCoords[obj.type.toLowerCase()].apply(this, [obj.coordinates])\n- } catch (err) {\n- throw err\n- }\n- }\n- if (this.internalProjection && this.externalProjection && !collection) {\n- geometry.transform(this.externalProjection, this.internalProjection)\n+ applyBackBuffer: function(resolution) {\n+ if (this.backBufferTimerId !== null) {\n+ this.removeBackBuffer()\n }\n- return geometry\n- },\n- parseCoords: {\n- point: function(array) {\n- if (this.ignoreExtraDims == false && array.length != 2) {\n- throw \"Only 2D points are supported: \" + array\n- }\n- return new OpenLayers.Geometry.Point(array[0], array[1])\n- },\n- multipoint: function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]])\n- } catch (err) {\n- throw err\n- }\n- points.push(p)\n- }\n- return new OpenLayers.Geometry.MultiPoint(points)\n- },\n- linestring: function(array) {\n- var points = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"point\"].apply(this, [array[i]])\n- } catch (err) {\n- throw err\n- }\n- points.push(p)\n- }\n- return new OpenLayers.Geometry.LineString(points)\n- },\n- multilinestring: function(array) {\n- var lines = [];\n- var l = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]])\n- } catch (err) {\n- throw err\n- }\n- lines.push(l)\n- }\n- return new OpenLayers.Geometry.MultiLineString(lines)\n- },\n- polygon: function(array) {\n- var rings = [];\n- var r, l;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- l = this.parseCoords[\"linestring\"].apply(this, [array[i]])\n- } catch (err) {\n- throw err\n- }\n- r = new OpenLayers.Geometry.LinearRing(l.components);\n- rings.push(r)\n- }\n- return new OpenLayers.Geometry.Polygon(rings)\n- },\n- multipolygon: function(array) {\n- var polys = [];\n- var p = null;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- try {\n- p = this.parseCoords[\"polygon\"].apply(this, [array[i]])\n- } catch (err) {\n- throw err\n- }\n- polys.push(p)\n+ var backBuffer = this.backBuffer;\n+ if (!backBuffer) {\n+ backBuffer = this.createBackBuffer();\n+ if (!backBuffer) {\n+ return\n }\n- return new OpenLayers.Geometry.MultiPolygon(polys)\n- },\n- box: function(array) {\n- if (array.length != 2) {\n- throw \"GeoJSON box coordinates must have 2 elements\"\n+ if (resolution === this.gridResolution) {\n+ this.div.insertBefore(backBuffer, this.div.firstChild)\n+ } else {\n+ this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div)\n }\n- return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(array[0][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[0][1])])])\n+ this.backBuffer = backBuffer;\n+ var topLeftTileBounds = this.grid[0][0].bounds;\n+ this.backBufferLonLat = {\n+ lon: topLeftTileBounds.left,\n+ lat: topLeftTileBounds.top\n+ };\n+ this.backBufferResolution = this.gridResolution\n }\n- },\n- write: function(obj, pretty) {\n- var geojson = {\n- type: null\n- };\n- if (OpenLayers.Util.isArray(obj)) {\n- geojson.type = \"FeatureCollection\";\n- var numFeatures = obj.length;\n- geojson.features = new Array(numFeatures);\n- for (var i = 0; i < numFeatures; ++i) {\n- var element = obj[i];\n- if (!element instanceof OpenLayers.Feature.Vector) {\n- var msg = \"FeatureCollection only supports collections \" + \"of features: \" + element;\n- throw msg\n- }\n- geojson.features[i] = this.extract.feature.apply(this, [element])\n- }\n- } else if (obj.CLASS_NAME.indexOf(\"OpenLayers.Geometry\") == 0) {\n- geojson = this.extract.geometry.apply(this, [obj])\n- } else if (obj instanceof OpenLayers.Feature.Vector) {\n- geojson = this.extract.feature.apply(this, [obj]);\n- if (obj.layer && obj.layer.projection) {\n- geojson.crs = this.createCRSObject(obj)\n- }\n+ var ratio = this.backBufferResolution / resolution;\n+ var tiles = backBuffer.childNodes,\n+ tile;\n+ for (var i = tiles.length - 1; i >= 0; --i) {\n+ tile = tiles[i];\n+ tile.style.top = (ratio * tile._i * tile._h | 0) + \"px\";\n+ tile.style.left = (ratio * tile._j * tile._w | 0) + \"px\";\n+ tile.style.width = Math.round(ratio * tile._w) + \"px\";\n+ tile.style.height = Math.round(ratio * tile._h) + \"px\"\n }\n- return OpenLayers.Format.JSON.prototype.write.apply(this, [geojson, pretty])\n+ var position = this.getViewPortPxFromLonLat(this.backBufferLonLat, resolution);\n+ var leftOffset = this.map.layerContainerOriginPx.x;\n+ var topOffset = this.map.layerContainerOriginPx.y;\n+ backBuffer.style.left = Math.round(position.x - leftOffset) + \"px\";\n+ backBuffer.style.top = Math.round(position.y - topOffset) + \"px\"\n },\n- createCRSObject: function(object) {\n- var proj = object.layer.projection.toString();\n- var crs = {};\n- if (proj.match(/epsg:/i)) {\n- var code = parseInt(proj.substring(proj.indexOf(\":\") + 1));\n- if (code == 4326) {\n- crs = {\n- type: \"name\",\n- properties: {\n- name: \"urn:ogc:def:crs:OGC:1.3:CRS84\"\n- }\n- }\n- } else {\n- crs = {\n- type: \"name\",\n- properties: {\n- name: \"EPSG:\" + code\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.grid.length > 0) {\n+ backBuffer = document.createElement(\"div\");\n+ backBuffer.id = this.div.id + \"_bb\";\n+ backBuffer.className = \"olBackBuffer\";\n+ backBuffer.style.position = \"absolute\";\n+ var map = this.map;\n+ backBuffer.style.zIndex = this.transitionEffect === \"resize\" ? this.getZIndex() - 1 : map.Z_INDEX_BASE.BaseLayer - (map.getNumLayers() - map.getLayerIndex(this));\n+ for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n+ for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n+ var tile = this.grid[i][j],\n+ markup = this.grid[i][j].createBackBuffer();\n+ if (markup) {\n+ markup._i = i;\n+ markup._j = j;\n+ markup._w = tile.size.w;\n+ markup._h = tile.size.h;\n+ markup.id = tile.id + \"_bb\";\n+ backBuffer.appendChild(markup)\n }\n }\n }\n }\n- return crs\n+ return backBuffer\n },\n- extract: {\n- feature: function(feature) {\n- var geom = this.extract.geometry.apply(this, [feature.geometry]);\n- var json = {\n- type: \"Feature\",\n- properties: feature.attributes,\n- geometry: geom\n- };\n- if (feature.fid != null) {\n- json.id = feature.fid\n- }\n- return json\n- },\n- geometry: function(geometry) {\n- if (geometry == null) {\n- return null\n- }\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection, this.externalProjection)\n- }\n- var geometryType = geometry.CLASS_NAME.split(\".\")[2];\n- var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);\n- var json;\n- if (geometryType == \"Collection\") {\n- json = {\n- type: \"GeometryCollection\",\n- geometries: data\n- }\n- } else {\n- json = {\n- type: geometryType,\n- coordinates: data\n- }\n- }\n- return json\n- },\n- point: function(point) {\n- return [point.x, point.y]\n- },\n- multipoint: function(multipoint) {\n- var array = [];\n- for (var i = 0, len = multipoint.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [multipoint.components[i]]))\n- }\n- return array\n- },\n- linestring: function(linestring) {\n- var array = [];\n- for (var i = 0, len = linestring.components.length; i < len; ++i) {\n- array.push(this.extract.point.apply(this, [linestring.components[i]]))\n- }\n- return array\n- },\n- multilinestring: function(multilinestring) {\n- var array = [];\n- for (var i = 0, len = multilinestring.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]))\n- }\n- return array\n- },\n- polygon: function(polygon) {\n- var array = [];\n- for (var i = 0, len = polygon.components.length; i < len; ++i) {\n- array.push(this.extract.linestring.apply(this, [polygon.components[i]]))\n- }\n- return array\n- },\n- multipolygon: function(multipolygon) {\n- var array = [];\n- for (var i = 0, len = multipolygon.components.length; i < len; ++i) {\n- array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]))\n- }\n- return array\n- },\n- collection: function(collection) {\n- var len = collection.components.length;\n- var array = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- array[i] = this.extract.geometry.apply(this, [collection.components[i]])\n+ removeBackBuffer: function() {\n+ if (this._transitionElement) {\n+ for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.stopObserving(this._transitionElement, this.transitionendEvents[i], this._removeBackBuffer)\n }\n- return array\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Format.GeoJSON\"\n-});\n-OpenLayers.Strategy = OpenLayers.Class({\n- layer: null,\n- options: null,\n- active: null,\n- autoActivate: true,\n- autoDestroy: true,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- this.active = false\n- },\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null\n- },\n- setLayer: function(layer) {\n- this.layer = layer\n- },\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true\n- }\n- return false\n- },\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true\n+ delete this._transitionElement\n }\n- return false\n- },\n- CLASS_NAME: \"OpenLayers.Strategy\"\n-});\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n- preload: false,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- refresh: this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load()\n- } else {\n- this.layer.events.on({\n- visibilitychanged: this.load,\n- scope: this\n- })\n+ if (this.backBuffer) {\n+ if (this.backBuffer.parentNode) {\n+ this.backBuffer.parentNode.removeChild(this.backBuffer)\n }\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- refresh: this.load,\n- visibilitychanged: this.load,\n- scope: this\n- })\n- }\n- return deactivated\n- },\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- visibilitychanged: this.load,\n- scope: this\n- })\n- },\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local)\n- }\n- }\n+ this.backBuffer = null;\n+ this.backBufferResolution = null;\n+ if (this.backBufferTimerId !== null) {\n+ window.clearTimeout(this.backBufferTimerId);\n+ this.backBufferTimerId = null\n }\n- layer.addFeatures(features)\n- }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- })\n- },\n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n-});\n-OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n- type: null,\n- property: null,\n- value: null,\n- distance: null,\n- distanceUnits: null,\n- evaluate: function(feature) {\n- var intersect = false;\n- switch (this.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- case OpenLayers.Filter.Spatial.INTERSECTS:\n- if (feature.geometry) {\n- var geom = this.value;\n- if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n- geom = this.value.toGeometry()\n- }\n- if (feature.geometry.intersects(geom)) {\n- intersect = true\n- }\n- }\n- break;\n- default:\n- throw new Error(\"evaluate is not implemented for this filter type.\")\n- }\n- return intersect\n- },\n- clone: function() {\n- var options = OpenLayers.Util.applyDefaults({\n- value: this.value && this.value.clone && this.value.clone()\n- }, this);\n- return new OpenLayers.Filter.Spatial(options)\n- },\n- CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n-});\n-OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n-OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n-OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n-OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n-OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n-OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n- bounds: null,\n- resolution: null,\n- ratio: 2,\n- resFactor: null,\n- response: null,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- moveend: this.update,\n- refresh: this.update,\n- visibilitychanged: this.update,\n- scope: this\n- });\n- this.update()\n }\n- return activated\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- moveend: this.update,\n- refresh: this.update,\n- visibilitychanged: this.update,\n- scope: this\n- })\n+ moveByPx: function(dx, dy) {\n+ if (!this.singleTile) {\n+ this.moveGriddedTiles()\n }\n- return deactivated\n },\n- update: function(options) {\n- var mapBounds = this.getMapBounds();\n- if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) {\n- this.calculateBounds(mapBounds);\n- this.resolution = this.layer.map.getResolution();\n- this.triggerRead(options)\n+ setTileSize: function(size) {\n+ if (this.singleTile) {\n+ size = this.map.getSize();\n+ size.h = parseInt(size.h * this.ratio, 10);\n+ size.w = parseInt(size.w * this.ratio, 10)\n }\n+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size])\n },\n- getMapBounds: function() {\n- if (this.layer.map === null) {\n- return null\n- }\n- var bounds = this.layer.map.getExtent();\n- if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) {\n- bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection)\n+ getTilesBounds: function() {\n+ var bounds = null;\n+ var length = this.grid.length;\n+ if (length) {\n+ var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n+ width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n+ height = this.grid.length * bottomLeftTileBounds.getHeight();\n+ bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height)\n }\n return bounds\n },\n- invalidBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds()\n- }\n- var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n- if (!invalid && this.resFactor) {\n- var ratio = this.resolution / this.layer.map.getResolution();\n- invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor\n- }\n- return invalid\n- },\n- calculateBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds()\n- }\n- var center = mapBounds.getCenterLonLat();\n- var dataWidth = mapBounds.getWidth() * this.ratio;\n- var dataHeight = mapBounds.getHeight() * this.ratio;\n- this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2)\n- },\n- triggerRead: function(options) {\n- if (this.response && !(options && options.noAbort === true)) {\n- this.layer.protocol.abort(this.response);\n- this.layer.events.triggerEvent(\"loadend\")\n- }\n- var evt = {\n- filter: this.createFilter()\n- };\n- this.layer.events.triggerEvent(\"loadstart\", evt);\n- this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({\n- filter: evt.filter,\n- callback: this.merge,\n- scope: this\n- }, options))\n- },\n- createFilter: function() {\n- var filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- value: this.bounds,\n- projection: this.layer.projection\n+ initSingleTile: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+ var center = bounds.getCenterLonLat();\n+ var tileWidth = bounds.getWidth() * this.ratio;\n+ var tileHeight = bounds.getHeight() * this.ratio;\n+ var tileBounds = new OpenLayers.Bounds(center.lon - tileWidth / 2, center.lat - tileHeight / 2, center.lon + tileWidth / 2, center.lat + tileHeight / 2);\n+ var px = this.map.getLayerPxFromLonLat({\n+ lon: tileBounds.left,\n+ lat: tileBounds.top\n });\n- if (this.layer.filter) {\n- filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.layer.filter, filter]\n- })\n+ if (!this.grid.length) {\n+ this.grid[0] = []\n }\n- return filter\n- },\n- merge: function(resp) {\n- this.layer.destroyFeatures();\n- if (resp.success()) {\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local)\n- }\n- }\n- }\n- this.layer.addFeatures(features)\n- }\n+ var tile = this.grid[0][0];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ tile.draw();\n+ this.grid[0][0] = tile\n } else {\n- this.bounds = null\n+ tile.moveTo(tileBounds, px)\n }\n- this.response = null;\n- this.layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- })\n+ this.removeExcessTiles(1, 1);\n+ this.gridResolution = this.getServerResolution()\n },\n- CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n-});\n-OpenLayers.Control = OpenLayers.Class({\n- id: null,\n- map: null,\n- div: null,\n- type: null,\n- allowSelection: false,\n- displayClass: \"\",\n- title: \"\",\n- autoActivate: false,\n- active: null,\n- handlerOptions: null,\n- handler: null,\n- eventListeners: null,\n- events: null,\n- initialize: function(options) {\n- this.displayClass = this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n- OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ calculateGridLayout: function(bounds, origin, resolution) {\n+ var tilelon = resolution * this.tileSize.w;\n+ var tilelat = resolution * this.tileSize.h;\n+ var offsetlon = bounds.left - origin.lon;\n+ var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n+ var rowSign = this.rowSign;\n+ var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n+ var tilerow = Math[~rowSign ? \"floor\" : \"ceil\"](offsetlat / tilelat) - this.buffer * rowSign;\n+ return {\n+ tilelon: tilelon,\n+ tilelat: tilelat,\n+ startcol: tilecol,\n+ startrow: tilerow\n }\n },\n- destroy: function() {\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n- }\n- this.events.destroy();\n- this.events = null\n- }\n- this.eventListeners = null;\n- if (this.handler) {\n- this.handler.destroy();\n- this.handler = null\n- }\n- if (this.handlers) {\n- for (var key in this.handlers) {\n- if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == \"function\") {\n- this.handlers[key].destroy()\n- }\n- }\n- this.handlers = null\n- }\n- if (this.map) {\n- this.map.removeControl(this);\n- this.map = null\n+ getTileOrigin: function() {\n+ var origin = this.tileOrigin;\n+ if (!origin) {\n+ var extent = this.getMaxExtent();\n+ var edges = {\n+ tl: [\"left\", \"top\"],\n+ tr: [\"right\", \"top\"],\n+ bl: [\"left\", \"bottom\"],\n+ br: [\"right\", \"bottom\"]\n+ } [this.tileOriginCorner];\n+ origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]])\n }\n- this.div = null\n+ return origin\n },\n- setMap: function(map) {\n- this.map = map;\n- if (this.handler) {\n- this.handler.setMap(map)\n- }\n+ getTileBoundsForGridIndex: function(row, col) {\n+ var origin = this.getTileOrigin();\n+ var tileLayout = this.gridLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var startcol = tileLayout.startcol;\n+ var startrow = tileLayout.startrow;\n+ var rowSign = this.rowSign;\n+ return new OpenLayers.Bounds(origin.lon + (startcol + col) * tilelon, origin.lat - (startrow + row * rowSign) * tilelat * rowSign, origin.lon + (startcol + col + 1) * tilelon, origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign)\n },\n- draw: function(px) {\n- if (this.div == null) {\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.className = this.displayClass;\n- if (!this.allowSelection) {\n- this.div.className += \" olControlNoSelect\";\n- this.div.setAttribute(\"unselectable\", \"on\", 0);\n- this.div.onselectstart = OpenLayers.Function.False\n- }\n- if (this.title != \"\") {\n- this.div.title = this.title\n+ initGriddedTiles: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+ var viewSize = this.map.getSize();\n+ var origin = this.getTileOrigin();\n+ var resolution = this.map.getResolution(),\n+ serverResolution = this.getServerResolution(),\n+ ratio = resolution / serverResolution,\n+ tileSize = {\n+ w: this.tileSize.w / ratio,\n+ h: this.tileSize.h / ratio\n+ };\n+ var minRows = Math.ceil(viewSize.h / tileSize.h) + 2 * this.buffer + 1;\n+ var minCols = Math.ceil(viewSize.w / tileSize.w) + 2 * this.buffer + 1;\n+ var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n+ this.gridLayout = tileLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n+ var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n+ var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n+ var startPx = this.map.getViewPortPxFromLonLat(new OpenLayers.LonLat(tileBounds.left, tileBounds.top));\n+ startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n+ startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n+ var tileData = [],\n+ center = this.map.getCenter();\n+ var rowidx = 0;\n+ do {\n+ var row = this.grid[rowidx];\n+ if (!row) {\n+ row = [];\n+ this.grid.push(row)\n }\n+ var colidx = 0;\n+ do {\n+ tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n+ var px = startPx.clone();\n+ px.x = px.x + colidx * Math.round(tileSize.w);\n+ px.y = px.y + rowidx * Math.round(tileSize.h);\n+ var tile = row[colidx];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ row.push(tile)\n+ } else {\n+ tile.moveTo(tileBounds, px, false)\n+ }\n+ var tileCenter = tileBounds.getCenterLonLat();\n+ tileData.push({\n+ tile: tile,\n+ distance: Math.pow(tileCenter.lon - center.lon, 2) + Math.pow(tileCenter.lat - center.lat, 2)\n+ });\n+ colidx += 1\n+ } while (tileBounds.right <= bounds.right + tilelon * this.buffer || colidx < minCols);\n+ rowidx += 1\n+ } while (tileBounds.bottom >= bounds.bottom - tilelat * this.buffer || rowidx < minRows);\n+ this.removeExcessTiles(rowidx, colidx);\n+ var resolution = this.getServerResolution();\n+ this.gridResolution = resolution;\n+ tileData.sort(function(a, b) {\n+ return a.distance - b.distance\n+ });\n+ for (var i = 0, ii = tileData.length; i < ii; ++i) {\n+ tileData[i].tile.draw()\n }\n- if (px != null) {\n- this.position = px.clone()\n- }\n- this.moveTo(this.position);\n- return this.div\n },\n- moveTo: function(px) {\n- if (px != null && this.div != null) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\"\n- }\n+ getMaxExtent: function() {\n+ return this.maxExtent\n },\n- activate: function() {\n- if (this.active) {\n- return false\n- }\n- if (this.handler) {\n- this.handler.activate()\n- }\n- this.active = true;\n- if (this.map) {\n- OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n- }\n- this.events.triggerEvent(\"activate\");\n- return true\n+ addTile: function(bounds, position) {\n+ var tile = new this.tileClass(this, position, bounds, null, this.tileSize, this.tileOptions);\n+ this.events.triggerEvent(\"addtile\", {\n+ tile: tile\n+ });\n+ return tile\n },\n- deactivate: function() {\n- if (this.active) {\n- if (this.handler) {\n- this.handler.deactivate()\n- }\n- this.active = false;\n- if (this.map) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n+ addTileMonitoringHooks: function(tile) {\n+ var replacingCls = \"olTileReplacing\";\n+ tile.onLoadStart = function() {\n+ if (this.loading === false) {\n+ this.loading = true;\n+ this.events.triggerEvent(\"loadstart\")\n }\n- this.events.triggerEvent(\"deactivate\");\n- return true\n- }\n- return false\n- },\n- CLASS_NAME: \"OpenLayers.Control\"\n-});\n-OpenLayers.Control.TYPE_BUTTON = 1;\n-OpenLayers.Control.TYPE_TOGGLE = 2;\n-OpenLayers.Control.TYPE_TOOL = 3;\n-OpenLayers.Handler = OpenLayers.Class({\n- id: null,\n- control: null,\n- map: null,\n- keyMask: null,\n- active: false,\n- evt: null,\n- touch: false,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Util.extend(this, options);\n- this.control = control;\n- this.callbacks = callbacks;\n- var map = this.map || control.map;\n- if (map) {\n- this.setMap(map)\n- }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- setMap: function(map) {\n- this.map = map\n- },\n- checkModifiers: function(evt) {\n- if (this.keyMask == null) {\n- return true\n- }\n- var keyModifiers = (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n- return keyModifiers == this.keyMask\n- },\n- activate: function() {\n- if (this.active) {\n- return false\n- }\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.register(events[i], this[events[i]])\n+ this.events.triggerEvent(\"tileloadstart\", {\n+ tile: tile\n+ });\n+ this.numLoadingTiles++;\n+ if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ OpenLayers.Element.addClass(tile.getTile(), replacingCls)\n }\n- }\n- this.active = true;\n- return true\n- },\n- deactivate: function() {\n- if (!this.active) {\n- return false\n- }\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]])\n+ };\n+ tile.onLoadEnd = function(evt) {\n+ this.numLoadingTiles--;\n+ var aborted = evt.type === \"unload\";\n+ this.events.triggerEvent(\"tileloaded\", {\n+ tile: tile,\n+ aborted: aborted\n+ });\n+ if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ var tileDiv = tile.getTile();\n+ if (OpenLayers.Element.getStyle(tileDiv, \"display\") === \"none\") {\n+ var bufferTile = document.getElementById(tile.id + \"_bb\");\n+ if (bufferTile) {\n+ bufferTile.parentNode.removeChild(bufferTile)\n+ }\n+ }\n+ OpenLayers.Element.removeClass(tileDiv, replacingCls)\n }\n- }\n- this.touch = false;\n- this.active = false;\n- return true\n- },\n- startTouch: function() {\n- if (!this.touch) {\n- this.touch = true;\n- var events = [\"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"mouseout\"];\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]])\n+ if (this.numLoadingTiles === 0) {\n+ if (this.backBuffer) {\n+ if (this.backBuffer.childNodes.length === 0) {\n+ this.removeBackBuffer()\n+ } else {\n+ this._transitionElement = aborted ? this.div.lastChild : tile.imgDiv;\n+ var transitionendEvents = this.transitionendEvents;\n+ for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.observe(this._transitionElement, transitionendEvents[i], this._removeBackBuffer)\n+ }\n+ this.backBufferTimerId = window.setTimeout(this._removeBackBuffer, this.removeBackBufferDelay)\n+ }\n }\n+ this.loading = false;\n+ this.events.triggerEvent(\"loadend\")\n }\n- }\n- },\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args)\n- }\n- },\n- register: function(name, method) {\n- this.map.events.registerPriority(name, this, method);\n- this.map.events.registerPriority(name, this, this.setEvent)\n- },\n- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent)\n- },\n- setEvent: function(evt) {\n- this.evt = evt;\n- return true\n+ };\n+ tile.onLoadError = function() {\n+ this.events.triggerEvent(\"tileerror\", {\n+ tile: tile\n+ })\n+ };\n+ tile.events.on({\n+ loadstart: tile.onLoadStart,\n+ loadend: tile.onLoadEnd,\n+ unload: tile.onLoadEnd,\n+ loaderror: tile.onLoadError,\n+ scope: this\n+ })\n },\n- destroy: function() {\n- this.deactivate();\n- this.control = this.map = null\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ loadstart: tile.onLoadStart,\n+ loadend: tile.onLoadEnd,\n+ unload: tile.onLoadEnd,\n+ loaderror: tile.onLoadError,\n+ scope: this\n+ })\n },\n- CLASS_NAME: \"OpenLayers.Handler\"\n-});\n-OpenLayers.Handler.MOD_NONE = 0;\n-OpenLayers.Handler.MOD_SHIFT = 1;\n-OpenLayers.Handler.MOD_CTRL = 2;\n-OpenLayers.Handler.MOD_ALT = 4;\n-OpenLayers.Handler.MOD_META = 8;\n-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n- started: false,\n- stopDown: true,\n- dragging: false,\n- last: null,\n- start: null,\n- lastMoveEvt: null,\n- oldOnselectstart: null,\n- interval: 0,\n- timeoutId: null,\n- documentDrag: false,\n- documentEvents: null,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- if (this.documentDrag === true) {\n- var me = this;\n- this._docMove = function(evt) {\n- me.mousemove({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- },\n- element: document\n- })\n+ moveGriddedTiles: function() {\n+ var buffer = this.buffer + 1;\n+ while (true) {\n+ var tlTile = this.grid[0][0];\n+ var tlViewPort = {\n+ x: tlTile.position.x + this.map.layerContainerOriginPx.x,\n+ y: tlTile.position.y + this.map.layerContainerOriginPx.y\n };\n- this._docUp = function(evt) {\n- me.mouseup({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- }\n- })\n+ var ratio = this.getServerResolution() / this.map.getResolution();\n+ var tileSize = {\n+ w: Math.round(this.tileSize.w * ratio),\n+ h: Math.round(this.tileSize.h * ratio)\n+ };\n+ if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n+ this.shiftColumn(true, tileSize)\n+ } else if (tlViewPort.x < -tileSize.w * buffer) {\n+ this.shiftColumn(false, tileSize)\n+ } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n+ this.shiftRow(true, tileSize)\n+ } else if (tlViewPort.y < -tileSize.h * buffer) {\n+ this.shiftRow(false, tileSize)\n+ } else {\n+ break\n }\n }\n },\n- dragstart: function(evt) {\n- var propagate = true;\n- this.dragging = false;\n- if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) {\n- this.started = true;\n- this.start = evt.xy;\n- this.last = evt.xy;\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDragDown\");\n- this.down(evt);\n- this.callback(\"down\", [evt.xy]);\n- OpenLayers.Event.preventDefault(evt);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True\n- }\n- document.onselectstart = OpenLayers.Function.False;\n- propagate = !this.stopDown\n- } else {\n- this.started = false;\n- this.start = null;\n- this.last = null\n+ shiftRow: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var rowIndex = prepend ? 0 : grid.length - 1;\n+ var sign = prepend ? -1 : 1;\n+ var rowSign = this.rowSign;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startrow += sign * rowSign;\n+ var modelRow = grid[rowIndex];\n+ var row = grid[prepend ? \"pop\" : \"shift\"]();\n+ for (var i = 0, len = row.length; i < len; i++) {\n+ var tile = row[i];\n+ var position = modelRow[i].position.clone();\n+ position.y += tileSize.h * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position)\n }\n- return propagate\n+ grid[prepend ? \"unshift\" : \"push\"](row)\n },\n- dragmove: function(evt) {\n- this.lastMoveEvt = evt;\n- if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {\n- if (this.documentDrag === true && this.documentEvents) {\n- if (evt.element === document) {\n- this.adjustXY(evt);\n- this.setEvent(evt)\n- } else {\n- this.removeDocumentEvents()\n- }\n- }\n- if (this.interval > 0) {\n- this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval)\n- }\n- this.dragging = true;\n- this.move(evt);\n- this.callback(\"move\", [evt.xy]);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart;\n- document.onselectstart = OpenLayers.Function.False\n- }\n- this.last = evt.xy\n+ shiftColumn: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var colIndex = prepend ? 0 : grid[0].length - 1;\n+ var sign = prepend ? -1 : 1;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startcol += sign;\n+ for (var i = 0, len = grid.length; i < len; i++) {\n+ var row = grid[i];\n+ var position = row[colIndex].position.clone();\n+ var tile = row[prepend ? \"pop\" : \"shift\"]();\n+ position.x += tileSize.w * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n+ row[prepend ? \"unshift\" : \"push\"](tile)\n }\n- return true\n },\n- dragend: function(evt) {\n- if (this.started) {\n- if (this.documentDrag === true && this.documentEvents) {\n- this.adjustXY(evt);\n- this.removeDocumentEvents()\n+ removeExcessTiles: function(rows, columns) {\n+ var i, l;\n+ while (this.grid.length > rows) {\n+ var row = this.grid.pop();\n+ for (i = 0, l = row.length; i < l; i++) {\n+ var tile = row[i];\n+ this.destroyTile(tile)\n }\n- var dragged = this.start != this.last;\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n- this.up(evt);\n- this.callback(\"up\", [evt.xy]);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy])\n+ }\n+ for (i = 0, l = this.grid.length; i < l; i++) {\n+ while (this.grid[i].length > columns) {\n+ var row = this.grid[i];\n+ var tile = row.pop();\n+ this.destroyTile(tile)\n }\n- document.onselectstart = this.oldOnselectstart\n }\n- return true\n- },\n- down: function(evt) {},\n- move: function(evt) {},\n- up: function(evt) {},\n- out: function(evt) {},\n- mousedown: function(evt) {\n- return this.dragstart(evt)\n- },\n- touchstart: function(evt) {\n- this.startTouch();\n- return this.dragstart(evt)\n- },\n- mousemove: function(evt) {\n- return this.dragmove(evt)\n- },\n- touchmove: function(evt) {\n- return this.dragmove(evt)\n },\n- removeTimeout: function() {\n- this.timeoutId = null;\n- if (this.dragging) {\n- this.mousemove(this.lastMoveEvt)\n+ onMapResize: function() {\n+ if (this.singleTile) {\n+ this.clearGrid();\n+ this.setTileSize()\n }\n },\n- mouseup: function(evt) {\n- return this.dragend(evt)\n- },\n- touchend: function(evt) {\n- evt.xy = this.last;\n- return this.dragend(evt)\n+ getTileBounds: function(viewPortPx) {\n+ var maxExtent = this.maxExtent;\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth);\n+ var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight);\n+ return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n },\n- mouseout: function(evt) {\n- if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- if (this.documentDrag === true) {\n- this.addDocumentEvents()\n- } else {\n- var dragged = this.start != this.last;\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n- this.out(evt);\n- this.callback(\"out\", []);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy])\n- }\n- if (document.onselectstart) {\n- document.onselectstart = this.oldOnselectstart\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Layer.Grid\"\n+});\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ sphericalMercator: false,\n+ zoomOffset: 0,\n+ serverResolutions: null,\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options)\n }\n- return true\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n },\n- click: function(evt) {\n- return this.start == this.last\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragging = false;\n- activated = true\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = \"\" + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url)\n }\n- return activated\n+ return OpenLayers.String.format(url, xyz)\n },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.started = false;\n- this.dragging = false;\n- this.start = null;\n- this.last = null;\n- deactivated = true;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\")\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = (x % limit + limit) % limit\n+ }\n+ return {\n+ x: x,\n+ y: y,\n+ z: z\n }\n- return deactivated\n },\n- adjustXY: function(evt) {\n- var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n- evt.xy.x -= pos[0];\n- evt.xy.y -= pos[1]\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n+ }\n },\n- addDocumentEvents: function() {\n- OpenLayers.Element.addClass(document.body, \"olDragDown\");\n- this.documentEvents = true;\n- OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.observe(document, \"mouseup\", this._docUp)\n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+});\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ name: \"OpenStreetMap\",\n+ url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+ sphericalMercator: true,\n+ wrapDateLine: true,\n+ tileOptions: null,\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options && this.options.tileOptions)\n },\n- removeDocumentEvents: function() {\n- OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n- this.documentEvents = false;\n- OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp)\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n });\n-OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n- dragHandler: null,\n- boxDivClassName: \"olHandlerBoxZoomBox\",\n- boxOffsets: null,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.dragHandler = new OpenLayers.Handler.Drag(this, {\n- down: this.startBox,\n- move: this.moveBox,\n- out: this.removeBox,\n- up: this.endBox\n- }, {\n- keyMask: this.keyMask\n- })\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n },\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.destroy();\n- this.dragHandler = null\n+ isBaseLayer: true,\n+ encodeBBOX: false,\n+ noMagic: false,\n+ yx: {},\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\"\n }\n- },\n- setMap: function(map) {\n- OpenLayers.Handler.prototype.setMap.apply(this, arguments);\n- if (this.dragHandler) {\n- this.dragHandler.setMap(map)\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n+ if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ if (options == null || !options.isBaseLayer) {\n+ this.isBaseLayer = false\n+ }\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n+ }\n }\n },\n- startBox: function(xy) {\n- this.callback(\"start\", []);\n- this.zoomBox = OpenLayers.Util.createDiv(\"zoomBox\", {\n- x: -9999,\n- y: -9999\n- });\n- this.zoomBox.className = this.boxDivClassName;\n- this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE[\"Popup\"] - 1;\n- this.map.viewPortDiv.appendChild(this.zoomBox);\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDrawBox\")\n- },\n- moveBox: function(xy) {\n- var startX = this.dragHandler.start.x;\n- var startY = this.dragHandler.start.y;\n- var deltaX = Math.abs(startX - xy.x);\n- var deltaY = Math.abs(startY - xy.y);\n- var offset = this.getBoxOffsets();\n- this.zoomBox.style.width = deltaX + offset.width + 1 + \"px\";\n- this.zoomBox.style.height = deltaY + offset.height + 1 + \"px\";\n- this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + \"px\";\n- this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + \"px\"\n- },\n- endBox: function(end) {\n- var result;\n- if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) {\n- var start = this.dragHandler.start;\n- var top = Math.min(start.y, end.y);\n- var bottom = Math.max(start.y, end.y);\n- var left = Math.min(start.x, end.x);\n- var right = Math.max(start.x, end.x);\n- result = new OpenLayers.Bounds(left, bottom, right, top)\n- } else {\n- result = this.dragHandler.start.clone()\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n }\n- this.removeBox();\n- this.callback(\"done\", [result])\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- removeBox: function() {\n- this.map.viewPortDiv.removeChild(this.zoomBox);\n- this.zoomBox = null;\n- this.boxOffsets = null;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDrawBox\")\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n },\n- activate: function() {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragHandler.activate();\n- return true\n- } else {\n- return false\n- }\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString\n },\n- deactivate: function() {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- if (this.dragHandler.deactivate()) {\n- if (this.zoomBox) {\n- this.removeBox()\n- }\n- }\n- return true\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ },\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n+ var value = projectionCode == \"none\" ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value\n } else {\n- return false\n+ this.params.SRS = value\n }\n- },\n- getBoxOffsets: function() {\n- if (!this.boxOffsets) {\n- var testDiv = document.createElement(\"div\");\n- testDiv.style.position = \"absolute\";\n- testDiv.style.border = \"1px solid black\";\n- testDiv.style.width = \"3px\";\n- document.body.appendChild(testDiv);\n- var w3cBoxModel = testDiv.clientWidth == 3;\n- document.body.removeChild(testDiv);\n- var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-left-width\"));\n- var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-right-width\"));\n- var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-top-width\"));\n- var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, \"border-bottom-width\"));\n- this.boxOffsets = {\n- left: left,\n- right: right,\n- top: top,\n- bottom: bottom,\n- width: w3cBoxModel === false ? left + right : 0,\n- height: w3cBoxModel === false ? top + bottom : 0\n- }\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n }\n- return this.boxOffsets\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Handler.Box\"\n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n });\n-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_TOOL,\n- out: false,\n- keyMask: null,\n- alwaysZoom: false,\n- zoomOnClick: true,\n- draw: function() {\n- this.handler = new OpenLayers.Handler.Box(this, {\n- done: this.zoomBox\n- }, {\n- keyMask: this.keyMask\n- })\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ key: null,\n+ serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n+ metadata: null,\n+ protocolRegex: /^http:/i,\n+ type: \"Road\",\n+ culture: \"en-US\",\n+ metadataParams: null,\n+ tileOptions: null,\n+ protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n+ initialize: function(options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options.tileOptions);\n+ this.loadMetadata()\n },\n- zoomBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var bounds, targetCenterPx = position.getCenterPixel();\n- if (!this.out) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n- } else {\n- var pixWidth = position.right - position.left;\n- var pixHeight = position.bottom - position.top;\n- var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth);\n- var extent = this.map.getExtent();\n- var center = this.map.getLonLatFromPixel(targetCenterPx);\n- var xmin = center.lon - extent.getWidth() / 2 * zoomFactor;\n- var xmax = center.lon + extent.getWidth() / 2 * zoomFactor;\n- var ymin = center.lat - extent.getHeight() / 2 * zoomFactor;\n- var ymax = center.lat + extent.getHeight() / 2 * zoomFactor;\n- bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax)\n- }\n- var lastZoom = this.map.getZoom(),\n- size = this.map.getSize(),\n- centerPx = {\n- x: size.w / 2,\n- y: size.h / 2\n- },\n- zoom = this.map.getZoomForExtent(bounds),\n- oldRes = this.map.getResolution(),\n- newRes = this.map.getResolutionForZoom(zoom);\n- if (oldRes == newRes) {\n- this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx))\n- } else {\n- var zoomOriginPx = {\n- x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes),\n- y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes)\n- };\n- this.map.zoomTo(zoom, zoomOriginPx)\n- }\n- if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n- this.map.zoomTo(lastZoom + (this.out ? -1 : 1))\n- }\n- } else if (this.zoomOnClick) {\n- if (!this.out) {\n- this.map.zoomTo(this.map.getZoom() + 1, position)\n- } else {\n- this.map.zoomTo(this.map.getZoom() - 1, position)\n- }\n- }\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script)\n },\n- CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n-});\n-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_TOOL,\n- panned: false,\n- interval: 0,\n- documentDrag: false,\n- kinetic: null,\n- enableKinetic: true,\n- kineticInterval: 10,\n- draw: function() {\n- if (this.enableKinetic && OpenLayers.Kinetic) {\n- var config = {\n- interval: this.kineticInterval\n- };\n- if (typeof this.enableKinetic === \"object\") {\n- config = OpenLayers.Util.extend(config, this.enableKinetic)\n- }\n- this.kinetic = new OpenLayers.Kinetic(config)\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n }\n- this.handler = new OpenLayers.Handler.Drag(this, {\n- move: this.panMap,\n- done: this.panMapDone,\n- down: this.panMapStart\n- }, {\n- interval: this.interval,\n- documentDrag: this.documentDrag\n- })\n- },\n- panMapStart: function() {\n- if (this.kinetic) {\n- this.kinetic.begin()\n+ this.addOptions({\n+ maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n+ numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw()\n }\n+ this.updateAttribution()\n },\n- panMap: function(xy) {\n- if (this.kinetic) {\n- this.kinetic.update(xy)\n+ getURL: function(bounds) {\n+ if (!this.url) {\n+ return\n }\n- this.panned = true;\n- this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n- dragging: true,\n- animate: false\n- })\n- },\n- panMapDone: function(xy) {\n- if (this.panned) {\n- var res = null;\n- if (this.kinetic) {\n- res = this.kinetic.end(xy)\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = \"0\";\n+ var mask = 1 << i - 1;\n+ if ((x & mask) != 0) {\n+ digit++\n }\n- this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n- dragging: !!res,\n- animate: false\n- });\n- if (res) {\n- var self = this;\n- this.kinetic.move(res, function(x, y, end) {\n- self.map.pan(x, y, {\n- dragging: !end,\n- animate: false\n- })\n- })\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++\n }\n- this.panned = false\n+ quadDigits.push(digit)\n }\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl(\"\" + x + y + z, this.url);\n+ return OpenLayers.String.format(url, {\n+ quadkey: quadKey\n+ })\n },\n- CLASS_NAME: \"OpenLayers.Control.DragPan\"\n-});\n-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n- wheelListener: null,\n- interval: 0,\n- maxDelta: Number.POSITIVE_INFINITY,\n- delta: 0,\n- cumulative: true,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this)\n- },\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- this.wheelListener = null\n- },\n- onWheelEvent: function(e) {\n- if (!this.map || !this.checkModifiers(e)) {\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n return\n }\n- var overScrollableDiv = false;\n- var allowScroll = false;\n- var overMapDiv = false;\n- var elem = OpenLayers.Event.element(e);\n- while (elem != null && !overMapDiv && !overScrollableDiv) {\n- if (!overScrollableDiv) {\n- try {\n- var overflow;\n- if (elem.currentStyle) {\n- overflow = elem.currentStyle[\"overflow\"]\n- } else {\n- var style = document.defaultView.getComputedStyle(elem, null);\n- overflow = style.getPropertyValue(\"overflow\")\n- }\n- overScrollableDiv = overflow && overflow == \"auto\" || overflow == \"scroll\"\n- } catch (err) {}\n- }\n- if (!allowScroll) {\n- allowScroll = OpenLayers.Element.hasClass(elem, \"olScrollable\");\n- if (!allowScroll) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (elem == layer.div || elem == layer.pane) {\n- allowScroll = true;\n- break\n- }\n- }\n- }\n- }\n- overMapDiv = elem == this.map.div;\n- elem = elem.parentNode\n- }\n- if (!overScrollableDiv && overMapDiv) {\n- if (allowScroll) {\n- var delta = 0;\n- if (e.wheelDelta) {\n- delta = e.wheelDelta;\n- if (delta % 160 === 0) {\n- delta = delta * .75\n- }\n- delta = delta / 120\n- } else if (e.detail) {\n- delta = -(e.detail / Math.abs(e.detail))\n- }\n- this.delta += delta;\n- window.clearTimeout(this._timeoutId);\n- if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n- var evt = OpenLayers.Util.extend({}, e);\n- this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() {\n- this.wheelZoom(evt)\n- }, this), this.interval)\n- } else {\n- this.wheelZoom(e)\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \"\n }\n }\n- OpenLayers.Event.stop(e)\n }\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ })\n },\n- wheelZoom: function(e) {\n- var delta = this.delta;\n- this.delta = 0;\n- if (delta) {\n- e.xy = this.map.events.getMousePosition(e);\n- if (delta < 0) {\n- this.callback(\"down\", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1])\n- } else {\n- this.callback(\"up\", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1])\n- }\n- }\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution)\n },\n- activate: function(evt) {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n- return true\n- } else {\n- return false\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options)\n }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- deactivate: function(evt) {\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n- return true\n+ destroy: function() {\n+ this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+});\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined;\n+ delete this._callbackId\n+};\n+OpenLayers.Layer.SphericalMercator = {\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.sphericalMercator) {\n+ extent = this.map.calculateBounds()\n } else {\n- return false\n+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)\n }\n+ return extent\n },\n- CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n-});\n-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n- delay: 300,\n- single: true,\n- double: false,\n- pixelTolerance: 0,\n- dblclickTolerance: 13,\n- stopSingle: false,\n- stopDouble: false,\n- timerId: null,\n- down: null,\n- last: null,\n- first: null,\n- rightclickTimerId: null,\n- touchstart: function(evt) {\n- this.startTouch();\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments)\n },\n- touchmove: function(evt) {\n- this.last = this.getEventInfo(evt);\n- return true\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments)\n },\n- touchend: function(evt) {\n- if (this.down) {\n- evt.xy = this.last.xy;\n- evt.lastTouches = this.last.touches;\n- this.handleSingle(evt);\n- this.down = null\n+ initMercatorParameters: function() {\n+ this.RESOLUTIONS = [];\n+ var maxResolution = 156543.03390625;\n+ for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom)\n }\n- return true\n- },\n- mousedown: function(evt) {\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true\n+ this.units = \"m\";\n+ this.projection = this.projection || \"EPSG:900913\"\n },\n- mouseup: function(evt) {\n- var propagate = true;\n- if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) {\n- propagate = this.rightclick(evt)\n+ forwardMercator: function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(lon, lat) {\n+ var point = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ }, gg, sm);\n+ return new OpenLayers.LonLat(point.x, point.y)\n }\n- return propagate\n- },\n- rightclick: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.rightclickTimerId != null) {\n- this.clearTimer();\n- this.callback(\"dblrightclick\", [evt]);\n- return !this.stopDouble\n- } else {\n- var clickEvent = this[\"double\"] ? OpenLayers.Util.extend({}, evt) : this.callback(\"rightclick\", [evt]);\n- var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent);\n- this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay)\n- }\n+ }(),\n+ inverseMercator: function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(x, y) {\n+ var point = OpenLayers.Projection.transform({\n+ x: x,\n+ y: y\n+ }, sm, gg);\n+ return new OpenLayers.LonLat(point.x, point.y)\n+ }\n+ }()\n+};\n+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n+ smoothDragPan: true,\n+ isBaseLayer: true,\n+ isFixed: true,\n+ pane: null,\n+ mapObject: null,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (this.pane == null) {\n+ this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\")\n }\n- return !this.stopSingle\n },\n- delayedRightCall: function(evt) {\n- this.rightclickTimerId = null;\n- if (evt) {\n- this.callback(\"rightclick\", [evt])\n+ destroy: function() {\n+ this.mapObject = null;\n+ this.pane = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n+ this.pane.style.display = this.div.style.display;\n+ this.pane.style.width = \"100%\";\n+ this.pane.style.height = \"100%\";\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.pane.style.background = \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\"\n+ }\n+ if (this.isFixed) {\n+ this.map.viewPortDiv.appendChild(this.pane)\n+ } else {\n+ this.map.layerContainerDiv.appendChild(this.pane)\n+ }\n+ this.loadMapObject();\n+ if (this.mapObject == null) {\n+ this.loadWarningMessage()\n }\n },\n- click: function(evt) {\n- if (!this.last) {\n- this.last = this.getEventInfo(evt)\n+ removeMap: function(map) {\n+ if (this.pane && this.pane.parentNode) {\n+ this.pane.parentNode.removeChild(this.pane)\n }\n- this.handleSingle(evt);\n- return !this.stopSingle\n+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments)\n },\n- dblclick: function(evt) {\n- this.handleDouble(evt);\n- return !this.stopDouble\n+ loadWarningMessage: function() {\n+ this.div.style.backgroundColor = \"darkblue\";\n+ var viewSize = this.map.getSize();\n+ var msgW = Math.min(viewSize.w, 300);\n+ var msgH = Math.min(viewSize.h, 200);\n+ var size = new OpenLayers.Size(msgW, msgH);\n+ var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n+ var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n+ var div = OpenLayers.Util.createDiv(this.name + \"_warning\", topLeft, size, null, null, null, \"auto\");\n+ div.style.padding = \"7px\";\n+ div.style.backgroundColor = \"yellow\";\n+ div.innerHTML = this.getWarningHTML();\n+ this.div.appendChild(div)\n },\n- handleDouble: function(evt) {\n- if (this.passesDblclickTolerance(evt)) {\n- if (this[\"double\"]) {\n- this.callback(\"dblclick\", [evt])\n- }\n- this.clearTimer()\n- }\n+ getWarningHTML: function() {\n+ return \"\"\n },\n- handleSingle: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.timerId != null) {\n- if (this.last.touches && this.last.touches.length === 1) {\n- if (this[\"double\"]) {\n- OpenLayers.Event.preventDefault(evt)\n- }\n- this.handleDouble(evt)\n- }\n- if (!this.last.touches || this.last.touches.length !== 2) {\n- this.clearTimer()\n- }\n- } else {\n- this.first = this.getEventInfo(evt);\n- var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null;\n- this.queuePotentialClick(clickEvent)\n- }\n- }\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ this.pane.style.display = this.div.style.display\n },\n- queuePotentialClick: function(evt) {\n- this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n+ setZIndex: function(zIndex) {\n+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1\n },\n- passesTolerance: function(evt) {\n- var passes = true;\n- if (this.pixelTolerance != null && this.down && this.down.xy) {\n- passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n- if (passes && this.touch && this.down.touches.length === this.last.touches.length) {\n- for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n- if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) {\n- passes = false;\n- break\n+ moveByPx: function(dx, dy) {\n+ OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n+ if (this.dragPanMapObject) {\n+ this.dragPanMapObject(dx, -dy)\n+ } else {\n+ this.moveTo(this.map.getCachedCenter())\n+ }\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ if (this.mapObject != null) {\n+ var newCenter = this.map.getCenter();\n+ var newZoom = this.map.getZoom();\n+ if (newCenter != null) {\n+ var moOldCenter = this.getMapObjectCenter();\n+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n+ var moOldZoom = this.getMapObjectZoom();\n+ var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n+ if (!newCenter.equals(oldCenter) || newZoom != oldZoom) {\n+ if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) {\n+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n+ this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y)\n+ } else {\n+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n+ this.setMapObjectCenter(center, zoom, dragging)\n }\n }\n }\n }\n- return passes\n- },\n- getTouchDistance: function(from, to) {\n- return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2))\n },\n- passesDblclickTolerance: function(evt) {\n- var passes = true;\n- if (this.down && this.first) {\n- passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.mapObject != null && this.getMapObjectCenter() != null) {\n+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat)\n }\n- return passes\n+ return lonlat\n },\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null\n- }\n- if (this.rightclickTimerId != null) {\n- window.clearTimeout(this.rightclickTimerId);\n- this.rightclickTimerId = null\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var viewPortPx = null;\n+ if (this.mapObject != null && this.getMapObjectCenter() != null) {\n+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel)\n }\n+ return viewPortPx\n },\n- delayedCall: function(evt) {\n- this.timerId = null;\n- if (evt) {\n- this.callback(\"click\", [evt])\n+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n+ var olLonLat = null;\n+ if (moLonLat != null) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ olLonLat = new OpenLayers.LonLat(lon, lat)\n }\n+ return olLonLat\n },\n- getEventInfo: function(evt) {\n- var touches;\n- if (evt.touches) {\n- var len = evt.touches.length;\n- touches = new Array(len);\n- var touch;\n- for (var i = 0; i < len; i++) {\n- touch = evt.touches[i];\n- touches[i] = {\n- clientX: touch.olClientX,\n- clientY: touch.olClientY\n- }\n- }\n+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n+ var moLatLng = null;\n+ if (olLonLat != null) {\n+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat)\n }\n- return {\n- xy: evt.xy,\n- touches: touches\n+ return moLatLng\n+ },\n+ getOLPixelFromMapObjectPixel: function(moPixel) {\n+ var olPixel = null;\n+ if (moPixel != null) {\n+ var x = this.getXFromMapObjectPixel(moPixel);\n+ var y = this.getYFromMapObjectPixel(moPixel);\n+ olPixel = new OpenLayers.Pixel(x, y)\n }\n+ return olPixel\n },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- this.down = null;\n- this.first = null;\n- this.last = null;\n- deactivated = true\n+ getMapObjectPixelFromOLPixel: function(olPixel) {\n+ var moPixel = null;\n+ if (olPixel != null) {\n+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y)\n }\n- return deactivated\n+ return moPixel\n },\n- CLASS_NAME: \"OpenLayers.Handler.Click\"\n+ CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n });\n-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n- dragPan: null,\n- dragPanOptions: null,\n- pinchZoom: null,\n- pinchZoomOptions: null,\n- documentDrag: false,\n- zoomBox: null,\n- zoomBoxEnabled: true,\n- zoomWheelEnabled: true,\n- mouseWheelOptions: null,\n- handleRightClicks: false,\n- zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n- autoActivate: true,\n- initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments)\n- },\n- destroy: function() {\n- this.deactivate();\n- if (this.dragPan) {\n- this.dragPan.destroy()\n- }\n- this.dragPan = null;\n- if (this.zoomBox) {\n- this.zoomBox.destroy()\n+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n+ initialize: function() {},\n+ initResolutions: function() {\n+ var props = [\"minZoomLevel\", \"maxZoomLevel\", \"numZoomLevels\"];\n+ for (var i = 0, len = props.length; i < len; i++) {\n+ var property = props[i];\n+ this[property] = this.options[property] != null ? this.options[property] : this.map[property]\n }\n- this.zoomBox = null;\n- if (this.pinchZoom) {\n- this.pinchZoom.destroy()\n+ if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) {\n+ this.minZoomLevel = this.MIN_ZOOM_LEVEL\n }\n- this.pinchZoom = null;\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n- },\n- activate: function() {\n- this.dragPan.activate();\n- if (this.zoomWheelEnabled) {\n- this.handlers.wheel.activate()\n+ var desiredZoomLevels;\n+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n+ if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) {\n+ desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1\n+ } else {\n+ desiredZoomLevels = this.numZoomLevels\n }\n- this.handlers.click.activate();\n- if (this.zoomBoxEnabled) {\n- this.zoomBox.activate()\n+ if (desiredZoomLevels != null) {\n+ this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels)\n+ } else {\n+ this.numZoomLevels = limitZoomLevels\n }\n- if (this.pinchZoom) {\n- this.pinchZoom.activate()\n+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n+ if (this.RESOLUTIONS != null) {\n+ var resolutionsIndex = 0;\n+ this.resolutions = [];\n+ for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]\n+ }\n+ this.maxResolution = this.resolutions[0];\n+ this.minResolution = this.resolutions[this.resolutions.length - 1]\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n- deactivate: function() {\n- if (this.pinchZoom) {\n- this.pinchZoom.deactivate()\n+ getResolution: function() {\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments)\n+ } else {\n+ var resolution = null;\n+ var viewSize = this.map.getSize();\n+ var extent = this.getExtent();\n+ if (viewSize != null && extent != null) {\n+ resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h)\n+ }\n+ return resolution\n }\n- this.zoomBox.deactivate();\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.handlers.wheel.deactivate();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- draw: function() {\n- if (this.handleRightClicks) {\n- this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False\n- }\n- var clickCallbacks = {\n- click: this.defaultClick,\n- dblclick: this.defaultDblClick,\n- dblrightclick: this.defaultDblRightClick\n- };\n- var clickOptions = {\n- double: true,\n- stopDouble: true\n- };\n- this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n- this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions));\n- this.zoomBox = new OpenLayers.Control.ZoomBox({\n- map: this.map,\n- keyMask: this.zoomBoxKeyMask\n+ getExtent: function() {\n+ var size = this.map.getSize();\n+ var tl = this.getLonLatFromViewPortPx({\n+ x: 0,\n+ y: 0\n });\n- this.dragPan.draw();\n- this.zoomBox.draw();\n- var wheelOptions = this.map.fractionalZoom ? {} : {\n- cumulative: false,\n- interval: 50,\n- maxDelta: 6\n- };\n- this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, {\n- up: this.wheelUp,\n- down: this.wheelDown\n- }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions));\n- if (OpenLayers.Control.PinchZoom) {\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions))\n+ var br = this.getLonLatFromViewPortPx({\n+ x: size.w,\n+ y: size.h\n+ });\n+ if (tl != null && br != null) {\n+ return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat)\n+ } else {\n+ return null\n }\n },\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut()\n+ getZoomForResolution: function(resolution) {\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments)\n+ } else {\n+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n+ return this.getZoomForExtent(extent)\n }\n },\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy)\n+ getOLZoomFromMapObjectZoom: function(moZoom) {\n+ var zoom = null;\n+ if (moZoom != null) {\n+ zoom = moZoom - this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom))\n+ }\n+ }\n+ return zoom\n },\n- defaultDblRightClick: function(evt) {\n- this.map.zoomTo(this.map.zoom - 1, evt.xy)\n+ getMapObjectZoomFromOLZoom: function(olZoom) {\n+ var zoom = null;\n+ if (olZoom != null) {\n+ zoom = olZoom + this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom))\n+ }\n+ }\n+ return zoom\n },\n- wheelChange: function(evt, deltaZ) {\n- if (!this.map.fractionalZoom) {\n- deltaZ = Math.round(deltaZ)\n+ CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n+});\n+OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {\n+ MIN_ZOOM_LEVEL: 0,\n+ MAX_ZOOM_LEVEL: 21,\n+ RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7],\n+ type: null,\n+ wrapDateLine: true,\n+ sphericalMercator: false,\n+ version: null,\n+ initialize: function(name, options) {\n+ options = options || {};\n+ if (!options.version) {\n+ options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\"\n }\n- var currentZoom = this.map.getZoom(),\n- newZoom = currentZoom + deltaZ;\n- newZoom = Math.max(newZoom, 0);\n- newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n- if (newZoom === currentZoom) {\n- return\n+ var mixin = OpenLayers.Layer.Google[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (mixin) {\n+ OpenLayers.Util.applyDefaults(options, mixin)\n+ } else {\n+ throw \"Unsupported Google Maps API version: \" + options.version\n }\n- this.map.zoomTo(newZoom, evt.xy)\n- },\n- wheelUp: function(evt, delta) {\n- this.wheelChange(evt, delta || 1)\n- },\n- wheelDown: function(evt, delta) {\n- this.wheelChange(evt, delta || -1)\n- },\n- disableZoomBox: function() {\n- this.zoomBoxEnabled = false;\n- this.zoomBox.deactivate()\n- },\n- enableZoomBox: function() {\n- this.zoomBoxEnabled = true;\n- if (this.active) {\n- this.zoomBox.activate()\n+ OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n+ if (options.maxExtent) {\n+ options.maxExtent = options.maxExtent.clone()\n }\n- },\n- disableZoomWheel: function() {\n- this.zoomWheelEnabled = false;\n- this.handlers.wheel.deactivate()\n- },\n- enableZoomWheel: function() {\n- this.zoomWheelEnabled = true;\n- if (this.active) {\n- this.handlers.wheel.activate()\n+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]);\n+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]);\n+ if (this.sphericalMercator) {\n+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n+ this.initMercatorParameters()\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.Navigation\"\n-});\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n- target: null,\n- events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n- startRegEx: /^mousedown|touchstart$/,\n- cancelRegEx: /^touchmove$/,\n- completeRegEx: /^mouseup|touchend$/,\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- })\n- }\n+ clone: function() {\n+ return new OpenLayers.Layer.Google(this.name, this.getOptions())\n },\n- destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick)\n- }\n- delete this.target\n+ setVisibility: function(visible) {\n+ var opacity = this.opacity == null ? 1 : this.opacity;\n+ OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n+ this.setOpacity(opacity)\n },\n- getPressedButton: function(element) {\n- var depth = 3,\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- button = element;\n- break\n- }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return button\n+ display: function(visible) {\n+ if (!this._dragging) {\n+ this.setGMapVisibility(visible)\n+ }\n+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments)\n },\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === \"a\") {\n- ignore = true;\n- break\n- }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return ignore\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ this._dragging = dragging;\n+ OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n+ delete this._dragging\n },\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- break\n- }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- })\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt\n- }\n- OpenLayers.Event.stop(evt);\n- propagate = false\n- }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false\n- }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt\n+ setOpacity: function(opacity) {\n+ if (opacity !== this.opacity) {\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ })\n }\n+ this.opacity = opacity\n+ }\n+ if (this.getVisibility()) {\n+ var container = this.getMapContainer();\n+ OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity)\n }\n- return propagate\n- }\n-});\n-OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n- controls: null,\n- autoActivate: true,\n- defaultControl: null,\n- saveState: false,\n- allowDepress: false,\n- activeState: null,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.controls = [];\n- this.activeState = {}\n },\n destroy: function() {\n if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick)\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n- ctl = this.controls[i];\n- if (ctl.events) {\n- ctl.events.un({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- })\n+ this.setGMapVisibility(false);\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache && cache.count <= 1) {\n+ this.removeGMapElements()\n }\n- ctl.panel_div = null\n }\n- this.activeState = null\n+ OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- if (control === this.defaultControl || this.saveState && this.activeState[control.id]) {\n- control.activate()\n- }\n+ removeGMapElements: function() {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject && this.getMapContainer();\n+ if (container && container.parentNode) {\n+ container.parentNode.removeChild(container)\n }\n- if (this.saveState === true) {\n- this.defaultControl = null\n+ var termsOfUse = cache.termsOfUse;\n+ if (termsOfUse && termsOfUse.parentNode) {\n+ termsOfUse.parentNode.removeChild(termsOfUse)\n }\n- this.redraw();\n- return true\n- } else {\n- return false\n- }\n- },\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- this.activeState[control.id] = control.deactivate()\n+ var poweredBy = cache.poweredBy;\n+ if (poweredBy && poweredBy.parentNode) {\n+ poweredBy.parentNode.removeChild(poweredBy)\n }\n- this.redraw();\n- return true\n- } else {\n- return false\n- }\n- },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick)\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n- }\n- this.addControlsToMap(this.controls);\n- return this.div\n- },\n- redraw: function() {\n- for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n- this.div.removeChild(this.div.childNodes[i])\n- }\n- this.div.innerHTML = \"\";\n- if (this.active) {\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.div.appendChild(this.controls[i].panel_div)\n+ if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) {\n+ google.maps.event.clearListeners(this.mapObject, \"tilesloaded\")\n }\n }\n },\n- activateControl: function(control) {\n- if (!this.active) {\n- return false\n- }\n- if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n- control.trigger();\n- return\n+ removeMap: function(map) {\n+ if (this.visibility && this.mapObject) {\n+ this.setGMapVisibility(false)\n }\n- if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n- if (control.active) {\n- control.deactivate()\n+ var cache = OpenLayers.Layer.Google.cache[map.id];\n+ if (cache) {\n+ if (cache.count <= 1) {\n+ this.removeGMapElements();\n+ delete OpenLayers.Layer.Google.cache[map.id]\n } else {\n- control.activate()\n- }\n- return\n- }\n- if (this.allowDepress && control.active) {\n- control.deactivate()\n- } else {\n- var c;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- c = this.controls[i];\n- if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n- c.deactivate()\n- }\n- }\n- control.activate()\n- }\n- },\n- addControls: function(controls) {\n- if (!OpenLayers.Util.isArray(controls)) {\n- controls = [controls]\n- }\n- this.controls = this.controls.concat(controls);\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var control = controls[i],\n- element = this.createControlMarkup(control);\n- OpenLayers.Element.addClass(element, control.displayClass + \"ItemInactive\");\n- OpenLayers.Element.addClass(element, \"olButton\");\n- if (control.title != \"\" && !element.title) {\n- element.title = control.title\n+ --cache.count\n }\n- control.panel_div = element\n- }\n- if (this.map) {\n- this.addControlsToMap(controls);\n- this.redraw()\n }\n+ delete this.termsOfUse;\n+ delete this.poweredBy;\n+ delete this.mapObject;\n+ delete this.dragObject;\n+ OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments)\n },\n- createControlMarkup: function(control) {\n- return document.createElement(\"div\")\n- },\n- addControlsToMap: function(controls) {\n- var control;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- control = controls[i];\n- if (control.autoActivate === true) {\n- control.autoActivate = false;\n- this.map.addControl(control);\n- control.autoActivate = true\n+ getOLBoundsFromMapObjectBounds: function(moBounds) {\n+ var olBounds = null;\n+ if (moBounds != null) {\n+ var sw = moBounds.getSouthWest();\n+ var ne = moBounds.getNorthEast();\n+ if (this.sphericalMercator) {\n+ sw = this.forwardMercator(sw.lng(), sw.lat());\n+ ne = this.forwardMercator(ne.lng(), ne.lat())\n } else {\n- this.map.addControl(control);\n- control.deactivate()\n+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat())\n }\n- control.events.on({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- })\n+ olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat)\n }\n+ return olBounds\n },\n- iconOn: function() {\n- var d = this.panel_div;\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n- d.className = d.className.replace(re, \"$1Active\")\n- },\n- iconOff: function() {\n- var d = this.panel_div;\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n- d.className = d.className.replace(re, \"$1Inactive\")\n- },\n- onButtonClick: function(evt) {\n- var controls = this.controls,\n- button = evt.buttonElement;\n- for (var i = controls.length - 1; i >= 0; --i) {\n- if (controls[i].panel_div === button) {\n- this.activateControl(controls[i]);\n- break\n- }\n- }\n+ getWarningHTML: function() {\n+ return OpenLayers.i18n(\"googleWarning\")\n },\n- getControlsBy: function(property, match) {\n- var test = typeof match.test == \"function\";\n- var found = OpenLayers.Array.filter(this.controls, function(item) {\n- return item[property] == match || test && match.test(item[property])\n- });\n- return found\n+ getMapObjectCenter: function() {\n+ return this.mapObject.getCenter()\n },\n- getControlsByName: function(match) {\n- return this.getControlsBy(\"name\", match)\n+ getMapObjectZoom: function() {\n+ return this.mapObject.getZoom()\n },\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match)\n+ getLongitudeFromMapObjectLonLat: function(moLonLat) {\n+ return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng()\n },\n- CLASS_NAME: \"OpenLayers.Control.Panel\"\n-});\n-OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n- separator: \", \",\n- template: \"${layers}\",\n- destroy: function() {\n- this.map.events.un({\n- removelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- changebaselayer: this.updateAttribution,\n- scope: this\n- });\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ getLatitudeFromMapObjectLonLat: function(moLonLat) {\n+ var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat();\n+ return lat\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- this.map.events.on({\n- changebaselayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- removelayer: this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n- return this.div\n+ getXFromMapObjectPixel: function(moPixel) {\n+ return moPixel.x\n },\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution)\n- }\n- }\n- }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- })\n- }\n+ getYFromMapObjectPixel: function(moPixel) {\n+ return moPixel.y\n },\n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+ CLASS_NAME: \"OpenLayers.Layer.Google\"\n });\n-OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n- zoomInText: \"+\",\n- zoomInId: \"olZoomInLink\",\n- zoomOutText: \"\u2212\",\n- zoomOutId: \"olZoomOutLink\",\n- draw: function() {\n- var div = OpenLayers.Control.prototype.draw.apply(this),\n- links = this.getOrCreateLinks(div),\n- zoomIn = links.zoomIn,\n- zoomOut = links.zoomOut,\n- eventsInstance = this.map.events;\n- if (zoomOut.parentNode !== div) {\n- eventsInstance = this.events;\n- eventsInstance.attachToElement(zoomOut.parentNode)\n+OpenLayers.Layer.Google.cache = {};\n+OpenLayers.Layer.Google.v2 = {\n+ termsOfUse: null,\n+ poweredBy: null,\n+ dragObject: null,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = G_NORMAL_MAP\n }\n- eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n- this.zoomInLink = zoomIn;\n- this.zoomOutLink = zoomOut;\n- return div\n- },\n- getOrCreateLinks: function(el) {\n- var zoomIn = document.getElementById(this.zoomInId),\n- zoomOut = document.getElementById(this.zoomOutId);\n- if (!zoomIn) {\n- zoomIn = document.createElement(\"a\");\n- zoomIn.href = \"#zoomIn\";\n- zoomIn.appendChild(document.createTextNode(this.zoomInText));\n- zoomIn.className = \"olControlZoomIn\";\n- el.appendChild(zoomIn)\n+ var mapObject, termsOfUse, poweredBy;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ mapObject = cache.mapObject;\n+ termsOfUse = cache.termsOfUse;\n+ poweredBy = cache.poweredBy;\n+ ++cache.count\n+ } else {\n+ var container = this.map.viewPortDiv;\n+ var div = document.createElement(\"div\");\n+ div.id = this.map.id + \"_GMap2Container\";\n+ div.style.position = \"absolute\";\n+ div.style.width = \"100%\";\n+ div.style.height = \"100%\";\n+ container.appendChild(div);\n+ try {\n+ mapObject = new GMap2(div);\n+ termsOfUse = div.lastChild;\n+ container.appendChild(termsOfUse);\n+ termsOfUse.style.zIndex = \"1100\";\n+ termsOfUse.style.right = \"\";\n+ termsOfUse.style.bottom = \"\";\n+ termsOfUse.className = \"olLayerGoogleCopyright\";\n+ poweredBy = div.lastChild;\n+ container.appendChild(poweredBy);\n+ poweredBy.style.zIndex = \"1100\";\n+ poweredBy.style.right = \"\";\n+ poweredBy.style.bottom = \"\";\n+ poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\"\n+ } catch (e) {\n+ throw e\n+ }\n+ OpenLayers.Layer.Google.cache[this.map.id] = {\n+ mapObject: mapObject,\n+ termsOfUse: termsOfUse,\n+ poweredBy: poweredBy,\n+ count: 1\n+ }\n }\n- OpenLayers.Element.addClass(zoomIn, \"olButton\");\n- if (!zoomOut) {\n- zoomOut = document.createElement(\"a\");\n- zoomOut.href = \"#zoomOut\";\n- zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n- zoomOut.className = \"olControlZoomOut\";\n- el.appendChild(zoomOut)\n+ this.mapObject = mapObject;\n+ this.termsOfUse = termsOfUse;\n+ this.poweredBy = poweredBy;\n+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) {\n+ this.mapObject.addMapType(this.type)\n }\n- OpenLayers.Element.addClass(zoomOut, \"olButton\");\n- return {\n- zoomIn: zoomIn,\n- zoomOut: zoomOut\n+ if (typeof mapObject.getDragObject == \"function\") {\n+ this.dragObject = mapObject.getDragObject()\n+ } else {\n+ this.dragPanMapObject = null\n }\n- },\n- onZoomClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.zoomInLink) {\n- this.map.zoomIn()\n- } else if (button === this.zoomOutLink) {\n- this.map.zoomOut()\n+ if (this.isBaseLayer === false) {\n+ this.setGMapVisibility(this.div.style.display !== \"none\")\n }\n },\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onZoomClick)\n+ onMapResize: function() {\n+ if (this.visibility && this.mapObject.isLoaded()) {\n+ this.mapObject.checkResize()\n+ } else {\n+ if (!this._resized) {\n+ var layer = this;\n+ var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n+ GEvent.removeListener(handle);\n+ delete layer._resized;\n+ layer.mapObject.checkResize();\n+ layer.moveTo(layer.map.getCenter(), layer.map.getZoom())\n+ })\n+ }\n+ this._resized = true\n }\n- delete this.zoomInLink;\n- delete this.zoomOutLink;\n- OpenLayers.Control.prototype.destroy.apply(this)\n },\n- CLASS_NAME: \"OpenLayers.Control.Zoom\"\n-});\n-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n- EVENTMAP: {\n- click: {\n- in: \"click\",\n- out: \"clickout\"\n- },\n- mousemove: {\n- in: \"over\",\n- out: \"out\"\n- },\n- dblclick: {\n- in: \"dblclick\",\n- out: null\n- },\n- mousedown: {\n- in: null,\n- out: null\n- },\n- mouseup: {\n- in: null,\n- out: null\n- },\n- touchstart: {\n- in: \"click\",\n- out: \"clickout\"\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject.getContainer();\n+ if (visible === true) {\n+ this.mapObject.setMapType(this.type);\n+ container.style.display = \"\";\n+ this.termsOfUse.style.left = \"\";\n+ this.termsOfUse.style.display = \"\";\n+ this.poweredBy.style.display = \"\";\n+ cache.displayed = this.id\n+ } else {\n+ if (cache.displayed === this.id) {\n+ delete cache.displayed\n+ }\n+ if (!cache.displayed) {\n+ container.style.display = \"none\";\n+ this.termsOfUse.style.display = \"none\";\n+ this.termsOfUse.style.left = \"-9999px\";\n+ this.poweredBy.style.display = \"none\"\n+ }\n+ }\n }\n },\n- feature: null,\n- lastFeature: null,\n- down: null,\n- up: null,\n- clickTolerance: 4,\n- geometryTypes: null,\n- stopClick: true,\n- stopDown: true,\n- stopUp: false,\n- initialize: function(control, layer, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.layer = layer\n+ getMapContainer: function() {\n+ return this.mapObject.getContainer()\n },\n- touchstart: function(evt) {\n- this.startTouch();\n- return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon))\n+ }\n+ return moBounds\n },\n- touchmove: function(evt) {\n- OpenLayers.Event.preventDefault(evt)\n+ setMapObjectCenter: function(center, zoom) {\n+ this.mapObject.setCenter(center, zoom)\n },\n- mousedown: function(evt) {\n- if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n- this.down = evt.xy\n- }\n- return this.handle(evt) ? !this.stopDown : true\n+ dragPanMapObject: function(dX, dY) {\n+ this.dragObject.moveBy(new GSize(-dX, dY))\n },\n- mouseup: function(evt) {\n- this.up = evt.xy;\n- return this.handle(evt) ? !this.stopUp : true\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ return this.mapObject.fromContainerPixelToLatLng(moPixel)\n },\n- click: function(evt) {\n- return this.handle(evt) ? !this.stopClick : true\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ return this.mapObject.fromLatLngToContainerPixel(moLonLat)\n },\n- mousemove: function(evt) {\n- if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n- return true\n- }\n- this.handle(evt);\n- return true\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds)\n },\n- dblclick: function(evt) {\n- return !this.handle(evt)\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon)\n+ } else {\n+ gLatLng = new GLatLng(lat, lon)\n+ }\n+ return gLatLng\n },\n- geometryTypeMatches: function(feature) {\n- return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new GPoint(x, y)\n+ }\n+};\n+OpenLayers.Layer.Google.v3 = {\n+ DEFAULTS: {\n+ sphericalMercator: true,\n+ projection: \"EPSG:900913\"\n },\n- handle: function(evt) {\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n+ animationEnabled: true,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = google.maps.MapTypeId.ROADMAP\n }\n- var type = evt.type;\n- var handled = false;\n- var previouslyIn = !!this.feature;\n- var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n- this.feature = this.layer.getFeatureFromEvent(evt);\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n+ var mapObject;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ mapObject = cache.mapObject;\n+ ++cache.count\n+ } else {\n+ var center = this.map.getCenter();\n+ var container = document.createElement(\"div\");\n+ container.className = \"olForeignContainer\";\n+ container.style.width = \"100%\";\n+ container.style.height = \"100%\";\n+ mapObject = new google.maps.Map(container, {\n+ center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n+ zoom: this.map.getZoom() || 0,\n+ mapTypeId: this.type,\n+ disableDefaultUI: true,\n+ keyboardShortcuts: false,\n+ draggable: false,\n+ disableDoubleClickZoom: true,\n+ scrollwheel: false,\n+ streetViewControl: false\n+ });\n+ var googleControl = document.createElement(\"div\");\n+ googleControl.style.width = \"100%\";\n+ googleControl.style.height = \"100%\";\n+ mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n+ cache = {\n+ googleControl: googleControl,\n+ mapObject: mapObject,\n+ count: 1\n+ };\n+ OpenLayers.Layer.Google.cache[this.map.id] = cache\n }\n- if (this.lastFeature && !this.lastFeature.layer) {\n- this.lastFeature = null\n+ this.mapObject = mapObject;\n+ this.setGMapVisibility(this.visibility)\n+ },\n+ onMapResize: function() {\n+ if (this.visibility) {\n+ google.maps.event.trigger(this.mapObject, \"resize\")\n }\n- if (this.feature) {\n- if (type === \"touchstart\") {\n- OpenLayers.Event.preventDefault(evt)\n+ },\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ var map = this.map;\n+ if (cache) {\n+ var type = this.type;\n+ var layers = map.layers;\n+ var layer;\n+ for (var i = layers.length - 1; i >= 0; --i) {\n+ layer = layers[i];\n+ if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) {\n+ type = layer.type;\n+ visible = true;\n+ break\n+ }\n }\n- var inNew = this.feature != this.lastFeature;\n- if (this.geometryTypeMatches(this.feature)) {\n- if (previouslyIn && inNew) {\n- if (this.lastFeature) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n+ var container = this.mapObject.getDiv();\n+ if (visible === true) {\n+ if (container.parentNode !== map.div) {\n+ if (!cache.rendered) {\n+ var me = this;\n+ google.maps.event.addListenerOnce(this.mapObject, \"tilesloaded\", function() {\n+ cache.rendered = true;\n+ me.setGMapVisibility(me.getVisibility());\n+ me.moveTo(me.map.getCenter())\n+ })\n+ } else {\n+ map.div.appendChild(container);\n+ cache.googleControl.appendChild(map.viewPortDiv);\n+ google.maps.event.trigger(this.mapObject, \"resize\")\n }\n- this.triggerCallback(type, \"in\", [this.feature])\n- } else if (!previouslyIn || click) {\n- this.triggerCallback(type, \"in\", [this.feature])\n- }\n- this.lastFeature = this.feature;\n- handled = true\n- } else {\n- if (this.lastFeature && (previouslyIn && inNew || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n- this.feature = null\n+ this.mapObject.setMapTypeId(type)\n+ } else if (cache.googleControl.hasChildNodes()) {\n+ map.div.appendChild(map.viewPortDiv);\n+ map.div.removeChild(container)\n }\n- } else if (this.lastFeature && (previouslyIn || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n- return handled\n },\n- triggerCallback: function(type, mode, args) {\n- var key = this.EVENTMAP[type][mode];\n- if (key) {\n- if (type == \"click\" && this.up && this.down) {\n- var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n- if (dpx <= this.clickTolerance) {\n- this.callback(key, args)\n- }\n- this.up = this.down = null\n- } else {\n- this.callback(key, args)\n- }\n+ getMapContainer: function() {\n+ return this.mapObject.getDiv()\n+ },\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon))\n }\n+ return moBounds\n },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.moveLayerToTop();\n- this.map.events.on({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n- });\n- activated = true\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ var size = this.map.getSize();\n+ var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n+ var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n+ var res = this.map.getResolution();\n+ var delta_x = moPixel.x - size.w / 2;\n+ var delta_y = moPixel.y - size.h / 2;\n+ var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res);\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent)\n }\n- return activated\n+ return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat)\n },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.feature = null;\n- this.lastFeature = null;\n- this.down = null;\n- this.up = null;\n- this.map.events.un({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ var res = this.map.getResolution();\n+ var extent = this.map.getExtent();\n+ return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat))\n+ },\n+ setMapObjectCenter: function(center, zoom) {\n+ if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n+ var mapContainer = this.getMapContainer();\n+ google.maps.event.addListenerOnce(this.mapObject, \"idle\", function() {\n+ mapContainer.style.visibility = \"\"\n });\n- deactivated = true\n+ mapContainer.style.visibility = \"hidden\"\n }\n- return deactivated\n+ this.mapObject.setOptions({\n+ center: center,\n+ zoom: zoom\n+ })\n },\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop()\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds)\n+ },\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon)\n+ } else {\n+ gLatLng = new google.maps.LatLng(lat, lon)\n }\n+ return gLatLng\n },\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index)\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new google.maps.Point(x, y)\n+ }\n+};\n+OpenLayers.Protocol = OpenLayers.Class({\n+ format: null,\n+ options: null,\n+ autoDestroy: true,\n+ defaultFilter: null,\n+ initialize: function(options) {\n+ options = options || {};\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options\n },\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n- this.layer.setZIndex(index)\n+ mergeWithDefaultFilter: function(filter) {\n+ var merged;\n+ if (filter && this.defaultFilter) {\n+ merged = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.defaultFilter, filter]\n+ })\n } else {\n- this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n+ merged = filter || this.defaultFilter || undefined\n }\n+ return merged\n },\n- CLASS_NAME: \"OpenLayers.Handler.Feature\"\n+ destroy: function() {\n+ this.options = null;\n+ this.format = null\n+ },\n+ read: function(options) {\n+ options = options || {};\n+ options.filter = this.mergeWithDefaultFilter(options.filter)\n+ },\n+ create: function() {},\n+ update: function() {},\n+ delete: function() {},\n+ commit: function() {},\n+ abort: function(response) {},\n+ createCallback: function(method, response, options) {\n+ return OpenLayers.Function.bind(function() {\n+ method.apply(this, [response, options])\n+ }, this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Protocol\"\n });\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n- displayInLayerSwitcher: false,\n- layers: null,\n- display: function() {},\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature\n- }\n- }\n+OpenLayers.Protocol.Response = OpenLayers.Class({\n+ code: null,\n+ requestType: null,\n+ last: true,\n+ features: null,\n+ data: null,\n+ reqFeatures: null,\n+ priv: null,\n+ error: null,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n },\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer)\n+ success: function() {\n+ return this.code > 0\n },\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n+ CLASS_NAME: \"OpenLayers.Protocol.Response\"\n+});\n+OpenLayers.Protocol.Response.SUCCESS = 1;\n+OpenLayers.Protocol.Response.FAILURE = 0;\n+OpenLayers.ProxyHost = \"\";\n+if (!OpenLayers.Request) {\n+ OpenLayers.Request = {}\n+}\n+OpenLayers.Util.extend(OpenLayers.Request, {\n+ DEFAULT_CONFIG: {\n+ method: \"GET\",\n+ url: window.location.href,\n+ async: true,\n+ user: undefined,\n+ password: undefined,\n+ params: null,\n+ proxy: OpenLayers.ProxyHost,\n+ headers: {},\n+ data: null,\n+ callback: function() {},\n+ success: null,\n+ failure: null,\n+ scope: null\n },\n- collectRoots: function() {\n- var layer;\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer)\n+ URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n+ events: new OpenLayers.Events(this),\n+ makeSameOrigin: function(url, proxy) {\n+ var sameOrigin = url.indexOf(\"http\") !== 0;\n+ var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n+ if (urlParts) {\n+ var location = window.location;\n+ sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname;\n+ var uPort = urlParts[4],\n+ lPort = location.port;\n+ if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n+ sameOrigin = sameOrigin && uPort == lPort\n }\n }\n- },\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer)\n+ if (!sameOrigin) {\n+ if (proxy) {\n+ if (typeof proxy == \"function\") {\n+ url = proxy(url)\n+ } else {\n+ url = proxy + encodeURIComponent(url)\n+ }\n }\n }\n+ return url\n },\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots()\n+ issue: function(config) {\n+ var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, {\n+ proxy: OpenLayers.ProxyHost\n+ });\n+ config = config || {};\n+ config.headers = config.headers || {};\n+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n+ config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n+ var customRequestedWithHeader = false,\n+ headerKey;\n+ for (headerKey in config.headers) {\n+ if (config.headers.hasOwnProperty(headerKey)) {\n+ if (headerKey.toLowerCase() === \"x-requested-with\") {\n+ customRequestedWithHeader = true\n+ }\n+ }\n }\n- },\n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n-});\n-OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n- multipleKey: null,\n- toggleKey: null,\n- multiple: false,\n- clickout: true,\n- toggle: false,\n- hover: false,\n- highlightOnly: false,\n- box: false,\n- onBeforeSelect: function() {},\n- onSelect: function() {},\n- onUnselect: function() {},\n- scope: null,\n- geometryTypes: null,\n- layer: null,\n- layers: null,\n- callbacks: null,\n- selectStyle: null,\n- renderIntent: \"select\",\n- handlers: null,\n- initialize: function(layers, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (this.scope === null) {\n- this.scope = this\n+ if (customRequestedWithHeader === false) {\n+ config.headers[\"X-Requested-With\"] = \"XMLHttpRequest\"\n }\n- this.initLayer(layers);\n- var callbacks = {\n- click: this.clickFeature,\n- clickout: this.clickoutFeature\n- };\n- if (this.hover) {\n- callbacks.over = this.overFeature;\n- callbacks.out = this.outFeature\n+ var request = new OpenLayers.Request.XMLHttpRequest;\n+ var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {}));\n+ url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n+ request.open(config.method, url, config.async, config.user, config.password);\n+ for (var header in config.headers) {\n+ request.setRequestHeader(header, config.headers[header])\n }\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlers = {\n- feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, {\n- geometryTypes: this.geometryTypes\n- })\n+ var events = this.events;\n+ var self = this;\n+ request.onreadystatechange = function() {\n+ if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n+ var proceed = events.triggerEvent(\"complete\", {\n+ request: request,\n+ config: config,\n+ requestUrl: url\n+ });\n+ if (proceed !== false) {\n+ self.runCallbacks({\n+ request: request,\n+ config: config,\n+ requestUrl: url\n+ })\n+ }\n+ }\n };\n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(this, {\n- done: this.selectBox\n- }, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- })\n- }\n- },\n- initLayer: function(layers) {\n- if (OpenLayers.Util.isArray(layers)) {\n- this.layers = layers;\n- this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + \"_container\", {\n- layers: layers\n- })\n+ if (config.async === false) {\n+ request.send(config.data)\n } else {\n- this.layer = layers\n+ window.setTimeout(function() {\n+ if (request.readyState !== 0) {\n+ request.send(config.data)\n+ }\n+ }, 0)\n }\n+ return request\n },\n- destroy: function() {\n- if (this.active && this.layers) {\n- this.map.removeLayer(this.layer)\n+ runCallbacks: function(options) {\n+ var request = options.request;\n+ var config = options.config;\n+ var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback;\n+ var success;\n+ if (config.success) {\n+ success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.layers) {\n- this.layer.destroy()\n+ var failure;\n+ if (config.failure) {\n+ failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure\n }\n- },\n- activate: function() {\n- if (!this.active) {\n- if (this.layers) {\n- this.map.addLayer(this.layer)\n- }\n- this.handlers.feature.activate();\n- if (this.box && this.handlers.box) {\n- this.handlers.box.activate()\n- }\n+ if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" && request.responseText) {\n+ request.status = 200\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments)\n- },\n- deactivate: function() {\n- if (this.active) {\n- this.handlers.feature.deactivate();\n- if (this.handlers.box) {\n- this.handlers.box.deactivate()\n+ complete(request);\n+ if (!request.status || request.status >= 200 && request.status < 300) {\n+ this.events.triggerEvent(\"success\", options);\n+ if (success) {\n+ success(request)\n }\n- if (this.layers) {\n- this.map.removeLayer(this.layer)\n+ }\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"failure\", options);\n+ if (failure) {\n+ failure(request)\n }\n }\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- unselectAll: function(options) {\n- var layers = this.layers || [this.layer],\n- layer, feature, l, numExcept;\n- for (l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- numExcept = 0;\n- if (layer.selectedFeatures != null) {\n- while (layer.selectedFeatures.length > numExcept) {\n- feature = layer.selectedFeatures[numExcept];\n- if (!options || options.except != feature) {\n- this.unselect(feature)\n- } else {\n- ++numExcept\n- }\n- }\n- }\n+ GET: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"GET\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ },\n+ POST: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"POST\"\n+ });\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\"\n }\n+ return OpenLayers.Request.issue(config)\n },\n- clickFeature: function(feature) {\n- if (!this.hover) {\n- var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1;\n- if (selected) {\n- if (this.toggleSelect()) {\n- this.unselect(feature)\n- } else if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- })\n- }\n- } else {\n- if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- })\n- }\n- this.select(feature)\n- }\n+ PUT: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"PUT\"\n+ });\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\"\n }\n+ return OpenLayers.Request.issue(config)\n },\n- multipleSelect: function() {\n- return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]\n+ DELETE: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"DELETE\"\n+ });\n+ return OpenLayers.Request.issue(config)\n },\n- toggleSelect: function() {\n- return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]\n+ HEAD: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"HEAD\"\n+ });\n+ return OpenLayers.Request.issue(config)\n },\n- clickoutFeature: function(feature) {\n- if (!this.hover && this.clickout) {\n- this.unselectAll()\n+ OPTIONS: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"OPTIONS\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ }\n+});\n+(function() {\n+ var oXMLHttpRequest = window.XMLHttpRequest;\n+ var bGecko = !!window.controllers,\n+ bIE = window.document.all && !window.opera,\n+ bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n+\n+ function fXMLHttpRequest() {\n+ this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n+ this._listeners = []\n+ }\n+\n+ function cXMLHttpRequest() {\n+ return new fXMLHttpRequest\n+ }\n+ cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n+ if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n+ cXMLHttpRequest.UNSENT = 0;\n+ cXMLHttpRequest.OPENED = 1;\n+ cXMLHttpRequest.HEADERS_RECEIVED = 2;\n+ cXMLHttpRequest.LOADING = 3;\n+ cXMLHttpRequest.DONE = 4;\n+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n+ cXMLHttpRequest.prototype.responseText = \"\";\n+ cXMLHttpRequest.prototype.responseXML = null;\n+ cXMLHttpRequest.prototype.status = 0;\n+ cXMLHttpRequest.prototype.statusText = \"\";\n+ cXMLHttpRequest.prototype.priority = \"NORMAL\";\n+ cXMLHttpRequest.prototype.onreadystatechange = null;\n+ cXMLHttpRequest.onreadystatechange = null;\n+ cXMLHttpRequest.onopen = null;\n+ cXMLHttpRequest.onsend = null;\n+ cXMLHttpRequest.onabort = null;\n+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n+ delete this._headers;\n+ if (arguments.length < 3) bAsync = true;\n+ this._async = bAsync;\n+ var oRequest = this,\n+ nState = this.readyState,\n+ fOnUnload;\n+ if (bIE && bAsync) {\n+ fOnUnload = function() {\n+ if (nState != cXMLHttpRequest.DONE) {\n+ fCleanTransport(oRequest);\n+ oRequest.abort()\n+ }\n+ };\n+ window.attachEvent(\"onunload\", fOnUnload)\n }\n- },\n- overFeature: function(feature) {\n- var layer = feature.layer;\n- if (this.hover) {\n- if (this.highlightOnly) {\n- this.highlight(feature)\n- } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature)\n+ if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments);\n+ if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser);\n+ else this._object.open(sMethod, sUrl, bAsync);\n+ this.readyState = cXMLHttpRequest.OPENED;\n+ fReadyStateChange(this);\n+ this._object.onreadystatechange = function() {\n+ if (bGecko && !bAsync) return;\n+ oRequest.readyState = oRequest._object.readyState;\n+ fSynchronizeValues(oRequest);\n+ if (oRequest._aborted) {\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+ return\n }\n- }\n- },\n- outFeature: function(feature) {\n- if (this.hover) {\n- if (this.highlightOnly) {\n- if (feature._lastHighlighter == this.id) {\n- if (feature._prevHighlighter && feature._prevHighlighter != this.id) {\n- delete feature._lastHighlighter;\n- var control = this.map.getControl(feature._prevHighlighter);\n- if (control) {\n- control.highlight(feature)\n- }\n- } else {\n- this.unhighlight(feature)\n- }\n- }\n- } else {\n- this.unselect(feature)\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ delete oRequest._data;\n+ fCleanTransport(oRequest);\n+ if (bIE && bAsync) window.detachEvent(\"onunload\", fOnUnload)\n }\n+ if (nState != oRequest.readyState) fReadyStateChange(oRequest);\n+ nState = oRequest.readyState\n }\n- },\n- highlight: function(feature) {\n- var layer = feature.layer;\n- var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- feature._prevHighlighter = feature._lastHighlighter;\n- feature._lastHighlighter = this.id;\n- var style = this.selectStyle || this.renderIntent;\n- layer.drawFeature(feature, style);\n- this.events.triggerEvent(\"featurehighlighted\", {\n- feature: feature\n- })\n+ };\n+\n+ function fXMLHttpRequest_send(oRequest) {\n+ oRequest._object.send(oRequest._data);\n+ if (bGecko && !oRequest._async) {\n+ oRequest.readyState = cXMLHttpRequest.OPENED;\n+ fSynchronizeValues(oRequest);\n+ while (oRequest.readyState < cXMLHttpRequest.DONE) {\n+ oRequest.readyState++;\n+ fReadyStateChange(oRequest);\n+ if (oRequest._aborted) return\n+ }\n }\n- },\n- unhighlight: function(feature) {\n- var layer = feature.layer;\n- if (feature._prevHighlighter == undefined) {\n- delete feature._lastHighlighter\n- } else if (feature._prevHighlighter == this.id) {\n- delete feature._prevHighlighter\n- } else {\n- feature._lastHighlighter = feature._prevHighlighter;\n- delete feature._prevHighlighter\n+ }\n+ cXMLHttpRequest.prototype.send = function(vData) {\n+ if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments);\n+ if (!arguments.length) vData = null;\n+ if (vData && vData.nodeType) {\n+ vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml;\n+ if (!this._headers[\"Content-Type\"]) this._object.setRequestHeader(\"Content-Type\", \"application/xml\")\n }\n- layer.drawFeature(feature, feature.style || feature.layer.style || \"default\");\n- this.events.triggerEvent(\"featureunhighlighted\", {\n- feature: feature\n+ this._data = vData;\n+ fXMLHttpRequest_send(this)\n+ };\n+ cXMLHttpRequest.prototype.abort = function() {\n+ if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments);\n+ if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true;\n+ this._object.abort();\n+ fCleanTransport(this);\n+ this.readyState = cXMLHttpRequest.UNSENT;\n+ delete this._data\n+ };\n+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n+ return this._object.getAllResponseHeaders()\n+ };\n+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n+ return this._object.getResponseHeader(sName)\n+ };\n+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n+ if (!this._headers) this._headers = {};\n+ this._headers[sName] = sValue;\n+ return this._object.setRequestHeader(sName, sValue)\n+ };\n+ cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return;\n+ this._listeners.push([sName, fHandler, bUseCapture])\n+ };\n+ cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break;\n+ if (oListener) this._listeners.splice(nIndex, 1)\n+ };\n+ cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n+ var oEventPseudo = {\n+ type: oEvent.type,\n+ target: this,\n+ currentTarget: this,\n+ eventPhase: 2,\n+ bubbles: oEvent.bubbles,\n+ cancelable: oEvent.cancelable,\n+ timeStamp: oEvent.timeStamp,\n+ stopPropagation: function() {},\n+ preventDefault: function() {},\n+ initEvent: function() {}\n+ };\n+ if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo])\n+ };\n+ cXMLHttpRequest.prototype.toString = function() {\n+ return \"[\" + \"object\" + \" \" + \"XMLHttpRequest\" + \"]\"\n+ };\n+ cXMLHttpRequest.toString = function() {\n+ return \"[\" + \"XMLHttpRequest\" + \"]\"\n+ };\n+\n+ function fReadyStateChange(oRequest) {\n+ if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest);\n+ oRequest.dispatchEvent({\n+ type: \"readystatechange\",\n+ bubbles: false,\n+ cancelable: false,\n+ timeStamp: new Date + 0\n })\n- },\n- select: function(feature) {\n- var cont = this.onBeforeSelect.call(this.scope, feature);\n- var layer = feature.layer;\n- if (cont !== false) {\n- cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n+ }\n+\n+ function fGetDocument(oRequest) {\n+ var oDocument = oRequest.responseXML,\n+ sResponse = oRequest.responseText;\n+ if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n+ oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n+ oDocument.async = false;\n+ oDocument.validateOnParse = false;\n+ oDocument.loadXML(sResponse)\n+ }\n+ if (oDocument)\n+ if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\") return null;\n+ return oDocument\n+ }\n+\n+ function fSynchronizeValues(oRequest) {\n+ try {\n+ oRequest.responseText = oRequest._object.responseText\n+ } catch (e) {}\n+ try {\n+ oRequest.responseXML = fGetDocument(oRequest._object)\n+ } catch (e) {}\n+ try {\n+ oRequest.status = oRequest._object.status\n+ } catch (e) {}\n+ try {\n+ oRequest.statusText = oRequest._object.statusText\n+ } catch (e) {}\n+ }\n+\n+ function fCleanTransport(oRequest) {\n+ oRequest._object.onreadystatechange = new window.Function\n+ }\n+ if (!window.Function.prototype.apply) {\n+ window.Function.prototype.apply = function(oRequest, oArguments) {\n+ if (!oArguments) oArguments = [];\n+ oRequest.__func = this;\n+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n+ delete oRequest.__func\n+ }\n+ }\n+ if (!OpenLayers.Request) {\n+ OpenLayers.Request = {}\n+ }\n+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest\n+})();\n+OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n+ url: null,\n+ headers: null,\n+ params: null,\n+ callback: null,\n+ scope: null,\n+ readWithPOST: false,\n+ updateWithPOST: false,\n+ deleteWithPOST: false,\n+ wildcarded: false,\n+ srsInBBOX: false,\n+ initialize: function(options) {\n+ options = options || {};\n+ this.params = {};\n+ this.headers = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ wildcarded: this.wildcarded,\n+ srsInBBOX: this.srsInBBOX\n });\n- if (cont !== false) {\n- layer.selectedFeatures.push(feature);\n- this.highlight(feature);\n- if (!this.handlers.feature.lastFeature) {\n- this.handlers.feature.lastFeature = layer.selectedFeatures[0]\n- }\n- layer.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- });\n- this.onSelect.call(this.scope, feature)\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params)\n }\n }\n },\n- unselect: function(feature) {\n- var layer = feature.layer;\n- this.unhighlight(feature);\n- OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n- layer.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n- });\n- this.onUnselect.call(this.scope, feature)\n+ destroy: function() {\n+ this.params = null;\n+ this.headers = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n },\n- selectBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);\n- if (!this.multipleSelect()) {\n- this.unselectAll()\n- }\n- var prevMultiple = this.multiple;\n- this.multiple = true;\n- var layers = this.layers || [this.layer];\n- this.events.triggerEvent(\"boxselectionstart\", {\n- layers: layers\n- });\n- var layer;\n- for (var l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- for (var i = 0, len = layer.features.length; i < len; ++i) {\n- var feature = layer.features[i];\n- if (!feature.getVisibility()) {\n- continue\n- }\n- if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n- if (bounds.toGeometry().intersects(feature.geometry)) {\n- if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature)\n- }\n- }\n- }\n- }\n- }\n- this.multiple = prevMultiple;\n- this.events.triggerEvent(\"boxselectionend\", {\n- layers: layers\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = options || {};\n+ options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(options.filter, options.params)\n+ }\n+ var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST;\n+ var resp = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ if (readWithPOST) {\n+ var headers = options.headers || {};\n+ headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ data: OpenLayers.Util.getParameterString(options.params),\n+ headers: headers\n+ })\n+ } else {\n+ resp.priv = OpenLayers.Request.GET({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ params: options.params,\n+ headers: options.headers\n })\n }\n+ return resp\n },\n- setMap: function(map) {\n- this.handlers.feature.setMap(map);\n- if (this.box) {\n- this.handlers.box.setMap(map)\n- }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ handleRead: function(resp, options) {\n+ this.handleResponse(resp, options)\n },\n- setLayer: function(layers) {\n- var isActive = this.active;\n- this.unselectAll();\n- this.deactivate();\n- if (this.layers) {\n- this.layer.destroy();\n- this.layers = null\n- }\n- this.initLayer(layers);\n- this.handlers.feature.layer = this.layer;\n- if (isActive) {\n- this.activate()\n- }\n+ create: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: features,\n+ requestType: \"create\"\n+ });\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleCreate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(features)\n+ });\n+ return resp\n },\n- CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n-});\n-OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n- layerStates: null,\n- layersDiv: null,\n- baseLayersDiv: null,\n- baseLayers: null,\n- dataLbl: null,\n- dataLayersDiv: null,\n- dataLayers: null,\n- minimizeDiv: null,\n- maximizeDiv: null,\n- ascending: true,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- this.layerStates = []\n+ handleCreate: function(resp, options) {\n+ this.handleResponse(resp, options)\n },\n- destroy: function() {\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n- this.map.events.un({\n- buttonclick: this.onButtonClick,\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n+ update: function(feature, options) {\n+ options = options || {};\n+ var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"update\"\n });\n- this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n+ resp.priv = OpenLayers.Request[method]({\n+ url: url,\n+ callback: this.createCallback(this.handleUpdate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(feature)\n+ });\n+ return resp\n },\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- this.map.events.on({\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n+ handleUpdate: function(resp, options) {\n+ this.handleResponse(resp, options)\n+ },\n+ delete: function(feature, options) {\n+ options = options || {};\n+ var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"delete\"\n });\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick)\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n+ var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n+ var requestOptions = {\n+ url: url,\n+ callback: this.createCallback(this.handleDelete, resp, options),\n+ headers: options.headers\n+ };\n+ if (this.deleteWithPOST) {\n+ requestOptions.data = this.format.write(feature)\n }\n+ resp.priv = OpenLayers.Request[method](requestOptions);\n+ return resp\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this);\n- this.loadContents();\n- if (!this.outsideViewport) {\n- this.minimizeControl()\n- }\n- this.redraw();\n- return this.div\n+ handleDelete: function(resp, options) {\n+ this.handleResponse(resp, options)\n },\n- onButtonClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.minimizeDiv) {\n- this.minimizeControl()\n- } else if (button === this.maximizeDiv) {\n- this.maximizeControl()\n- } else if (button._layerSwitcher === this.id) {\n- if (button[\"for\"]) {\n- button = document.getElementById(button[\"for\"])\n- }\n- if (!button.disabled) {\n- if (button.type == \"radio\") {\n- button.checked = true;\n- this.map.setBaseLayer(this.map.getLayer(button._layer))\n- } else {\n- button.checked = !button.checked;\n- this.updateMap()\n+ handleResponse: function(resp, options) {\n+ var request = resp.priv;\n+ if (options.callback) {\n+ if (request.status >= 200 && request.status < 300) {\n+ if (resp.requestType != \"delete\") {\n+ resp.features = this.parseFeatures(request)\n }\n+ resp.code = OpenLayers.Protocol.Response.SUCCESS\n+ } else {\n+ resp.code = OpenLayers.Protocol.Response.FAILURE\n }\n+ options.callback.call(options.scope, resp)\n }\n },\n- clearLayersArray: function(layersType) {\n- this[layersType + \"LayersDiv\"].innerHTML = \"\";\n- this[layersType + \"Layers\"] = []\n- },\n- checkRedraw: function() {\n- if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) {\n- return true\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- for (var i = 0, len = this.layerStates.length; i < len; i++) {\n- var layerState = this.layerStates[i];\n- var layer = this.map.layers[i];\n- if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) {\n- return true\n- }\n+ if (!doc || doc.length <= 0) {\n+ return null\n }\n- return false\n+ return this.format.read(doc)\n },\n- redraw: function() {\n- if (!this.checkRedraw()) {\n- return this.div\n- }\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n- var containsOverlays = false;\n- var containsBaseLayers = false;\n- var len = this.map.layers.length;\n- this.layerStates = new Array(len);\n- for (var i = 0; i < len; i++) {\n- var layer = this.map.layers[i];\n- this.layerStates[i] = {\n- name: layer.name,\n- visibility: layer.visibility,\n- inRange: layer.inRange,\n- id: layer.id\n+ commit: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = [],\n+ nResponses = 0;\n+ var types = {};\n+ types[OpenLayers.State.INSERT] = [];\n+ types[OpenLayers.State.UPDATE] = [];\n+ types[OpenLayers.State.DELETE] = [];\n+ var feature, list, requestFeatures = [];\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ list = types[feature.state];\n+ if (list) {\n+ list.push(feature);\n+ requestFeatures.push(feature)\n }\n }\n- var layers = this.map.layers.slice();\n- if (!this.ascending) {\n- layers.reverse()\n+ var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length;\n+ var success = true;\n+ var finalResponse = new OpenLayers.Protocol.Response({\n+ reqFeatures: requestFeatures\n+ });\n+\n+ function insertCallback(response) {\n+ var len = response.features ? response.features.length : 0;\n+ var fids = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ fids[i] = response.features[i].fid\n+ }\n+ finalResponse.insertIds = fids;\n+ callback.apply(this, [response])\n }\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var baseLayer = layer.isBaseLayer;\n- if (layer.displayInLayerSwitcher) {\n- if (baseLayer) {\n- containsBaseLayers = true\n- } else {\n- containsOverlays = true\n- }\n- var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility();\n- var inputElem = document.createElement(\"input\"),\n- inputId = OpenLayers.Util.createUniqueID(this.id + \"_input_\");\n- inputElem.id = inputId;\n- inputElem.name = baseLayer ? this.id + \"_baseLayers\" : layer.name;\n- inputElem.type = baseLayer ? \"radio\" : \"checkbox\";\n- inputElem.value = layer.name;\n- inputElem.checked = checked;\n- inputElem.defaultChecked = checked;\n- inputElem.className = \"olButton\";\n- inputElem._layer = layer.id;\n- inputElem._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- inputElem.disabled = true\n- }\n- var labelSpan = document.createElement(\"label\");\n- labelSpan[\"for\"] = inputElem.id;\n- OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n- labelSpan._layer = layer.id;\n- labelSpan._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- labelSpan.style.color = \"gray\"\n+\n+ function callback(response) {\n+ this.callUserCallback(response, options);\n+ success = success && response.success();\n+ nResponses++;\n+ if (nResponses >= nRequests) {\n+ if (options.callback) {\n+ finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE;\n+ options.callback.apply(options.scope, [finalResponse])\n }\n- labelSpan.innerHTML = layer.name;\n- labelSpan.style.verticalAlign = baseLayer ? \"bottom\" : \"baseline\";\n- var br = document.createElement(\"br\");\n- var groupArray = baseLayer ? this.baseLayers : this.dataLayers;\n- groupArray.push({\n- layer: layer,\n- inputElem: inputElem,\n- labelSpan: labelSpan\n- });\n- var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv;\n- groupDiv.appendChild(inputElem);\n- groupDiv.appendChild(labelSpan);\n- groupDiv.appendChild(br)\n }\n }\n- this.dataLbl.style.display = containsOverlays ? \"\" : \"none\";\n- this.baseLbl.style.display = containsBaseLayers ? \"\" : \"none\";\n- return this.div\n- },\n- updateMap: function() {\n- for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n- var layerEntry = this.baseLayers[i];\n- if (layerEntry.inputElem.checked) {\n- this.map.setBaseLayer(layerEntry.layer, false)\n- }\n+ var queue = types[OpenLayers.State.INSERT];\n+ if (queue.length > 0) {\n+ resp.push(this.create(queue, OpenLayers.Util.applyDefaults({\n+ callback: insertCallback,\n+ scope: this\n+ }, options.create)))\n }\n- for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n- var layerEntry = this.dataLayers[i];\n- layerEntry.layer.setVisibility(layerEntry.inputElem.checked)\n+ queue = types[OpenLayers.State.UPDATE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options.update)))\n }\n- },\n- maximizeControl: function(e) {\n- this.div.style.width = \"\";\n- this.div.style.height = \"\";\n- this.showControls(false);\n- if (e != null) {\n- OpenLayers.Event.stop(e)\n+ queue = types[OpenLayers.State.DELETE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this[\"delete\"](queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options[\"delete\"])))\n }\n+ return resp\n },\n- minimizeControl: function(e) {\n- this.div.style.width = \"0px\";\n- this.div.style.height = \"0px\";\n- this.showControls(true);\n- if (e != null) {\n- OpenLayers.Event.stop(e)\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort()\n }\n },\n- showControls: function(minimize) {\n- this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n- this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n- this.layersDiv.style.display = minimize ? \"none\" : \"\"\n- },\n- loadContents: function() {\n- this.layersDiv = document.createElement(\"div\");\n- this.layersDiv.id = this.id + \"_layersDiv\";\n- OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n- this.baseLbl = document.createElement(\"div\");\n- this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n- OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n- this.baseLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n- this.dataLbl = document.createElement(\"div\");\n- this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n- OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n- this.dataLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n- if (this.ascending) {\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv)\n- } else {\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv)\n+ callUserCallback: function(resp, options) {\n+ var opt = options[resp.requestType];\n+ if (opt && opt.callback) {\n+ opt.callback.call(opt.scope, resp)\n }\n- this.div.appendChild(this.layersDiv);\n- var img = OpenLayers.Util.getImageLocation(\"layer-switcher-maximize.png\");\n- this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MaximizeDiv\", null, null, img, \"absolute\");\n- OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n- this.maximizeDiv.style.display = \"none\";\n- this.div.appendChild(this.maximizeDiv);\n- var img = OpenLayers.Util.getImageLocation(\"layer-switcher-minimize.png\");\n- this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MinimizeDiv\", null, null, img, \"absolute\");\n- OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n- this.minimizeDiv.style.display = \"none\";\n- this.div.appendChild(this.minimizeDiv)\n },\n- CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+ CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n });\n OpenLayers.ElementsIndexer = OpenLayers.Class({\n maxZIndex: null,\n order: null,\n indices: null,\n compare: null,\n initialize: function(yOrdering) {\n@@ -14214,674 +14611,277 @@\n OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n t: 0,\n b: -1\n };\n OpenLayers.Renderer.SVG.preventDefault = function(e) {\n OpenLayers.Event.preventDefault(e)\n };\n-OpenLayers.Protocol = OpenLayers.Class({\n- format: null,\n+OpenLayers.Strategy = OpenLayers.Class({\n+ layer: null,\n options: null,\n+ active: null,\n+ autoActivate: true,\n autoDestroy: true,\n- defaultFilter: null,\n initialize: function(options) {\n- options = options || {};\n OpenLayers.Util.extend(this, options);\n- this.options = options\n- },\n- mergeWithDefaultFilter: function(filter) {\n- var merged;\n- if (filter && this.defaultFilter) {\n- merged = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.defaultFilter, filter]\n- })\n- } else {\n- merged = filter || this.defaultFilter || undefined\n- }\n- return merged\n+ this.options = options;\n+ this.active = false\n },\n destroy: function() {\n- this.options = null;\n- this.format = null\n- },\n- read: function(options) {\n- options = options || {};\n- options.filter = this.mergeWithDefaultFilter(options.filter)\n- },\n- create: function() {},\n- update: function() {},\n- delete: function() {},\n- commit: function() {},\n- abort: function(response) {},\n- createCallback: function(method, response, options) {\n- return OpenLayers.Function.bind(function() {\n- method.apply(this, [response, options])\n- }, this)\n- },\n- CLASS_NAME: \"OpenLayers.Protocol\"\n-});\n-OpenLayers.Protocol.Response = OpenLayers.Class({\n- code: null,\n- requestType: null,\n- last: true,\n- features: null,\n- data: null,\n- reqFeatures: null,\n- priv: null,\n- error: null,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- success: function() {\n- return this.code > 0\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null\n },\n- CLASS_NAME: \"OpenLayers.Protocol.Response\"\n-});\n-OpenLayers.Protocol.Response.SUCCESS = 1;\n-OpenLayers.Protocol.Response.FAILURE = 0;\n-OpenLayers.ProxyHost = \"\";\n-if (!OpenLayers.Request) {\n- OpenLayers.Request = {}\n-}\n-OpenLayers.Util.extend(OpenLayers.Request, {\n- DEFAULT_CONFIG: {\n- method: \"GET\",\n- url: window.location.href,\n- async: true,\n- user: undefined,\n- password: undefined,\n- params: null,\n- proxy: OpenLayers.ProxyHost,\n- headers: {},\n- data: null,\n- callback: function() {},\n- success: null,\n- failure: null,\n- scope: null\n+ setLayer: function(layer) {\n+ this.layer = layer\n },\n- URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n- events: new OpenLayers.Events(this),\n- makeSameOrigin: function(url, proxy) {\n- var sameOrigin = url.indexOf(\"http\") !== 0;\n- var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n- if (urlParts) {\n- var location = window.location;\n- sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname;\n- var uPort = urlParts[4],\n- lPort = location.port;\n- if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n- sameOrigin = sameOrigin && uPort == lPort\n- }\n- }\n- if (!sameOrigin) {\n- if (proxy) {\n- if (typeof proxy == \"function\") {\n- url = proxy(url)\n- } else {\n- url = proxy + encodeURIComponent(url)\n- }\n- }\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true\n }\n- return url\n+ return false\n },\n- issue: function(config) {\n- var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, {\n- proxy: OpenLayers.ProxyHost\n- });\n- config = config || {};\n- config.headers = config.headers || {};\n- config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n- config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n- var customRequestedWithHeader = false,\n- headerKey;\n- for (headerKey in config.headers) {\n- if (config.headers.hasOwnProperty(headerKey)) {\n- if (headerKey.toLowerCase() === \"x-requested-with\") {\n- customRequestedWithHeader = true\n- }\n- }\n- }\n- if (customRequestedWithHeader === false) {\n- config.headers[\"X-Requested-With\"] = \"XMLHttpRequest\"\n- }\n- var request = new OpenLayers.Request.XMLHttpRequest;\n- var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {}));\n- url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n- request.open(config.method, url, config.async, config.user, config.password);\n- for (var header in config.headers) {\n- request.setRequestHeader(header, config.headers[header])\n- }\n- var events = this.events;\n- var self = this;\n- request.onreadystatechange = function() {\n- if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n- var proceed = events.triggerEvent(\"complete\", {\n- request: request,\n- config: config,\n- requestUrl: url\n- });\n- if (proceed !== false) {\n- self.runCallbacks({\n- request: request,\n- config: config,\n- requestUrl: url\n- })\n- }\n- }\n- };\n- if (config.async === false) {\n- request.send(config.data)\n- } else {\n- window.setTimeout(function() {\n- if (request.readyState !== 0) {\n- request.send(config.data)\n- }\n- }, 0)\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true\n }\n- return request\n+ return false\n },\n- runCallbacks: function(options) {\n- var request = options.request;\n- var config = options.config;\n- var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback;\n- var success;\n- if (config.success) {\n- success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success\n- }\n- var failure;\n- if (config.failure) {\n- failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure\n- }\n- if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" && request.responseText) {\n- request.status = 200\n- }\n- complete(request);\n- if (!request.status || request.status >= 200 && request.status < 300) {\n- this.events.triggerEvent(\"success\", options);\n- if (success) {\n- success(request)\n- }\n- }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"failure\", options);\n- if (failure) {\n- failure(request)\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n+});\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n+ preload: false,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ refresh: this.load,\n+ scope: this\n+ });\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load()\n+ } else {\n+ this.layer.events.on({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n }\n }\n+ return activated\n },\n- GET: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"GET\"\n- });\n- return OpenLayers.Request.issue(config)\n- },\n- POST: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"POST\"\n- });\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\"\n- }\n- return OpenLayers.Request.issue(config)\n- },\n- PUT: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"PUT\"\n- });\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\"\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ refresh: this.load,\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n }\n- return OpenLayers.Request.issue(config)\n- },\n- DELETE: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"DELETE\"\n- });\n- return OpenLayers.Request.issue(config)\n+ return deactivated\n },\n- HEAD: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"HEAD\"\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n });\n- return OpenLayers.Request.issue(config)\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n },\n- OPTIONS: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"OPTIONS\"\n- });\n- return OpenLayers.Request.issue(config)\n- }\n-});\n-(function() {\n- var oXMLHttpRequest = window.XMLHttpRequest;\n- var bGecko = !!window.controllers,\n- bIE = window.document.all && !window.opera,\n- bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n-\n- function fXMLHttpRequest() {\n- this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n- this._listeners = []\n- }\n-\n- function cXMLHttpRequest() {\n- return new fXMLHttpRequest\n- }\n- cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n- if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n- cXMLHttpRequest.UNSENT = 0;\n- cXMLHttpRequest.OPENED = 1;\n- cXMLHttpRequest.HEADERS_RECEIVED = 2;\n- cXMLHttpRequest.LOADING = 3;\n- cXMLHttpRequest.DONE = 4;\n- cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n- cXMLHttpRequest.prototype.responseText = \"\";\n- cXMLHttpRequest.prototype.responseXML = null;\n- cXMLHttpRequest.prototype.status = 0;\n- cXMLHttpRequest.prototype.statusText = \"\";\n- cXMLHttpRequest.prototype.priority = \"NORMAL\";\n- cXMLHttpRequest.prototype.onreadystatechange = null;\n- cXMLHttpRequest.onreadystatechange = null;\n- cXMLHttpRequest.onopen = null;\n- cXMLHttpRequest.onsend = null;\n- cXMLHttpRequest.onabort = null;\n- cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n- delete this._headers;\n- if (arguments.length < 3) bAsync = true;\n- this._async = bAsync;\n- var oRequest = this,\n- nState = this.readyState,\n- fOnUnload;\n- if (bIE && bAsync) {\n- fOnUnload = function() {\n- if (nState != cXMLHttpRequest.DONE) {\n- fCleanTransport(oRequest);\n- oRequest.abort()\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local)\n+ }\n }\n- };\n- window.attachEvent(\"onunload\", fOnUnload)\n- }\n- if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments);\n- if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser);\n- else this._object.open(sMethod, sUrl, bAsync);\n- this.readyState = cXMLHttpRequest.OPENED;\n- fReadyStateChange(this);\n- this._object.onreadystatechange = function() {\n- if (bGecko && !bAsync) return;\n- oRequest.readyState = oRequest._object.readyState;\n- fSynchronizeValues(oRequest);\n- if (oRequest._aborted) {\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n- return\n- }\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- delete oRequest._data;\n- fCleanTransport(oRequest);\n- if (bIE && bAsync) window.detachEvent(\"onunload\", fOnUnload)\n- }\n- if (nState != oRequest.readyState) fReadyStateChange(oRequest);\n- nState = oRequest.readyState\n- }\n- };\n-\n- function fXMLHttpRequest_send(oRequest) {\n- oRequest._object.send(oRequest._data);\n- if (bGecko && !oRequest._async) {\n- oRequest.readyState = cXMLHttpRequest.OPENED;\n- fSynchronizeValues(oRequest);\n- while (oRequest.readyState < cXMLHttpRequest.DONE) {\n- oRequest.readyState++;\n- fReadyStateChange(oRequest);\n- if (oRequest._aborted) return\n }\n+ layer.addFeatures(features)\n }\n- }\n- cXMLHttpRequest.prototype.send = function(vData) {\n- if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments);\n- if (!arguments.length) vData = null;\n- if (vData && vData.nodeType) {\n- vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml;\n- if (!this._headers[\"Content-Type\"]) this._object.setRequestHeader(\"Content-Type\", \"application/xml\")\n- }\n- this._data = vData;\n- fXMLHttpRequest_send(this)\n- };\n- cXMLHttpRequest.prototype.abort = function() {\n- if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments);\n- if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true;\n- this._object.abort();\n- fCleanTransport(this);\n- this.readyState = cXMLHttpRequest.UNSENT;\n- delete this._data\n- };\n- cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n- return this._object.getAllResponseHeaders()\n- };\n- cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n- return this._object.getResponseHeader(sName)\n- };\n- cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n- if (!this._headers) this._headers = {};\n- this._headers[sName] = sValue;\n- return this._object.setRequestHeader(sName, sValue)\n- };\n- cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return;\n- this._listeners.push([sName, fHandler, bUseCapture])\n- };\n- cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break;\n- if (oListener) this._listeners.splice(nIndex, 1)\n- };\n- cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n- var oEventPseudo = {\n- type: oEvent.type,\n- target: this,\n- currentTarget: this,\n- eventPhase: 2,\n- bubbles: oEvent.bubbles,\n- cancelable: oEvent.cancelable,\n- timeStamp: oEvent.timeStamp,\n- stopPropagation: function() {},\n- preventDefault: function() {},\n- initEvent: function() {}\n- };\n- if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo])\n- };\n- cXMLHttpRequest.prototype.toString = function() {\n- return \"[\" + \"object\" + \" \" + \"XMLHttpRequest\" + \"]\"\n- };\n- cXMLHttpRequest.toString = function() {\n- return \"[\" + \"XMLHttpRequest\" + \"]\"\n- };\n-\n- function fReadyStateChange(oRequest) {\n- if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest);\n- oRequest.dispatchEvent({\n- type: \"readystatechange\",\n- bubbles: false,\n- cancelable: false,\n- timeStamp: new Date + 0\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n })\n- }\n-\n- function fGetDocument(oRequest) {\n- var oDocument = oRequest.responseXML,\n- sResponse = oRequest.responseText;\n- if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n- oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n- oDocument.async = false;\n- oDocument.validateOnParse = false;\n- oDocument.loadXML(sResponse)\n- }\n- if (oDocument)\n- if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\") return null;\n- return oDocument\n- }\n-\n- function fSynchronizeValues(oRequest) {\n- try {\n- oRequest.responseText = oRequest._object.responseText\n- } catch (e) {}\n- try {\n- oRequest.responseXML = fGetDocument(oRequest._object)\n- } catch (e) {}\n- try {\n- oRequest.status = oRequest._object.status\n- } catch (e) {}\n- try {\n- oRequest.statusText = oRequest._object.statusText\n- } catch (e) {}\n- }\n-\n- function fCleanTransport(oRequest) {\n- oRequest._object.onreadystatechange = new window.Function\n- }\n- if (!window.Function.prototype.apply) {\n- window.Function.prototype.apply = function(oRequest, oArguments) {\n- if (!oArguments) oArguments = [];\n- oRequest.__func = this;\n- oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n- delete oRequest.__func\n- }\n- }\n- if (!OpenLayers.Request) {\n- OpenLayers.Request = {}\n- }\n- OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest\n-})();\n-OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n- url: null,\n- headers: null,\n- params: null,\n- callback: null,\n- scope: null,\n- readWithPOST: false,\n- updateWithPOST: false,\n- deleteWithPOST: false,\n- wildcarded: false,\n- srsInBBOX: false,\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.headers = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- wildcarded: this.wildcarded,\n- srsInBBOX: this.srsInBBOX\n- });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params)\n- }\n- }\n },\n- destroy: function() {\n- this.params = null;\n- this.headers = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n- },\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = options || {};\n- options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(options.filter, options.params)\n- }\n- var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST;\n- var resp = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- if (readWithPOST) {\n- var headers = options.headers || {};\n- headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- data: OpenLayers.Util.getParameterString(options.params),\n- headers: headers\n- })\n- } else {\n- resp.priv = OpenLayers.Request.GET({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- params: options.params,\n- headers: options.headers\n- })\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n+});\n+OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n+ type: null,\n+ property: null,\n+ value: null,\n+ distance: null,\n+ distanceUnits: null,\n+ evaluate: function(feature) {\n+ var intersect = false;\n+ switch (this.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ case OpenLayers.Filter.Spatial.INTERSECTS:\n+ if (feature.geometry) {\n+ var geom = this.value;\n+ if (this.value.CLASS_NAME == \"OpenLayers.Bounds\") {\n+ geom = this.value.toGeometry()\n+ }\n+ if (feature.geometry.intersects(geom)) {\n+ intersect = true\n+ }\n+ }\n+ break;\n+ default:\n+ throw new Error(\"evaluate is not implemented for this filter type.\")\n }\n- return resp\n- },\n- handleRead: function(resp, options) {\n- this.handleResponse(resp, options)\n- },\n- create: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: features,\n- requestType: \"create\"\n- });\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleCreate, resp, options),\n- headers: options.headers,\n- data: this.format.write(features)\n- });\n- return resp\n- },\n- handleCreate: function(resp, options) {\n- this.handleResponse(resp, options)\n- },\n- update: function(feature, options) {\n- options = options || {};\n- var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"update\"\n- });\n- var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n- resp.priv = OpenLayers.Request[method]({\n- url: url,\n- callback: this.createCallback(this.handleUpdate, resp, options),\n- headers: options.headers,\n- data: this.format.write(feature)\n- });\n- return resp\n+ return intersect\n },\n- handleUpdate: function(resp, options) {\n- this.handleResponse(resp, options)\n+ clone: function() {\n+ var options = OpenLayers.Util.applyDefaults({\n+ value: this.value && this.value.clone && this.value.clone()\n+ }, this);\n+ return new OpenLayers.Filter.Spatial(options)\n },\n- delete: function(feature, options) {\n- options = options || {};\n- var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"delete\"\n- });\n- var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n- var requestOptions = {\n- url: url,\n- callback: this.createCallback(this.handleDelete, resp, options),\n- headers: options.headers\n- };\n- if (this.deleteWithPOST) {\n- requestOptions.data = this.format.write(feature)\n+ CLASS_NAME: \"OpenLayers.Filter.Spatial\"\n+});\n+OpenLayers.Filter.Spatial.BBOX = \"BBOX\";\n+OpenLayers.Filter.Spatial.INTERSECTS = \"INTERSECTS\";\n+OpenLayers.Filter.Spatial.DWITHIN = \"DWITHIN\";\n+OpenLayers.Filter.Spatial.WITHIN = \"WITHIN\";\n+OpenLayers.Filter.Spatial.CONTAINS = \"CONTAINS\";\n+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n+ bounds: null,\n+ resolution: null,\n+ ratio: 2,\n+ resFactor: null,\n+ response: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ moveend: this.update,\n+ refresh: this.update,\n+ visibilitychanged: this.update,\n+ scope: this\n+ });\n+ this.update()\n }\n- resp.priv = OpenLayers.Request[method](requestOptions);\n- return resp\n- },\n- handleDelete: function(resp, options) {\n- this.handleResponse(resp, options)\n+ return activated\n },\n- handleResponse: function(resp, options) {\n- var request = resp.priv;\n- if (options.callback) {\n- if (request.status >= 200 && request.status < 300) {\n- if (resp.requestType != \"delete\") {\n- resp.features = this.parseFeatures(request)\n- }\n- resp.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- resp.code = OpenLayers.Protocol.Response.FAILURE\n- }\n- options.callback.call(options.scope, resp)\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ moveend: this.update,\n+ refresh: this.update,\n+ visibilitychanged: this.update,\n+ scope: this\n+ })\n }\n+ return deactivated\n },\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n- }\n- if (!doc || doc.length <= 0) {\n- return null\n+ update: function(options) {\n+ var mapBounds = this.getMapBounds();\n+ if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) {\n+ this.calculateBounds(mapBounds);\n+ this.resolution = this.layer.map.getResolution();\n+ this.triggerRead(options)\n }\n- return this.format.read(doc)\n },\n- commit: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = [],\n- nResponses = 0;\n- var types = {};\n- types[OpenLayers.State.INSERT] = [];\n- types[OpenLayers.State.UPDATE] = [];\n- types[OpenLayers.State.DELETE] = [];\n- var feature, list, requestFeatures = [];\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- list = types[feature.state];\n- if (list) {\n- list.push(feature);\n- requestFeatures.push(feature)\n- }\n+ getMapBounds: function() {\n+ if (this.layer.map === null) {\n+ return null\n }\n- var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length;\n- var success = true;\n- var finalResponse = new OpenLayers.Protocol.Response({\n- reqFeatures: requestFeatures\n- });\n-\n- function insertCallback(response) {\n- var len = response.features ? response.features.length : 0;\n- var fids = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- fids[i] = response.features[i].fid\n- }\n- finalResponse.insertIds = fids;\n- callback.apply(this, [response])\n+ var bounds = this.layer.map.getExtent();\n+ if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) {\n+ bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection)\n }\n-\n- function callback(response) {\n- this.callUserCallback(response, options);\n- success = success && response.success();\n- nResponses++;\n- if (nResponses >= nRequests) {\n- if (options.callback) {\n- finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE;\n- options.callback.apply(options.scope, [finalResponse])\n- }\n- }\n+ return bounds\n+ },\n+ invalidBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds()\n }\n- var queue = types[OpenLayers.State.INSERT];\n- if (queue.length > 0) {\n- resp.push(this.create(queue, OpenLayers.Util.applyDefaults({\n- callback: insertCallback,\n- scope: this\n- }, options.create)))\n+ var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n+ if (!invalid && this.resFactor) {\n+ var ratio = this.resolution / this.layer.map.getResolution();\n+ invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor\n }\n- queue = types[OpenLayers.State.UPDATE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options.update)))\n+ return invalid\n+ },\n+ calculateBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds()\n }\n- queue = types[OpenLayers.State.DELETE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this[\"delete\"](queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options[\"delete\"])))\n+ var center = mapBounds.getCenterLonLat();\n+ var dataWidth = mapBounds.getWidth() * this.ratio;\n+ var dataHeight = mapBounds.getHeight() * this.ratio;\n+ this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2)\n+ },\n+ triggerRead: function(options) {\n+ if (this.response && !(options && options.noAbort === true)) {\n+ this.layer.protocol.abort(this.response);\n+ this.layer.events.triggerEvent(\"loadend\")\n }\n- return resp\n+ var evt = {\n+ filter: this.createFilter()\n+ };\n+ this.layer.events.triggerEvent(\"loadstart\", evt);\n+ this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ filter: evt.filter,\n+ callback: this.merge,\n+ scope: this\n+ }, options))\n },\n- abort: function(response) {\n- if (response) {\n- response.priv.abort()\n+ createFilter: function() {\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ value: this.bounds,\n+ projection: this.layer.projection\n+ });\n+ if (this.layer.filter) {\n+ filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.layer.filter, filter]\n+ })\n }\n+ return filter\n },\n- callUserCallback: function(resp, options) {\n- var opt = options[resp.requestType];\n- if (opt && opt.callback) {\n- opt.callback.call(opt.scope, resp)\n+ merge: function(resp) {\n+ this.layer.destroyFeatures();\n+ if (resp.success()) {\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local)\n+ }\n+ }\n+ }\n+ this.layer.addFeatures(features)\n+ }\n+ } else {\n+ this.bounds = null\n }\n+ this.response = null;\n+ this.layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ })\n },\n- CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n+ CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n });\n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.min.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.min.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -62,676 +62,218 @@\n var sourceIsEvt = typeof window.Event == \"function\" && source instanceof window.Event;\n if (!sourceIsEvt && source.hasOwnProperty && source.hasOwnProperty(\"toString\")) {\n destination.toString = source.toString\n }\n }\n return destination\n };\n-OpenLayers.Console = {\n- log: function() {},\n- debug: function() {},\n- info: function() {},\n- warn: function() {},\n- error: function() {},\n- userError: function(error) {\n- alert(error)\n- },\n- assert: function() {},\n- dir: function() {},\n- dirxml: function() {},\n- trace: function() {},\n- group: function() {},\n- groupEnd: function() {},\n- time: function() {},\n- timeEnd: function() {},\n- profile: function() {},\n- profileEnd: function() {},\n- count: function() {},\n- CLASS_NAME: \"OpenLayers.Console\"\n-};\n-(function() {\n- var scripts = document.getElementsByTagName(\"script\");\n- for (var i = 0, len = scripts.length; i < len; ++i) {\n- if (scripts[i].src.indexOf(\"firebug.js\") != -1) {\n- if (console) {\n- OpenLayers.Util.extend(OpenLayers.Console, console);\n- break\n- }\n- }\n- }\n-})();\n-OpenLayers.Popup = OpenLayers.Class({\n- events: null,\n- id: \"\",\n- lonlat: null,\n- div: null,\n- contentSize: null,\n- size: null,\n- contentHTML: null,\n- backgroundColor: \"\",\n- opacity: \"\",\n- border: \"\",\n- contentDiv: null,\n- groupDiv: null,\n- closeDiv: null,\n- autoSize: false,\n- minSize: null,\n- maxSize: null,\n- displayClass: \"olPopup\",\n- contentDisplayClass: \"olPopupContent\",\n- padding: 0,\n- disableFirefoxOverflowHack: false,\n- fixPadding: function() {\n- if (typeof this.padding == \"number\") {\n- this.padding = new OpenLayers.Bounds(this.padding, this.padding, this.padding, this.padding)\n- }\n- },\n- panMapIfOutOfView: false,\n- keepInMap: false,\n- closeOnMove: false,\n- map: null,\n- initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n- if (id == null) {\n- id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- }\n- this.id = id;\n- this.lonlat = lonlat;\n- this.contentSize = contentSize != null ? contentSize : new OpenLayers.Size(OpenLayers.Popup.WIDTH, OpenLayers.Popup.HEIGHT);\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML\n- }\n- this.backgroundColor = OpenLayers.Popup.COLOR;\n- this.opacity = OpenLayers.Popup.OPACITY;\n- this.border = OpenLayers.Popup.BORDER;\n- this.div = OpenLayers.Util.createDiv(this.id, null, null, null, null, null, \"hidden\");\n- this.div.className = this.displayClass;\n- var groupDivId = this.id + \"_GroupDiv\";\n- this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, null, \"relative\", null, \"hidden\");\n- var id = this.div.id + \"_contentDiv\";\n- this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), null, \"relative\");\n- this.contentDiv.className = this.contentDisplayClass;\n- this.groupDiv.appendChild(this.contentDiv);\n- this.div.appendChild(this.groupDiv);\n- if (closeBox) {\n- this.addCloseBox(closeBoxCallback)\n- }\n- this.registerEvents()\n+OpenLayers.Geometry = OpenLayers.Class({\n+ id: null,\n+ parent: null,\n+ bounds: null,\n+ initialize: function() {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n destroy: function() {\n this.id = null;\n- this.lonlat = null;\n- this.size = null;\n- this.contentHTML = null;\n- this.backgroundColor = null;\n- this.opacity = null;\n- this.border = null;\n- if (this.closeOnMove && this.map) {\n- this.map.events.unregister(\"movestart\", this, this.hide)\n- }\n- this.events.destroy();\n- this.events = null;\n- if (this.closeDiv) {\n- OpenLayers.Event.stopObservingElement(this.closeDiv);\n- this.groupDiv.removeChild(this.closeDiv)\n- }\n- this.closeDiv = null;\n- this.div.removeChild(this.groupDiv);\n- this.groupDiv = null;\n- if (this.map != null) {\n- this.map.removePopup(this)\n- }\n- this.map = null;\n- this.div = null;\n- this.autoSize = null;\n- this.minSize = null;\n- this.maxSize = null;\n- this.padding = null;\n- this.panMapIfOutOfView = null\n+ this.bounds = null\n },\n- draw: function(px) {\n- if (px == null) {\n- if (this.lonlat != null && this.map != null) {\n- px = this.map.getLayerPxFromLonLat(this.lonlat)\n- }\n- }\n- if (this.closeOnMove) {\n- this.map.events.register(\"movestart\", this, this.hide)\n- }\n- if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == \"firefox\") {\n- this.map.events.register(\"movestart\", this, function() {\n- var style = document.defaultView.getComputedStyle(this.contentDiv, null);\n- var currentOverflow = style.getPropertyValue(\"overflow\");\n- if (currentOverflow != \"hidden\") {\n- this.contentDiv._oldOverflow = currentOverflow;\n- this.contentDiv.style.overflow = \"hidden\"\n- }\n- });\n- this.map.events.register(\"moveend\", this, function() {\n- var oldOverflow = this.contentDiv._oldOverflow;\n- if (oldOverflow) {\n- this.contentDiv.style.overflow = oldOverflow;\n- this.contentDiv._oldOverflow = null\n- }\n- })\n- }\n- this.moveTo(px);\n- if (!this.autoSize && !this.size) {\n- this.setSize(this.contentSize)\n- }\n- this.setBackgroundColor();\n- this.setOpacity();\n- this.setBorder();\n- this.setContentHTML();\n- if (this.panMapIfOutOfView) {\n- this.panIntoView()\n- }\n- return this.div\n+ clone: function() {\n+ return new OpenLayers.Geometry\n },\n- updatePosition: function() {\n- if (this.lonlat && this.map) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- if (px) {\n- this.moveTo(px)\n- }\n+ setBounds: function(bounds) {\n+ if (bounds) {\n+ this.bounds = bounds.clone()\n }\n },\n- moveTo: function(px) {\n- if (px != null && this.div != null) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\"\n+ clearBounds: function() {\n+ this.bounds = null;\n+ if (this.parent) {\n+ this.parent.clearBounds()\n }\n },\n- visible: function() {\n- return OpenLayers.Element.visible(this.div)\n- },\n- toggle: function() {\n- if (this.visible()) {\n- this.hide()\n+ extendBounds: function(newBounds) {\n+ var bounds = this.getBounds();\n+ if (!bounds) {\n+ this.setBounds(newBounds)\n } else {\n- this.show()\n+ this.bounds.extend(newBounds)\n }\n },\n- show: function() {\n- this.div.style.display = \"\";\n- if (this.panMapIfOutOfView) {\n- this.panIntoView()\n+ getBounds: function() {\n+ if (this.bounds == null) {\n+ this.calculateBounds()\n }\n+ return this.bounds\n },\n- hide: function() {\n- this.div.style.display = \"none\"\n- },\n- setSize: function(contentSize) {\n- this.size = contentSize.clone();\n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right\n- }\n- this.size.w += wPadding;\n- this.size.h += hPadding;\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.contentSize.w += contentDivPadding.left + contentDivPadding.right;\n- this.contentSize.h += contentDivPadding.bottom + contentDivPadding.top\n- }\n- if (this.div != null) {\n- this.div.style.width = this.size.w + \"px\";\n- this.div.style.height = this.size.h + \"px\"\n- }\n- if (this.contentDiv != null) {\n- this.contentDiv.style.width = contentSize.w + \"px\";\n- this.contentDiv.style.height = contentSize.h + \"px\"\n+ calculateBounds: function() {},\n+ distanceTo: function(geometry, options) {},\n+ getVertices: function(nodes) {},\n+ atPoint: function(lonlat, toleranceLon, toleranceLat) {\n+ var atPoint = false;\n+ var bounds = this.getBounds();\n+ if (bounds != null && lonlat != null) {\n+ var dX = toleranceLon != null ? toleranceLon : 0;\n+ var dY = toleranceLat != null ? toleranceLat : 0;\n+ var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY);\n+ atPoint = toleranceBounds.containsLonLat(lonlat)\n }\n+ return atPoint\n },\n- updateSize: function() {\n- var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" + this.contentDiv.innerHTML + \"</div>\";\n- var containerElement = this.map ? this.map.div : document.body;\n- var realSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, null, {\n- displayClass: this.displayClass,\n- containerElement: containerElement\n- });\n- var safeSize = this.getSafeContentSize(realSize);\n- var newSize = null;\n- if (safeSize.equals(realSize)) {\n- newSize = realSize\n- } else {\n- var fixedSize = {\n- w: safeSize.w < realSize.w ? safeSize.w : null,\n- h: safeSize.h < realSize.h ? safeSize.h : null\n- };\n- if (fixedSize.w && fixedSize.h) {\n- newSize = safeSize\n- } else {\n- var clippedSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, fixedSize, {\n- displayClass: this.contentDisplayClass,\n- containerElement: containerElement\n- });\n- var currentOverflow = OpenLayers.Element.getStyle(this.contentDiv, \"overflow\");\n- if (currentOverflow != \"hidden\" && clippedSize.equals(safeSize)) {\n- var scrollBar = OpenLayers.Util.getScrollbarWidth();\n- if (fixedSize.w) {\n- clippedSize.h += scrollBar\n- } else {\n- clippedSize.w += scrollBar\n- }\n- }\n- newSize = this.getSafeContentSize(clippedSize)\n- }\n- }\n- this.setSize(newSize)\n+ getLength: function() {\n+ return 0\n },\n- setBackgroundColor: function(color) {\n- if (color != undefined) {\n- this.backgroundColor = color\n- }\n- if (this.div != null) {\n- this.div.style.backgroundColor = this.backgroundColor\n- }\n+ getArea: function() {\n+ return 0\n },\n- setOpacity: function(opacity) {\n- if (opacity != undefined) {\n- this.opacity = opacity\n- }\n- if (this.div != null) {\n- this.div.style.opacity = this.opacity;\n- this.div.style.filter = \"alpha(opacity=\" + this.opacity * 100 + \")\"\n- }\n+ getCentroid: function() {\n+ return null\n },\n- setBorder: function(border) {\n- if (border != undefined) {\n- this.border = border\n- }\n- if (this.div != null) {\n- this.div.style.border = this.border\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ string = OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this))\n+ } else {\n+ string = Object.prototype.toString.call(this)\n }\n+ return string\n },\n- setContentHTML: function(contentHTML) {\n- if (contentHTML != null) {\n- this.contentHTML = contentHTML\n- }\n- if (this.contentDiv != null && this.contentHTML != null && this.contentHTML != this.contentDiv.innerHTML) {\n- this.contentDiv.innerHTML = this.contentHTML;\n- if (this.autoSize) {\n- this.registerImageListeners();\n- this.updateSize()\n- }\n+ CLASS_NAME: \"OpenLayers.Geometry\"\n+});\n+OpenLayers.Geometry.fromWKT = function(wkt) {\n+ var geom;\n+ if (OpenLayers.Format && OpenLayers.Format.WKT) {\n+ var format = OpenLayers.Geometry.fromWKT.format;\n+ if (!format) {\n+ format = new OpenLayers.Format.WKT;\n+ OpenLayers.Geometry.fromWKT.format = format\n }\n- },\n- registerImageListeners: function() {\n- var onImgLoad = function() {\n- if (this.popup.id === null) {\n- return\n- }\n- this.popup.updateSize();\n- if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n- this.popup.panIntoView()\n- }\n- OpenLayers.Event.stopObserving(this.img, \"load\", this.img._onImgLoad)\n- };\n- var images = this.contentDiv.getElementsByTagName(\"img\");\n- for (var i = 0, len = images.length; i < len; i++) {\n- var img = images[i];\n- if (img.width == 0 || img.height == 0) {\n- var context = {\n- popup: this,\n- img: img\n- };\n- img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n- OpenLayers.Event.observe(img, \"load\", img._onImgLoad)\n+ var result = format.read(wkt);\n+ if (result instanceof OpenLayers.Feature.Vector) {\n+ geom = result.geometry\n+ } else if (OpenLayers.Util.isArray(result)) {\n+ var len = result.length;\n+ var components = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ components[i] = result[i].geometry\n }\n+ geom = new OpenLayers.Geometry.Collection(components)\n }\n- },\n- getSafeContentSize: function(size) {\n- var safeContentSize = size.clone();\n- var contentDivPadding = this.getContentDivPadding();\n- var wPadding = contentDivPadding.left + contentDivPadding.right;\n- var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n- this.fixPadding();\n- wPadding += this.padding.left + this.padding.right;\n- hPadding += this.padding.top + this.padding.bottom;\n- if (this.closeDiv) {\n- var closeDivWidth = parseInt(this.closeDiv.style.width);\n- wPadding += closeDivWidth + contentDivPadding.right\n- }\n- if (this.minSize) {\n- safeContentSize.w = Math.max(safeContentSize.w, this.minSize.w - wPadding);\n- safeContentSize.h = Math.max(safeContentSize.h, this.minSize.h - hPadding)\n- }\n- if (this.maxSize) {\n- safeContentSize.w = Math.min(safeContentSize.w, this.maxSize.w - wPadding);\n- safeContentSize.h = Math.min(safeContentSize.h, this.maxSize.h - hPadding)\n- }\n- if (this.map && this.map.size) {\n- var extraX = 0,\n- extraY = 0;\n- if (this.keepInMap && !this.panMapIfOutOfView) {\n- var px = this.map.getPixelFromLonLat(this.lonlat);\n- switch (this.relativePosition) {\n- case \"tr\":\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"tl\":\n- extraX = this.map.size.w - px.x;\n- extraY = this.map.size.h - px.y;\n- break;\n- case \"bl\":\n- extraX = this.map.size.w - px.x;\n- extraY = px.y;\n- break;\n- case \"br\":\n- extraX = px.x;\n- extraY = px.y;\n- break;\n- default:\n- extraX = px.x;\n- extraY = this.map.size.h - px.y;\n- break\n- }\n- }\n- var maxY = this.map.size.h - this.map.paddingForPopups.top - this.map.paddingForPopups.bottom - hPadding - extraY;\n- var maxX = this.map.size.w - this.map.paddingForPopups.left - this.map.paddingForPopups.right - wPadding - extraX;\n- safeContentSize.w = Math.min(safeContentSize.w, maxX);\n- safeContentSize.h = Math.min(safeContentSize.h, maxY)\n+ }\n+ return geom\n+};\n+OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n+ var point = options && options.point;\n+ var tolerance = options && options.tolerance;\n+ var intersection = false;\n+ var x11_21 = seg1.x1 - seg2.x1;\n+ var y11_21 = seg1.y1 - seg2.y1;\n+ var x12_11 = seg1.x2 - seg1.x1;\n+ var y12_11 = seg1.y2 - seg1.y1;\n+ var y22_21 = seg2.y2 - seg2.y1;\n+ var x22_21 = seg2.x2 - seg2.x1;\n+ var d = y22_21 * x12_11 - x22_21 * y12_11;\n+ var n1 = x22_21 * y11_21 - y22_21 * x11_21;\n+ var n2 = x12_11 * y11_21 - y12_11 * x11_21;\n+ if (d == 0) {\n+ if (n1 == 0 && n2 == 0) {\n+ intersection = true\n }\n- return safeContentSize\n- },\n- getContentDivPadding: function() {\n- var contentDivPadding = this._contentDivPadding;\n- if (!contentDivPadding) {\n- if (this.div.parentNode == null) {\n- this.div.style.display = \"none\";\n- document.body.appendChild(this.div)\n- }\n- contentDivPadding = new OpenLayers.Bounds(OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\"));\n- this._contentDivPadding = contentDivPadding;\n- if (this.div.parentNode == document.body) {\n- document.body.removeChild(this.div);\n- this.div.style.display = \"\"\n+ } else {\n+ var along1 = n1 / d;\n+ var along2 = n2 / d;\n+ if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n+ if (!point) {\n+ intersection = true\n+ } else {\n+ var x = seg1.x1 + along1 * x12_11;\n+ var y = seg1.y1 + along1 * y12_11;\n+ intersection = new OpenLayers.Geometry.Point(x, y)\n }\n }\n- return contentDivPadding\n- },\n- addCloseBox: function(callback) {\n- this.closeDiv = OpenLayers.Util.createDiv(this.id + \"_close\", null, {\n- w: 17,\n- h: 17\n- });\n- this.closeDiv.className = \"olPopupCloseBox\";\n- var contentDivPadding = this.getContentDivPadding();\n- this.closeDiv.style.right = contentDivPadding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top + \"px\";\n- this.groupDiv.appendChild(this.closeDiv);\n- var closePopup = callback || function(e) {\n- this.hide();\n- OpenLayers.Event.stop(e)\n- };\n- OpenLayers.Event.observe(this.closeDiv, \"touchend\", OpenLayers.Function.bindAsEventListener(closePopup, this));\n- OpenLayers.Event.observe(this.closeDiv, \"click\", OpenLayers.Function.bindAsEventListener(closePopup, this))\n- },\n- panIntoView: function() {\n- var mapSize = this.map.getSize();\n- var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(parseInt(this.div.style.left), parseInt(this.div.style.top)));\n- var newTL = origTL.clone();\n- if (origTL.x < this.map.paddingForPopups.left) {\n- newTL.x = this.map.paddingForPopups.left\n- } else if (origTL.x + this.size.w > mapSize.w - this.map.paddingForPopups.right) {\n- newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w\n- }\n- if (origTL.y < this.map.paddingForPopups.top) {\n- newTL.y = this.map.paddingForPopups.top\n- } else if (origTL.y + this.size.h > mapSize.h - this.map.paddingForPopups.bottom) {\n- newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h\n- }\n- var dx = origTL.x - newTL.x;\n- var dy = origTL.y - newTL.y;\n- this.map.pan(dx, dy)\n- },\n- registerEvents: function() {\n- this.events = new OpenLayers.Events(this, this.div, null, true);\n-\n- function onTouchstart(evt) {\n- OpenLayers.Event.stop(evt, true)\n- }\n- this.events.on({\n- mousedown: this.onmousedown,\n- mousemove: this.onmousemove,\n- mouseup: this.onmouseup,\n- click: this.onclick,\n- mouseout: this.onmouseout,\n- dblclick: this.ondblclick,\n- touchstart: onTouchstart,\n- scope: this\n- })\n- },\n- onmousedown: function(evt) {\n- this.mousedown = true;\n- OpenLayers.Event.stop(evt, true)\n- },\n- onmousemove: function(evt) {\n- if (this.mousedown) {\n- OpenLayers.Event.stop(evt, true)\n- }\n- },\n- onmouseup: function(evt) {\n- if (this.mousedown) {\n- this.mousedown = false;\n- OpenLayers.Event.stop(evt, true)\n- }\n- },\n- onclick: function(evt) {\n- OpenLayers.Event.stop(evt, true)\n- },\n- onmouseout: function(evt) {\n- this.mousedown = false\n- },\n- ondblclick: function(evt) {\n- OpenLayers.Event.stop(evt, true)\n- },\n- CLASS_NAME: \"OpenLayers.Popup\"\n-});\n-OpenLayers.Popup.WIDTH = 200;\n-OpenLayers.Popup.HEIGHT = 200;\n-OpenLayers.Popup.COLOR = \"white\";\n-OpenLayers.Popup.OPACITY = 1;\n-OpenLayers.Popup.BORDER = \"0px\";\n-OpenLayers.Util = OpenLayers.Util || {};\n-OpenLayers.Util.vendorPrefix = function() {\n- \"use strict\";\n- var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n- divStyle = document.createElement(\"div\").style,\n- cssCache = {},\n- jsCache = {};\n-\n- function domToCss(prefixedDom) {\n- if (!prefixedDom) {\n- return null\n- }\n- return prefixedDom.replace(/([A-Z])/g, function(c) {\n- return \"-\" + c.toLowerCase()\n- }).replace(/^ms-/, \"-ms-\")\n- }\n-\n- function css(property) {\n- if (cssCache[property] === undefined) {\n- var domProperty = property.replace(/(-[\\s\\S])/g, function(c) {\n- return c.charAt(1).toUpperCase()\n- });\n- var prefixedDom = style(domProperty);\n- cssCache[property] = domToCss(prefixedDom)\n- }\n- return cssCache[property]\n }\n-\n- function js(obj, property) {\n- if (jsCache[property] === undefined) {\n- var tmpProp, i = 0,\n- l = VENDOR_PREFIXES.length,\n- prefix, isStyleObj = typeof obj.cssText !== \"undefined\";\n- jsCache[property] = null;\n- for (; i < l; i++) {\n- prefix = VENDOR_PREFIXES[i];\n- if (prefix) {\n- if (!isStyleObj) {\n- prefix = prefix.toLowerCase()\n+ if (tolerance) {\n+ var dist;\n+ if (intersection) {\n+ if (point) {\n+ var segs = [seg1, seg2];\n+ var seg, x, y;\n+ outer: for (var i = 0; i < 2; ++i) {\n+ seg = segs[i];\n+ for (var j = 1; j < 3; ++j) {\n+ x = seg[\"x\" + j];\n+ y = seg[\"y\" + j];\n+ dist = Math.sqrt(Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2));\n+ if (dist < tolerance) {\n+ intersection.x = x;\n+ intersection.y = y;\n+ break outer\n+ }\n }\n- tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1)\n- } else {\n- tmpProp = property\n- }\n- if (obj[tmpProp] !== undefined) {\n- jsCache[property] = tmpProp;\n- break\n }\n }\n- }\n- return jsCache[property]\n- }\n-\n- function style(property) {\n- return js(divStyle, property)\n- }\n- return {\n- css: css,\n- js: js,\n- style: style,\n- cssCache: cssCache,\n- jsCache: jsCache\n- }\n-}();\n-OpenLayers.Animation = function(window) {\n- var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n- var isNative = !!requestAnimationFrame;\n- var requestFrame = function() {\n- var request = window[requestAnimationFrame] || function(callback, element) {\n- window.setTimeout(callback, 16)\n- };\n- return function(callback, element) {\n- request.apply(window, [callback, element])\n- }\n- }();\n- var counter = 0;\n- var loops = {};\n-\n- function start(callback, duration, element) {\n- duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n- var id = ++counter;\n- var start = +new Date;\n- loops[id] = function() {\n- if (loops[id] && +new Date - start <= duration) {\n- callback();\n- if (loops[id]) {\n- requestFrame(loops[id], element)\n+ } else {\n+ var segs = [seg1, seg2];\n+ var source, target, x, y, p, result;\n+ outer: for (var i = 0; i < 2; ++i) {\n+ source = segs[i];\n+ target = segs[(i + 1) % 2];\n+ for (var j = 1; j < 3; ++j) {\n+ p = {\n+ x: source[\"x\" + j],\n+ y: source[\"y\" + j]\n+ };\n+ result = OpenLayers.Geometry.distanceToSegment(p, target);\n+ if (result.distance < tolerance) {\n+ if (point) {\n+ intersection = new OpenLayers.Geometry.Point(p.x, p.y)\n+ } else {\n+ intersection = true\n+ }\n+ break outer\n+ }\n }\n- } else {\n- delete loops[id]\n }\n- };\n- requestFrame(loops[id], element);\n- return id\n+ }\n }\n-\n- function stop(id) {\n- delete loops[id]\n+ return intersection\n+};\n+OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n+ var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n+ result.distance = Math.sqrt(result.distance);\n+ return result\n+};\n+OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n+ var x0 = point.x;\n+ var y0 = point.y;\n+ var x1 = segment.x1;\n+ var y1 = segment.y1;\n+ var x2 = segment.x2;\n+ var y2 = segment.y2;\n+ var dx = x2 - x1;\n+ var dy = y2 - y1;\n+ var along = (dx * (x0 - x1) + dy * (y0 - y1)) / (Math.pow(dx, 2) + Math.pow(dy, 2));\n+ var x, y;\n+ if (along <= 0) {\n+ x = x1;\n+ y = y1\n+ } else if (along >= 1) {\n+ x = x2;\n+ y = y2\n+ } else {\n+ x = x1 + along * dx;\n+ y = y1 + along * dy\n }\n return {\n- isNative: isNative,\n- requestFrame: requestFrame,\n- start: start,\n- stop: stop\n+ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n+ x: x,\n+ y: y,\n+ along: along\n }\n-}(window);\n-OpenLayers.Kinetic = OpenLayers.Class({\n- threshold: 0,\n- deceleration: .0035,\n- nbPoints: 100,\n- delay: 200,\n- points: undefined,\n- timerId: undefined,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = []\n- },\n- update: function(xy) {\n- this.points.unshift({\n- xy: xy,\n- tick: (new Date).getTime()\n- });\n- if (this.points.length > this.nbPoints) {\n- this.points.pop()\n- }\n- },\n- end: function(xy) {\n- var last, now = (new Date).getTime();\n- for (var i = 0, l = this.points.length, point; i < l; i++) {\n- point = this.points[i];\n- if (now - point.tick > this.delay) {\n- break\n- }\n- last = point\n- }\n- if (!last) {\n- return\n- }\n- var time = (new Date).getTime() - last.tick;\n- var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2));\n- var speed = dist / time;\n- if (speed == 0 || speed < this.threshold) {\n- return\n- }\n- var theta = Math.asin((xy.y - last.xy.y) / dist);\n- if (last.xy.x <= xy.x) {\n- theta = Math.PI - theta\n- }\n- return {\n- speed: speed,\n- theta: theta\n- }\n- },\n- move: function(info, callback) {\n- var v0 = info.speed;\n- var fx = Math.cos(info.theta);\n- var fy = -Math.sin(info.theta);\n- var initialTime = (new Date).getTime();\n- var lastX = 0;\n- var lastY = 0;\n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return\n- }\n- var t = (new Date).getTime() - initialTime;\n- var p = -this.deceleration * Math.pow(t, 2) / 2 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\n- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true\n- }\n- args.x = x - lastX;\n- args.y = y - lastY;\n- lastX = x;\n- lastY = y;\n- callback(args.x, args.y, args.end)\n- };\n- this.timerId = OpenLayers.Animation.start(OpenLayers.Function.bind(timerCallback, this))\n- },\n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n+};\n OpenLayers.String = {\n startsWith: function(str, sub) {\n return str.indexOf(sub) == 0\n },\n contains: function(str, sub) {\n return str.indexOf(sub) != -1\n },\n@@ -1381,14 +923,47 @@\n if (sz != null) {\n equals = this.w == sz.w && this.h == sz.h || isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)\n }\n return equals\n },\n CLASS_NAME: \"OpenLayers.Size\"\n });\n+OpenLayers.Console = {\n+ log: function() {},\n+ debug: function() {},\n+ info: function() {},\n+ warn: function() {},\n+ error: function() {},\n+ userError: function(error) {\n+ alert(error)\n+ },\n+ assert: function() {},\n+ dir: function() {},\n+ dirxml: function() {},\n+ trace: function() {},\n+ group: function() {},\n+ groupEnd: function() {},\n+ time: function() {},\n+ timeEnd: function() {},\n+ profile: function() {},\n+ profileEnd: function() {},\n+ count: function() {},\n+ CLASS_NAME: \"OpenLayers.Console\"\n+};\n+(function() {\n+ var scripts = document.getElementsByTagName(\"script\");\n+ for (var i = 0, len = scripts.length; i < len; ++i) {\n+ if (scripts[i].src.indexOf(\"firebug.js\") != -1) {\n+ if (console) {\n+ OpenLayers.Util.extend(OpenLayers.Console, console);\n+ break\n+ }\n+ }\n+ }\n+})();\n OpenLayers.Lang = {\n code: null,\n defaultCode: \"en\",\n getCode: function() {\n if (!OpenLayers.Lang.code) {\n OpenLayers.Lang.setCode()\n }\n@@ -2234,3054 +1809,14 @@\n if (axis == \"lon\") {\n str += coordinate < 0 ? OpenLayers.i18n(\"W\") : OpenLayers.i18n(\"E\")\n } else {\n str += coordinate < 0 ? OpenLayers.i18n(\"S\") : OpenLayers.i18n(\"N\")\n }\n return str\n };\n-OpenLayers.Event = {\n- observers: false,\n- KEY_SPACE: 32,\n- KEY_BACKSPACE: 8,\n- KEY_TAB: 9,\n- KEY_RETURN: 13,\n- KEY_ESC: 27,\n- KEY_LEFT: 37,\n- KEY_UP: 38,\n- KEY_RIGHT: 39,\n- KEY_DOWN: 40,\n- KEY_DELETE: 46,\n- element: function(event) {\n- return event.target || event.srcElement\n- },\n- isSingleTouch: function(event) {\n- return event.touches && event.touches.length == 1\n- },\n- isMultiTouch: function(event) {\n- return event.touches && event.touches.length > 1\n- },\n- isLeftClick: function(event) {\n- return event.which && event.which == 1 || event.button && event.button == 1\n- },\n- isRightClick: function(event) {\n- return event.which && event.which == 3 || event.button && event.button == 2\n- },\n- stop: function(event, allowDefault) {\n- if (!allowDefault) {\n- OpenLayers.Event.preventDefault(event)\n- }\n- if (event.stopPropagation) {\n- event.stopPropagation()\n- } else {\n- event.cancelBubble = true\n- }\n- },\n- preventDefault: function(event) {\n- if (event.preventDefault) {\n- event.preventDefault()\n- } else {\n- event.returnValue = false\n- }\n- },\n- findElement: function(event, tagName) {\n- var element = OpenLayers.Event.element(event);\n- while (element.parentNode && (!element.tagName || element.tagName.toUpperCase() != tagName.toUpperCase())) {\n- element = element.parentNode\n- }\n- return element\n- },\n- observe: function(elementParam, name, observer, useCapture) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- useCapture = useCapture || false;\n- if (name == \"keypress\" && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) {\n- name = \"keydown\"\n- }\n- if (!this.observers) {\n- this.observers = {}\n- }\n- if (!element._eventCacheID) {\n- var idPrefix = \"eventCacheID_\";\n- if (element.id) {\n- idPrefix = element.id + \"_\" + idPrefix\n- }\n- element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix)\n- }\n- var cacheID = element._eventCacheID;\n- if (!this.observers[cacheID]) {\n- this.observers[cacheID] = []\n- }\n- this.observers[cacheID].push({\n- element: element,\n- name: name,\n- observer: observer,\n- useCapture: useCapture\n- });\n- if (element.addEventListener) {\n- element.addEventListener(name, observer, useCapture)\n- } else if (element.attachEvent) {\n- element.attachEvent(\"on\" + name, observer)\n- }\n- },\n- stopObservingElement: function(elementParam) {\n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n- this._removeElementObservers(OpenLayers.Event.observers[cacheID])\n- },\n- _removeElementObservers: function(elementObservers) {\n- if (elementObservers) {\n- for (var i = elementObservers.length - 1; i >= 0; i--) {\n- var entry = elementObservers[i];\n- OpenLayers.Event.stopObserving.apply(this, [entry.element, entry.name, entry.observer, entry.useCapture])\n- }\n- }\n- },\n- stopObserving: function(elementParam, name, observer, useCapture) {\n- useCapture = useCapture || false;\n- var element = OpenLayers.Util.getElement(elementParam);\n- var cacheID = element._eventCacheID;\n- if (name == \"keypress\") {\n- if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent) {\n- name = \"keydown\"\n- }\n- }\n- var foundEntry = false;\n- var elementObservers = OpenLayers.Event.observers[cacheID];\n- if (elementObservers) {\n- var i = 0;\n- while (!foundEntry && i < elementObservers.length) {\n- var cacheEntry = elementObservers[i];\n- if (cacheEntry.name == name && cacheEntry.observer == observer && cacheEntry.useCapture == useCapture) {\n- elementObservers.splice(i, 1);\n- if (elementObservers.length == 0) {\n- delete OpenLayers.Event.observers[cacheID]\n- }\n- foundEntry = true;\n- break\n- }\n- i++\n- }\n- }\n- if (foundEntry) {\n- if (element.removeEventListener) {\n- element.removeEventListener(name, observer, useCapture)\n- } else if (element && element.detachEvent) {\n- element.detachEvent(\"on\" + name, observer)\n- }\n- }\n- return foundEntry\n- },\n- unloadCache: function() {\n- if (OpenLayers.Event && OpenLayers.Event.observers) {\n- for (var cacheID in OpenLayers.Event.observers) {\n- var elementObservers = OpenLayers.Event.observers[cacheID];\n- OpenLayers.Event._removeElementObservers.apply(this, [elementObservers])\n- }\n- OpenLayers.Event.observers = false\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Event\"\n-};\n-OpenLayers.Event.observe(window, \"unload\", OpenLayers.Event.unloadCache, false);\n-OpenLayers.Events = OpenLayers.Class({\n- BROWSER_EVENTS: [\"mouseover\", \"mouseout\", \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"rightclick\", \"dblrightclick\", \"resize\", \"focus\", \"blur\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n- listeners: null,\n- object: null,\n- element: null,\n- eventHandler: null,\n- fallThrough: null,\n- includeXY: false,\n- extensions: null,\n- extensionCount: null,\n- clearMouseListener: null,\n- initialize: function(object, element, eventTypes, fallThrough, options) {\n- OpenLayers.Util.extend(this, options);\n- this.object = object;\n- this.fallThrough = fallThrough;\n- this.listeners = {};\n- this.extensions = {};\n- this.extensionCount = {};\n- this._msTouches = [];\n- if (element != null) {\n- this.attachToElement(element)\n- }\n- },\n- destroy: function() {\n- for (var e in this.extensions) {\n- if (typeof this.extensions[e] !== \"boolean\") {\n- this.extensions[e].destroy()\n- }\n- }\n- this.extensions = null;\n- if (this.element) {\n- OpenLayers.Event.stopObservingElement(this.element);\n- if (this.element.hasScrollEvent) {\n- OpenLayers.Event.stopObserving(window, \"scroll\", this.clearMouseListener)\n- }\n- }\n- this.element = null;\n- this.listeners = null;\n- this.object = null;\n- this.fallThrough = null;\n- this.eventHandler = null\n- },\n- addEventType: function(eventName) {},\n- attachToElement: function(element) {\n- if (this.element) {\n- OpenLayers.Event.stopObservingElement(this.element)\n- } else {\n- this.eventHandler = OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent, this);\n- this.clearMouseListener = OpenLayers.Function.bind(this.clearMouseCache, this)\n- }\n- this.element = element;\n- var msTouch = !!window.navigator.msMaxTouchPoints;\n- var type;\n- for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {\n- type = this.BROWSER_EVENTS[i];\n- OpenLayers.Event.observe(element, type, this.eventHandler);\n- if (msTouch && type.indexOf(\"touch\") === 0) {\n- this.addMsTouchListener(element, type, this.eventHandler)\n- }\n- }\n- OpenLayers.Event.observe(element, \"dragstart\", OpenLayers.Event.stop)\n- },\n- on: function(object) {\n- for (var type in object) {\n- if (type != \"scope\" && object.hasOwnProperty(type)) {\n- this.register(type, object.scope, object[type])\n- }\n- }\n- },\n- register: function(type, obj, func, priority) {\n- if (type in OpenLayers.Events && !this.extensions[type]) {\n- this.extensions[type] = new OpenLayers.Events[type](this)\n- }\n- if (func != null) {\n- if (obj == null) {\n- obj = this.object\n- }\n- var listeners = this.listeners[type];\n- if (!listeners) {\n- listeners = [];\n- this.listeners[type] = listeners;\n- this.extensionCount[type] = 0\n- }\n- var listener = {\n- obj: obj,\n- func: func\n- };\n- if (priority) {\n- listeners.splice(this.extensionCount[type], 0, listener);\n- if (typeof priority === \"object\" && priority.extension) {\n- this.extensionCount[type]++\n- }\n- } else {\n- listeners.push(listener)\n- }\n- }\n- },\n- registerPriority: function(type, obj, func) {\n- this.register(type, obj, func, true)\n- },\n- un: function(object) {\n- for (var type in object) {\n- if (type != \"scope\" && object.hasOwnProperty(type)) {\n- this.unregister(type, object.scope, object[type])\n- }\n- }\n- },\n- unregister: function(type, obj, func) {\n- if (obj == null) {\n- obj = this.object\n- }\n- var listeners = this.listeners[type];\n- if (listeners != null) {\n- for (var i = 0, len = listeners.length; i < len; i++) {\n- if (listeners[i].obj == obj && listeners[i].func == func) {\n- listeners.splice(i, 1);\n- break\n- }\n- }\n- }\n- },\n- remove: function(type) {\n- if (this.listeners[type] != null) {\n- this.listeners[type] = []\n- }\n- },\n- triggerEvent: function(type, evt) {\n- var listeners = this.listeners[type];\n- if (!listeners || listeners.length == 0) {\n- return undefined\n- }\n- if (evt == null) {\n- evt = {}\n- }\n- evt.object = this.object;\n- evt.element = this.element;\n- if (!evt.type) {\n- evt.type = type\n- }\n- listeners = listeners.slice();\n- var continueChain;\n- for (var i = 0, len = listeners.length; i < len; i++) {\n- var callback = listeners[i];\n- continueChain = callback.func.apply(callback.obj, [evt]);\n- if (continueChain != undefined && continueChain == false) {\n- break\n- }\n- }\n- if (!this.fallThrough) {\n- OpenLayers.Event.stop(evt, true)\n- }\n- return continueChain\n- },\n- handleBrowserEvent: function(evt) {\n- var type = evt.type,\n- listeners = this.listeners[type];\n- if (!listeners || listeners.length == 0) {\n- return\n- }\n- var touches = evt.touches;\n- if (touches && touches[0]) {\n- var x = 0;\n- var y = 0;\n- var num = touches.length;\n- var touch;\n- for (var i = 0; i < num; ++i) {\n- touch = this.getTouchClientXY(touches[i]);\n- x += touch.clientX;\n- y += touch.clientY\n- }\n- evt.clientX = x / num;\n- evt.clientY = y / num\n- }\n- if (this.includeXY) {\n- evt.xy = this.getMousePosition(evt)\n- }\n- this.triggerEvent(type, evt)\n- },\n- getTouchClientXY: function(evt) {\n- var win = window.olMockWin || window,\n- winPageX = win.pageXOffset,\n- winPageY = win.pageYOffset,\n- x = evt.clientX,\n- y = evt.clientY;\n- if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) || evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {\n- x = x - winPageX;\n- y = y - winPageY\n- } else if (y < evt.pageY - winPageY || x < evt.pageX - winPageX) {\n- x = evt.pageX - winPageX;\n- y = evt.pageY - winPageY\n- }\n- evt.olClientX = x;\n- evt.olClientY = y;\n- return {\n- clientX: x,\n- clientY: y\n- }\n- },\n- clearMouseCache: function() {\n- this.element.scrolls = null;\n- this.element.lefttop = null;\n- this.element.offsets = null\n- },\n- getMousePosition: function(evt) {\n- if (!this.includeXY) {\n- this.clearMouseCache()\n- } else if (!this.element.hasScrollEvent) {\n- OpenLayers.Event.observe(window, \"scroll\", this.clearMouseListener);\n- this.element.hasScrollEvent = true\n- }\n- if (!this.element.scrolls) {\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- this.element.scrolls = [window.pageXOffset || viewportElement.scrollLeft, window.pageYOffset || viewportElement.scrollTop]\n- }\n- if (!this.element.lefttop) {\n- this.element.lefttop = [document.documentElement.clientLeft || 0, document.documentElement.clientTop || 0]\n- }\n- if (!this.element.offsets) {\n- this.element.offsets = OpenLayers.Util.pagePosition(this.element)\n- }\n- return new OpenLayers.Pixel(evt.clientX + this.element.scrolls[0] - this.element.offsets[0] - this.element.lefttop[0], evt.clientY + this.element.scrolls[1] - this.element.offsets[1] - this.element.lefttop[1])\n- },\n- addMsTouchListener: function(element, type, handler) {\n- var eventHandler = this.eventHandler;\n- var touches = this._msTouches;\n-\n- function msHandler(evt) {\n- handler(OpenLayers.Util.applyDefaults({\n- stopPropagation: function() {\n- for (var i = touches.length - 1; i >= 0; --i) {\n- touches[i].stopPropagation()\n- }\n- },\n- preventDefault: function() {\n- for (var i = touches.length - 1; i >= 0; --i) {\n- touches[i].preventDefault()\n- }\n- },\n- type: type\n- }, evt))\n- }\n- switch (type) {\n- case \"touchstart\":\n- return this.addMsTouchListenerStart(element, type, msHandler);\n- case \"touchend\":\n- return this.addMsTouchListenerEnd(element, type, msHandler);\n- case \"touchmove\":\n- return this.addMsTouchListenerMove(element, type, msHandler);\n- default:\n- throw \"Unknown touch event type\"\n- }\n- },\n- addMsTouchListenerStart: function(element, type, handler) {\n- var touches = this._msTouches;\n- var cb = function(e) {\n- var alreadyInArray = false;\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- alreadyInArray = true;\n- break\n- }\n- }\n- if (!alreadyInArray) {\n- touches.push(e)\n- }\n- e.touches = touches.slice();\n- handler(e)\n- };\n- OpenLayers.Event.observe(element, \"MSPointerDown\", cb);\n- var internalCb = function(e) {\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches.splice(i, 1);\n- break\n- }\n- }\n- };\n- OpenLayers.Event.observe(element, \"MSPointerUp\", internalCb)\n- },\n- addMsTouchListenerMove: function(element, type, handler) {\n- var touches = this._msTouches;\n- var cb = function(e) {\n- if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {\n- return\n- }\n- if (touches.length == 1 && touches[0].pageX == e.pageX && touches[0].pageY == e.pageY) {\n- return\n- }\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches[i] = e;\n- break\n- }\n- }\n- e.touches = touches.slice();\n- handler(e)\n- };\n- OpenLayers.Event.observe(element, \"MSPointerMove\", cb)\n- },\n- addMsTouchListenerEnd: function(element, type, handler) {\n- var touches = this._msTouches;\n- var cb = function(e) {\n- for (var i = 0, ii = touches.length; i < ii; ++i) {\n- if (touches[i].pointerId == e.pointerId) {\n- touches.splice(i, 1);\n- break\n- }\n- }\n- e.touches = touches.slice();\n- handler(e)\n- };\n- OpenLayers.Event.observe(element, \"MSPointerUp\", cb)\n- },\n- CLASS_NAME: \"OpenLayers.Events\"\n-});\n-OpenLayers.Tween = OpenLayers.Class({\n- easing: null,\n- begin: null,\n- finish: null,\n- duration: null,\n- callbacks: null,\n- time: null,\n- minFrameRate: null,\n- startTime: null,\n- animationId: null,\n- playing: false,\n- initialize: function(easing) {\n- this.easing = easing ? easing : OpenLayers.Easing.Expo.easeOut\n- },\n- start: function(begin, finish, duration, options) {\n- this.playing = true;\n- this.begin = begin;\n- this.finish = finish;\n- this.duration = duration;\n- this.callbacks = options.callbacks;\n- this.minFrameRate = options.minFrameRate || 30;\n- this.time = 0;\n- this.startTime = (new Date).getTime();\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- if (this.callbacks && this.callbacks.start) {\n- this.callbacks.start.call(this, this.begin)\n- }\n- this.animationId = OpenLayers.Animation.start(OpenLayers.Function.bind(this.play, this))\n- },\n- stop: function() {\n- if (!this.playing) {\n- return\n- }\n- if (this.callbacks && this.callbacks.done) {\n- this.callbacks.done.call(this, this.finish)\n- }\n- OpenLayers.Animation.stop(this.animationId);\n- this.animationId = null;\n- this.playing = false\n- },\n- play: function() {\n- var value = {};\n- for (var i in this.begin) {\n- var b = this.begin[i];\n- var f = this.finish[i];\n- if (b == null || f == null || isNaN(b) || isNaN(f)) {\n- throw new TypeError(\"invalid value for Tween\")\n- }\n- var c = f - b;\n- value[i] = this.easing.apply(this, [this.time, b, c, this.duration])\n- }\n- this.time++;\n- if (this.callbacks && this.callbacks.eachStep) {\n- if (((new Date).getTime() - this.startTime) / this.time <= 1e3 / this.minFrameRate) {\n- this.callbacks.eachStep.call(this, value)\n- }\n- }\n- if (this.time > this.duration) {\n- this.stop()\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Tween\"\n-});\n-OpenLayers.Easing = {\n- CLASS_NAME: \"OpenLayers.Easing\"\n-};\n-OpenLayers.Easing.Linear = {\n- easeIn: function(t, b, c, d) {\n- return c * t / d + b\n- },\n- easeOut: function(t, b, c, d) {\n- return c * t / d + b\n- },\n- easeInOut: function(t, b, c, d) {\n- return c * t / d + b\n- },\n- CLASS_NAME: \"OpenLayers.Easing.Linear\"\n-};\n-OpenLayers.Easing.Expo = {\n- easeIn: function(t, b, c, d) {\n- return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b\n- },\n- easeOut: function(t, b, c, d) {\n- return t == d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b\n- },\n- easeInOut: function(t, b, c, d) {\n- if (t == 0) return b;\n- if (t == d) return b + c;\n- if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;\n- return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b\n- },\n- CLASS_NAME: \"OpenLayers.Easing.Expo\"\n-};\n-OpenLayers.Easing.Quad = {\n- easeIn: function(t, b, c, d) {\n- return c * (t /= d) * t + b\n- },\n- easeOut: function(t, b, c, d) {\n- return -c * (t /= d) * (t - 2) + b\n- },\n- easeInOut: function(t, b, c, d) {\n- if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n- return -c / 2 * (--t * (t - 2) - 1) + b\n- },\n- CLASS_NAME: \"OpenLayers.Easing.Quad\"\n-};\n-OpenLayers.Projection = OpenLayers.Class({\n- proj: null,\n- projCode: null,\n- titleRegEx: /\\+title=[^\\+]*/,\n- initialize: function(projCode, options) {\n- OpenLayers.Util.extend(this, options);\n- this.projCode = projCode;\n- if (typeof Proj4js == \"object\") {\n- this.proj = new Proj4js.Proj(projCode)\n- }\n- },\n- getCode: function() {\n- return this.proj ? this.proj.srsCode : this.projCode\n- },\n- getUnits: function() {\n- return this.proj ? this.proj.units : null\n- },\n- toString: function() {\n- return this.getCode()\n- },\n- equals: function(projection) {\n- var p = projection,\n- equals = false;\n- if (p) {\n- if (!(p instanceof OpenLayers.Projection)) {\n- p = new OpenLayers.Projection(p)\n- }\n- if (typeof Proj4js == \"object\" && this.proj.defData && p.proj.defData) {\n- equals = this.proj.defData.replace(this.titleRegEx, \"\") == p.proj.defData.replace(this.titleRegEx, \"\")\n- } else if (p.getCode) {\n- var source = this.getCode(),\n- target = p.getCode();\n- equals = source == target || !!OpenLayers.Projection.transforms[source] && OpenLayers.Projection.transforms[source][target] === OpenLayers.Projection.nullTransform\n- }\n- }\n- return equals\n- },\n- destroy: function() {\n- delete this.proj;\n- delete this.projCode\n- },\n- CLASS_NAME: \"OpenLayers.Projection\"\n-});\n-OpenLayers.Projection.transforms = {};\n-OpenLayers.Projection.defaults = {\n- \"EPSG:4326\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90],\n- yx: true\n- },\n- \"CRS:84\": {\n- units: \"degrees\",\n- maxExtent: [-180, -90, 180, 90]\n- },\n- \"EPSG:900913\": {\n- units: \"m\",\n- maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n- }\n-};\n-OpenLayers.Projection.addTransform = function(from, to, method) {\n- if (method === OpenLayers.Projection.nullTransform) {\n- var defaults = OpenLayers.Projection.defaults[from];\n- if (defaults && !OpenLayers.Projection.defaults[to]) {\n- OpenLayers.Projection.defaults[to] = defaults\n- }\n- }\n- if (!OpenLayers.Projection.transforms[from]) {\n- OpenLayers.Projection.transforms[from] = {}\n- }\n- OpenLayers.Projection.transforms[from][to] = method\n-};\n-OpenLayers.Projection.transform = function(point, source, dest) {\n- if (source && dest) {\n- if (!(source instanceof OpenLayers.Projection)) {\n- source = new OpenLayers.Projection(source)\n- }\n- if (!(dest instanceof OpenLayers.Projection)) {\n- dest = new OpenLayers.Projection(dest)\n- }\n- if (source.proj && dest.proj) {\n- point = Proj4js.transform(source.proj, dest.proj, point)\n- } else {\n- var sourceCode = source.getCode();\n- var destCode = dest.getCode();\n- var transforms = OpenLayers.Projection.transforms;\n- if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n- transforms[sourceCode][destCode](point)\n- }\n- }\n- }\n- return point\n-};\n-OpenLayers.Projection.nullTransform = function(point) {\n- return point\n-};\n-(function() {\n- var pole = 20037508.34;\n-\n- function inverseMercator(xy) {\n- xy.x = 180 * xy.x / pole;\n- xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2);\n- return xy\n- }\n-\n- function forwardMercator(xy) {\n- xy.x = xy.x * pole / 180;\n- var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n- xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n- return xy\n- }\n-\n- function map(base, codes) {\n- var add = OpenLayers.Projection.addTransform;\n- var same = OpenLayers.Projection.nullTransform;\n- var i, len, code, other, j;\n- for (i = 0, len = codes.length; i < len; ++i) {\n- code = codes[i];\n- add(base, code, forwardMercator);\n- add(code, base, inverseMercator);\n- for (j = i + 1; j < len; ++j) {\n- other = codes[j];\n- add(code, other, same);\n- add(other, code, same)\n- }\n- }\n- }\n- var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n- geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n- i;\n- for (i = mercator.length - 1; i >= 0; --i) {\n- map(mercator[i], geographic)\n- }\n- for (i = geographic.length - 1; i >= 0; --i) {\n- map(geographic[i], mercator)\n- }\n-})();\n-OpenLayers.Map = OpenLayers.Class({\n- Z_INDEX_BASE: {\n- BaseLayer: 100,\n- Overlay: 325,\n- Feature: 725,\n- Popup: 750,\n- Control: 1e3\n- },\n- id: null,\n- fractionalZoom: false,\n- events: null,\n- allOverlays: false,\n- div: null,\n- dragging: false,\n- size: null,\n- viewPortDiv: null,\n- layerContainerOrigin: null,\n- layerContainerDiv: null,\n- layers: null,\n- controls: null,\n- popups: null,\n- baseLayer: null,\n- center: null,\n- resolution: null,\n- zoom: 0,\n- panRatio: 1.5,\n- options: null,\n- tileSize: null,\n- projection: \"EPSG:4326\",\n- units: null,\n- resolutions: null,\n- maxResolution: null,\n- minResolution: null,\n- maxScale: null,\n- minScale: null,\n- maxExtent: null,\n- minExtent: null,\n- restrictedExtent: null,\n- numZoomLevels: 16,\n- theme: null,\n- displayProjection: null,\n- fallThrough: false,\n- autoUpdateSize: true,\n- eventListeners: null,\n- panTween: null,\n- panMethod: OpenLayers.Easing.Expo.easeOut,\n- panDuration: 50,\n- zoomTween: null,\n- zoomMethod: OpenLayers.Easing.Quad.easeOut,\n- zoomDuration: 20,\n- paddingForPopups: null,\n- layerContainerOriginPx: null,\n- minPx: null,\n- maxPx: null,\n- initialize: function(div, options) {\n- if (arguments.length === 1 && typeof div === \"object\") {\n- options = div;\n- div = options && options.div\n- }\n- this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, OpenLayers.Map.TILE_HEIGHT);\n- this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n- this.theme = OpenLayers._getScriptLocation() + \"theme/default/style.css\";\n- this.options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.extend(this, options);\n- var projCode = this.projection instanceof OpenLayers.Projection ? this.projection.projCode : this.projection;\n- OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n- if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n- this.maxExtent = new OpenLayers.Bounds(this.maxExtent)\n- }\n- if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n- this.minExtent = new OpenLayers.Bounds(this.minExtent)\n- }\n- if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n- this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent)\n- }\n- if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n- this.center = new OpenLayers.LonLat(this.center)\n- }\n- this.layers = [];\n- this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n- this.div = OpenLayers.Util.getElement(div);\n- if (!this.div) {\n- this.div = document.createElement(\"div\");\n- this.div.style.height = \"1px\";\n- this.div.style.width = \"1px\"\n- }\n- OpenLayers.Element.addClass(this.div, \"olMap\");\n- var id = this.id + \"_OpenLayers_ViewPort\";\n- this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null, \"relative\", null, \"hidden\");\n- this.viewPortDiv.style.width = \"100%\";\n- this.viewPortDiv.style.height = \"100%\";\n- this.viewPortDiv.className = \"olMapViewport\";\n- this.div.appendChild(this.viewPortDiv);\n- this.events = new OpenLayers.Events(this, this.viewPortDiv, null, this.fallThrough, {\n- includeXY: true\n- });\n- if (OpenLayers.TileManager && this.tileManager !== null) {\n- if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n- this.tileManager = new OpenLayers.TileManager(this.tileManager)\n- }\n- this.tileManager.addMap(this)\n- }\n- id = this.id + \"_OpenLayers_Container\";\n- this.layerContainerDiv = OpenLayers.Util.createDiv(id);\n- this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE[\"Popup\"] - 1;\n- this.layerContainerOriginPx = {\n- x: 0,\n- y: 0\n- };\n- this.applyTransform();\n- this.viewPortDiv.appendChild(this.layerContainerDiv);\n- this.updateSize();\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- if (this.autoUpdateSize === true) {\n- this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, this);\n- OpenLayers.Event.observe(window, \"resize\", this.updateSizeDestroy)\n- }\n- if (this.theme) {\n- var addNode = true;\n- var nodes = document.getElementsByTagName(\"link\");\n- for (var i = 0, len = nodes.length; i < len; ++i) {\n- if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href, this.theme)) {\n- addNode = false;\n- break\n- }\n- }\n- if (addNode) {\n- var cssNode = document.createElement(\"link\");\n- cssNode.setAttribute(\"rel\", \"stylesheet\");\n- cssNode.setAttribute(\"type\", \"text/css\");\n- cssNode.setAttribute(\"href\", this.theme);\n- document.getElementsByTagName(\"head\")[0].appendChild(cssNode)\n- }\n- }\n- if (this.controls == null) {\n- this.controls = [];\n- if (OpenLayers.Control != null) {\n- if (OpenLayers.Control.Navigation) {\n- this.controls.push(new OpenLayers.Control.Navigation)\n- } else if (OpenLayers.Control.TouchNavigation) {\n- this.controls.push(new OpenLayers.Control.TouchNavigation)\n- }\n- if (OpenLayers.Control.Zoom) {\n- this.controls.push(new OpenLayers.Control.Zoom)\n- } else if (OpenLayers.Control.PanZoom) {\n- this.controls.push(new OpenLayers.Control.PanZoom)\n- }\n- if (OpenLayers.Control.ArgParser) {\n- this.controls.push(new OpenLayers.Control.ArgParser)\n- }\n- if (OpenLayers.Control.Attribution) {\n- this.controls.push(new OpenLayers.Control.Attribution)\n- }\n- }\n- }\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.addControlToMap(this.controls[i])\n- }\n- this.popups = [];\n- this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);\n- OpenLayers.Event.observe(window, \"unload\", this.unloadDestroy);\n- if (options && options.layers) {\n- delete this.center;\n- delete this.zoom;\n- this.addLayers(options.layers);\n- if (options.center && !this.getCenter()) {\n- this.setCenter(options.center, options.zoom)\n- }\n- }\n- if (this.panMethod) {\n- this.panTween = new OpenLayers.Tween(this.panMethod)\n- }\n- if (this.zoomMethod && this.applyTransform.transform) {\n- this.zoomTween = new OpenLayers.Tween(this.zoomMethod)\n- }\n- },\n- getViewport: function() {\n- return this.viewPortDiv\n- },\n- render: function(div) {\n- this.div = OpenLayers.Util.getElement(div);\n- OpenLayers.Element.addClass(this.div, \"olMap\");\n- this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n- this.div.appendChild(this.viewPortDiv);\n- this.updateSize()\n- },\n- unloadDestroy: null,\n- updateSizeDestroy: null,\n- destroy: function() {\n- if (!this.unloadDestroy) {\n- return false\n- }\n- if (this.panTween) {\n- this.panTween.stop();\n- this.panTween = null\n- }\n- if (this.zoomTween) {\n- this.zoomTween.stop();\n- this.zoomTween = null\n- }\n- OpenLayers.Event.stopObserving(window, \"unload\", this.unloadDestroy);\n- this.unloadDestroy = null;\n- if (this.updateSizeDestroy) {\n- OpenLayers.Event.stopObserving(window, \"resize\", this.updateSizeDestroy)\n- }\n- this.paddingForPopups = null;\n- if (this.controls != null) {\n- for (var i = this.controls.length - 1; i >= 0; --i) {\n- this.controls[i].destroy()\n- }\n- this.controls = null\n- }\n- if (this.layers != null) {\n- for (var i = this.layers.length - 1; i >= 0; --i) {\n- this.layers[i].destroy(false)\n- }\n- this.layers = null\n- }\n- if (this.viewPortDiv && this.viewPortDiv.parentNode) {\n- this.viewPortDiv.parentNode.removeChild(this.viewPortDiv)\n- }\n- this.viewPortDiv = null;\n- if (this.tileManager) {\n- this.tileManager.removeMap(this);\n- this.tileManager = null\n- }\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners);\n- this.eventListeners = null\n- }\n- this.events.destroy();\n- this.events = null;\n- this.options = null\n- },\n- setOptions: function(options) {\n- var updatePxExtent = this.minPx && options.restrictedExtent != this.restrictedExtent;\n- OpenLayers.Util.extend(this, options);\n- updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {\n- forceZoomChange: true\n- })\n- },\n- getTileSize: function() {\n- return this.tileSize\n- },\n- getBy: function(array, property, match) {\n- var test = typeof match.test == \"function\";\n- var found = OpenLayers.Array.filter(this[array], function(item) {\n- return item[property] == match || test && match.test(item[property])\n- });\n- return found\n- },\n- getLayersBy: function(property, match) {\n- return this.getBy(\"layers\", property, match)\n- },\n- getLayersByName: function(match) {\n- return this.getLayersBy(\"name\", match)\n- },\n- getLayersByClass: function(match) {\n- return this.getLayersBy(\"CLASS_NAME\", match)\n- },\n- getControlsBy: function(property, match) {\n- return this.getBy(\"controls\", property, match)\n- },\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match)\n- },\n- getLayer: function(id) {\n- var foundLayer = null;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer.id == id) {\n- foundLayer = layer;\n- break\n- }\n- }\n- return foundLayer\n- },\n- setLayerZIndex: function(layer, zIdx) {\n- layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer ? \"BaseLayer\" : \"Overlay\"] + zIdx * 5)\n- },\n- resetLayersZIndex: function() {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- this.setLayerZIndex(layer, i)\n- }\n- },\n- addLayer: function(layer) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- if (this.layers[i] == layer) {\n- return false\n- }\n- }\n- if (this.events.triggerEvent(\"preaddlayer\", {\n- layer: layer\n- }) === false) {\n- return false\n- }\n- if (this.allOverlays) {\n- layer.isBaseLayer = false\n- }\n- layer.div.className = \"olLayerDiv\";\n- layer.div.style.overflow = \"\";\n- this.setLayerZIndex(layer, this.layers.length);\n- if (layer.isFixed) {\n- this.viewPortDiv.appendChild(layer.div)\n- } else {\n- this.layerContainerDiv.appendChild(layer.div)\n- }\n- this.layers.push(layer);\n- layer.setMap(this);\n- if (layer.isBaseLayer || this.allOverlays && !this.baseLayer) {\n- if (this.baseLayer == null) {\n- this.setBaseLayer(layer)\n- } else {\n- layer.setVisibility(false)\n- }\n- } else {\n- layer.redraw()\n- }\n- this.events.triggerEvent(\"addlayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"added\", {\n- map: this,\n- layer: layer\n- });\n- layer.afterAdd();\n- return true\n- },\n- addLayers: function(layers) {\n- for (var i = 0, len = layers.length; i < len; i++) {\n- this.addLayer(layers[i])\n- }\n- },\n- removeLayer: function(layer, setNewBaseLayer) {\n- if (this.events.triggerEvent(\"preremovelayer\", {\n- layer: layer\n- }) === false) {\n- return\n- }\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true\n- }\n- if (layer.isFixed) {\n- this.viewPortDiv.removeChild(layer.div)\n- } else {\n- this.layerContainerDiv.removeChild(layer.div)\n- }\n- OpenLayers.Util.removeItem(this.layers, layer);\n- layer.removeMap(this);\n- layer.map = null;\n- if (this.baseLayer == layer) {\n- this.baseLayer = null;\n- if (setNewBaseLayer) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var iLayer = this.layers[i];\n- if (iLayer.isBaseLayer || this.allOverlays) {\n- this.setBaseLayer(iLayer);\n- break\n- }\n- }\n- }\n- }\n- this.resetLayersZIndex();\n- this.events.triggerEvent(\"removelayer\", {\n- layer: layer\n- });\n- layer.events.triggerEvent(\"removed\", {\n- map: this,\n- layer: layer\n- })\n- },\n- getNumLayers: function() {\n- return this.layers.length\n- },\n- getLayerIndex: function(layer) {\n- return OpenLayers.Util.indexOf(this.layers, layer)\n- },\n- setLayerIndex: function(layer, idx) {\n- var base = this.getLayerIndex(layer);\n- if (idx < 0) {\n- idx = 0\n- } else if (idx > this.layers.length) {\n- idx = this.layers.length\n- }\n- if (base != idx) {\n- this.layers.splice(base, 1);\n- this.layers.splice(idx, 0, layer);\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- this.setLayerZIndex(this.layers[i], i)\n- }\n- this.events.triggerEvent(\"changelayer\", {\n- layer: layer,\n- property: \"order\"\n- });\n- if (this.allOverlays) {\n- if (idx === 0) {\n- this.setBaseLayer(layer)\n- } else if (this.baseLayer !== this.layers[0]) {\n- this.setBaseLayer(this.layers[0])\n- }\n- }\n- }\n- },\n- raiseLayer: function(layer, delta) {\n- var idx = this.getLayerIndex(layer) + delta;\n- this.setLayerIndex(layer, idx)\n- },\n- setBaseLayer: function(newBaseLayer) {\n- if (newBaseLayer != this.baseLayer) {\n- if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n- var center = this.getCachedCenter();\n- var newResolution = OpenLayers.Util.getResolutionFromScale(this.getScale(), newBaseLayer.units);\n- if (this.baseLayer != null && !this.allOverlays) {\n- this.baseLayer.setVisibility(false)\n- }\n- this.baseLayer = newBaseLayer;\n- if (!this.allOverlays || this.baseLayer.visibility) {\n- this.baseLayer.setVisibility(true);\n- if (this.baseLayer.inRange === false) {\n- this.baseLayer.redraw()\n- }\n- }\n- if (center != null) {\n- var newZoom = this.getZoomForResolution(newResolution || this.resolution, true);\n- this.setCenter(center, newZoom, false, true)\n- }\n- this.events.triggerEvent(\"changebaselayer\", {\n- layer: this.baseLayer\n- })\n- }\n- }\n- },\n- addControl: function(control, px) {\n- this.controls.push(control);\n- this.addControlToMap(control, px)\n- },\n- addControls: function(controls, pixels) {\n- var pxs = arguments.length === 1 ? [] : pixels;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var ctrl = controls[i];\n- var px = pxs[i] ? pxs[i] : null;\n- this.addControl(ctrl, px)\n- }\n- },\n- addControlToMap: function(control, px) {\n- control.outsideViewport = control.div != null;\n- if (this.displayProjection && !control.displayProjection) {\n- control.displayProjection = this.displayProjection\n- }\n- control.setMap(this);\n- var div = control.draw(px);\n- if (div) {\n- if (!control.outsideViewport) {\n- div.style.zIndex = this.Z_INDEX_BASE[\"Control\"] + this.controls.length;\n- this.viewPortDiv.appendChild(div)\n- }\n- }\n- if (control.autoActivate) {\n- control.activate()\n- }\n- },\n- getControl: function(id) {\n- var returnControl = null;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- var control = this.controls[i];\n- if (control.id == id) {\n- returnControl = control;\n- break\n- }\n- }\n- return returnControl\n- },\n- removeControl: function(control) {\n- if (control && control == this.getControl(control.id)) {\n- if (control.div && control.div.parentNode == this.viewPortDiv) {\n- this.viewPortDiv.removeChild(control.div)\n- }\n- OpenLayers.Util.removeItem(this.controls, control)\n- }\n- },\n- addPopup: function(popup, exclusive) {\n- if (exclusive) {\n- for (var i = this.popups.length - 1; i >= 0; --i) {\n- this.removePopup(this.popups[i])\n- }\n- }\n- popup.map = this;\n- this.popups.push(popup);\n- var popupDiv = popup.draw();\n- if (popupDiv) {\n- popupDiv.style.zIndex = this.Z_INDEX_BASE[\"Popup\"] + this.popups.length;\n- this.layerContainerDiv.appendChild(popupDiv)\n- }\n- },\n- removePopup: function(popup) {\n- OpenLayers.Util.removeItem(this.popups, popup);\n- if (popup.div) {\n- try {\n- this.layerContainerDiv.removeChild(popup.div)\n- } catch (e) {}\n- }\n- popup.map = null\n- },\n- getSize: function() {\n- var size = null;\n- if (this.size != null) {\n- size = this.size.clone()\n- }\n- return size\n- },\n- updateSize: function() {\n- var newSize = this.getCurrentSize();\n- if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {\n- this.events.clearMouseCache();\n- var oldSize = this.getSize();\n- if (oldSize == null) {\n- this.size = oldSize = newSize\n- }\n- if (!newSize.equals(oldSize)) {\n- this.size = newSize;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- this.layers[i].onMapResize()\n- }\n- var center = this.getCachedCenter();\n- if (this.baseLayer != null && center != null) {\n- var zoom = this.getZoom();\n- this.zoom = null;\n- this.setCenter(center, zoom)\n- }\n- }\n- }\n- this.events.triggerEvent(\"updatesize\")\n- },\n- getCurrentSize: function() {\n- var size = new OpenLayers.Size(this.div.clientWidth, this.div.clientHeight);\n- if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n- size.w = this.div.offsetWidth;\n- size.h = this.div.offsetHeight\n- }\n- if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n- size.w = parseInt(this.div.style.width);\n- size.h = parseInt(this.div.style.height)\n- }\n- return size\n- },\n- calculateBounds: function(center, resolution) {\n- var extent = null;\n- if (center == null) {\n- center = this.getCachedCenter()\n- }\n- if (resolution == null) {\n- resolution = this.getResolution()\n- }\n- if (center != null && resolution != null) {\n- var halfWDeg = this.size.w * resolution / 2;\n- var halfHDeg = this.size.h * resolution / 2;\n- extent = new OpenLayers.Bounds(center.lon - halfWDeg, center.lat - halfHDeg, center.lon + halfWDeg, center.lat + halfHDeg)\n- }\n- return extent\n- },\n- getCenter: function() {\n- var center = null;\n- var cachedCenter = this.getCachedCenter();\n- if (cachedCenter) {\n- center = cachedCenter.clone()\n- }\n- return center\n- },\n- getCachedCenter: function() {\n- if (!this.center && this.size) {\n- this.center = this.getLonLatFromViewPortPx({\n- x: this.size.w / 2,\n- y: this.size.h / 2\n- })\n- }\n- return this.center\n- },\n- getZoom: function() {\n- return this.zoom\n- },\n- pan: function(dx, dy, options) {\n- options = OpenLayers.Util.applyDefaults(options, {\n- animate: true,\n- dragging: false\n- });\n- if (options.dragging) {\n- if (dx != 0 || dy != 0) {\n- this.moveByPx(dx, dy)\n- }\n- } else {\n- var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());\n- var newCenterPx = centerPx.add(dx, dy);\n- if (this.dragging || !newCenterPx.equals(centerPx)) {\n- var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);\n- if (options.animate) {\n- this.panTo(newCenterLonLat)\n- } else {\n- this.moveTo(newCenterLonLat);\n- if (this.dragging) {\n- this.dragging = false;\n- this.events.triggerEvent(\"moveend\")\n- }\n- }\n- }\n- }\n- },\n- panTo: function(lonlat) {\n- if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {\n- var center = this.getCachedCenter();\n- if (lonlat.equals(center)) {\n- return\n- }\n- var from = this.getPixelFromLonLat(center);\n- var to = this.getPixelFromLonLat(lonlat);\n- var vector = {\n- x: to.x - from.x,\n- y: to.y - from.y\n- };\n- var last = {\n- x: 0,\n- y: 0\n- };\n- this.panTween.start({\n- x: 0,\n- y: 0\n- }, vector, this.panDuration, {\n- callbacks: {\n- eachStep: OpenLayers.Function.bind(function(px) {\n- var x = px.x - last.x,\n- y = px.y - last.y;\n- this.moveByPx(x, y);\n- last.x = Math.round(px.x);\n- last.y = Math.round(px.y)\n- }, this),\n- done: OpenLayers.Function.bind(function(px) {\n- this.moveTo(lonlat);\n- this.dragging = false;\n- this.events.triggerEvent(\"moveend\")\n- }, this)\n- }\n- })\n- } else {\n- this.setCenter(lonlat)\n- }\n- },\n- setCenter: function(lonlat, zoom, dragging, forceZoomChange) {\n- if (this.panTween) {\n- this.panTween.stop()\n- }\n- if (this.zoomTween) {\n- this.zoomTween.stop()\n- }\n- this.moveTo(lonlat, zoom, {\n- dragging: dragging,\n- forceZoomChange: forceZoomChange\n- })\n- },\n- moveByPx: function(dx, dy) {\n- var hw = this.size.w / 2;\n- var hh = this.size.h / 2;\n- var x = hw + dx;\n- var y = hh + dy;\n- var wrapDateLine = this.baseLayer.wrapDateLine;\n- var xRestriction = 0;\n- var yRestriction = 0;\n- if (this.restrictedExtent) {\n- xRestriction = hw;\n- yRestriction = hh;\n- wrapDateLine = false\n- }\n- dx = wrapDateLine || x <= this.maxPx.x - xRestriction && x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;\n- dy = y <= this.maxPx.y - yRestriction && y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;\n- if (dx || dy) {\n- if (!this.dragging) {\n- this.dragging = true;\n- this.events.triggerEvent(\"movestart\")\n- }\n- this.center = null;\n- if (dx) {\n- this.layerContainerOriginPx.x -= dx;\n- this.minPx.x -= dx;\n- this.maxPx.x -= dx\n- }\n- if (dy) {\n- this.layerContainerOriginPx.y -= dy;\n- this.minPx.y -= dy;\n- this.maxPx.y -= dy\n- }\n- this.applyTransform();\n- var layer, i, len;\n- for (i = 0, len = this.layers.length; i < len; ++i) {\n- layer = this.layers[i];\n- if (layer.visibility && (layer === this.baseLayer || layer.inRange)) {\n- layer.moveByPx(dx, dy);\n- layer.events.triggerEvent(\"move\")\n- }\n- }\n- this.events.triggerEvent(\"move\")\n- }\n- },\n- adjustZoom: function(zoom) {\n- if (this.baseLayer && this.baseLayer.wrapDateLine) {\n- var resolution, resolutions = this.baseLayer.resolutions,\n- maxResolution = this.getMaxExtent().getWidth() / this.size.w;\n- if (this.getResolutionForZoom(zoom) > maxResolution) {\n- if (this.fractionalZoom) {\n- zoom = this.getZoomForResolution(maxResolution)\n- } else {\n- for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) {\n- if (resolutions[i] <= maxResolution) {\n- zoom = i;\n- break\n- }\n- }\n- }\n- }\n- }\n- return zoom\n- },\n- getMinZoom: function() {\n- return this.adjustZoom(0)\n- },\n- moveTo: function(lonlat, zoom, options) {\n- if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {\n- lonlat = new OpenLayers.LonLat(lonlat)\n- }\n- if (!options) {\n- options = {}\n- }\n- if (zoom != null) {\n- zoom = parseFloat(zoom);\n- if (!this.fractionalZoom) {\n- zoom = Math.round(zoom)\n- }\n- }\n- var requestedZoom = zoom;\n- zoom = this.adjustZoom(zoom);\n- if (zoom !== requestedZoom) {\n- lonlat = this.getCenter()\n- }\n- var dragging = options.dragging || this.dragging;\n- var forceZoomChange = options.forceZoomChange;\n- if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {\n- lonlat = this.maxExtent.getCenterLonLat();\n- this.center = lonlat.clone()\n- }\n- if (this.restrictedExtent != null) {\n- if (lonlat == null) {\n- lonlat = this.center\n- }\n- if (zoom == null) {\n- zoom = this.getZoom()\n- }\n- var resolution = this.getResolutionForZoom(zoom);\n- var extent = this.calculateBounds(lonlat, resolution);\n- if (!this.restrictedExtent.containsBounds(extent)) {\n- var maxCenter = this.restrictedExtent.getCenterLonLat();\n- if (extent.getWidth() > this.restrictedExtent.getWidth()) {\n- lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat)\n- } else if (extent.left < this.restrictedExtent.left) {\n- lonlat = lonlat.add(this.restrictedExtent.left - extent.left, 0)\n- } else if (extent.right > this.restrictedExtent.right) {\n- lonlat = lonlat.add(this.restrictedExtent.right - extent.right, 0)\n- }\n- if (extent.getHeight() > this.restrictedExtent.getHeight()) {\n- lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat)\n- } else if (extent.bottom < this.restrictedExtent.bottom) {\n- lonlat = lonlat.add(0, this.restrictedExtent.bottom - extent.bottom)\n- } else if (extent.top > this.restrictedExtent.top) {\n- lonlat = lonlat.add(0, this.restrictedExtent.top - extent.top)\n- }\n- }\n- }\n- var zoomChanged = forceZoomChange || this.isValidZoomLevel(zoom) && zoom != this.getZoom();\n- var centerChanged = this.isValidLonLat(lonlat) && !lonlat.equals(this.center);\n- if (zoomChanged || centerChanged || dragging) {\n- dragging || this.events.triggerEvent(\"movestart\", {\n- zoomChanged: zoomChanged\n- });\n- if (centerChanged) {\n- if (!zoomChanged && this.center) {\n- this.centerLayerContainer(lonlat)\n- }\n- this.center = lonlat.clone()\n- }\n- var res = zoomChanged ? this.getResolutionForZoom(zoom) : this.getResolution();\n- if (zoomChanged || this.layerContainerOrigin == null) {\n- this.layerContainerOrigin = this.getCachedCenter();\n- this.layerContainerOriginPx.x = 0;\n- this.layerContainerOriginPx.y = 0;\n- this.applyTransform();\n- var maxExtent = this.getMaxExtent({\n- restricted: true\n- });\n- var maxExtentCenter = maxExtent.getCenterLonLat();\n- var lonDelta = this.center.lon - maxExtentCenter.lon;\n- var latDelta = maxExtentCenter.lat - this.center.lat;\n- var extentWidth = Math.round(maxExtent.getWidth() / res);\n- var extentHeight = Math.round(maxExtent.getHeight() / res);\n- this.minPx = {\n- x: (this.size.w - extentWidth) / 2 - lonDelta / res,\n- y: (this.size.h - extentHeight) / 2 - latDelta / res\n- };\n- this.maxPx = {\n- x: this.minPx.x + Math.round(maxExtent.getWidth() / res),\n- y: this.minPx.y + Math.round(maxExtent.getHeight() / res)\n- }\n- }\n- if (zoomChanged) {\n- this.zoom = zoom;\n- this.resolution = res\n- }\n- var bounds = this.getExtent();\n- if (this.baseLayer.visibility) {\n- this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);\n- options.dragging || this.baseLayer.events.triggerEvent(\"moveend\", {\n- zoomChanged: zoomChanged\n- })\n- }\n- bounds = this.baseLayer.getExtent();\n- for (var i = this.layers.length - 1; i >= 0; --i) {\n- var layer = this.layers[i];\n- if (layer !== this.baseLayer && !layer.isBaseLayer) {\n- var inRange = layer.calculateInRange();\n- if (layer.inRange != inRange) {\n- layer.inRange = inRange;\n- if (!inRange) {\n- layer.display(false)\n- }\n- this.events.triggerEvent(\"changelayer\", {\n- layer: layer,\n- property: \"visibility\"\n- })\n- }\n- if (inRange && layer.visibility) {\n- layer.moveTo(bounds, zoomChanged, options.dragging);\n- options.dragging || layer.events.triggerEvent(\"moveend\", {\n- zoomChanged: zoomChanged\n- })\n- }\n- }\n- }\n- this.events.triggerEvent(\"move\");\n- dragging || this.events.triggerEvent(\"moveend\");\n- if (zoomChanged) {\n- for (var i = 0, len = this.popups.length; i < len; i++) {\n- this.popups[i].updatePosition()\n- }\n- this.events.triggerEvent(\"zoomend\")\n- }\n- }\n- },\n- centerLayerContainer: function(lonlat) {\n- var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);\n- var newPx = this.getViewPortPxFromLonLat(lonlat);\n- if (originPx != null && newPx != null) {\n- var oldLeft = this.layerContainerOriginPx.x;\n- var oldTop = this.layerContainerOriginPx.y;\n- var newLeft = Math.round(originPx.x - newPx.x);\n- var newTop = Math.round(originPx.y - newPx.y);\n- this.applyTransform(this.layerContainerOriginPx.x = newLeft, this.layerContainerOriginPx.y = newTop);\n- var dx = oldLeft - newLeft;\n- var dy = oldTop - newTop;\n- this.minPx.x -= dx;\n- this.maxPx.x -= dx;\n- this.minPx.y -= dy;\n- this.maxPx.y -= dy\n- }\n- },\n- isValidZoomLevel: function(zoomLevel) {\n- return zoomLevel != null && zoomLevel >= 0 && zoomLevel < this.getNumZoomLevels()\n- },\n- isValidLonLat: function(lonlat) {\n- var valid = false;\n- if (lonlat != null) {\n- var maxExtent = this.getMaxExtent();\n- var worldBounds = this.baseLayer.wrapDateLine && maxExtent;\n- valid = maxExtent.containsLonLat(lonlat, {\n- worldBounds: worldBounds\n- })\n- }\n- return valid\n- },\n- getProjection: function() {\n- var projection = this.getProjectionObject();\n- return projection ? projection.getCode() : null\n- },\n- getProjectionObject: function() {\n- var projection = null;\n- if (this.baseLayer != null) {\n- projection = this.baseLayer.projection\n- }\n- return projection\n- },\n- getMaxResolution: function() {\n- var maxResolution = null;\n- if (this.baseLayer != null) {\n- maxResolution = this.baseLayer.maxResolution\n- }\n- return maxResolution\n- },\n- getMaxExtent: function(options) {\n- var maxExtent = null;\n- if (options && options.restricted && this.restrictedExtent) {\n- maxExtent = this.restrictedExtent\n- } else if (this.baseLayer != null) {\n- maxExtent = this.baseLayer.maxExtent\n- }\n- return maxExtent\n- },\n- getNumZoomLevels: function() {\n- var numZoomLevels = null;\n- if (this.baseLayer != null) {\n- numZoomLevels = this.baseLayer.numZoomLevels\n- }\n- return numZoomLevels\n- },\n- getExtent: function() {\n- var extent = null;\n- if (this.baseLayer != null) {\n- extent = this.baseLayer.getExtent()\n- }\n- return extent\n- },\n- getResolution: function() {\n- var resolution = null;\n- if (this.baseLayer != null) {\n- resolution = this.baseLayer.getResolution()\n- } else if (this.allOverlays === true && this.layers.length > 0) {\n- resolution = this.layers[0].getResolution()\n- }\n- return resolution\n- },\n- getUnits: function() {\n- var units = null;\n- if (this.baseLayer != null) {\n- units = this.baseLayer.units\n- }\n- return units\n- },\n- getScale: function() {\n- var scale = null;\n- if (this.baseLayer != null) {\n- var res = this.getResolution();\n- var units = this.baseLayer.units;\n- scale = OpenLayers.Util.getScaleFromResolution(res, units)\n- }\n- return scale\n- },\n- getZoomForExtent: function(bounds, closest) {\n- var zoom = null;\n- if (this.baseLayer != null) {\n- zoom = this.baseLayer.getZoomForExtent(bounds, closest)\n- }\n- return zoom\n- },\n- getResolutionForZoom: function(zoom) {\n- var resolution = null;\n- if (this.baseLayer) {\n- resolution = this.baseLayer.getResolutionForZoom(zoom)\n- }\n- return resolution\n- },\n- getZoomForResolution: function(resolution, closest) {\n- var zoom = null;\n- if (this.baseLayer != null) {\n- zoom = this.baseLayer.getZoomForResolution(resolution, closest)\n- }\n- return zoom\n- },\n- zoomTo: function(zoom, xy) {\n- var map = this;\n- if (map.isValidZoomLevel(zoom)) {\n- if (map.baseLayer.wrapDateLine) {\n- zoom = map.adjustZoom(zoom)\n- }\n- if (map.zoomTween) {\n- var currentRes = map.getResolution(),\n- targetRes = map.getResolutionForZoom(zoom),\n- start = {\n- scale: 1\n- },\n- end = {\n- scale: currentRes / targetRes\n- };\n- if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {\n- map.zoomTween.finish = {\n- scale: map.zoomTween.finish.scale * end.scale\n- }\n- } else {\n- if (!xy) {\n- var size = map.getSize();\n- xy = {\n- x: size.w / 2,\n- y: size.h / 2\n- }\n- }\n- map.zoomTween.start(start, end, map.zoomDuration, {\n- minFrameRate: 50,\n- callbacks: {\n- eachStep: function(data) {\n- var containerOrigin = map.layerContainerOriginPx,\n- scale = data.scale,\n- dx = (scale - 1) * (containerOrigin.x - xy.x) | 0,\n- dy = (scale - 1) * (containerOrigin.y - xy.y) | 0;\n- map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale)\n- },\n- done: function(data) {\n- map.applyTransform();\n- var resolution = map.getResolution() / data.scale,\n- zoom = map.getZoomForResolution(resolution, true);\n- map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true)\n- }\n- }\n- })\n- }\n- } else {\n- var center = xy ? map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) : null;\n- map.setCenter(center, zoom)\n- }\n- }\n- },\n- zoomIn: function() {\n- this.zoomTo(this.getZoom() + 1)\n- },\n- zoomOut: function() {\n- this.zoomTo(this.getZoom() - 1)\n- },\n- zoomToExtent: function(bounds, closest) {\n- if (!(bounds instanceof OpenLayers.Bounds)) {\n- bounds = new OpenLayers.Bounds(bounds)\n- }\n- var center = bounds.getCenterLonLat();\n- if (this.baseLayer.wrapDateLine) {\n- var maxExtent = this.getMaxExtent();\n- bounds = bounds.clone();\n- while (bounds.right < bounds.left) {\n- bounds.right += maxExtent.getWidth()\n- }\n- center = bounds.getCenterLonLat().wrapDateLine(maxExtent)\n- }\n- this.setCenter(center, this.getZoomForExtent(bounds, closest))\n- },\n- zoomToMaxExtent: function(options) {\n- var restricted = options ? options.restricted : true;\n- var maxExtent = this.getMaxExtent({\n- restricted: restricted\n- });\n- this.zoomToExtent(maxExtent)\n- },\n- zoomToScale: function(scale, closest) {\n- var res = OpenLayers.Util.getResolutionFromScale(scale, this.baseLayer.units);\n- var halfWDeg = this.size.w * res / 2;\n- var halfHDeg = this.size.h * res / 2;\n- var center = this.getCachedCenter();\n- var extent = new OpenLayers.Bounds(center.lon - halfWDeg, center.lat - halfHDeg, center.lon + halfWDeg, center.lat + halfHDeg);\n- this.zoomToExtent(extent, closest)\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if (this.baseLayer != null) {\n- lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx)\n- }\n- return lonlat\n- },\n- getViewPortPxFromLonLat: function(lonlat) {\n- var px = null;\n- if (this.baseLayer != null) {\n- px = this.baseLayer.getViewPortPxFromLonLat(lonlat)\n- }\n- return px\n- },\n- getZoomTargetCenter: function(xy, resolution) {\n- var lonlat = null,\n- size = this.getSize(),\n- deltaX = size.w / 2 - xy.x,\n- deltaY = xy.y - size.h / 2,\n- zoomPoint = this.getLonLatFromPixel(xy);\n- if (zoomPoint) {\n- lonlat = new OpenLayers.LonLat(zoomPoint.lon + deltaX * resolution, zoomPoint.lat + deltaY * resolution)\n- }\n- return lonlat\n- },\n- getLonLatFromPixel: function(px) {\n- return this.getLonLatFromViewPortPx(px)\n- },\n- getPixelFromLonLat: function(lonlat) {\n- var px = this.getViewPortPxFromLonLat(lonlat);\n- px.x = Math.round(px.x);\n- px.y = Math.round(px.y);\n- return px\n- },\n- getGeodesicPixelSize: function(px) {\n- var lonlat = px ? this.getLonLatFromPixel(px) : this.getCachedCenter() || new OpenLayers.LonLat(0, 0);\n- var res = this.getResolution();\n- var left = lonlat.add(-res / 2, 0);\n- var right = lonlat.add(res / 2, 0);\n- var bottom = lonlat.add(0, -res / 2);\n- var top = lonlat.add(0, res / 2);\n- var dest = new OpenLayers.Projection(\"EPSG:4326\");\n- var source = this.getProjectionObject() || dest;\n- if (!source.equals(dest)) {\n- left.transform(source, dest);\n- right.transform(source, dest);\n- bottom.transform(source, dest);\n- top.transform(source, dest)\n- }\n- return new OpenLayers.Size(OpenLayers.Util.distVincenty(left, right), OpenLayers.Util.distVincenty(bottom, top))\n- },\n- getViewPortPxFromLayerPx: function(layerPx) {\n- var viewPortPx = null;\n- if (layerPx != null) {\n- var dX = this.layerContainerOriginPx.x;\n- var dY = this.layerContainerOriginPx.y;\n- viewPortPx = layerPx.add(dX, dY)\n- }\n- return viewPortPx\n- },\n- getLayerPxFromViewPortPx: function(viewPortPx) {\n- var layerPx = null;\n- if (viewPortPx != null) {\n- var dX = -this.layerContainerOriginPx.x;\n- var dY = -this.layerContainerOriginPx.y;\n- layerPx = viewPortPx.add(dX, dY);\n- if (isNaN(layerPx.x) || isNaN(layerPx.y)) {\n- layerPx = null\n- }\n- }\n- return layerPx\n- },\n- getLonLatFromLayerPx: function(px) {\n- px = this.getViewPortPxFromLayerPx(px);\n- return this.getLonLatFromViewPortPx(px)\n- },\n- getLayerPxFromLonLat: function(lonlat) {\n- var px = this.getPixelFromLonLat(lonlat);\n- return this.getLayerPxFromViewPortPx(px)\n- },\n- applyTransform: function(x, y, scale) {\n- scale = scale || 1;\n- var origin = this.layerContainerOriginPx,\n- needTransform = scale !== 1;\n- x = x || origin.x;\n- y = y || origin.y;\n- var style = this.layerContainerDiv.style,\n- transform = this.applyTransform.transform,\n- template = this.applyTransform.template;\n- if (transform === undefined) {\n- transform = OpenLayers.Util.vendorPrefix.style(\"transform\");\n- this.applyTransform.transform = transform;\n- if (transform) {\n- var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv, OpenLayers.Util.vendorPrefix.css(\"transform\"));\n- if (!computedStyle || computedStyle !== \"none\") {\n- template = [\"translate3d(\", \",0) \", \"scale3d(\", \",1)\"];\n- style[transform] = [template[0], \"0,0\", template[1]].join(\"\")\n- }\n- if (!template || !~style[transform].indexOf(template[0])) {\n- template = [\"translate(\", \") \", \"scale(\", \")\"]\n- }\n- this.applyTransform.template = template\n- }\n- }\n- if (transform !== null && (template[0] === \"translate3d(\" || needTransform === true)) {\n- if (needTransform === true && template[0] === \"translate(\") {\n- x -= origin.x;\n- y -= origin.y;\n- style.left = origin.x + \"px\";\n- style.top = origin.y + \"px\"\n- }\n- style[transform] = [template[0], x, \"px,\", y, \"px\", template[1], template[2], scale, \",\", scale, template[3]].join(\"\")\n- } else {\n- style.left = x + \"px\";\n- style.top = y + \"px\";\n- if (transform !== null) {\n- style[transform] = \"\"\n- }\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Map\"\n-});\n-OpenLayers.Map.TILE_WIDTH = 256;\n-OpenLayers.Map.TILE_HEIGHT = 256;\n-OpenLayers.Layer = OpenLayers.Class({\n- id: null,\n- name: null,\n- div: null,\n- opacity: 1,\n- alwaysInRange: null,\n- RESOLUTION_PROPERTIES: [\"scales\", \"resolutions\", \"maxScale\", \"minScale\", \"maxResolution\", \"minResolution\", \"numZoomLevels\", \"maxZoomLevel\"],\n- events: null,\n- map: null,\n- isBaseLayer: false,\n- alpha: false,\n- displayInLayerSwitcher: true,\n- visibility: true,\n- attribution: null,\n- inRange: false,\n- imageSize: null,\n- options: null,\n- eventListeners: null,\n- gutter: 0,\n- projection: null,\n- units: null,\n- scales: null,\n- resolutions: null,\n- maxExtent: null,\n- minExtent: null,\n- maxResolution: null,\n- minResolution: null,\n- numZoomLevels: null,\n- minScale: null,\n- maxScale: null,\n- displayOutsideMaxExtent: false,\n- wrapDateLine: false,\n- metadata: null,\n- initialize: function(name, options) {\n- this.metadata = {};\n- options = OpenLayers.Util.extend({}, options);\n- if (this.alwaysInRange != null) {\n- options.alwaysInRange = this.alwaysInRange\n- }\n- this.addOptions(options);\n- this.name = name;\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.style.width = \"100%\";\n- this.div.style.height = \"100%\";\n- this.div.dir = \"ltr\";\n- this.events = new OpenLayers.Events(this, this.div);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- }\n- },\n- destroy: function(setNewBaseLayer) {\n- if (setNewBaseLayer == null) {\n- setNewBaseLayer = true\n- }\n- if (this.map != null) {\n- this.map.removeLayer(this, setNewBaseLayer)\n- }\n- this.projection = null;\n- this.map = null;\n- this.name = null;\n- this.div = null;\n- this.options = null;\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n- }\n- this.events.destroy()\n- }\n- this.eventListeners = null;\n- this.events = null\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer(this.name, this.getOptions())\n- }\n- OpenLayers.Util.applyDefaults(obj, this);\n- obj.map = null;\n- return obj\n- },\n- getOptions: function() {\n- var options = {};\n- for (var o in this.options) {\n- options[o] = this[o]\n- }\n- return options\n- },\n- setName: function(newName) {\n- if (newName != this.name) {\n- this.name = newName;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"name\"\n- })\n- }\n- }\n- },\n- addOptions: function(newOptions, reinitialize) {\n- if (this.options == null) {\n- this.options = {}\n- }\n- if (newOptions) {\n- if (typeof newOptions.projection == \"string\") {\n- newOptions.projection = new OpenLayers.Projection(newOptions.projection)\n- }\n- if (newOptions.projection) {\n- OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()])\n- }\n- if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n- newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent)\n- }\n- if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n- newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent)\n- }\n- }\n- OpenLayers.Util.extend(this.options, newOptions);\n- OpenLayers.Util.extend(this, newOptions);\n- if (this.projection && this.projection.getUnits()) {\n- this.units = this.projection.getUnits()\n- }\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- var properties = this.RESOLUTION_PROPERTIES.concat([\"projection\", \"units\", \"minExtent\", \"maxExtent\"]);\n- for (var o in newOptions) {\n- if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) {\n- this.initResolutions();\n- if (reinitialize && this.map.baseLayer === this) {\n- this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true);\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- })\n- }\n- break\n- }\n- }\n- }\n- },\n- onMapResize: function() {},\n- redraw: function() {\n- var redrawn = false;\n- if (this.map) {\n- this.inRange = this.calculateInRange();\n- var extent = this.getExtent();\n- if (extent && this.inRange && this.visibility) {\n- var zoomChanged = true;\n- this.moveTo(extent, zoomChanged, false);\n- this.events.triggerEvent(\"moveend\", {\n- zoomChanged: zoomChanged\n- });\n- redrawn = true\n- }\n- }\n- return redrawn\n- },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- var display = this.visibility;\n- if (!this.isBaseLayer) {\n- display = display && this.inRange\n- }\n- this.display(display)\n- },\n- moveByPx: function(dx, dy) {},\n- setMap: function(map) {\n- if (this.map == null) {\n- this.map = map;\n- this.maxExtent = this.maxExtent || this.map.maxExtent;\n- this.minExtent = this.minExtent || this.map.minExtent;\n- this.projection = this.projection || this.map.projection;\n- if (typeof this.projection == \"string\") {\n- this.projection = new OpenLayers.Projection(this.projection)\n- }\n- this.units = this.projection.getUnits() || this.units || this.map.units;\n- this.initResolutions();\n- if (!this.isBaseLayer) {\n- this.inRange = this.calculateInRange();\n- var show = this.visibility && this.inRange;\n- this.div.style.display = show ? \"\" : \"none\"\n- }\n- this.setTileSize()\n- }\n- },\n- afterAdd: function() {},\n- removeMap: function(map) {},\n- getImageSize: function(bounds) {\n- return this.imageSize || this.tileSize\n- },\n- setTileSize: function(size) {\n- var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize();\n- this.tileSize = tileSize;\n- if (this.gutter) {\n- this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter)\n- }\n- },\n- getVisibility: function() {\n- return this.visibility\n- },\n- setVisibility: function(visibility) {\n- if (visibility != this.visibility) {\n- this.visibility = visibility;\n- this.display(visibility);\n- this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"visibility\"\n- })\n- }\n- this.events.triggerEvent(\"visibilitychanged\")\n- }\n- },\n- display: function(display) {\n- if (display != (this.div.style.display != \"none\")) {\n- this.div.style.display = display && this.calculateInRange() ? \"block\" : \"none\"\n- }\n- },\n- calculateInRange: function() {\n- var inRange = false;\n- if (this.alwaysInRange) {\n- inRange = true\n- } else {\n- if (this.map) {\n- var resolution = this.map.getResolution();\n- inRange = resolution >= this.minResolution && resolution <= this.maxResolution\n- }\n- }\n- return inRange\n- },\n- setIsBaseLayer: function(isBaseLayer) {\n- if (isBaseLayer != this.isBaseLayer) {\n- this.isBaseLayer = isBaseLayer;\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changebaselayer\", {\n- layer: this\n- })\n- }\n- }\n- },\n- initResolutions: function() {\n- var i, len, p;\n- var props = {},\n- alwaysInRange = true;\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p];\n- if (alwaysInRange && this.options[p]) {\n- alwaysInRange = false\n- }\n- }\n- if (this.options.alwaysInRange == null) {\n- this.alwaysInRange = alwaysInRange\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales)\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props)\n- }\n- if (props.resolutions == null) {\n- for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n- p = this.RESOLUTION_PROPERTIES[i];\n- props[p] = this.options[p] != null ? this.options[p] : this.map[p]\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.resolutionsFromScales(props.scales)\n- }\n- if (props.resolutions == null) {\n- props.resolutions = this.calculateResolutions(props)\n- }\n- }\n- var maxResolution;\n- if (this.options.maxResolution && this.options.maxResolution !== \"auto\") {\n- maxResolution = this.options.maxResolution\n- }\n- if (this.options.minScale) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(this.options.minScale, this.units)\n- }\n- var minResolution;\n- if (this.options.minResolution && this.options.minResolution !== \"auto\") {\n- minResolution = this.options.minResolution\n- }\n- if (this.options.maxScale) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units)\n- }\n- if (props.resolutions) {\n- props.resolutions.sort(function(a, b) {\n- return b - a\n- });\n- if (!maxResolution) {\n- maxResolution = props.resolutions[0]\n- }\n- if (!minResolution) {\n- var lastIdx = props.resolutions.length - 1;\n- minResolution = props.resolutions[lastIdx]\n- }\n- }\n- this.resolutions = props.resolutions;\n- if (this.resolutions) {\n- len = this.resolutions.length;\n- this.scales = new Array(len);\n- for (i = 0; i < len; i++) {\n- this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units)\n- }\n- this.numZoomLevels = len\n- }\n- this.minResolution = minResolution;\n- if (minResolution) {\n- this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units)\n- }\n- this.maxResolution = maxResolution;\n- if (maxResolution) {\n- this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units)\n- }\n- },\n- resolutionsFromScales: function(scales) {\n- if (scales == null) {\n- return\n- }\n- var resolutions, i, len;\n- len = scales.length;\n- resolutions = new Array(len);\n- for (i = 0; i < len; i++) {\n- resolutions[i] = OpenLayers.Util.getResolutionFromScale(scales[i], this.units)\n- }\n- return resolutions\n- },\n- calculateResolutions: function(props) {\n- var viewSize, wRes, hRes;\n- var maxResolution = props.maxResolution;\n- if (props.minScale != null) {\n- maxResolution = OpenLayers.Util.getResolutionFromScale(props.minScale, this.units)\n- } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.maxExtent.getWidth() / viewSize.w;\n- hRes = this.maxExtent.getHeight() / viewSize.h;\n- maxResolution = Math.max(wRes, hRes)\n- }\n- var minResolution = props.minResolution;\n- if (props.maxScale != null) {\n- minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units)\n- } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n- viewSize = this.map.getSize();\n- wRes = this.minExtent.getWidth() / viewSize.w;\n- hRes = this.minExtent.getHeight() / viewSize.h;\n- minResolution = Math.max(wRes, hRes)\n- }\n- if (typeof maxResolution !== \"number\" && typeof minResolution !== \"number\" && this.maxExtent != null) {\n- var tileSize = this.map.getTileSize();\n- maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h)\n- }\n- var maxZoomLevel = props.maxZoomLevel;\n- var numZoomLevels = props.numZoomLevels;\n- if (typeof minResolution === \"number\" && typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n- var ratio = maxResolution / minResolution;\n- numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1\n- } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n- numZoomLevels = maxZoomLevel + 1\n- }\n- if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 || typeof maxResolution !== \"number\" && typeof minResolution !== \"number\") {\n- return\n- }\n- var resolutions = new Array(numZoomLevels);\n- var base = 2;\n- if (typeof minResolution == \"number\" && typeof maxResolution == \"number\") {\n- base = Math.pow(maxResolution / minResolution, 1 / (numZoomLevels - 1))\n- }\n- var i;\n- if (typeof maxResolution === \"number\") {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[i] = maxResolution / Math.pow(base, i)\n- }\n- } else {\n- for (i = 0; i < numZoomLevels; i++) {\n- resolutions[numZoomLevels - 1 - i] = minResolution * Math.pow(base, i)\n- }\n- }\n- return resolutions\n- },\n- getResolution: function() {\n- var zoom = this.map.getZoom();\n- return this.getResolutionForZoom(zoom)\n- },\n- getExtent: function() {\n- return this.map.calculateBounds()\n- },\n- getZoomForExtent: function(extent, closest) {\n- var viewSize = this.map.getSize();\n- var idealResolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);\n- return this.getZoomForResolution(idealResolution, closest)\n- },\n- getDataExtent: function() {},\n- getResolutionForZoom: function(zoom) {\n- zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n- var resolution;\n- if (this.map.fractionalZoom) {\n- var low = Math.floor(zoom);\n- var high = Math.ceil(zoom);\n- resolution = this.resolutions[low] - (zoom - low) * (this.resolutions[low] - this.resolutions[high])\n- } else {\n- resolution = this.resolutions[Math.round(zoom)]\n- }\n- return resolution\n- },\n- getZoomForResolution: function(resolution, closest) {\n- var zoom, i, len;\n- if (this.map.fractionalZoom) {\n- var lowZoom = 0;\n- var highZoom = this.resolutions.length - 1;\n- var highRes = this.resolutions[lowZoom];\n- var lowRes = this.resolutions[highZoom];\n- var res;\n- for (i = 0, len = this.resolutions.length; i < len; ++i) {\n- res = this.resolutions[i];\n- if (res >= resolution) {\n- highRes = res;\n- lowZoom = i\n- }\n- if (res <= resolution) {\n- lowRes = res;\n- highZoom = i;\n- break\n- }\n- }\n- var dRes = highRes - lowRes;\n- if (dRes > 0) {\n- zoom = lowZoom + (highRes - resolution) / dRes\n- } else {\n- zoom = lowZoom\n- }\n- } else {\n- var diff;\n- var minDiff = Number.POSITIVE_INFINITY;\n- for (i = 0, len = this.resolutions.length; i < len; i++) {\n- if (closest) {\n- diff = Math.abs(this.resolutions[i] - resolution);\n- if (diff > minDiff) {\n- break\n- }\n- minDiff = diff\n- } else {\n- if (this.resolutions[i] < resolution) {\n- break\n- }\n- }\n- }\n- zoom = Math.max(0, i - 1)\n- }\n- return zoom\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- var map = this.map;\n- if (viewPortPx != null && map.minPx) {\n- var res = map.getResolution();\n- var maxExtent = map.getMaxExtent({\n- restricted: true\n- });\n- var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n- var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n- lonlat = new OpenLayers.LonLat(lon, lat);\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent)\n- }\n- }\n- return lonlat\n- },\n- getViewPortPxFromLonLat: function(lonlat, resolution) {\n- var px = null;\n- if (lonlat != null) {\n- resolution = resolution || this.map.getResolution();\n- var extent = this.map.calculateBounds(null, resolution);\n- px = new OpenLayers.Pixel(1 / resolution * (lonlat.lon - extent.left), 1 / resolution * (extent.top - lonlat.lat))\n- }\n- return px\n- },\n- setOpacity: function(opacity) {\n- if (opacity != this.opacity) {\n- this.opacity = opacity;\n- var childNodes = this.div.childNodes;\n- for (var i = 0, len = childNodes.length; i < len; ++i) {\n- var element = childNodes[i].firstChild || childNodes[i];\n- var lastChild = childNodes[i].lastChild;\n- if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n- element = lastChild.parentNode\n- }\n- OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity)\n- }\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- })\n- }\n- }\n- },\n- getZIndex: function() {\n- return this.div.style.zIndex\n- },\n- setZIndex: function(zIndex) {\n- this.div.style.zIndex = zIndex\n- },\n- adjustBounds: function(bounds) {\n- if (this.gutter) {\n- var mapGutter = this.gutter * this.map.getResolution();\n- bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter)\n- }\n- if (this.wrapDateLine) {\n- var wrappingOptions = {\n- rightTolerance: this.getResolution(),\n- leftTolerance: this.getResolution()\n- };\n- bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions)\n- }\n- return bounds\n- },\n- CLASS_NAME: \"OpenLayers.Layer\"\n-});\n-(function() {\n- var oXMLHttpRequest = window.XMLHttpRequest;\n- var bGecko = !!window.controllers,\n- bIE = window.document.all && !window.opera,\n- bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n-\n- function fXMLHttpRequest() {\n- this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n- this._listeners = []\n- }\n-\n- function cXMLHttpRequest() {\n- return new fXMLHttpRequest\n- }\n- cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n- if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n- cXMLHttpRequest.UNSENT = 0;\n- cXMLHttpRequest.OPENED = 1;\n- cXMLHttpRequest.HEADERS_RECEIVED = 2;\n- cXMLHttpRequest.LOADING = 3;\n- cXMLHttpRequest.DONE = 4;\n- cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n- cXMLHttpRequest.prototype.responseText = \"\";\n- cXMLHttpRequest.prototype.responseXML = null;\n- cXMLHttpRequest.prototype.status = 0;\n- cXMLHttpRequest.prototype.statusText = \"\";\n- cXMLHttpRequest.prototype.priority = \"NORMAL\";\n- cXMLHttpRequest.prototype.onreadystatechange = null;\n- cXMLHttpRequest.onreadystatechange = null;\n- cXMLHttpRequest.onopen = null;\n- cXMLHttpRequest.onsend = null;\n- cXMLHttpRequest.onabort = null;\n- cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n- delete this._headers;\n- if (arguments.length < 3) bAsync = true;\n- this._async = bAsync;\n- var oRequest = this,\n- nState = this.readyState,\n- fOnUnload;\n- if (bIE && bAsync) {\n- fOnUnload = function() {\n- if (nState != cXMLHttpRequest.DONE) {\n- fCleanTransport(oRequest);\n- oRequest.abort()\n- }\n- };\n- window.attachEvent(\"onunload\", fOnUnload)\n- }\n- if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments);\n- if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n- else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser);\n- else this._object.open(sMethod, sUrl, bAsync);\n- this.readyState = cXMLHttpRequest.OPENED;\n- fReadyStateChange(this);\n- this._object.onreadystatechange = function() {\n- if (bGecko && !bAsync) return;\n- oRequest.readyState = oRequest._object.readyState;\n- fSynchronizeValues(oRequest);\n- if (oRequest._aborted) {\n- oRequest.readyState = cXMLHttpRequest.UNSENT;\n- return\n- }\n- if (oRequest.readyState == cXMLHttpRequest.DONE) {\n- delete oRequest._data;\n- fCleanTransport(oRequest);\n- if (bIE && bAsync) window.detachEvent(\"onunload\", fOnUnload)\n- }\n- if (nState != oRequest.readyState) fReadyStateChange(oRequest);\n- nState = oRequest.readyState\n- }\n- };\n-\n- function fXMLHttpRequest_send(oRequest) {\n- oRequest._object.send(oRequest._data);\n- if (bGecko && !oRequest._async) {\n- oRequest.readyState = cXMLHttpRequest.OPENED;\n- fSynchronizeValues(oRequest);\n- while (oRequest.readyState < cXMLHttpRequest.DONE) {\n- oRequest.readyState++;\n- fReadyStateChange(oRequest);\n- if (oRequest._aborted) return\n- }\n- }\n- }\n- cXMLHttpRequest.prototype.send = function(vData) {\n- if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments);\n- if (!arguments.length) vData = null;\n- if (vData && vData.nodeType) {\n- vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml;\n- if (!this._headers[\"Content-Type\"]) this._object.setRequestHeader(\"Content-Type\", \"application/xml\")\n- }\n- this._data = vData;\n- fXMLHttpRequest_send(this)\n- };\n- cXMLHttpRequest.prototype.abort = function() {\n- if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments);\n- if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true;\n- this._object.abort();\n- fCleanTransport(this);\n- this.readyState = cXMLHttpRequest.UNSENT;\n- delete this._data\n- };\n- cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n- return this._object.getAllResponseHeaders()\n- };\n- cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n- return this._object.getResponseHeader(sName)\n- };\n- cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n- if (!this._headers) this._headers = {};\n- this._headers[sName] = sValue;\n- return this._object.setRequestHeader(sName, sValue)\n- };\n- cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return;\n- this._listeners.push([sName, fHandler, bUseCapture])\n- };\n- cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break;\n- if (oListener) this._listeners.splice(nIndex, 1)\n- };\n- cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n- var oEventPseudo = {\n- type: oEvent.type,\n- target: this,\n- currentTarget: this,\n- eventPhase: 2,\n- bubbles: oEvent.bubbles,\n- cancelable: oEvent.cancelable,\n- timeStamp: oEvent.timeStamp,\n- stopPropagation: function() {},\n- preventDefault: function() {},\n- initEvent: function() {}\n- };\n- if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n- for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n- if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo])\n- };\n- cXMLHttpRequest.prototype.toString = function() {\n- return \"[\" + \"object\" + \" \" + \"XMLHttpRequest\" + \"]\"\n- };\n- cXMLHttpRequest.toString = function() {\n- return \"[\" + \"XMLHttpRequest\" + \"]\"\n- };\n-\n- function fReadyStateChange(oRequest) {\n- if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest);\n- oRequest.dispatchEvent({\n- type: \"readystatechange\",\n- bubbles: false,\n- cancelable: false,\n- timeStamp: new Date + 0\n- })\n- }\n-\n- function fGetDocument(oRequest) {\n- var oDocument = oRequest.responseXML,\n- sResponse = oRequest.responseText;\n- if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n- oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n- oDocument.async = false;\n- oDocument.validateOnParse = false;\n- oDocument.loadXML(sResponse)\n- }\n- if (oDocument)\n- if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\") return null;\n- return oDocument\n- }\n-\n- function fSynchronizeValues(oRequest) {\n- try {\n- oRequest.responseText = oRequest._object.responseText\n- } catch (e) {}\n- try {\n- oRequest.responseXML = fGetDocument(oRequest._object)\n- } catch (e) {}\n- try {\n- oRequest.status = oRequest._object.status\n- } catch (e) {}\n- try {\n- oRequest.statusText = oRequest._object.statusText\n- } catch (e) {}\n- }\n-\n- function fCleanTransport(oRequest) {\n- oRequest._object.onreadystatechange = new window.Function\n- }\n- if (!window.Function.prototype.apply) {\n- window.Function.prototype.apply = function(oRequest, oArguments) {\n- if (!oArguments) oArguments = [];\n- oRequest.__func = this;\n- oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n- delete oRequest.__func\n- }\n- }\n- if (!OpenLayers.Request) {\n- OpenLayers.Request = {}\n- }\n- OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest\n-})();\n-OpenLayers.ProxyHost = \"\";\n-if (!OpenLayers.Request) {\n- OpenLayers.Request = {}\n-}\n-OpenLayers.Util.extend(OpenLayers.Request, {\n- DEFAULT_CONFIG: {\n- method: \"GET\",\n- url: window.location.href,\n- async: true,\n- user: undefined,\n- password: undefined,\n- params: null,\n- proxy: OpenLayers.ProxyHost,\n- headers: {},\n- data: null,\n- callback: function() {},\n- success: null,\n- failure: null,\n- scope: null\n- },\n- URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n- events: new OpenLayers.Events(this),\n- makeSameOrigin: function(url, proxy) {\n- var sameOrigin = url.indexOf(\"http\") !== 0;\n- var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n- if (urlParts) {\n- var location = window.location;\n- sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname;\n- var uPort = urlParts[4],\n- lPort = location.port;\n- if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n- sameOrigin = sameOrigin && uPort == lPort\n- }\n- }\n- if (!sameOrigin) {\n- if (proxy) {\n- if (typeof proxy == \"function\") {\n- url = proxy(url)\n- } else {\n- url = proxy + encodeURIComponent(url)\n- }\n- }\n- }\n- return url\n- },\n- issue: function(config) {\n- var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, {\n- proxy: OpenLayers.ProxyHost\n- });\n- config = config || {};\n- config.headers = config.headers || {};\n- config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n- config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n- var customRequestedWithHeader = false,\n- headerKey;\n- for (headerKey in config.headers) {\n- if (config.headers.hasOwnProperty(headerKey)) {\n- if (headerKey.toLowerCase() === \"x-requested-with\") {\n- customRequestedWithHeader = true\n- }\n- }\n- }\n- if (customRequestedWithHeader === false) {\n- config.headers[\"X-Requested-With\"] = \"XMLHttpRequest\"\n- }\n- var request = new OpenLayers.Request.XMLHttpRequest;\n- var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {}));\n- url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n- request.open(config.method, url, config.async, config.user, config.password);\n- for (var header in config.headers) {\n- request.setRequestHeader(header, config.headers[header])\n- }\n- var events = this.events;\n- var self = this;\n- request.onreadystatechange = function() {\n- if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n- var proceed = events.triggerEvent(\"complete\", {\n- request: request,\n- config: config,\n- requestUrl: url\n- });\n- if (proceed !== false) {\n- self.runCallbacks({\n- request: request,\n- config: config,\n- requestUrl: url\n- })\n- }\n- }\n- };\n- if (config.async === false) {\n- request.send(config.data)\n- } else {\n- window.setTimeout(function() {\n- if (request.readyState !== 0) {\n- request.send(config.data)\n- }\n- }, 0)\n- }\n- return request\n- },\n- runCallbacks: function(options) {\n- var request = options.request;\n- var config = options.config;\n- var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback;\n- var success;\n- if (config.success) {\n- success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success\n- }\n- var failure;\n- if (config.failure) {\n- failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure\n- }\n- if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" && request.responseText) {\n- request.status = 200\n- }\n- complete(request);\n- if (!request.status || request.status >= 200 && request.status < 300) {\n- this.events.triggerEvent(\"success\", options);\n- if (success) {\n- success(request)\n- }\n- }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"failure\", options);\n- if (failure) {\n- failure(request)\n- }\n- }\n- },\n- GET: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"GET\"\n- });\n- return OpenLayers.Request.issue(config)\n- },\n- POST: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"POST\"\n- });\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\"\n- }\n- return OpenLayers.Request.issue(config)\n- },\n- PUT: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"PUT\"\n- });\n- config.headers = config.headers ? config.headers : {};\n- if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n- config.headers[\"Content-Type\"] = \"application/xml\"\n- }\n- return OpenLayers.Request.issue(config)\n- },\n- DELETE: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"DELETE\"\n- });\n- return OpenLayers.Request.issue(config)\n- },\n- HEAD: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"HEAD\"\n- });\n- return OpenLayers.Request.issue(config)\n- },\n- OPTIONS: function(config) {\n- config = OpenLayers.Util.extend(config, {\n- method: \"OPTIONS\"\n- });\n- return OpenLayers.Request.issue(config)\n- }\n-});\n-OpenLayers.Icon = OpenLayers.Class({\n- url: null,\n- size: null,\n- offset: null,\n- calculateOffset: null,\n- imageDiv: null,\n- px: null,\n- initialize: function(url, size, offset, calculateOffset) {\n- this.url = url;\n- this.size = size || {\n- w: 20,\n- h: 20\n- };\n- this.offset = offset || {\n- x: -(this.size.w / 2),\n- y: -(this.size.h / 2)\n- };\n- this.calculateOffset = calculateOffset;\n- var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n- this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id)\n- },\n- destroy: function() {\n- this.erase();\n- OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n- this.imageDiv.innerHTML = \"\";\n- this.imageDiv = null\n- },\n- clone: function() {\n- return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset)\n- },\n- setSize: function(size) {\n- if (size != null) {\n- this.size = size\n- }\n- this.draw()\n- },\n- setUrl: function(url) {\n- if (url != null) {\n- this.url = url\n- }\n- this.draw()\n- },\n- draw: function(px) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, \"absolute\");\n- this.moveTo(px);\n- return this.imageDiv\n- },\n- erase: function() {\n- if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n- OpenLayers.Element.remove(this.imageDiv)\n- }\n- },\n- setOpacity: function(opacity) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity)\n- },\n- moveTo: function(px) {\n- if (px != null) {\n- this.px = px\n- }\n- if (this.imageDiv != null) {\n- if (this.px == null) {\n- this.display(false)\n- } else {\n- if (this.calculateOffset) {\n- this.offset = this.calculateOffset(this.size)\n- }\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n- x: this.px.x + this.offset.x,\n- y: this.px.y + this.offset.y\n- })\n- }\n- }\n- },\n- display: function(display) {\n- this.imageDiv.style.display = display ? \"\" : \"none\"\n- },\n- isDrawn: function() {\n- var isDrawn = this.imageDiv && this.imageDiv.parentNode && this.imageDiv.parentNode.nodeType != 11;\n- return isDrawn\n- },\n- CLASS_NAME: \"OpenLayers.Icon\"\n-});\n-OpenLayers.Marker = OpenLayers.Class({\n- icon: null,\n- lonlat: null,\n- events: null,\n- map: null,\n- initialize: function(lonlat, icon) {\n- this.lonlat = lonlat;\n- var newIcon = icon ? icon : OpenLayers.Marker.defaultIcon();\n- if (this.icon == null) {\n- this.icon = newIcon\n- } else {\n- this.icon.url = newIcon.url;\n- this.icon.size = newIcon.size;\n- this.icon.offset = newIcon.offset;\n- this.icon.calculateOffset = newIcon.calculateOffset\n- }\n- this.events = new OpenLayers.Events(this, this.icon.imageDiv)\n- },\n- destroy: function() {\n- this.erase();\n- this.map = null;\n- this.events.destroy();\n- this.events = null;\n- if (this.icon != null) {\n- this.icon.destroy();\n- this.icon = null\n- }\n- },\n- draw: function(px) {\n- return this.icon.draw(px)\n- },\n- erase: function() {\n- if (this.icon != null) {\n- this.icon.erase()\n- }\n- },\n- moveTo: function(px) {\n- if (px != null && this.icon != null) {\n- this.icon.moveTo(px)\n- }\n- this.lonlat = this.map.getLonLatFromLayerPx(px)\n- },\n- isDrawn: function() {\n- var isDrawn = this.icon && this.icon.isDrawn();\n- return isDrawn\n- },\n- onScreen: function() {\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat)\n- }\n- return onScreen\n- },\n- inflate: function(inflate) {\n- if (this.icon) {\n- this.icon.setSize({\n- w: this.icon.size.w * inflate,\n- h: this.icon.size.h * inflate\n- })\n- }\n- },\n- setOpacity: function(opacity) {\n- this.icon.setOpacity(opacity)\n- },\n- setUrl: function(url) {\n- this.icon.setUrl(url)\n- },\n- display: function(display) {\n- this.icon.display(display)\n- },\n- CLASS_NAME: \"OpenLayers.Marker\"\n-});\n-OpenLayers.Marker.defaultIcon = function() {\n- return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n- w: 21,\n- h: 25\n- }, {\n- x: -10.5,\n- y: -25\n- })\n-};\n-OpenLayers.Handler = OpenLayers.Class({\n- id: null,\n- control: null,\n- map: null,\n- keyMask: null,\n- active: false,\n- evt: null,\n- touch: false,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Util.extend(this, options);\n- this.control = control;\n- this.callbacks = callbacks;\n- var map = this.map || control.map;\n- if (map) {\n- this.setMap(map)\n- }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- setMap: function(map) {\n- this.map = map\n- },\n- checkModifiers: function(evt) {\n- if (this.keyMask == null) {\n- return true\n- }\n- var keyModifiers = (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n- return keyModifiers == this.keyMask\n- },\n- activate: function() {\n- if (this.active) {\n- return false\n- }\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.register(events[i], this[events[i]])\n- }\n- }\n- this.active = true;\n- return true\n- },\n- deactivate: function() {\n- if (!this.active) {\n- return false\n- }\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]])\n- }\n- }\n- this.touch = false;\n- this.active = false;\n- return true\n- },\n- startTouch: function() {\n- if (!this.touch) {\n- this.touch = true;\n- var events = [\"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"mouseout\"];\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]])\n- }\n- }\n- }\n- },\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args)\n- }\n- },\n- register: function(name, method) {\n- this.map.events.registerPriority(name, this, method);\n- this.map.events.registerPriority(name, this, this.setEvent)\n- },\n- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent)\n- },\n- setEvent: function(evt) {\n- this.evt = evt;\n- return true\n- },\n- destroy: function() {\n- this.deactivate();\n- this.control = this.map = null\n- },\n- CLASS_NAME: \"OpenLayers.Handler\"\n-});\n-OpenLayers.Handler.MOD_NONE = 0;\n-OpenLayers.Handler.MOD_SHIFT = 1;\n-OpenLayers.Handler.MOD_CTRL = 2;\n-OpenLayers.Handler.MOD_ALT = 4;\n-OpenLayers.Handler.MOD_META = 8;\n OpenLayers.Feature = OpenLayers.Class({\n layer: null,\n id: null,\n lonlat: null,\n data: null,\n marker: null,\n popupClass: null,\n@@ -5547,211 +2082,14 @@\n labelOutlineColor: \"white\",\n labelOutlineWidth: 3\n },\n delete: {\n display: \"none\"\n }\n };\n-OpenLayers.Style = OpenLayers.Class({\n- id: null,\n- name: null,\n- title: null,\n- description: null,\n- layerName: null,\n- isDefault: false,\n- rules: null,\n- context: null,\n- defaultStyle: null,\n- defaultsPerSymbolizer: false,\n- propertyStyles: null,\n- initialize: function(style, options) {\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules)\n- }\n- this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null\n- }\n- this.rules = null;\n- this.defaultStyle = null\n- },\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n- var rules = this.rules;\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- var applies = rule.evaluate(feature);\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule)\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature)\n- }\n- }\n- }\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature)\n- }\n- }\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\"\n- }\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label)\n- }\n- return style\n- },\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- })\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- })\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- })\n- }\n- }\n- return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n- },\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n- }\n- return style\n- },\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- this.addPropertyStyles(propertyStyles, value)\n- } else {\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break\n- }\n- }\n- }\n- return propertyStyles\n- },\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true\n- }\n- }\n- return propertyStyles\n- },\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles()\n- },\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles()\n- },\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i]\n- }\n- }\n- },\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone())\n- }\n- }\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options)\n- },\n- CLASS_NAME: \"OpenLayers.Style\"\n-});\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = isNaN(value) || !value ? value : parseFloat(value)\n- }\n- return value\n-};\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n-OpenLayers.Filter = OpenLayers.Class({\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {},\n- evaluate: function(context) {\n- return true\n- },\n- clone: function() {\n- return null\n- },\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this)\n- } else {\n- string = Object.prototype.toString.call(this)\n- }\n- return string\n- },\n- CLASS_NAME: \"OpenLayers.Filter\"\n-});\n OpenLayers.Format = OpenLayers.Class({\n options: null,\n externalProjection: null,\n internalProjection: null,\n data: null,\n keepData: false,\n initialize: function(options) {\n@@ -5763,1760 +2101,31 @@\n throw new Error(\"Read not implemented.\")\n },\n write: function(object) {\n throw new Error(\"Write not implemented.\")\n },\n CLASS_NAME: \"OpenLayers.Format\"\n });\n-OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n- URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n- url: null,\n- params: null,\n- reproject: false,\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.url = url;\n- if (!this.params) {\n- this.params = OpenLayers.Util.extend({}, params)\n- }\n- },\n- destroy: function() {\n- this.url = null;\n- this.params = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n+ x: null,\n+ y: null,\n+ initialize: function(x, y) {\n+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n+ this.x = parseFloat(x);\n+ this.y = parseFloat(y)\n },\n clone: function(obj) {\n if (obj == null) {\n- obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions())\n+ obj = new OpenLayers.Geometry.Point(this.x, this.y)\n }\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+ OpenLayers.Util.applyDefaults(obj, this);\n return obj\n },\n- setUrl: function(newUrl) {\n- this.url = newUrl\n- },\n- mergeNewParams: function(newParams) {\n- this.params = OpenLayers.Util.extend(this.params, newParams);\n- var ret = this.redraw();\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"params\"\n- })\n- }\n- return ret\n- },\n- redraw: function(force) {\n- if (force) {\n- return this.mergeNewParams({\n- _olSalt: Math.random()\n- })\n- } else {\n- return OpenLayers.Layer.prototype.redraw.apply(this, [])\n- }\n- },\n- selectUrl: function(paramString, urls) {\n- var product = 1;\n- for (var i = 0, len = paramString.length; i < len; i++) {\n- product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n- product -= Math.floor(product)\n- }\n- return urls[Math.floor(product * urls.length)]\n- },\n- getFullRequestString: function(newParams, altUrl) {\n- var url = altUrl || this.url;\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url)\n- }\n- var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key]\n- }\n- }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n- return OpenLayers.Util.urlAppend(url, paramsString)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n-});\n-OpenLayers.Tile = OpenLayers.Class({\n- events: null,\n- eventListeners: null,\n- id: null,\n- layer: null,\n- url: null,\n- bounds: null,\n- size: null,\n- position: null,\n- isLoading: false,\n- initialize: function(layer, position, bounds, url, size, options) {\n- this.layer = layer;\n- this.position = position.clone();\n- this.setBounds(bounds);\n- this.url = url;\n- if (size) {\n- this.size = size.clone()\n- }\n- this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n- OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- },\n- unload: function() {\n- if (this.isLoading) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"unload\")\n- }\n- },\n- destroy: function() {\n- this.layer = null;\n- this.bounds = null;\n- this.size = null;\n- this.position = null;\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n- }\n- this.events.destroy();\n- this.eventListeners = null;\n- this.events = null\n- },\n- draw: function(force) {\n- if (!force) {\n- this.clear()\n- }\n- var draw = this.shouldDraw();\n- if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n- draw = null\n- }\n- return draw\n- },\n- shouldDraw: function() {\n- var withinMaxExtent = false,\n- maxExtent = this.layer.maxExtent;\n- if (maxExtent) {\n- var map = this.layer.map;\n- var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n- if (this.bounds.intersectsBounds(maxExtent, {\n- inclusive: false,\n- worldBounds: worldBounds\n- })) {\n- withinMaxExtent = true\n- }\n- }\n- return withinMaxExtent || this.layer.displayOutsideMaxExtent\n- },\n- setBounds: function(bounds) {\n- bounds = bounds.clone();\n- if (this.layer.map.baseLayer.wrapDateLine) {\n- var worldExtent = this.layer.map.getMaxExtent(),\n- tolerance = this.layer.map.getResolution();\n- bounds = bounds.wrapDateLine(worldExtent, {\n- leftTolerance: tolerance,\n- rightTolerance: tolerance\n- })\n- }\n- this.bounds = bounds\n- },\n- moveTo: function(bounds, position, redraw) {\n- if (redraw == null) {\n- redraw = true\n- }\n- this.setBounds(bounds);\n- this.position = position.clone();\n- if (redraw) {\n- this.draw()\n- }\n- },\n- clear: function(draw) {},\n- CLASS_NAME: \"OpenLayers.Tile\"\n-});\n-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n- url: null,\n- imgDiv: null,\n- frame: null,\n- imageReloadAttempts: null,\n- layerAlphaHack: null,\n- asyncRequestId: null,\n- maxGetUrlLength: null,\n- canvasContext: null,\n- crossOriginKeyword: null,\n- initialize: function(layer, position, bounds, url, size, options) {\n- OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n- this.url = url;\n- this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n- if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n- this.frame = document.createElement(\"div\");\n- this.frame.style.position = \"absolute\";\n- this.frame.style.overflow = \"hidden\"\n- }\n- if (this.maxGetUrlLength != null) {\n- OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame)\n- }\n- },\n- destroy: function() {\n- if (this.imgDiv) {\n- this.clear();\n- this.imgDiv = null;\n- this.frame = null\n- }\n- this.asyncRequestId = null;\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n- },\n- draw: function() {\n- var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (shouldDraw) {\n- if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n- this.bounds = this.getBoundsFromBaseLayer(this.position)\n- }\n- if (this.isLoading) {\n- this._loadEvent = \"reload\"\n- } else {\n- this.isLoading = true;\n- this._loadEvent = \"loadstart\"\n- }\n- this.renderTile();\n- this.positionTile()\n- } else if (shouldDraw === false) {\n- this.unload()\n- }\n- return shouldDraw\n- },\n- renderTile: function() {\n- if (this.layer.async) {\n- var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n- this.layer.getURLasync(this.bounds, function(url) {\n- if (id == this.asyncRequestId) {\n- this.url = url;\n- this.initImage()\n- }\n- }, this)\n- } else {\n- this.url = this.layer.getURL(this.bounds);\n- this.initImage()\n- }\n- },\n- positionTile: function() {\n- var style = this.getTile().style,\n- size = this.frame ? this.size : this.layer.getImageSize(this.bounds),\n- ratio = 1;\n- if (this.layer instanceof OpenLayers.Layer.Grid) {\n- ratio = this.layer.getServerResolution() / this.layer.map.getResolution()\n- }\n- style.left = this.position.x + \"px\";\n- style.top = this.position.y + \"px\";\n- style.width = Math.round(ratio * size.w) + \"px\";\n- style.height = Math.round(ratio * size.h) + \"px\"\n- },\n- clear: function() {\n- OpenLayers.Tile.prototype.clear.apply(this, arguments);\n- var img = this.imgDiv;\n- if (img) {\n- var tile = this.getTile();\n- if (tile.parentNode === this.layer.div) {\n- this.layer.div.removeChild(tile)\n- }\n- this.setImgSrc();\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"\"\n- }\n- OpenLayers.Element.removeClass(img, \"olImageLoadError\")\n- }\n- this.canvasContext = null\n- },\n- getImage: function() {\n- if (!this.imgDiv) {\n- this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n- var style = this.imgDiv.style;\n- if (this.frame) {\n- var left = 0,\n- top = 0;\n- if (this.layer.gutter) {\n- left = this.layer.gutter / this.layer.tileSize.w * 100;\n- top = this.layer.gutter / this.layer.tileSize.h * 100\n- }\n- style.left = -left + \"%\";\n- style.top = -top + \"%\";\n- style.width = 2 * left + 100 + \"%\";\n- style.height = 2 * top + 100 + \"%\"\n- }\n- style.visibility = \"hidden\";\n- style.opacity = 0;\n- if (this.layer.opacity < 1) {\n- style.filter = \"alpha(opacity=\" + this.layer.opacity * 100 + \")\"\n- }\n- style.position = \"absolute\";\n- if (this.layerAlphaHack) {\n- style.paddingTop = style.height;\n- style.height = \"0\";\n- style.width = \"100%\"\n- }\n- if (this.frame) {\n- this.frame.appendChild(this.imgDiv)\n- }\n- }\n- return this.imgDiv\n- },\n- setImage: function(img) {\n- this.imgDiv = img\n- },\n- initImage: function() {\n- if (!this.url && !this.imgDiv) {\n- this.isLoading = false;\n- return\n- }\n- this.events.triggerEvent(\"beforeload\");\n- this.layer.div.appendChild(this.getTile());\n- this.events.triggerEvent(this._loadEvent);\n- var img = this.getImage();\n- var src = img.getAttribute(\"src\") || \"\";\n- if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n- this._loadTimeout = window.setTimeout(OpenLayers.Function.bind(this.onImageLoad, this), 0)\n- } else {\n- this.stopLoading();\n- if (this.crossOriginKeyword) {\n- img.removeAttribute(\"crossorigin\")\n- }\n- OpenLayers.Event.observe(img, \"load\", OpenLayers.Function.bind(this.onImageLoad, this));\n- OpenLayers.Event.observe(img, \"error\", OpenLayers.Function.bind(this.onImageError, this));\n- this.imageReloadAttempts = 0;\n- this.setImgSrc(this.url)\n- }\n- },\n- setImgSrc: function(url) {\n- var img = this.imgDiv;\n- if (url) {\n- img.style.visibility = \"hidden\";\n- img.style.opacity = 0;\n- if (this.crossOriginKeyword) {\n- if (url.substr(0, 5) !== \"data:\") {\n- img.setAttribute(\"crossorigin\", this.crossOriginKeyword)\n- } else {\n- img.removeAttribute(\"crossorigin\")\n- }\n- }\n- img.src = url\n- } else {\n- this.stopLoading();\n- this.imgDiv = null;\n- if (img.parentNode) {\n- img.parentNode.removeChild(img)\n- }\n- }\n- },\n- getTile: function() {\n- return this.frame ? this.frame : this.getImage()\n- },\n- createBackBuffer: function() {\n- if (!this.imgDiv || this.isLoading) {\n- return\n- }\n- var backBuffer;\n- if (this.frame) {\n- backBuffer = this.frame.cloneNode(false);\n- backBuffer.appendChild(this.imgDiv)\n- } else {\n- backBuffer = this.imgDiv\n- }\n- this.imgDiv = null;\n- return backBuffer\n- },\n- onImageLoad: function() {\n- var img = this.imgDiv;\n- this.stopLoading();\n- img.style.visibility = \"inherit\";\n- img.style.opacity = this.layer.opacity;\n- this.isLoading = false;\n- this.canvasContext = null;\n- this.events.triggerEvent(\"loadend\");\n- if (this.layerAlphaHack === true) {\n- img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"', sizingMethod='scale')\"\n- }\n- },\n- onImageError: function() {\n- var img = this.imgDiv;\n- if (img.src != null) {\n- this.imageReloadAttempts++;\n- if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n- this.setImgSrc(this.layer.getURL(this.bounds))\n- } else {\n- OpenLayers.Element.addClass(img, \"olImageLoadError\");\n- this.events.triggerEvent(\"loaderror\");\n- this.onImageLoad()\n- }\n- }\n- },\n- stopLoading: function() {\n- OpenLayers.Event.stopObservingElement(this.imgDiv);\n- window.clearTimeout(this._loadTimeout);\n- delete this._loadTimeout\n- },\n- getCanvasContext: function() {\n- if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n- if (!this.canvasContext) {\n- var canvas = document.createElement(\"canvas\");\n- canvas.width = this.size.w;\n- canvas.height = this.size.h;\n- this.canvasContext = canvas.getContext(\"2d\");\n- this.canvasContext.drawImage(this.imgDiv, 0, 0)\n- }\n- return this.canvasContext\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Tile.Image\"\n-});\n-OpenLayers.Tile.Image.IMAGE = function() {\n- var img = new Image;\n- img.className = \"olTileImage\";\n- img.galleryImg = \"no\";\n- return img\n-}();\n-OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n- tileSize: null,\n- tileOriginCorner: \"bl\",\n- tileOrigin: null,\n- tileOptions: null,\n- tileClass: OpenLayers.Tile.Image,\n- grid: null,\n- singleTile: false,\n- ratio: 1.5,\n- buffer: 0,\n- transitionEffect: \"resize\",\n- numLoadingTiles: 0,\n- serverResolutions: null,\n- loading: false,\n- backBuffer: null,\n- gridResolution: null,\n- backBufferResolution: null,\n- backBufferLonLat: null,\n- backBufferTimerId: null,\n- removeBackBufferDelay: null,\n- className: null,\n- gridLayout: null,\n- rowSign: null,\n- transitionendEvents: [\"transitionend\", \"webkitTransitionEnd\", \"otransitionend\", \"oTransitionEnd\"],\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments);\n- this.grid = [];\n- this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n- this.initProperties();\n- this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1\n- },\n- initProperties: function() {\n- if (this.options.removeBackBufferDelay === undefined) {\n- this.removeBackBufferDelay = this.singleTile ? 0 : 2500\n- }\n- if (this.options.className === undefined) {\n- this.className = this.singleTile ? \"olLayerGridSingleTile\" : \"olLayerGrid\"\n- }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n- OpenLayers.Element.addClass(this.div, this.className)\n- },\n- removeMap: function(map) {\n- this.removeBackBuffer()\n- },\n- destroy: function() {\n- this.removeBackBuffer();\n- this.clearGrid();\n- this.grid = null;\n- this.tileSize = null;\n- OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments)\n- },\n- clearGrid: function() {\n- if (this.grid) {\n- for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n- var row = this.grid[iRow];\n- for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n- var tile = row[iCol];\n- this.destroyTile(tile)\n- }\n- }\n- this.grid = [];\n- this.gridResolution = null;\n- this.gridLayout = null\n- }\n- },\n- addOptions: function(newOptions, reinitialize) {\n- var singleTileChanged = newOptions.singleTile !== undefined && newOptions.singleTile !== this.singleTile;\n- OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n- if (this.map && singleTileChanged) {\n- this.initProperties();\n- this.clearGrid();\n- this.tileSize = this.options.tileSize;\n- this.setTileSize();\n- this.moveTo(null, true)\n- }\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone()\n- }\n- obj.grid = [];\n- obj.gridResolution = null;\n- obj.backBuffer = null;\n- obj.backBufferTimerId = null;\n- obj.loading = false;\n- obj.numLoadingTiles = 0;\n- return obj\n- },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n- bounds = bounds || this.map.getExtent();\n- if (bounds != null) {\n- var forceReTile = !this.grid.length || zoomChanged;\n- var tilesBounds = this.getTilesBounds();\n- var resolution = this.map.getResolution();\n- var serverResolution = this.getServerResolution(resolution);\n- if (this.singleTile) {\n- if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) {\n- if (zoomChanged && this.transitionEffect !== \"resize\") {\n- this.removeBackBuffer()\n- }\n- if (!zoomChanged || this.transitionEffect === \"resize\") {\n- this.applyBackBuffer(resolution)\n- }\n- this.initSingleTile(bounds)\n- }\n- } else {\n- forceReTile = forceReTile || !tilesBounds.intersectsBounds(bounds, {\n- worldBounds: this.map.baseLayer.wrapDateLine && this.map.getMaxExtent()\n- });\n- if (forceReTile) {\n- if (zoomChanged && (this.transitionEffect === \"resize\" || this.gridResolution === resolution)) {\n- this.applyBackBuffer(resolution)\n- }\n- this.initGriddedTiles(bounds)\n- } else {\n- this.moveGriddedTiles()\n- }\n- }\n- }\n- },\n- getTileData: function(loc) {\n- var data = null,\n- x = loc.lon,\n- y = loc.lat,\n- numRows = this.grid.length;\n- if (this.map && numRows) {\n- var res = this.map.getResolution(),\n- tileWidth = this.tileSize.w,\n- tileHeight = this.tileSize.h,\n- bounds = this.grid[0][0].bounds,\n- left = bounds.left,\n- top = bounds.top;\n- if (x < left) {\n- if (this.map.baseLayer.wrapDateLine) {\n- var worldWidth = this.map.getMaxExtent().getWidth();\n- var worldsAway = Math.ceil((left - x) / worldWidth);\n- x += worldWidth * worldsAway\n- }\n- }\n- var dtx = (x - left) / (res * tileWidth);\n- var dty = (top - y) / (res * tileHeight);\n- var col = Math.floor(dtx);\n- var row = Math.floor(dty);\n- if (row >= 0 && row < numRows) {\n- var tile = this.grid[row][col];\n- if (tile) {\n- data = {\n- tile: tile,\n- i: Math.floor((dtx - col) * tileWidth),\n- j: Math.floor((dty - row) * tileHeight)\n- }\n- }\n- }\n- }\n- return data\n- },\n- destroyTile: function(tile) {\n- this.removeTileMonitoringHooks(tile);\n- tile.destroy()\n- },\n- getServerResolution: function(resolution) {\n- var distance = Number.POSITIVE_INFINITY;\n- resolution = resolution || this.map.getResolution();\n- if (this.serverResolutions && OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n- var i, newDistance, newResolution, serverResolution;\n- for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n- newResolution = this.serverResolutions[i];\n- newDistance = Math.abs(newResolution - resolution);\n- if (newDistance > distance) {\n- break\n- }\n- distance = newDistance;\n- serverResolution = newResolution\n- }\n- resolution = serverResolution\n- }\n- return resolution\n- },\n- getServerZoom: function() {\n- var resolution = this.getServerResolution();\n- return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0)\n- },\n- applyBackBuffer: function(resolution) {\n- if (this.backBufferTimerId !== null) {\n- this.removeBackBuffer()\n- }\n- var backBuffer = this.backBuffer;\n- if (!backBuffer) {\n- backBuffer = this.createBackBuffer();\n- if (!backBuffer) {\n- return\n- }\n- if (resolution === this.gridResolution) {\n- this.div.insertBefore(backBuffer, this.div.firstChild)\n- } else {\n- this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div)\n- }\n- this.backBuffer = backBuffer;\n- var topLeftTileBounds = this.grid[0][0].bounds;\n- this.backBufferLonLat = {\n- lon: topLeftTileBounds.left,\n- lat: topLeftTileBounds.top\n- };\n- this.backBufferResolution = this.gridResolution\n- }\n- var ratio = this.backBufferResolution / resolution;\n- var tiles = backBuffer.childNodes,\n- tile;\n- for (var i = tiles.length - 1; i >= 0; --i) {\n- tile = tiles[i];\n- tile.style.top = (ratio * tile._i * tile._h | 0) + \"px\";\n- tile.style.left = (ratio * tile._j * tile._w | 0) + \"px\";\n- tile.style.width = Math.round(ratio * tile._w) + \"px\";\n- tile.style.height = Math.round(ratio * tile._h) + \"px\"\n- }\n- var position = this.getViewPortPxFromLonLat(this.backBufferLonLat, resolution);\n- var leftOffset = this.map.layerContainerOriginPx.x;\n- var topOffset = this.map.layerContainerOriginPx.y;\n- backBuffer.style.left = Math.round(position.x - leftOffset) + \"px\";\n- backBuffer.style.top = Math.round(position.y - topOffset) + \"px\"\n- },\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.grid.length > 0) {\n- backBuffer = document.createElement(\"div\");\n- backBuffer.id = this.div.id + \"_bb\";\n- backBuffer.className = \"olBackBuffer\";\n- backBuffer.style.position = \"absolute\";\n- var map = this.map;\n- backBuffer.style.zIndex = this.transitionEffect === \"resize\" ? this.getZIndex() - 1 : map.Z_INDEX_BASE.BaseLayer - (map.getNumLayers() - map.getLayerIndex(this));\n- for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n- for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n- var tile = this.grid[i][j],\n- markup = this.grid[i][j].createBackBuffer();\n- if (markup) {\n- markup._i = i;\n- markup._j = j;\n- markup._w = tile.size.w;\n- markup._h = tile.size.h;\n- markup.id = tile.id + \"_bb\";\n- backBuffer.appendChild(markup)\n- }\n- }\n- }\n- }\n- return backBuffer\n- },\n- removeBackBuffer: function() {\n- if (this._transitionElement) {\n- for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.stopObserving(this._transitionElement, this.transitionendEvents[i], this._removeBackBuffer)\n- }\n- delete this._transitionElement\n- }\n- if (this.backBuffer) {\n- if (this.backBuffer.parentNode) {\n- this.backBuffer.parentNode.removeChild(this.backBuffer)\n- }\n- this.backBuffer = null;\n- this.backBufferResolution = null;\n- if (this.backBufferTimerId !== null) {\n- window.clearTimeout(this.backBufferTimerId);\n- this.backBufferTimerId = null\n- }\n- }\n- },\n- moveByPx: function(dx, dy) {\n- if (!this.singleTile) {\n- this.moveGriddedTiles()\n- }\n- },\n- setTileSize: function(size) {\n- if (this.singleTile) {\n- size = this.map.getSize();\n- size.h = parseInt(size.h * this.ratio, 10);\n- size.w = parseInt(size.w * this.ratio, 10)\n- }\n- OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size])\n- },\n- getTilesBounds: function() {\n- var bounds = null;\n- var length = this.grid.length;\n- if (length) {\n- var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n- width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n- height = this.grid.length * bottomLeftTileBounds.getHeight();\n- bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height)\n- }\n- return bounds\n- },\n- initSingleTile: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n- var center = bounds.getCenterLonLat();\n- var tileWidth = bounds.getWidth() * this.ratio;\n- var tileHeight = bounds.getHeight() * this.ratio;\n- var tileBounds = new OpenLayers.Bounds(center.lon - tileWidth / 2, center.lat - tileHeight / 2, center.lon + tileWidth / 2, center.lat + tileHeight / 2);\n- var px = this.map.getLayerPxFromLonLat({\n- lon: tileBounds.left,\n- lat: tileBounds.top\n- });\n- if (!this.grid.length) {\n- this.grid[0] = []\n- }\n- var tile = this.grid[0][0];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- tile.draw();\n- this.grid[0][0] = tile\n- } else {\n- tile.moveTo(tileBounds, px)\n- }\n- this.removeExcessTiles(1, 1);\n- this.gridResolution = this.getServerResolution()\n- },\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n- var offsetlon = bounds.left - origin.lon;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n- var rowSign = this.rowSign;\n- var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n- var tilerow = Math[~rowSign ? \"floor\" : \"ceil\"](offsetlat / tilelat) - this.buffer * rowSign;\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n- }\n- },\n- getTileOrigin: function() {\n- var origin = this.tileOrigin;\n- if (!origin) {\n- var extent = this.getMaxExtent();\n- var edges = {\n- tl: [\"left\", \"top\"],\n- tr: [\"right\", \"top\"],\n- bl: [\"left\", \"bottom\"],\n- br: [\"right\", \"bottom\"]\n- } [this.tileOriginCorner];\n- origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]])\n- }\n- return origin\n- },\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var startcol = tileLayout.startcol;\n- var startrow = tileLayout.startrow;\n- var rowSign = this.rowSign;\n- return new OpenLayers.Bounds(origin.lon + (startcol + col) * tilelon, origin.lat - (startrow + row * rowSign) * tilelat * rowSign, origin.lon + (startcol + col + 1) * tilelon, origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign)\n- },\n- initGriddedTiles: function(bounds) {\n- this.events.triggerEvent(\"retile\");\n- var viewSize = this.map.getSize();\n- var origin = this.getTileOrigin();\n- var resolution = this.map.getResolution(),\n- serverResolution = this.getServerResolution(),\n- ratio = resolution / serverResolution,\n- tileSize = {\n- w: this.tileSize.w / ratio,\n- h: this.tileSize.h / ratio\n- };\n- var minRows = Math.ceil(viewSize.h / tileSize.h) + 2 * this.buffer + 1;\n- var minCols = Math.ceil(viewSize.w / tileSize.w) + 2 * this.buffer + 1;\n- var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n- this.gridLayout = tileLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n- var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n- var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n- var startPx = this.map.getViewPortPxFromLonLat(new OpenLayers.LonLat(tileBounds.left, tileBounds.top));\n- startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n- startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n- var tileData = [],\n- center = this.map.getCenter();\n- var rowidx = 0;\n- do {\n- var row = this.grid[rowidx];\n- if (!row) {\n- row = [];\n- this.grid.push(row)\n- }\n- var colidx = 0;\n- do {\n- tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n- var px = startPx.clone();\n- px.x = px.x + colidx * Math.round(tileSize.w);\n- px.y = px.y + rowidx * Math.round(tileSize.h);\n- var tile = row[colidx];\n- if (!tile) {\n- tile = this.addTile(tileBounds, px);\n- this.addTileMonitoringHooks(tile);\n- row.push(tile)\n- } else {\n- tile.moveTo(tileBounds, px, false)\n- }\n- var tileCenter = tileBounds.getCenterLonLat();\n- tileData.push({\n- tile: tile,\n- distance: Math.pow(tileCenter.lon - center.lon, 2) + Math.pow(tileCenter.lat - center.lat, 2)\n- });\n- colidx += 1\n- } while (tileBounds.right <= bounds.right + tilelon * this.buffer || colidx < minCols);\n- rowidx += 1\n- } while (tileBounds.bottom >= bounds.bottom - tilelat * this.buffer || rowidx < minRows);\n- this.removeExcessTiles(rowidx, colidx);\n- var resolution = this.getServerResolution();\n- this.gridResolution = resolution;\n- tileData.sort(function(a, b) {\n- return a.distance - b.distance\n- });\n- for (var i = 0, ii = tileData.length; i < ii; ++i) {\n- tileData[i].tile.draw()\n- }\n- },\n- getMaxExtent: function() {\n- return this.maxExtent\n- },\n- addTile: function(bounds, position) {\n- var tile = new this.tileClass(this, position, bounds, null, this.tileSize, this.tileOptions);\n- this.events.triggerEvent(\"addtile\", {\n- tile: tile\n- });\n- return tile\n- },\n- addTileMonitoringHooks: function(tile) {\n- var replacingCls = \"olTileReplacing\";\n- tile.onLoadStart = function() {\n- if (this.loading === false) {\n- this.loading = true;\n- this.events.triggerEvent(\"loadstart\")\n- }\n- this.events.triggerEvent(\"tileloadstart\", {\n- tile: tile\n- });\n- this.numLoadingTiles++;\n- if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- OpenLayers.Element.addClass(tile.getTile(), replacingCls)\n- }\n- };\n- tile.onLoadEnd = function(evt) {\n- this.numLoadingTiles--;\n- var aborted = evt.type === \"unload\";\n- this.events.triggerEvent(\"tileloaded\", {\n- tile: tile,\n- aborted: aborted\n- });\n- if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n- var tileDiv = tile.getTile();\n- if (OpenLayers.Element.getStyle(tileDiv, \"display\") === \"none\") {\n- var bufferTile = document.getElementById(tile.id + \"_bb\");\n- if (bufferTile) {\n- bufferTile.parentNode.removeChild(bufferTile)\n- }\n- }\n- OpenLayers.Element.removeClass(tileDiv, replacingCls)\n- }\n- if (this.numLoadingTiles === 0) {\n- if (this.backBuffer) {\n- if (this.backBuffer.childNodes.length === 0) {\n- this.removeBackBuffer()\n- } else {\n- this._transitionElement = aborted ? this.div.lastChild : tile.imgDiv;\n- var transitionendEvents = this.transitionendEvents;\n- for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n- OpenLayers.Event.observe(this._transitionElement, transitionendEvents[i], this._removeBackBuffer)\n- }\n- this.backBufferTimerId = window.setTimeout(this._removeBackBuffer, this.removeBackBufferDelay)\n- }\n- }\n- this.loading = false;\n- this.events.triggerEvent(\"loadend\")\n- }\n- };\n- tile.onLoadError = function() {\n- this.events.triggerEvent(\"tileerror\", {\n- tile: tile\n- })\n- };\n- tile.events.on({\n- loadstart: tile.onLoadStart,\n- loadend: tile.onLoadEnd,\n- unload: tile.onLoadEnd,\n- loaderror: tile.onLoadError,\n- scope: this\n- })\n- },\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- loadstart: tile.onLoadStart,\n- loadend: tile.onLoadEnd,\n- unload: tile.onLoadEnd,\n- loaderror: tile.onLoadError,\n- scope: this\n- })\n- },\n- moveGriddedTiles: function() {\n- var buffer = this.buffer + 1;\n- while (true) {\n- var tlTile = this.grid[0][0];\n- var tlViewPort = {\n- x: tlTile.position.x + this.map.layerContainerOriginPx.x,\n- y: tlTile.position.y + this.map.layerContainerOriginPx.y\n- };\n- var ratio = this.getServerResolution() / this.map.getResolution();\n- var tileSize = {\n- w: Math.round(this.tileSize.w * ratio),\n- h: Math.round(this.tileSize.h * ratio)\n- };\n- if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n- this.shiftColumn(true, tileSize)\n- } else if (tlViewPort.x < -tileSize.w * buffer) {\n- this.shiftColumn(false, tileSize)\n- } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n- this.shiftRow(true, tileSize)\n- } else if (tlViewPort.y < -tileSize.h * buffer) {\n- this.shiftRow(false, tileSize)\n- } else {\n- break\n- }\n- }\n- },\n- shiftRow: function(prepend, tileSize) {\n- var grid = this.grid;\n- var rowIndex = prepend ? 0 : grid.length - 1;\n- var sign = prepend ? -1 : 1;\n- var rowSign = this.rowSign;\n- var tileLayout = this.gridLayout;\n- tileLayout.startrow += sign * rowSign;\n- var modelRow = grid[rowIndex];\n- var row = grid[prepend ? \"pop\" : \"shift\"]();\n- for (var i = 0, len = row.length; i < len; i++) {\n- var tile = row[i];\n- var position = modelRow[i].position.clone();\n- position.y += tileSize.h * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position)\n- }\n- grid[prepend ? \"unshift\" : \"push\"](row)\n- },\n- shiftColumn: function(prepend, tileSize) {\n- var grid = this.grid;\n- var colIndex = prepend ? 0 : grid[0].length - 1;\n- var sign = prepend ? -1 : 1;\n- var tileLayout = this.gridLayout;\n- tileLayout.startcol += sign;\n- for (var i = 0, len = grid.length; i < len; i++) {\n- var row = grid[i];\n- var position = row[colIndex].position.clone();\n- var tile = row[prepend ? \"pop\" : \"shift\"]();\n- position.x += tileSize.w * sign;\n- tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n- row[prepend ? \"unshift\" : \"push\"](tile)\n- }\n- },\n- removeExcessTiles: function(rows, columns) {\n- var i, l;\n- while (this.grid.length > rows) {\n- var row = this.grid.pop();\n- for (i = 0, l = row.length; i < l; i++) {\n- var tile = row[i];\n- this.destroyTile(tile)\n- }\n- }\n- for (i = 0, l = this.grid.length; i < l; i++) {\n- while (this.grid[i].length > columns) {\n- var row = this.grid[i];\n- var tile = row.pop();\n- this.destroyTile(tile)\n- }\n- }\n- },\n- onMapResize: function() {\n- if (this.singleTile) {\n- this.clearGrid();\n- this.setTileSize()\n- }\n- },\n- getTileBounds: function(viewPortPx) {\n- var maxExtent = this.maxExtent;\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth);\n- var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight);\n- return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.Grid\"\n-});\n-OpenLayers.TileManager = OpenLayers.Class({\n- cacheSize: 256,\n- tilesPerFrame: 2,\n- frameDelay: 16,\n- moveDelay: 100,\n- zoomDelay: 200,\n- maps: null,\n- tileQueueId: null,\n- tileQueue: null,\n- tileCache: null,\n- tileCacheIndex: null,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.maps = [];\n- this.tileQueueId = {};\n- this.tileQueue = {};\n- this.tileCache = {};\n- this.tileCacheIndex = []\n- },\n- addMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return\n- }\n- this.maps.push(map);\n- this.tileQueue[map.id] = [];\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.addLayer({\n- layer: map.layers[i]\n- })\n- }\n- map.events.on({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- })\n- },\n- removeMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return\n- }\n- window.clearTimeout(this.tileQueueId[map.id]);\n- if (map.layers) {\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.removeLayer({\n- layer: map.layers[i]\n- })\n- }\n- }\n- if (map.events) {\n- map.events.un({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- })\n- }\n- delete this.tileQueue[map.id];\n- delete this.tileQueueId[map.id];\n- OpenLayers.Util.removeItem(this.maps, map)\n- },\n- move: function(evt) {\n- this.updateTimeout(evt.object, this.moveDelay, true)\n- },\n- zoomEnd: function(evt) {\n- this.updateTimeout(evt.object, this.zoomDelay)\n- },\n- changeLayer: function(evt) {\n- if (evt.property === \"visibility\" || evt.property === \"params\") {\n- this.updateTimeout(evt.object, 0)\n- }\n- },\n- addLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- layer.events.on({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.addTile({\n- tile: tile\n- });\n- if (tile.url && !tile.imgDiv) {\n- this.manageTileCache({\n- object: tile\n- })\n- }\n- }\n- }\n- }\n- },\n- removeLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- this.clearTileQueue({\n- object: layer\n- });\n- if (layer.events) {\n- layer.events.un({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- })\n- }\n- if (layer.grid) {\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.unloadTile({\n- object: tile\n- })\n- }\n- }\n- }\n- }\n- },\n- updateTimeout: function(map, delay, nice) {\n- window.clearTimeout(this.tileQueueId[map.id]);\n- var tileQueue = this.tileQueue[map.id];\n- if (!nice || tileQueue.length) {\n- this.tileQueueId[map.id] = window.setTimeout(OpenLayers.Function.bind(function() {\n- this.drawTilesFromQueue(map);\n- if (tileQueue.length) {\n- this.updateTimeout(map, this.frameDelay)\n- }\n- }, this), delay)\n- }\n- },\n- addTile: function(evt) {\n- if (evt.tile instanceof OpenLayers.Tile.Image) {\n- evt.tile.events.on({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- })\n- } else {\n- this.removeLayer({\n- layer: evt.tile.layer\n- })\n- }\n- },\n- unloadTile: function(evt) {\n- var tile = evt.object;\n- tile.events.un({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile)\n- },\n- queueTileDraw: function(evt) {\n- var tile = evt.object;\n- var queued = false;\n- var layer = tile.layer;\n- var url = layer.getURL(tile.bounds);\n- var img = this.tileCache[url];\n- if (img && img.className !== \"olTileImage\") {\n- delete this.tileCache[url];\n- OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n- img = null\n- }\n- if (layer.url && (layer.async || !img)) {\n- var tileQueue = this.tileQueue[layer.map.id];\n- if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n- tileQueue.push(tile)\n- }\n- queued = true\n- }\n- return !queued\n- },\n- drawTilesFromQueue: function(map) {\n- var tileQueue = this.tileQueue[map.id];\n- var limit = this.tilesPerFrame;\n- var animating = map.zoomTween && map.zoomTween.playing;\n- while (!animating && tileQueue.length && limit) {\n- tileQueue.shift().draw(true);\n- --limit\n- }\n- },\n- manageTileCache: function(evt) {\n- var tile = evt.object;\n- var img = this.tileCache[tile.url];\n- if (img) {\n- if (img.parentNode && OpenLayers.Element.hasClass(img.parentNode, \"olBackBuffer\")) {\n- img.parentNode.removeChild(img);\n- img.id = null\n- }\n- if (!img.parentNode) {\n- img.style.visibility = \"hidden\";\n- img.style.opacity = 0;\n- tile.setImage(img);\n- OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n- this.tileCacheIndex.push(tile.url)\n- }\n- }\n- },\n- addToCache: function(evt) {\n- var tile = evt.object;\n- if (!this.tileCache[tile.url]) {\n- if (!OpenLayers.Element.hasClass(tile.imgDiv, \"olImageLoadError\")) {\n- if (this.tileCacheIndex.length >= this.cacheSize) {\n- delete this.tileCache[this.tileCacheIndex[0]];\n- this.tileCacheIndex.shift()\n- }\n- this.tileCache[tile.url] = tile.imgDiv;\n- this.tileCacheIndex.push(tile.url)\n- }\n- }\n- },\n- clearTileQueue: function(evt) {\n- var layer = evt.object;\n- var tileQueue = this.tileQueue[layer.map.id];\n- for (var i = tileQueue.length - 1; i >= 0; --i) {\n- if (tileQueue[i].layer === layer) {\n- tileQueue.splice(i, 1)\n- }\n- }\n- },\n- destroy: function() {\n- for (var i = this.maps.length - 1; i >= 0; --i) {\n- this.removeMap(this.maps[i])\n- }\n- this.maps = null;\n- this.tileQueue = null;\n- this.tileQueueId = null;\n- this.tileCache = null;\n- this.tileCacheIndex = null;\n- this._destroyed = true\n- }\n-});\n-OpenLayers.Control = OpenLayers.Class({\n- id: null,\n- map: null,\n- div: null,\n- type: null,\n- allowSelection: false,\n- displayClass: \"\",\n- title: \"\",\n- autoActivate: false,\n- active: null,\n- handlerOptions: null,\n- handler: null,\n- eventListeners: null,\n- events: null,\n- initialize: function(options) {\n- this.displayClass = this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n- OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- if (this.eventListeners instanceof Object) {\n- this.events.on(this.eventListeners)\n- }\n- if (this.id == null) {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- }\n- },\n- destroy: function() {\n- if (this.events) {\n- if (this.eventListeners) {\n- this.events.un(this.eventListeners)\n- }\n- this.events.destroy();\n- this.events = null\n- }\n- this.eventListeners = null;\n- if (this.handler) {\n- this.handler.destroy();\n- this.handler = null\n- }\n- if (this.handlers) {\n- for (var key in this.handlers) {\n- if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == \"function\") {\n- this.handlers[key].destroy()\n- }\n- }\n- this.handlers = null\n- }\n- if (this.map) {\n- this.map.removeControl(this);\n- this.map = null\n- }\n- this.div = null\n- },\n- setMap: function(map) {\n- this.map = map;\n- if (this.handler) {\n- this.handler.setMap(map)\n- }\n- },\n- draw: function(px) {\n- if (this.div == null) {\n- this.div = OpenLayers.Util.createDiv(this.id);\n- this.div.className = this.displayClass;\n- if (!this.allowSelection) {\n- this.div.className += \" olControlNoSelect\";\n- this.div.setAttribute(\"unselectable\", \"on\", 0);\n- this.div.onselectstart = OpenLayers.Function.False\n- }\n- if (this.title != \"\") {\n- this.div.title = this.title\n- }\n- }\n- if (px != null) {\n- this.position = px.clone()\n- }\n- this.moveTo(this.position);\n- return this.div\n- },\n- moveTo: function(px) {\n- if (px != null && this.div != null) {\n- this.div.style.left = px.x + \"px\";\n- this.div.style.top = px.y + \"px\"\n- }\n- },\n- activate: function() {\n- if (this.active) {\n- return false\n- }\n- if (this.handler) {\n- this.handler.activate()\n- }\n- this.active = true;\n- if (this.map) {\n- OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n- }\n- this.events.triggerEvent(\"activate\");\n- return true\n- },\n- deactivate: function() {\n- if (this.active) {\n- if (this.handler) {\n- this.handler.deactivate()\n- }\n- this.active = false;\n- if (this.map) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n- }\n- this.events.triggerEvent(\"deactivate\");\n- return true\n- }\n- return false\n- },\n- CLASS_NAME: \"OpenLayers.Control\"\n-});\n-OpenLayers.Control.TYPE_BUTTON = 1;\n-OpenLayers.Control.TYPE_TOGGLE = 2;\n-OpenLayers.Control.TYPE_TOOL = 3;\n-OpenLayers.Protocol = OpenLayers.Class({\n- format: null,\n- options: null,\n- autoDestroy: true,\n- defaultFilter: null,\n- initialize: function(options) {\n- options = options || {};\n- OpenLayers.Util.extend(this, options);\n- this.options = options\n- },\n- mergeWithDefaultFilter: function(filter) {\n- var merged;\n- if (filter && this.defaultFilter) {\n- merged = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.defaultFilter, filter]\n- })\n- } else {\n- merged = filter || this.defaultFilter || undefined\n- }\n- return merged\n- },\n- destroy: function() {\n- this.options = null;\n- this.format = null\n- },\n- read: function(options) {\n- options = options || {};\n- options.filter = this.mergeWithDefaultFilter(options.filter)\n- },\n- create: function() {},\n- update: function() {},\n- delete: function() {},\n- commit: function() {},\n- abort: function(response) {},\n- createCallback: function(method, response, options) {\n- return OpenLayers.Function.bind(function() {\n- method.apply(this, [response, options])\n- }, this)\n- },\n- CLASS_NAME: \"OpenLayers.Protocol\"\n-});\n-OpenLayers.Protocol.Response = OpenLayers.Class({\n- code: null,\n- requestType: null,\n- last: true,\n- features: null,\n- data: null,\n- reqFeatures: null,\n- priv: null,\n- error: null,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options)\n- },\n- success: function() {\n- return this.code > 0\n- },\n- CLASS_NAME: \"OpenLayers.Protocol.Response\"\n-});\n-OpenLayers.Protocol.Response.SUCCESS = 1;\n-OpenLayers.Protocol.Response.FAILURE = 0;\n-OpenLayers.Symbolizer = OpenLayers.Class({\n- zIndex: 0,\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config)\n- },\n- clone: function() {\n- var Type = eval(this.CLASS_NAME);\n- return new Type(OpenLayers.Util.extend({}, this))\n- },\n- CLASS_NAME: \"OpenLayers.Symbolizer\"\n-});\n-OpenLayers.Rule = OpenLayers.Class({\n- id: null,\n- name: null,\n- title: null,\n- description: null,\n- context: null,\n- filter: null,\n- elseFilter: false,\n- symbolizer: null,\n- symbolizers: null,\n- minScaleDenominator: null,\n- maxScaleDenominator: null,\n- initialize: function(options) {\n- this.symbolizer = {};\n- OpenLayers.Util.extend(this, options);\n- if (this.symbolizers) {\n- delete this.symbolizer\n- }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null\n- }\n- this.symbolizer = null;\n- delete this.symbolizers\n- },\n- evaluate: function(feature) {\n- var context = this.getContext(feature);\n- var applies = true;\n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale()\n- }\n- if (this.minScaleDenominator) {\n- applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context)\n- }\n- if (applies && this.maxScaleDenominator) {\n- applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context)\n- }\n- if (applies && this.filter) {\n- if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n- applies = this.filter.evaluate(feature)\n- } else {\n- applies = this.filter.evaluate(context)\n- }\n- }\n- return applies\n- },\n- getContext: function(feature) {\n- var context = this.context;\n- if (!context) {\n- context = feature.attributes || feature.data\n- }\n- if (typeof this.context == \"function\") {\n- context = this.context(feature)\n- }\n- return context\n- },\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.symbolizers) {\n- var len = this.symbolizers.length;\n- options.symbolizers = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- options.symbolizers[i] = this.symbolizers[i].clone()\n- }\n- } else {\n- options.symbolizer = {};\n- var value, type;\n- for (var key in this.symbolizer) {\n- value = this.symbolizer[key];\n- type = typeof value;\n- if (type === \"object\") {\n- options.symbolizer[key] = OpenLayers.Util.extend({}, value)\n- } else if (type === \"string\") {\n- options.symbolizer[key] = value\n- }\n- }\n- }\n- options.filter = this.filter && this.filter.clone();\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- return new OpenLayers.Rule(options)\n- },\n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n-OpenLayers.Geometry = OpenLayers.Class({\n- id: null,\n- parent: null,\n- bounds: null,\n- initialize: function() {\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- destroy: function() {\n- this.id = null;\n- this.bounds = null\n- },\n- clone: function() {\n- return new OpenLayers.Geometry\n- },\n- setBounds: function(bounds) {\n- if (bounds) {\n- this.bounds = bounds.clone()\n- }\n- },\n- clearBounds: function() {\n- this.bounds = null;\n- if (this.parent) {\n- this.parent.clearBounds()\n- }\n- },\n- extendBounds: function(newBounds) {\n- var bounds = this.getBounds();\n- if (!bounds) {\n- this.setBounds(newBounds)\n- } else {\n- this.bounds.extend(newBounds)\n- }\n- },\n- getBounds: function() {\n- if (this.bounds == null) {\n- this.calculateBounds()\n- }\n- return this.bounds\n- },\n- calculateBounds: function() {},\n- distanceTo: function(geometry, options) {},\n- getVertices: function(nodes) {},\n- atPoint: function(lonlat, toleranceLon, toleranceLat) {\n- var atPoint = false;\n- var bounds = this.getBounds();\n- if (bounds != null && lonlat != null) {\n- var dX = toleranceLon != null ? toleranceLon : 0;\n- var dY = toleranceLat != null ? toleranceLat : 0;\n- var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY);\n- atPoint = toleranceBounds.containsLonLat(lonlat)\n- }\n- return atPoint\n- },\n- getLength: function() {\n- return 0\n- },\n- getArea: function() {\n- return 0\n- },\n- getCentroid: function() {\n- return null\n- },\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- string = OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this))\n- } else {\n- string = Object.prototype.toString.call(this)\n- }\n- return string\n- },\n- CLASS_NAME: \"OpenLayers.Geometry\"\n-});\n-OpenLayers.Geometry.fromWKT = function(wkt) {\n- var geom;\n- if (OpenLayers.Format && OpenLayers.Format.WKT) {\n- var format = OpenLayers.Geometry.fromWKT.format;\n- if (!format) {\n- format = new OpenLayers.Format.WKT;\n- OpenLayers.Geometry.fromWKT.format = format\n- }\n- var result = format.read(wkt);\n- if (result instanceof OpenLayers.Feature.Vector) {\n- geom = result.geometry\n- } else if (OpenLayers.Util.isArray(result)) {\n- var len = result.length;\n- var components = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- components[i] = result[i].geometry\n- }\n- geom = new OpenLayers.Geometry.Collection(components)\n- }\n- }\n- return geom\n-};\n-OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {\n- var point = options && options.point;\n- var tolerance = options && options.tolerance;\n- var intersection = false;\n- var x11_21 = seg1.x1 - seg2.x1;\n- var y11_21 = seg1.y1 - seg2.y1;\n- var x12_11 = seg1.x2 - seg1.x1;\n- var y12_11 = seg1.y2 - seg1.y1;\n- var y22_21 = seg2.y2 - seg2.y1;\n- var x22_21 = seg2.x2 - seg2.x1;\n- var d = y22_21 * x12_11 - x22_21 * y12_11;\n- var n1 = x22_21 * y11_21 - y22_21 * x11_21;\n- var n2 = x12_11 * y11_21 - y12_11 * x11_21;\n- if (d == 0) {\n- if (n1 == 0 && n2 == 0) {\n- intersection = true\n- }\n- } else {\n- var along1 = n1 / d;\n- var along2 = n2 / d;\n- if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {\n- if (!point) {\n- intersection = true\n- } else {\n- var x = seg1.x1 + along1 * x12_11;\n- var y = seg1.y1 + along1 * y12_11;\n- intersection = new OpenLayers.Geometry.Point(x, y)\n- }\n- }\n- }\n- if (tolerance) {\n- var dist;\n- if (intersection) {\n- if (point) {\n- var segs = [seg1, seg2];\n- var seg, x, y;\n- outer: for (var i = 0; i < 2; ++i) {\n- seg = segs[i];\n- for (var j = 1; j < 3; ++j) {\n- x = seg[\"x\" + j];\n- y = seg[\"y\" + j];\n- dist = Math.sqrt(Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2));\n- if (dist < tolerance) {\n- intersection.x = x;\n- intersection.y = y;\n- break outer\n- }\n- }\n- }\n- }\n- } else {\n- var segs = [seg1, seg2];\n- var source, target, x, y, p, result;\n- outer: for (var i = 0; i < 2; ++i) {\n- source = segs[i];\n- target = segs[(i + 1) % 2];\n- for (var j = 1; j < 3; ++j) {\n- p = {\n- x: source[\"x\" + j],\n- y: source[\"y\" + j]\n- };\n- result = OpenLayers.Geometry.distanceToSegment(p, target);\n- if (result.distance < tolerance) {\n- if (point) {\n- intersection = new OpenLayers.Geometry.Point(p.x, p.y)\n- } else {\n- intersection = true\n- }\n- break outer\n- }\n- }\n- }\n- }\n- }\n- return intersection\n-};\n-OpenLayers.Geometry.distanceToSegment = function(point, segment) {\n- var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);\n- result.distance = Math.sqrt(result.distance);\n- return result\n-};\n-OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {\n- var x0 = point.x;\n- var y0 = point.y;\n- var x1 = segment.x1;\n- var y1 = segment.y1;\n- var x2 = segment.x2;\n- var y2 = segment.y2;\n- var dx = x2 - x1;\n- var dy = y2 - y1;\n- var along = (dx * (x0 - x1) + dy * (y0 - y1)) / (Math.pow(dx, 2) + Math.pow(dy, 2));\n- var x, y;\n- if (along <= 0) {\n- x = x1;\n- y = y1\n- } else if (along >= 1) {\n- x = x2;\n- y = y2\n- } else {\n- x = x1 + along * dx;\n- y = y1 + along * dy\n- }\n- return {\n- distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),\n- x: x,\n- y: y,\n- along: along\n- }\n-};\n-OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {\n- x: null,\n- y: null,\n- initialize: function(x, y) {\n- OpenLayers.Geometry.prototype.initialize.apply(this, arguments);\n- this.x = parseFloat(x);\n- this.y = parseFloat(y)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Geometry.Point(this.x, this.y)\n- }\n- OpenLayers.Util.applyDefaults(obj, this);\n- return obj\n- },\n- calculateBounds: function() {\n- this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x, this.y)\n+ calculateBounds: function() {\n+ this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x, this.y)\n },\n distanceTo: function(geometry, options) {\n var edge = !(options && options.edge === false);\n var details = edge && options && options.details;\n var distance, x0, y0, x1, y1, result;\n if (geometry instanceof OpenLayers.Geometry.Point) {\n x0 = this.x;\n@@ -10457,14 +5066,211 @@\n throw \"Unsupported WFST version: \" + options.version\n }\n return new cls(options)\n };\n OpenLayers.Format.WFST.DEFAULTS = {\n version: \"1.0.0\"\n };\n+OpenLayers.Style = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ title: null,\n+ description: null,\n+ layerName: null,\n+ isDefault: false,\n+ rules: null,\n+ context: null,\n+ defaultStyle: null,\n+ defaultsPerSymbolizer: false,\n+ propertyStyles: null,\n+ initialize: function(style, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules)\n+ }\n+ this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ },\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null\n+ },\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+ var rules = this.rules;\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ var applies = rule.evaluate(feature);\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule)\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature)\n+ }\n+ }\n+ }\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature)\n+ }\n+ }\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\"\n+ }\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label)\n+ }\n+ return style\n+ },\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ })\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ })\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ })\n+ }\n+ }\n+ return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n+ },\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n+ }\n+ return style\n+ },\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ this.addPropertyStyles(propertyStyles, value)\n+ } else {\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break\n+ }\n+ }\n+ }\n+ return propertyStyles\n+ },\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true\n+ }\n+ }\n+ return propertyStyles\n+ },\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles()\n+ },\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles()\n+ },\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i]\n+ }\n+ }\n+ },\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone())\n+ }\n+ }\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options)\n+ },\n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = isNaN(value) || !value ? value : parseFloat(value)\n+ }\n+ return value\n+};\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n+OpenLayers.Filter = OpenLayers.Class({\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n+ },\n+ destroy: function() {},\n+ evaluate: function(context) {\n+ return true\n+ },\n+ clone: function() {\n+ return null\n+ },\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this)\n+ } else {\n+ string = Object.prototype.toString.call(this)\n+ }\n+ return string\n+ },\n+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {\n type: null,\n property: null,\n value: null,\n distance: null,\n distanceUnits: null,\n evaluate: function(feature) {\n@@ -13109,14 +7915,849 @@\n this.readChildNodes(node, output.boundingBoxData)\n }\n },\n ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n CLASS_NAME: \"OpenLayers.Format.WPSExecute\"\n });\n+OpenLayers.Event = {\n+ observers: false,\n+ KEY_SPACE: 32,\n+ KEY_BACKSPACE: 8,\n+ KEY_TAB: 9,\n+ KEY_RETURN: 13,\n+ KEY_ESC: 27,\n+ KEY_LEFT: 37,\n+ KEY_UP: 38,\n+ KEY_RIGHT: 39,\n+ KEY_DOWN: 40,\n+ KEY_DELETE: 46,\n+ element: function(event) {\n+ return event.target || event.srcElement\n+ },\n+ isSingleTouch: function(event) {\n+ return event.touches && event.touches.length == 1\n+ },\n+ isMultiTouch: function(event) {\n+ return event.touches && event.touches.length > 1\n+ },\n+ isLeftClick: function(event) {\n+ return event.which && event.which == 1 || event.button && event.button == 1\n+ },\n+ isRightClick: function(event) {\n+ return event.which && event.which == 3 || event.button && event.button == 2\n+ },\n+ stop: function(event, allowDefault) {\n+ if (!allowDefault) {\n+ OpenLayers.Event.preventDefault(event)\n+ }\n+ if (event.stopPropagation) {\n+ event.stopPropagation()\n+ } else {\n+ event.cancelBubble = true\n+ }\n+ },\n+ preventDefault: function(event) {\n+ if (event.preventDefault) {\n+ event.preventDefault()\n+ } else {\n+ event.returnValue = false\n+ }\n+ },\n+ findElement: function(event, tagName) {\n+ var element = OpenLayers.Event.element(event);\n+ while (element.parentNode && (!element.tagName || element.tagName.toUpperCase() != tagName.toUpperCase())) {\n+ element = element.parentNode\n+ }\n+ return element\n+ },\n+ observe: function(elementParam, name, observer, useCapture) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ useCapture = useCapture || false;\n+ if (name == \"keypress\" && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) {\n+ name = \"keydown\"\n+ }\n+ if (!this.observers) {\n+ this.observers = {}\n+ }\n+ if (!element._eventCacheID) {\n+ var idPrefix = \"eventCacheID_\";\n+ if (element.id) {\n+ idPrefix = element.id + \"_\" + idPrefix\n+ }\n+ element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix)\n+ }\n+ var cacheID = element._eventCacheID;\n+ if (!this.observers[cacheID]) {\n+ this.observers[cacheID] = []\n+ }\n+ this.observers[cacheID].push({\n+ element: element,\n+ name: name,\n+ observer: observer,\n+ useCapture: useCapture\n+ });\n+ if (element.addEventListener) {\n+ element.addEventListener(name, observer, useCapture)\n+ } else if (element.attachEvent) {\n+ element.attachEvent(\"on\" + name, observer)\n+ }\n+ },\n+ stopObservingElement: function(elementParam) {\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\n+ this._removeElementObservers(OpenLayers.Event.observers[cacheID])\n+ },\n+ _removeElementObservers: function(elementObservers) {\n+ if (elementObservers) {\n+ for (var i = elementObservers.length - 1; i >= 0; i--) {\n+ var entry = elementObservers[i];\n+ OpenLayers.Event.stopObserving.apply(this, [entry.element, entry.name, entry.observer, entry.useCapture])\n+ }\n+ }\n+ },\n+ stopObserving: function(elementParam, name, observer, useCapture) {\n+ useCapture = useCapture || false;\n+ var element = OpenLayers.Util.getElement(elementParam);\n+ var cacheID = element._eventCacheID;\n+ if (name == \"keypress\") {\n+ if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent) {\n+ name = \"keydown\"\n+ }\n+ }\n+ var foundEntry = false;\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ if (elementObservers) {\n+ var i = 0;\n+ while (!foundEntry && i < elementObservers.length) {\n+ var cacheEntry = elementObservers[i];\n+ if (cacheEntry.name == name && cacheEntry.observer == observer && cacheEntry.useCapture == useCapture) {\n+ elementObservers.splice(i, 1);\n+ if (elementObservers.length == 0) {\n+ delete OpenLayers.Event.observers[cacheID]\n+ }\n+ foundEntry = true;\n+ break\n+ }\n+ i++\n+ }\n+ }\n+ if (foundEntry) {\n+ if (element.removeEventListener) {\n+ element.removeEventListener(name, observer, useCapture)\n+ } else if (element && element.detachEvent) {\n+ element.detachEvent(\"on\" + name, observer)\n+ }\n+ }\n+ return foundEntry\n+ },\n+ unloadCache: function() {\n+ if (OpenLayers.Event && OpenLayers.Event.observers) {\n+ for (var cacheID in OpenLayers.Event.observers) {\n+ var elementObservers = OpenLayers.Event.observers[cacheID];\n+ OpenLayers.Event._removeElementObservers.apply(this, [elementObservers])\n+ }\n+ OpenLayers.Event.observers = false\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Event\"\n+};\n+OpenLayers.Event.observe(window, \"unload\", OpenLayers.Event.unloadCache, false);\n+OpenLayers.Events = OpenLayers.Class({\n+ BROWSER_EVENTS: [\"mouseover\", \"mouseout\", \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"rightclick\", \"dblrightclick\", \"resize\", \"focus\", \"blur\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n+ listeners: null,\n+ object: null,\n+ element: null,\n+ eventHandler: null,\n+ fallThrough: null,\n+ includeXY: false,\n+ extensions: null,\n+ extensionCount: null,\n+ clearMouseListener: null,\n+ initialize: function(object, element, eventTypes, fallThrough, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.object = object;\n+ this.fallThrough = fallThrough;\n+ this.listeners = {};\n+ this.extensions = {};\n+ this.extensionCount = {};\n+ this._msTouches = [];\n+ if (element != null) {\n+ this.attachToElement(element)\n+ }\n+ },\n+ destroy: function() {\n+ for (var e in this.extensions) {\n+ if (typeof this.extensions[e] !== \"boolean\") {\n+ this.extensions[e].destroy()\n+ }\n+ }\n+ this.extensions = null;\n+ if (this.element) {\n+ OpenLayers.Event.stopObservingElement(this.element);\n+ if (this.element.hasScrollEvent) {\n+ OpenLayers.Event.stopObserving(window, \"scroll\", this.clearMouseListener)\n+ }\n+ }\n+ this.element = null;\n+ this.listeners = null;\n+ this.object = null;\n+ this.fallThrough = null;\n+ this.eventHandler = null\n+ },\n+ addEventType: function(eventName) {},\n+ attachToElement: function(element) {\n+ if (this.element) {\n+ OpenLayers.Event.stopObservingElement(this.element)\n+ } else {\n+ this.eventHandler = OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent, this);\n+ this.clearMouseListener = OpenLayers.Function.bind(this.clearMouseCache, this)\n+ }\n+ this.element = element;\n+ var msTouch = !!window.navigator.msMaxTouchPoints;\n+ var type;\n+ for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {\n+ type = this.BROWSER_EVENTS[i];\n+ OpenLayers.Event.observe(element, type, this.eventHandler);\n+ if (msTouch && type.indexOf(\"touch\") === 0) {\n+ this.addMsTouchListener(element, type, this.eventHandler)\n+ }\n+ }\n+ OpenLayers.Event.observe(element, \"dragstart\", OpenLayers.Event.stop)\n+ },\n+ on: function(object) {\n+ for (var type in object) {\n+ if (type != \"scope\" && object.hasOwnProperty(type)) {\n+ this.register(type, object.scope, object[type])\n+ }\n+ }\n+ },\n+ register: function(type, obj, func, priority) {\n+ if (type in OpenLayers.Events && !this.extensions[type]) {\n+ this.extensions[type] = new OpenLayers.Events[type](this)\n+ }\n+ if (func != null) {\n+ if (obj == null) {\n+ obj = this.object\n+ }\n+ var listeners = this.listeners[type];\n+ if (!listeners) {\n+ listeners = [];\n+ this.listeners[type] = listeners;\n+ this.extensionCount[type] = 0\n+ }\n+ var listener = {\n+ obj: obj,\n+ func: func\n+ };\n+ if (priority) {\n+ listeners.splice(this.extensionCount[type], 0, listener);\n+ if (typeof priority === \"object\" && priority.extension) {\n+ this.extensionCount[type]++\n+ }\n+ } else {\n+ listeners.push(listener)\n+ }\n+ }\n+ },\n+ registerPriority: function(type, obj, func) {\n+ this.register(type, obj, func, true)\n+ },\n+ un: function(object) {\n+ for (var type in object) {\n+ if (type != \"scope\" && object.hasOwnProperty(type)) {\n+ this.unregister(type, object.scope, object[type])\n+ }\n+ }\n+ },\n+ unregister: function(type, obj, func) {\n+ if (obj == null) {\n+ obj = this.object\n+ }\n+ var listeners = this.listeners[type];\n+ if (listeners != null) {\n+ for (var i = 0, len = listeners.length; i < len; i++) {\n+ if (listeners[i].obj == obj && listeners[i].func == func) {\n+ listeners.splice(i, 1);\n+ break\n+ }\n+ }\n+ }\n+ },\n+ remove: function(type) {\n+ if (this.listeners[type] != null) {\n+ this.listeners[type] = []\n+ }\n+ },\n+ triggerEvent: function(type, evt) {\n+ var listeners = this.listeners[type];\n+ if (!listeners || listeners.length == 0) {\n+ return undefined\n+ }\n+ if (evt == null) {\n+ evt = {}\n+ }\n+ evt.object = this.object;\n+ evt.element = this.element;\n+ if (!evt.type) {\n+ evt.type = type\n+ }\n+ listeners = listeners.slice();\n+ var continueChain;\n+ for (var i = 0, len = listeners.length; i < len; i++) {\n+ var callback = listeners[i];\n+ continueChain = callback.func.apply(callback.obj, [evt]);\n+ if (continueChain != undefined && continueChain == false) {\n+ break\n+ }\n+ }\n+ if (!this.fallThrough) {\n+ OpenLayers.Event.stop(evt, true)\n+ }\n+ return continueChain\n+ },\n+ handleBrowserEvent: function(evt) {\n+ var type = evt.type,\n+ listeners = this.listeners[type];\n+ if (!listeners || listeners.length == 0) {\n+ return\n+ }\n+ var touches = evt.touches;\n+ if (touches && touches[0]) {\n+ var x = 0;\n+ var y = 0;\n+ var num = touches.length;\n+ var touch;\n+ for (var i = 0; i < num; ++i) {\n+ touch = this.getTouchClientXY(touches[i]);\n+ x += touch.clientX;\n+ y += touch.clientY\n+ }\n+ evt.clientX = x / num;\n+ evt.clientY = y / num\n+ }\n+ if (this.includeXY) {\n+ evt.xy = this.getMousePosition(evt)\n+ }\n+ this.triggerEvent(type, evt)\n+ },\n+ getTouchClientXY: function(evt) {\n+ var win = window.olMockWin || window,\n+ winPageX = win.pageXOffset,\n+ winPageY = win.pageYOffset,\n+ x = evt.clientX,\n+ y = evt.clientY;\n+ if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) || evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {\n+ x = x - winPageX;\n+ y = y - winPageY\n+ } else if (y < evt.pageY - winPageY || x < evt.pageX - winPageX) {\n+ x = evt.pageX - winPageX;\n+ y = evt.pageY - winPageY\n+ }\n+ evt.olClientX = x;\n+ evt.olClientY = y;\n+ return {\n+ clientX: x,\n+ clientY: y\n+ }\n+ },\n+ clearMouseCache: function() {\n+ this.element.scrolls = null;\n+ this.element.lefttop = null;\n+ this.element.offsets = null\n+ },\n+ getMousePosition: function(evt) {\n+ if (!this.includeXY) {\n+ this.clearMouseCache()\n+ } else if (!this.element.hasScrollEvent) {\n+ OpenLayers.Event.observe(window, \"scroll\", this.clearMouseListener);\n+ this.element.hasScrollEvent = true\n+ }\n+ if (!this.element.scrolls) {\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ this.element.scrolls = [window.pageXOffset || viewportElement.scrollLeft, window.pageYOffset || viewportElement.scrollTop]\n+ }\n+ if (!this.element.lefttop) {\n+ this.element.lefttop = [document.documentElement.clientLeft || 0, document.documentElement.clientTop || 0]\n+ }\n+ if (!this.element.offsets) {\n+ this.element.offsets = OpenLayers.Util.pagePosition(this.element)\n+ }\n+ return new OpenLayers.Pixel(evt.clientX + this.element.scrolls[0] - this.element.offsets[0] - this.element.lefttop[0], evt.clientY + this.element.scrolls[1] - this.element.offsets[1] - this.element.lefttop[1])\n+ },\n+ addMsTouchListener: function(element, type, handler) {\n+ var eventHandler = this.eventHandler;\n+ var touches = this._msTouches;\n+\n+ function msHandler(evt) {\n+ handler(OpenLayers.Util.applyDefaults({\n+ stopPropagation: function() {\n+ for (var i = touches.length - 1; i >= 0; --i) {\n+ touches[i].stopPropagation()\n+ }\n+ },\n+ preventDefault: function() {\n+ for (var i = touches.length - 1; i >= 0; --i) {\n+ touches[i].preventDefault()\n+ }\n+ },\n+ type: type\n+ }, evt))\n+ }\n+ switch (type) {\n+ case \"touchstart\":\n+ return this.addMsTouchListenerStart(element, type, msHandler);\n+ case \"touchend\":\n+ return this.addMsTouchListenerEnd(element, type, msHandler);\n+ case \"touchmove\":\n+ return this.addMsTouchListenerMove(element, type, msHandler);\n+ default:\n+ throw \"Unknown touch event type\"\n+ }\n+ },\n+ addMsTouchListenerStart: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n+ var alreadyInArray = false;\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ alreadyInArray = true;\n+ break\n+ }\n+ }\n+ if (!alreadyInArray) {\n+ touches.push(e)\n+ }\n+ e.touches = touches.slice();\n+ handler(e)\n+ };\n+ OpenLayers.Event.observe(element, \"MSPointerDown\", cb);\n+ var internalCb = function(e) {\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches.splice(i, 1);\n+ break\n+ }\n+ }\n+ };\n+ OpenLayers.Event.observe(element, \"MSPointerUp\", internalCb)\n+ },\n+ addMsTouchListenerMove: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n+ if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {\n+ return\n+ }\n+ if (touches.length == 1 && touches[0].pageX == e.pageX && touches[0].pageY == e.pageY) {\n+ return\n+ }\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches[i] = e;\n+ break\n+ }\n+ }\n+ e.touches = touches.slice();\n+ handler(e)\n+ };\n+ OpenLayers.Event.observe(element, \"MSPointerMove\", cb)\n+ },\n+ addMsTouchListenerEnd: function(element, type, handler) {\n+ var touches = this._msTouches;\n+ var cb = function(e) {\n+ for (var i = 0, ii = touches.length; i < ii; ++i) {\n+ if (touches[i].pointerId == e.pointerId) {\n+ touches.splice(i, 1);\n+ break\n+ }\n+ }\n+ e.touches = touches.slice();\n+ handler(e)\n+ };\n+ OpenLayers.Event.observe(element, \"MSPointerUp\", cb)\n+ },\n+ CLASS_NAME: \"OpenLayers.Events\"\n+});\n+(function() {\n+ var oXMLHttpRequest = window.XMLHttpRequest;\n+ var bGecko = !!window.controllers,\n+ bIE = window.document.all && !window.opera,\n+ bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);\n+\n+ function fXMLHttpRequest() {\n+ this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n+ this._listeners = []\n+ }\n+\n+ function cXMLHttpRequest() {\n+ return new fXMLHttpRequest\n+ }\n+ cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;\n+ if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;\n+ cXMLHttpRequest.UNSENT = 0;\n+ cXMLHttpRequest.OPENED = 1;\n+ cXMLHttpRequest.HEADERS_RECEIVED = 2;\n+ cXMLHttpRequest.LOADING = 3;\n+ cXMLHttpRequest.DONE = 4;\n+ cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;\n+ cXMLHttpRequest.prototype.responseText = \"\";\n+ cXMLHttpRequest.prototype.responseXML = null;\n+ cXMLHttpRequest.prototype.status = 0;\n+ cXMLHttpRequest.prototype.statusText = \"\";\n+ cXMLHttpRequest.prototype.priority = \"NORMAL\";\n+ cXMLHttpRequest.prototype.onreadystatechange = null;\n+ cXMLHttpRequest.onreadystatechange = null;\n+ cXMLHttpRequest.onopen = null;\n+ cXMLHttpRequest.onsend = null;\n+ cXMLHttpRequest.onabort = null;\n+ cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {\n+ delete this._headers;\n+ if (arguments.length < 3) bAsync = true;\n+ this._async = bAsync;\n+ var oRequest = this,\n+ nState = this.readyState,\n+ fOnUnload;\n+ if (bIE && bAsync) {\n+ fOnUnload = function() {\n+ if (nState != cXMLHttpRequest.DONE) {\n+ fCleanTransport(oRequest);\n+ oRequest.abort()\n+ }\n+ };\n+ window.attachEvent(\"onunload\", fOnUnload)\n+ }\n+ if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments);\n+ if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);\n+ else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser);\n+ else this._object.open(sMethod, sUrl, bAsync);\n+ this.readyState = cXMLHttpRequest.OPENED;\n+ fReadyStateChange(this);\n+ this._object.onreadystatechange = function() {\n+ if (bGecko && !bAsync) return;\n+ oRequest.readyState = oRequest._object.readyState;\n+ fSynchronizeValues(oRequest);\n+ if (oRequest._aborted) {\n+ oRequest.readyState = cXMLHttpRequest.UNSENT;\n+ return\n+ }\n+ if (oRequest.readyState == cXMLHttpRequest.DONE) {\n+ delete oRequest._data;\n+ fCleanTransport(oRequest);\n+ if (bIE && bAsync) window.detachEvent(\"onunload\", fOnUnload)\n+ }\n+ if (nState != oRequest.readyState) fReadyStateChange(oRequest);\n+ nState = oRequest.readyState\n+ }\n+ };\n+\n+ function fXMLHttpRequest_send(oRequest) {\n+ oRequest._object.send(oRequest._data);\n+ if (bGecko && !oRequest._async) {\n+ oRequest.readyState = cXMLHttpRequest.OPENED;\n+ fSynchronizeValues(oRequest);\n+ while (oRequest.readyState < cXMLHttpRequest.DONE) {\n+ oRequest.readyState++;\n+ fReadyStateChange(oRequest);\n+ if (oRequest._aborted) return\n+ }\n+ }\n+ }\n+ cXMLHttpRequest.prototype.send = function(vData) {\n+ if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments);\n+ if (!arguments.length) vData = null;\n+ if (vData && vData.nodeType) {\n+ vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml;\n+ if (!this._headers[\"Content-Type\"]) this._object.setRequestHeader(\"Content-Type\", \"application/xml\")\n+ }\n+ this._data = vData;\n+ fXMLHttpRequest_send(this)\n+ };\n+ cXMLHttpRequest.prototype.abort = function() {\n+ if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments);\n+ if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true;\n+ this._object.abort();\n+ fCleanTransport(this);\n+ this.readyState = cXMLHttpRequest.UNSENT;\n+ delete this._data\n+ };\n+ cXMLHttpRequest.prototype.getAllResponseHeaders = function() {\n+ return this._object.getAllResponseHeaders()\n+ };\n+ cXMLHttpRequest.prototype.getResponseHeader = function(sName) {\n+ return this._object.getResponseHeader(sName)\n+ };\n+ cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {\n+ if (!this._headers) this._headers = {};\n+ this._headers[sName] = sValue;\n+ return this._object.setRequestHeader(sName, sValue)\n+ };\n+ cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return;\n+ this._listeners.push([sName, fHandler, bUseCapture])\n+ };\n+ cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break;\n+ if (oListener) this._listeners.splice(nIndex, 1)\n+ };\n+ cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {\n+ var oEventPseudo = {\n+ type: oEvent.type,\n+ target: this,\n+ currentTarget: this,\n+ eventPhase: 2,\n+ bubbles: oEvent.bubbles,\n+ cancelable: oEvent.cancelable,\n+ timeStamp: oEvent.timeStamp,\n+ stopPropagation: function() {},\n+ preventDefault: function() {},\n+ initEvent: function() {}\n+ };\n+ if (oEventPseudo.type == \"readystatechange\" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);\n+ for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)\n+ if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo])\n+ };\n+ cXMLHttpRequest.prototype.toString = function() {\n+ return \"[\" + \"object\" + \" \" + \"XMLHttpRequest\" + \"]\"\n+ };\n+ cXMLHttpRequest.toString = function() {\n+ return \"[\" + \"XMLHttpRequest\" + \"]\"\n+ };\n+\n+ function fReadyStateChange(oRequest) {\n+ if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest);\n+ oRequest.dispatchEvent({\n+ type: \"readystatechange\",\n+ bubbles: false,\n+ cancelable: false,\n+ timeStamp: new Date + 0\n+ })\n+ }\n+\n+ function fGetDocument(oRequest) {\n+ var oDocument = oRequest.responseXML,\n+ sResponse = oRequest.responseText;\n+ if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader(\"Content-Type\").match(/[^\\/]+\\/[^\\+]+\\+xml/)) {\n+ oDocument = new window.ActiveXObject(\"Microsoft.XMLDOM\");\n+ oDocument.async = false;\n+ oDocument.validateOnParse = false;\n+ oDocument.loadXML(sResponse)\n+ }\n+ if (oDocument)\n+ if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == \"parsererror\") return null;\n+ return oDocument\n+ }\n+\n+ function fSynchronizeValues(oRequest) {\n+ try {\n+ oRequest.responseText = oRequest._object.responseText\n+ } catch (e) {}\n+ try {\n+ oRequest.responseXML = fGetDocument(oRequest._object)\n+ } catch (e) {}\n+ try {\n+ oRequest.status = oRequest._object.status\n+ } catch (e) {}\n+ try {\n+ oRequest.statusText = oRequest._object.statusText\n+ } catch (e) {}\n+ }\n+\n+ function fCleanTransport(oRequest) {\n+ oRequest._object.onreadystatechange = new window.Function\n+ }\n+ if (!window.Function.prototype.apply) {\n+ window.Function.prototype.apply = function(oRequest, oArguments) {\n+ if (!oArguments) oArguments = [];\n+ oRequest.__func = this;\n+ oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);\n+ delete oRequest.__func\n+ }\n+ }\n+ if (!OpenLayers.Request) {\n+ OpenLayers.Request = {}\n+ }\n+ OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest\n+})();\n+OpenLayers.ProxyHost = \"\";\n+if (!OpenLayers.Request) {\n+ OpenLayers.Request = {}\n+}\n+OpenLayers.Util.extend(OpenLayers.Request, {\n+ DEFAULT_CONFIG: {\n+ method: \"GET\",\n+ url: window.location.href,\n+ async: true,\n+ user: undefined,\n+ password: undefined,\n+ params: null,\n+ proxy: OpenLayers.ProxyHost,\n+ headers: {},\n+ data: null,\n+ callback: function() {},\n+ success: null,\n+ failure: null,\n+ scope: null\n+ },\n+ URL_SPLIT_REGEX: /([^:]*:)\\/\\/([^:]*:?[^@]*@)?([^:\\/\\?]*):?([^\\/\\?]*)/,\n+ events: new OpenLayers.Events(this),\n+ makeSameOrigin: function(url, proxy) {\n+ var sameOrigin = url.indexOf(\"http\") !== 0;\n+ var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);\n+ if (urlParts) {\n+ var location = window.location;\n+ sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname;\n+ var uPort = urlParts[4],\n+ lPort = location.port;\n+ if (uPort != 80 && uPort != \"\" || lPort != \"80\" && lPort != \"\") {\n+ sameOrigin = sameOrigin && uPort == lPort\n+ }\n+ }\n+ if (!sameOrigin) {\n+ if (proxy) {\n+ if (typeof proxy == \"function\") {\n+ url = proxy(url)\n+ } else {\n+ url = proxy + encodeURIComponent(url)\n+ }\n+ }\n+ }\n+ return url\n+ },\n+ issue: function(config) {\n+ var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, {\n+ proxy: OpenLayers.ProxyHost\n+ });\n+ config = config || {};\n+ config.headers = config.headers || {};\n+ config = OpenLayers.Util.applyDefaults(config, defaultConfig);\n+ config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);\n+ var customRequestedWithHeader = false,\n+ headerKey;\n+ for (headerKey in config.headers) {\n+ if (config.headers.hasOwnProperty(headerKey)) {\n+ if (headerKey.toLowerCase() === \"x-requested-with\") {\n+ customRequestedWithHeader = true\n+ }\n+ }\n+ }\n+ if (customRequestedWithHeader === false) {\n+ config.headers[\"X-Requested-With\"] = \"XMLHttpRequest\"\n+ }\n+ var request = new OpenLayers.Request.XMLHttpRequest;\n+ var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {}));\n+ url = OpenLayers.Request.makeSameOrigin(url, config.proxy);\n+ request.open(config.method, url, config.async, config.user, config.password);\n+ for (var header in config.headers) {\n+ request.setRequestHeader(header, config.headers[header])\n+ }\n+ var events = this.events;\n+ var self = this;\n+ request.onreadystatechange = function() {\n+ if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {\n+ var proceed = events.triggerEvent(\"complete\", {\n+ request: request,\n+ config: config,\n+ requestUrl: url\n+ });\n+ if (proceed !== false) {\n+ self.runCallbacks({\n+ request: request,\n+ config: config,\n+ requestUrl: url\n+ })\n+ }\n+ }\n+ };\n+ if (config.async === false) {\n+ request.send(config.data)\n+ } else {\n+ window.setTimeout(function() {\n+ if (request.readyState !== 0) {\n+ request.send(config.data)\n+ }\n+ }, 0)\n+ }\n+ return request\n+ },\n+ runCallbacks: function(options) {\n+ var request = options.request;\n+ var config = options.config;\n+ var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback;\n+ var success;\n+ if (config.success) {\n+ success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success\n+ }\n+ var failure;\n+ if (config.failure) {\n+ failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure\n+ }\n+ if (OpenLayers.Util.createUrlObject(config.url).protocol == \"file:\" && request.responseText) {\n+ request.status = 200\n+ }\n+ complete(request);\n+ if (!request.status || request.status >= 200 && request.status < 300) {\n+ this.events.triggerEvent(\"success\", options);\n+ if (success) {\n+ success(request)\n+ }\n+ }\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"failure\", options);\n+ if (failure) {\n+ failure(request)\n+ }\n+ }\n+ },\n+ GET: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"GET\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ },\n+ POST: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"POST\"\n+ });\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\"\n+ }\n+ return OpenLayers.Request.issue(config)\n+ },\n+ PUT: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"PUT\"\n+ });\n+ config.headers = config.headers ? config.headers : {};\n+ if (!(\"CONTENT-TYPE\" in OpenLayers.Util.upperCaseObject(config.headers))) {\n+ config.headers[\"Content-Type\"] = \"application/xml\"\n+ }\n+ return OpenLayers.Request.issue(config)\n+ },\n+ DELETE: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"DELETE\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ },\n+ HEAD: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"HEAD\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ },\n+ OPTIONS: function(config) {\n+ config = OpenLayers.Util.extend(config, {\n+ method: \"OPTIONS\"\n+ });\n+ return OpenLayers.Request.issue(config)\n+ }\n+});\n OpenLayers.WPSProcess = OpenLayers.Class({\n client: null,\n server: null,\n identifier: null,\n description: null,\n localWPS: \"http://geoserver/wps\",\n formats: null,\n@@ -13311,49 +8952,141 @@\n process: null,\n output: null,\n initialize: function(options) {\n OpenLayers.Util.extend(this, options)\n },\n CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\n });\n-OpenLayers.Strategy = OpenLayers.Class({\n- layer: null,\n- options: null,\n+OpenLayers.Symbolizer = OpenLayers.Class({\n+ zIndex: 0,\n+ initialize: function(config) {\n+ OpenLayers.Util.extend(this, config)\n+ },\n+ clone: function() {\n+ var Type = eval(this.CLASS_NAME);\n+ return new Type(OpenLayers.Util.extend({}, this))\n+ },\n+ CLASS_NAME: \"OpenLayers.Symbolizer\"\n+});\n+OpenLayers.Control = OpenLayers.Class({\n+ id: null,\n+ map: null,\n+ div: null,\n+ type: null,\n+ allowSelection: false,\n+ displayClass: \"\",\n+ title: \"\",\n+ autoActivate: false,\n active: null,\n- autoActivate: true,\n- autoDestroy: true,\n+ handlerOptions: null,\n+ handler: null,\n+ eventListeners: null,\n+ events: null,\n initialize: function(options) {\n+ this.displayClass = this.CLASS_NAME.replace(\"OpenLayers.\", \"ol\").replace(/\\./g, \"\");\n OpenLayers.Util.extend(this, options);\n- this.options = options;\n- this.active = false\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n+ }\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n+ }\n },\n destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n+ }\n+ this.events.destroy();\n+ this.events = null\n+ }\n+ this.eventListeners = null;\n+ if (this.handler) {\n+ this.handler.destroy();\n+ this.handler = null\n+ }\n+ if (this.handlers) {\n+ for (var key in this.handlers) {\n+ if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == \"function\") {\n+ this.handlers[key].destroy()\n+ }\n+ }\n+ this.handlers = null\n+ }\n+ if (this.map) {\n+ this.map.removeControl(this);\n+ this.map = null\n+ }\n+ this.div = null\n },\n- setLayer: function(layer) {\n- this.layer = layer\n+ setMap: function(map) {\n+ this.map = map;\n+ if (this.handler) {\n+ this.handler.setMap(map)\n+ }\n+ },\n+ draw: function(px) {\n+ if (this.div == null) {\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.className = this.displayClass;\n+ if (!this.allowSelection) {\n+ this.div.className += \" olControlNoSelect\";\n+ this.div.setAttribute(\"unselectable\", \"on\", 0);\n+ this.div.onselectstart = OpenLayers.Function.False\n+ }\n+ if (this.title != \"\") {\n+ this.div.title = this.title\n+ }\n+ }\n+ if (px != null) {\n+ this.position = px.clone()\n+ }\n+ this.moveTo(this.position);\n+ return this.div\n+ },\n+ moveTo: function(px) {\n+ if (px != null && this.div != null) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\"\n+ }\n },\n activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true\n+ if (this.active) {\n+ return false\n }\n- return false\n+ if (this.handler) {\n+ this.handler.activate()\n+ }\n+ this.active = true;\n+ if (this.map) {\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n+ }\n+ this.events.triggerEvent(\"activate\");\n+ return true\n },\n deactivate: function() {\n if (this.active) {\n+ if (this.handler) {\n+ this.handler.deactivate()\n+ }\n this.active = false;\n+ if (this.map) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, \"\") + \"Active\")\n+ }\n+ this.events.triggerEvent(\"deactivate\");\n return true\n }\n return false\n },\n- CLASS_NAME: \"OpenLayers.Strategy\"\n+ CLASS_NAME: \"OpenLayers.Control\"\n });\n+OpenLayers.Control.TYPE_BUTTON = 1;\n+OpenLayers.Control.TYPE_TOGGLE = 2;\n+OpenLayers.Control.TYPE_TOOL = 3;\n OpenLayers.StyleMap = OpenLayers.Class({\n styles: null,\n extendDefault: true,\n initialize: function(style, options) {\n this.styles = {\n default: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"default\"]),\n select: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"select\"]),\n@@ -13415,360 +9148,99 @@\n })\n }))\n }\n this.styles[renderIntent].addRules(rules)\n },\n CLASS_NAME: \"OpenLayers.StyleMap\"\n });\n-OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(OpenLayers.Format.XML, {\n- VERSION: \"1.0.0\",\n- namespaces: {\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- ows: \"http://www.opengis.net/ows/1.1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n- schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n- defaultPrefix: \"wps\",\n- regExes: {\n- trimSpace: /^\\s*|\\s*$/g,\n- removeSpace: /\\s*/g,\n- splitSpace: /\\s+/,\n- trimComma: /\\s*,\\s*/g\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement\n- }\n- var info = {};\n- this.readNode(data, info);\n- return info\n- },\n- readers: {\n- wps: {\n- ProcessDescriptions: function(node, obj) {\n- obj.processDescriptions = {};\n- this.readChildNodes(node, obj.processDescriptions)\n- },\n- ProcessDescription: function(node, processDescriptions) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var processDescription = {\n- processVersion: processVersion,\n- statusSupported: node.getAttribute(\"statusSupported\") === \"true\",\n- storeSupported: node.getAttribute(\"storeSupported\") === \"true\"\n- };\n- this.readChildNodes(node, processDescription);\n- processDescriptions[processDescription.identifier] = processDescription\n- },\n- DataInputs: function(node, processDescription) {\n- processDescription.dataInputs = [];\n- this.readChildNodes(node, processDescription.dataInputs)\n- },\n- ProcessOutputs: function(node, processDescription) {\n- processDescription.processOutputs = [];\n- this.readChildNodes(node, processDescription.processOutputs)\n- },\n- Output: function(node, processOutputs) {\n- var output = {};\n- this.readChildNodes(node, output);\n- processOutputs.push(output)\n- },\n- ComplexOutput: function(node, output) {\n- output.complexOutput = {};\n- this.readChildNodes(node, output.complexOutput)\n- },\n- LiteralOutput: function(node, output) {\n- output.literalOutput = {};\n- this.readChildNodes(node, output.literalOutput)\n- },\n- Input: function(node, dataInputs) {\n- var input = {\n- maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n- minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n- };\n- this.readChildNodes(node, input);\n- dataInputs.push(input)\n- },\n- BoundingBoxData: function(node, input) {\n- input.boundingBoxData = {};\n- this.readChildNodes(node, input.boundingBoxData)\n- },\n- CRS: function(node, obj) {\n- if (!obj.CRSs) {\n- obj.CRSs = {}\n- }\n- obj.CRSs[this.getChildValue(node)] = true\n- },\n- LiteralData: function(node, input) {\n- input.literalData = {};\n- this.readChildNodes(node, input.literalData)\n- },\n- ComplexData: function(node, input) {\n- input.complexData = {};\n- this.readChildNodes(node, input.complexData)\n- },\n- Default: function(node, complexData) {\n- complexData[\"default\"] = {};\n- this.readChildNodes(node, complexData[\"default\"])\n- },\n- Supported: function(node, complexData) {\n- complexData[\"supported\"] = {};\n- this.readChildNodes(node, complexData[\"supported\"])\n- },\n- Format: function(node, obj) {\n- var format = {};\n- this.readChildNodes(node, format);\n- if (!obj.formats) {\n- obj.formats = {}\n- }\n- obj.formats[format.mimeType] = true\n- },\n- MimeType: function(node, format) {\n- format.mimeType = this.getChildValue(node)\n- }\n- },\n- ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n- CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n-});\n-OpenLayers.WPSClient = OpenLayers.Class({\n- servers: null,\n- version: \"1.0.0\",\n- lazy: false,\n- events: null,\n+OpenLayers.Rule = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ title: null,\n+ description: null,\n+ context: null,\n+ filter: null,\n+ elseFilter: false,\n+ symbolizer: null,\n+ symbolizers: null,\n+ minScaleDenominator: null,\n+ maxScaleDenominator: null,\n initialize: function(options) {\n+ this.symbolizer = {};\n OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- this.servers = {};\n- for (var s in options.servers) {\n- this.servers[s] = typeof options.servers[s] == \"string\" ? {\n- url: options.servers[s],\n- version: this.version,\n- processDescription: {}\n- } : options.servers[s]\n- }\n- },\n- execute: function(options) {\n- var process = this.getProcess(options.server, options.process);\n- process.execute({\n- inputs: options.inputs,\n- success: options.success,\n- scope: options.scope\n- })\n- },\n- getProcess: function(serverID, processID) {\n- var process = new OpenLayers.WPSProcess({\n- client: this,\n- server: serverID,\n- identifier: processID\n- });\n- if (!this.lazy) {\n- process.describe()\n- }\n- return process\n- },\n- describeProcess: function(serverID, processID, callback, scope) {\n- var server = this.servers[serverID];\n- if (!server.processDescription[processID]) {\n- if (!(processID in server.processDescription)) {\n- server.processDescription[processID] = null;\n- OpenLayers.Request.GET({\n- url: server.url,\n- params: {\n- SERVICE: \"WPS\",\n- VERSION: server.version,\n- REQUEST: \"DescribeProcess\",\n- IDENTIFIER: processID\n- },\n- success: function(response) {\n- server.processDescription[processID] = response.responseText;\n- this.events.triggerEvent(\"describeprocess\", {\n- identifier: processID,\n- raw: response.responseText\n- })\n- },\n- scope: this\n- })\n- } else {\n- this.events.register(\"describeprocess\", this, function describe(evt) {\n- if (evt.identifier === processID) {\n- this.events.unregister(\"describeprocess\", this, describe);\n- callback.call(scope, evt.raw)\n- }\n- })\n- }\n- } else {\n- window.setTimeout(function() {\n- callback.call(scope, server.processDescription[processID])\n- }, 0)\n+ if (this.symbolizers) {\n+ delete this.symbolizer\n }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n destroy: function() {\n- this.events.destroy();\n- this.events = null;\n- this.servers = null\n- },\n- CLASS_NAME: \"OpenLayers.WPSClient\"\n-});\n-OpenLayers.Renderer = OpenLayers.Class({\n- container: null,\n- root: null,\n- extent: null,\n- locked: false,\n- size: null,\n- resolution: null,\n- map: null,\n- featureDx: 0,\n- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {\n- this.container = null;\n- this.extent = null;\n- this.size = null;\n- this.resolution = null;\n- this.map = null\n- },\n- supported: function() {\n- return false\n+ for (var i in this.symbolizer) {\n+ this.symbolizer[i] = null\n+ }\n+ this.symbolizer = null;\n+ delete this.symbolizers\n },\n- setExtent: function(extent, resolutionChanged) {\n- this.extent = extent.clone();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio);\n- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio)\n+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale()\n }\n- if (resolutionChanged) {\n- this.resolution = null\n+ if (this.minScaleDenominator) {\n+ applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context)\n }\n- return true\n- },\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null\n- },\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution\n- },\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style\n+ if (applies && this.maxScaleDenominator) {\n+ applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context)\n }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent()\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n- }\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds)\n- }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n- var location = feature.geometry.getCentroid();\n- if (style.labelXOffset || style.labelYOffset) {\n- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n- var res = this.getResolution();\n- location.move(xOffset * res, yOffset * res)\n- }\n- this.drawText(feature.id, style, location)\n- } else {\n- this.removeText(feature.id)\n- }\n- return rendered\n+ if (applies && this.filter) {\n+ if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n+ applies = this.filter.evaluate(feature)\n+ } else {\n+ applies = this.filter.evaluate(context)\n }\n }\n+ return applies\n },\n- calculateFeatureDx: function(bounds, worldBounds) {\n- this.featureDx = 0;\n- if (worldBounds) {\n- var worldWidth = worldBounds.getWidth(),\n- rendererCenterX = (this.extent.left + this.extent.right) / 2,\n- featureCenterX = (bounds.left + bounds.right) / 2,\n- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n- this.featureDx = worldsAway * worldWidth\n- }\n- },\n- drawGeometry: function(geometry, style, featureId) {},\n- drawText: function(featureId, style, location) {},\n- removeText: function(featureId) {},\n- clear: function() {},\n- getFeatureIdFromEvent: function(evt) {},\n- eraseFeatures: function(features) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n+ getContext: function(feature) {\n+ var context = this.context;\n+ if (!context) {\n+ context = feature.attributes || feature.data\n }\n- for (var i = 0, len = features.length; i < len; ++i) {\n- var feature = features[i];\n- this.eraseGeometry(feature.geometry, feature.id);\n- this.removeText(feature.id)\n+ if (typeof this.context == \"function\") {\n+ context = this.context(feature)\n }\n+ return context\n },\n- eraseGeometry: function(geometry, featureId) {},\n- moveRoot: function(renderer) {},\n- getRenderLayerId: function() {\n- return this.container.id\n- },\n- applyDefaultSymbolizer: function(symbolizer) {\n- var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer);\n- if (symbolizer.stroke === false) {\n- delete result.strokeWidth;\n- delete result.strokeColor\n- }\n- if (symbolizer.fill === false) {\n- delete result.fillColor\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.symbolizers) {\n+ var len = this.symbolizers.length;\n+ options.symbolizers = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ options.symbolizers[i] = this.symbolizers[i].clone()\n+ }\n+ } else {\n+ options.symbolizer = {};\n+ var value, type;\n+ for (var key in this.symbolizer) {\n+ value = this.symbolizer[key];\n+ type = typeof value;\n+ if (type === \"object\") {\n+ options.symbolizer[key] = OpenLayers.Util.extend({}, value)\n+ } else if (type === \"string\") {\n+ options.symbolizer[key] = value\n+ }\n+ }\n }\n- OpenLayers.Util.extend(result, symbolizer);\n- return result\n+ options.filter = this.filter && this.filter.clone();\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ return new OpenLayers.Rule(options)\n },\n- CLASS_NAME: \"OpenLayers.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Rule\"\n });\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: \"cm\"\n-};\n-OpenLayers.Renderer.symbol = {\n- star: [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75],\n- cross: [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0],\n- x: [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n- square: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n- triangle: [0, 10, 10, 10, 5, 0, 0, 10]\n-};\n-OpenLayers.Spherical = OpenLayers.Spherical || {};\n-OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n-OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n- var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n- var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n- var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n- var a = sinHalfDeltaLat * sinHalfDeltaLat + sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n- return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))\n-};\n-OpenLayers.Spherical.computeHeading = function(from, to) {\n- var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n- var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) - Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n- return 180 * Math.atan2(y, x) / Math.PI\n-};\n OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {\n initialize: function(config) {\n OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments)\n },\n CLASS_NAME: \"OpenLayers.Symbolizer.Point\"\n });\n OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {\n@@ -13821,4854 +9293,5299 @@\n config.rules.push(this.rules[i].clone())\n }\n }\n return new OpenLayers.Style2(config)\n },\n CLASS_NAME: \"OpenLayers.Style2\"\n });\n-OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- format: \"image/png\",\n- serverResolutions: null,\n- initialize: function(name, url, layername, options) {\n- this.layername = layername;\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, {}, options]);\n- this.extension = this.format.split(\"/\")[1].toLowerCase();\n- this.extension = this.extension == \"jpg\" ? \"jpeg\" : this.extension\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.TileCache(this.name, this.url, this.layername, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- getURL: function(bounds) {\n- var res = this.getServerResolution();\n- var bbox = this.maxExtent;\n- var size = this.tileSize;\n- var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));\n- var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));\n- var tileZ = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom();\n- var components = [this.layername, OpenLayers.Number.zeroPad(tileZ, 2), OpenLayers.Number.zeroPad(parseInt(tileX / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileX / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileX) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY) % 1e3, 3) + \".\" + this.extension];\n- var path = components.join(\"/\");\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url)\n- }\n- url = url.charAt(url.length - 1) == \"/\" ? url : url + \"/\";\n- return url + path\n- },\n- CLASS_NAME: \"OpenLayers.Layer.TileCache\"\n-});\n-OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n- smoothDragPan: true,\n- isBaseLayer: true,\n- isFixed: true,\n- pane: null,\n- mapObject: null,\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (this.pane == null) {\n- this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\")\n- }\n- },\n- destroy: function() {\n- this.mapObject = null;\n- this.pane = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n- this.pane.style.display = this.div.style.display;\n- this.pane.style.width = \"100%\";\n- this.pane.style.height = \"100%\";\n- if (OpenLayers.BROWSER_NAME == \"msie\") {\n- this.pane.style.background = \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\"\n- }\n- if (this.isFixed) {\n- this.map.viewPortDiv.appendChild(this.pane)\n- } else {\n- this.map.layerContainerDiv.appendChild(this.pane)\n- }\n- this.loadMapObject();\n- if (this.mapObject == null) {\n- this.loadWarningMessage()\n- }\n- },\n- removeMap: function(map) {\n- if (this.pane && this.pane.parentNode) {\n- this.pane.parentNode.removeChild(this.pane)\n+OpenLayers.Util = OpenLayers.Util || {};\n+OpenLayers.Util.vendorPrefix = function() {\n+ \"use strict\";\n+ var VENDOR_PREFIXES = [\"\", \"O\", \"ms\", \"Moz\", \"Webkit\"],\n+ divStyle = document.createElement(\"div\").style,\n+ cssCache = {},\n+ jsCache = {};\n+\n+ function domToCss(prefixedDom) {\n+ if (!prefixedDom) {\n+ return null\n }\n- OpenLayers.Layer.prototype.removeMap.apply(this, arguments)\n- },\n- loadWarningMessage: function() {\n- this.div.style.backgroundColor = \"darkblue\";\n- var viewSize = this.map.getSize();\n- var msgW = Math.min(viewSize.w, 300);\n- var msgH = Math.min(viewSize.h, 200);\n- var size = new OpenLayers.Size(msgW, msgH);\n- var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n- var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n- var div = OpenLayers.Util.createDiv(this.name + \"_warning\", topLeft, size, null, null, null, \"auto\");\n- div.style.padding = \"7px\";\n- div.style.backgroundColor = \"yellow\";\n- div.innerHTML = this.getWarningHTML();\n- this.div.appendChild(div)\n- },\n- getWarningHTML: function() {\n- return \"\"\n- },\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- this.pane.style.display = this.div.style.display\n- },\n- setZIndex: function(zIndex) {\n- OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n- this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1\n- },\n- moveByPx: function(dx, dy) {\n- OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n- if (this.dragPanMapObject) {\n- this.dragPanMapObject(dx, -dy)\n- } else {\n- this.moveTo(this.map.getCachedCenter())\n+ return prefixedDom.replace(/([A-Z])/g, function(c) {\n+ return \"-\" + c.toLowerCase()\n+ }).replace(/^ms-/, \"-ms-\")\n+ }\n+\n+ function css(property) {\n+ if (cssCache[property] === undefined) {\n+ var domProperty = property.replace(/(-[\\s\\S])/g, function(c) {\n+ return c.charAt(1).toUpperCase()\n+ });\n+ var prefixedDom = style(domProperty);\n+ cssCache[property] = domToCss(prefixedDom)\n }\n- },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- if (this.mapObject != null) {\n- var newCenter = this.map.getCenter();\n- var newZoom = this.map.getZoom();\n- if (newCenter != null) {\n- var moOldCenter = this.getMapObjectCenter();\n- var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n- var moOldZoom = this.getMapObjectZoom();\n- var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n- if (!newCenter.equals(oldCenter) || newZoom != oldZoom) {\n- if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) {\n- var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n- var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n- this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y)\n- } else {\n- var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n- var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n- this.setMapObjectCenter(center, zoom, dragging)\n+ return cssCache[property]\n+ }\n+\n+ function js(obj, property) {\n+ if (jsCache[property] === undefined) {\n+ var tmpProp, i = 0,\n+ l = VENDOR_PREFIXES.length,\n+ prefix, isStyleObj = typeof obj.cssText !== \"undefined\";\n+ jsCache[property] = null;\n+ for (; i < l; i++) {\n+ prefix = VENDOR_PREFIXES[i];\n+ if (prefix) {\n+ if (!isStyleObj) {\n+ prefix = prefix.toLowerCase()\n }\n+ tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1)\n+ } else {\n+ tmpProp = property\n+ }\n+ if (obj[tmpProp] !== undefined) {\n+ jsCache[property] = tmpProp;\n+ break\n }\n }\n }\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- var lonlat = null;\n- if (this.mapObject != null && this.getMapObjectCenter() != null) {\n- var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n- var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n- lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat)\n- }\n- return lonlat\n- },\n- getViewPortPxFromLonLat: function(lonlat) {\n- var viewPortPx = null;\n- if (this.mapObject != null && this.getMapObjectCenter() != null) {\n- var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n- var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n- viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel)\n+ return jsCache[property]\n+ }\n+\n+ function style(property) {\n+ return js(divStyle, property)\n+ }\n+ return {\n+ css: css,\n+ js: js,\n+ style: style,\n+ cssCache: cssCache,\n+ jsCache: jsCache\n+ }\n+}();\n+OpenLayers.Animation = function(window) {\n+ var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, \"requestAnimationFrame\");\n+ var isNative = !!requestAnimationFrame;\n+ var requestFrame = function() {\n+ var request = window[requestAnimationFrame] || function(callback, element) {\n+ window.setTimeout(callback, 16)\n+ };\n+ return function(callback, element) {\n+ request.apply(window, [callback, element])\n }\n- return viewPortPx\n+ }();\n+ var counter = 0;\n+ var loops = {};\n+\n+ function start(callback, duration, element) {\n+ duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;\n+ var id = ++counter;\n+ var start = +new Date;\n+ loops[id] = function() {\n+ if (loops[id] && +new Date - start <= duration) {\n+ callback();\n+ if (loops[id]) {\n+ requestFrame(loops[id], element)\n+ }\n+ } else {\n+ delete loops[id]\n+ }\n+ };\n+ requestFrame(loops[id], element);\n+ return id\n+ }\n+\n+ function stop(id) {\n+ delete loops[id]\n+ }\n+ return {\n+ isNative: isNative,\n+ requestFrame: requestFrame,\n+ start: start,\n+ stop: stop\n+ }\n+}(window);\n+OpenLayers.Kinetic = OpenLayers.Class({\n+ threshold: 0,\n+ deceleration: .0035,\n+ nbPoints: 100,\n+ delay: 200,\n+ points: undefined,\n+ timerId: undefined,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n },\n- getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n- var olLonLat = null;\n- if (moLonLat != null) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- olLonLat = new OpenLayers.LonLat(lon, lat)\n- }\n- return olLonLat\n+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = []\n },\n- getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n- var moLatLng = null;\n- if (olLonLat != null) {\n- moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat)\n+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: (new Date).getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop()\n }\n- return moLatLng\n },\n- getOLPixelFromMapObjectPixel: function(moPixel) {\n- var olPixel = null;\n- if (moPixel != null) {\n- var x = this.getXFromMapObjectPixel(moPixel);\n- var y = this.getYFromMapObjectPixel(moPixel);\n- olPixel = new OpenLayers.Pixel(x, y)\n+ end: function(xy) {\n+ var last, now = (new Date).getTime();\n+ for (var i = 0, l = this.points.length, point; i < l; i++) {\n+ point = this.points[i];\n+ if (now - point.tick > this.delay) {\n+ break\n+ }\n+ last = point\n }\n- return olPixel\n- },\n- getMapObjectPixelFromOLPixel: function(olPixel) {\n- var moPixel = null;\n- if (olPixel != null) {\n- moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y)\n+ if (!last) {\n+ return\n }\n- return moPixel\n- },\n- CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n-});\n-OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {},\n- isBaseLayer: true,\n- lzd: null,\n- zoomLevels: null,\n- initialize: function(name, url, lzd, zoomLevels, params, options) {\n- this.lzd = lzd;\n- this.zoomLevels = zoomLevels;\n- var newArguments = [];\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS)\n- },\n- getZoom: function() {\n- var zoom = this.map.getZoom();\n- var extent = this.map.getMaxExtent();\n- zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);\n- return zoom\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var zoom = this.getZoom();\n- var extent = this.map.getMaxExtent();\n- var deg = this.lzd / Math.pow(2, this.getZoom());\n- var x = Math.floor((bounds.left - extent.left) / deg);\n- var y = Math.floor((bounds.bottom - extent.bottom) / deg);\n- if (this.map.getResolution() <= this.lzd / 512 && this.getZoom() <= this.zoomLevels) {\n- return this.getFullRequestString({\n- L: zoom,\n- X: x,\n- Y: y\n- })\n- } else {\n- return OpenLayers.Util.getImageLocation(\"blank.gif\")\n+ var time = (new Date).getTime() - last.tick;\n+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2));\n+ var speed = dist / time;\n+ if (speed == 0 || speed < this.threshold) {\n+ return\n }\n- },\n- CLASS_NAME: \"OpenLayers.Layer.WorldWind\"\n-});\n-OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- serviceVersion: \"1.0.0\",\n- layername: null,\n- type: null,\n- isBaseLayer: true,\n- tileOrigin: null,\n- serverResolutions: null,\n- zoomOffset: 0,\n- initialize: function(name, url, options) {\n- var newArguments = [];\n- newArguments.push(name, url, {}, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.TMS(this.name, this.url, this.getOptions())\n+ var theta = Math.asin((xy.y - last.xy.y) / dist);\n+ if (last.xy.x <= xy.x) {\n+ theta = Math.PI - theta\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- var path = this.serviceVersion + \"/\" + this.layername + \"/\" + z + \"/\" + x + \"/\" + y + \".\" + this.type;\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url)\n+ return {\n+ speed: speed,\n+ theta: theta\n }\n- return url + path\n },\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.bottom)\n- }\n+ move: function(info, callback) {\n+ var v0 = info.speed;\n+ var fx = Math.cos(info.theta);\n+ var fy = -Math.sin(info.theta);\n+ var initialTime = (new Date).getTime();\n+ var lastX = 0;\n+ var lastY = 0;\n+ var timerCallback = function() {\n+ if (this.timerId == null) {\n+ return\n+ }\n+ var t = (new Date).getTime() - initialTime;\n+ var p = -this.deceleration * Math.pow(t, 2) / 2 + v0 * t;\n+ var x = p * fx;\n+ var y = p * fy;\n+ var args = {};\n+ args.end = false;\n+ var v = -this.deceleration * t + v0;\n+ if (v <= 0) {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = null;\n+ args.end = true\n+ }\n+ args.x = x - lastX;\n+ args.y = y - lastY;\n+ lastX = x;\n+ lastY = y;\n+ callback(args.x, args.y, args.end)\n+ };\n+ this.timerId = OpenLayers.Animation.start(OpenLayers.Function.bind(timerCallback, this))\n },\n- CLASS_NAME: \"OpenLayers.Layer.TMS\"\n+ CLASS_NAME: \"OpenLayers.Kinetic\"\n });\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- sphericalMercator: false,\n- zoomOffset: 0,\n- serverResolutions: null,\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options)\n- }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n+OpenLayers.Tween = OpenLayers.Class({\n+ easing: null,\n+ begin: null,\n+ finish: null,\n+ duration: null,\n+ callbacks: null,\n+ time: null,\n+ minFrameRate: null,\n+ startTime: null,\n+ animationId: null,\n+ playing: false,\n+ initialize: function(easing) {\n+ this.easing = easing ? easing : OpenLayers.Easing.Expo.easeOut\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n+ start: function(begin, finish, duration, options) {\n+ this.playing = true;\n+ this.begin = begin;\n+ this.finish = finish;\n+ this.duration = duration;\n+ this.callbacks = options.callbacks;\n+ this.minFrameRate = options.minFrameRate || 30;\n+ this.time = 0;\n+ this.startTime = (new Date).getTime();\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ if (this.callbacks && this.callbacks.start) {\n+ this.callbacks.start.call(this, this.begin)\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ this.animationId = OpenLayers.Animation.start(OpenLayers.Function.bind(this.play, this))\n },\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = \"\" + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url)\n+ stop: function() {\n+ if (!this.playing) {\n+ return\n }\n- return OpenLayers.String.format(url, xyz)\n+ if (this.callbacks && this.callbacks.done) {\n+ this.callbacks.done.call(this, this.finish)\n+ }\n+ OpenLayers.Animation.stop(this.animationId);\n+ this.animationId = null;\n+ this.playing = false\n },\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = (x % limit + limit) % limit\n+ play: function() {\n+ var value = {};\n+ for (var i in this.begin) {\n+ var b = this.begin[i];\n+ var f = this.finish[i];\n+ if (b == null || f == null || isNaN(b) || isNaN(f)) {\n+ throw new TypeError(\"invalid value for Tween\")\n+ }\n+ var c = f - b;\n+ value[i] = this.easing.apply(this, [this.time, b, c, this.duration])\n }\n- return {\n- x: x,\n- y: y,\n- z: z\n+ this.time++;\n+ if (this.callbacks && this.callbacks.eachStep) {\n+ if (((new Date).getTime() - this.startTime) / this.time <= 1e3 / this.minFrameRate) {\n+ this.callbacks.eachStep.call(this, value)\n+ }\n }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n+ if (this.time > this.duration) {\n+ this.stop()\n }\n },\n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+ CLASS_NAME: \"OpenLayers.Tween\"\n });\n-OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- url: null,\n- tileOrigin: null,\n- tileSize: new OpenLayers.Size(256, 256),\n- useArcGISServer: true,\n- type: \"png\",\n- useScales: false,\n- overrideDPI: false,\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- if (this.resolutions) {\n- this.serverResolutions = this.resolutions;\n- this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0])\n- }\n- if (this.layerInfo) {\n- var info = this.layerInfo;\n- var startingTileExtent = new OpenLayers.Bounds(info.fullExtent.xmin, info.fullExtent.ymin, info.fullExtent.xmax, info.fullExtent.ymax);\n- this.projection = \"EPSG:\" + info.spatialReference.wkid;\n- this.sphericalMercator = info.spatialReference.wkid == 102100;\n- this.units = info.units == \"esriFeet\" ? \"ft\" : \"m\";\n- if (!!info.tileInfo) {\n- this.tileSize = new OpenLayers.Size(info.tileInfo.width || info.tileInfo.cols, info.tileInfo.height || info.tileInfo.rows);\n- this.tileOrigin = new OpenLayers.LonLat(info.tileInfo.origin.x, info.tileInfo.origin.y);\n- var upperLeft = new OpenLayers.Geometry.Point(startingTileExtent.left, startingTileExtent.top);\n- var bottomRight = new OpenLayers.Geometry.Point(startingTileExtent.right, startingTileExtent.bottom);\n- if (this.useScales) {\n- this.scales = []\n- } else {\n- this.resolutions = []\n- }\n- this.lods = [];\n- for (var key in info.tileInfo.lods) {\n- if (info.tileInfo.lods.hasOwnProperty(key)) {\n- var lod = info.tileInfo.lods[key];\n- if (this.useScales) {\n- this.scales.push(lod.scale)\n- } else {\n- this.resolutions.push(lod.resolution)\n- }\n- var start = this.getContainingTileCoords(upperLeft, lod.resolution);\n- lod.startTileCol = start.x;\n- lod.startTileRow = start.y;\n- var end = this.getContainingTileCoords(bottomRight, lod.resolution);\n- lod.endTileCol = end.x;\n- lod.endTileRow = end.y;\n- this.lods.push(lod)\n- }\n- }\n- this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);\n- this.serverResolutions = this.resolutions;\n- if (this.overrideDPI && info.tileInfo.dpi) {\n- OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi\n- }\n- }\n- }\n+OpenLayers.Easing = {\n+ CLASS_NAME: \"OpenLayers.Easing\"\n+};\n+OpenLayers.Easing.Linear = {\n+ easeIn: function(t, b, c, d) {\n+ return c * t / d + b\n },\n- getContainingTileCoords: function(point, res) {\n- return new OpenLayers.Pixel(Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0))\n+ easeOut: function(t, b, c, d) {\n+ return c * t / d + b\n },\n- calculateMaxExtentWithLOD: function(lod) {\n- var numTileCols = lod.endTileCol - lod.startTileCol + 1;\n- var numTileRows = lod.endTileRow - lod.startTileRow + 1;\n- var minX = this.tileOrigin.lon + lod.startTileCol * this.tileSize.w * lod.resolution;\n- var maxX = minX + numTileCols * this.tileSize.w * lod.resolution;\n- var maxY = this.tileOrigin.lat - lod.startTileRow * this.tileSize.h * lod.resolution;\n- var minY = maxY - numTileRows * this.tileSize.h * lod.resolution;\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY)\n+ easeInOut: function(t, b, c, d) {\n+ return c * t / d + b\n },\n- calculateMaxExtentWithExtent: function(extent, res) {\n- var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);\n- var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);\n- var start = this.getContainingTileCoords(upperLeft, res);\n- var end = this.getContainingTileCoords(bottomRight, res);\n- var lod = {\n- resolution: res,\n- startTileCol: start.x,\n- startTileRow: start.y,\n- endTileCol: end.x,\n- endTileRow: end.y\n- };\n- return this.calculateMaxExtentWithLOD(lod)\n+ CLASS_NAME: \"OpenLayers.Easing.Linear\"\n+};\n+OpenLayers.Easing.Expo = {\n+ easeIn: function(t, b, c, d) {\n+ return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b\n },\n- getUpperLeftTileCoord: function(res) {\n- var upperLeft = new OpenLayers.Geometry.Point(this.maxExtent.left, this.maxExtent.top);\n- return this.getContainingTileCoords(upperLeft, res)\n+ easeOut: function(t, b, c, d) {\n+ return t == d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b\n },\n- getLowerRightTileCoord: function(res) {\n- var bottomRight = new OpenLayers.Geometry.Point(this.maxExtent.right, this.maxExtent.bottom);\n- return this.getContainingTileCoords(bottomRight, res)\n+ easeInOut: function(t, b, c, d) {\n+ if (t == 0) return b;\n+ if (t == d) return b + c;\n+ if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;\n+ return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b\n },\n- getMaxExtentForResolution: function(res) {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n- var numTileCols = end.x - start.x + 1;\n- var numTileRows = end.y - start.y + 1;\n- var minX = this.tileOrigin.lon + start.x * this.tileSize.w * res;\n- var maxX = minX + numTileCols * this.tileSize.w * res;\n- var maxY = this.tileOrigin.lat - start.y * this.tileSize.h * res;\n- var minY = maxY - numTileRows * this.tileSize.h * res;\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY)\n+ CLASS_NAME: \"OpenLayers.Easing.Expo\"\n+};\n+OpenLayers.Easing.Quad = {\n+ easeIn: function(t, b, c, d) {\n+ return c * (t /= d) * t + b\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options)\n+ easeOut: function(t, b, c, d) {\n+ return -c * (t /= d) * (t - 2) + b\n+ },\n+ easeInOut: function(t, b, c, d) {\n+ if ((t /= d / 2) < 1) return c / 2 * t * t + b;\n+ return -c / 2 * (--t * (t - 2) - 1) + b\n+ },\n+ CLASS_NAME: \"OpenLayers.Easing.Quad\"\n+};\n+OpenLayers.Projection = OpenLayers.Class({\n+ proj: null,\n+ projCode: null,\n+ titleRegEx: /\\+title=[^\\+]*/,\n+ initialize: function(projCode, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.projCode = projCode;\n+ if (typeof Proj4js == \"object\") {\n+ this.proj = new Proj4js.Proj(projCode)\n }\n- return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj])\n },\n- initGriddedTiles: function(bounds) {\n- delete this._tileOrigin;\n- OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments)\n+ getCode: function() {\n+ return this.proj ? this.proj.srsCode : this.projCode\n },\n- getMaxExtent: function() {\n- var resolution = this.map.getResolution();\n- return this.maxExtent = this.getMaxExtentForResolution(resolution)\n+ getUnits: function() {\n+ return this.proj ? this.proj.units : null\n },\n- getTileOrigin: function() {\n- if (!this._tileOrigin) {\n- var extent = this.getMaxExtent();\n- this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom)\n- }\n- return this._tileOrigin\n+ toString: function() {\n+ return this.getCode()\n },\n- getURL: function(bounds) {\n- var res = this.getResolution();\n- var originTileX = this.tileOrigin.lon + res * this.tileSize.w / 2;\n- var originTileY = this.tileOrigin.lat - res * this.tileSize.h / 2;\n- var center = bounds.getCenterLonLat();\n- var point = {\n- x: center.lon,\n- y: center.lat\n- };\n- var x = Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)));\n- var y = Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)));\n- var z = this.map.getZoom();\n- if (this.lods) {\n- var lod = this.lods[this.map.getZoom()];\n- if (x < lod.startTileCol || x > lod.endTileCol || (y < lod.startTileRow || y > lod.endTileRow)) {\n- return null\n+ equals: function(projection) {\n+ var p = projection,\n+ equals = false;\n+ if (p) {\n+ if (!(p instanceof OpenLayers.Projection)) {\n+ p = new OpenLayers.Projection(p)\n }\n- } else {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n- if (x < start.x || x >= end.x || (y < start.y || y >= end.y)) {\n- return null\n+ if (typeof Proj4js == \"object\" && this.proj.defData && p.proj.defData) {\n+ equals = this.proj.defData.replace(this.titleRegEx, \"\") == p.proj.defData.replace(this.titleRegEx, \"\")\n+ } else if (p.getCode) {\n+ var source = this.getCode(),\n+ target = p.getCode();\n+ equals = source == target || !!OpenLayers.Projection.transforms[source] && OpenLayers.Projection.transforms[source][target] === OpenLayers.Projection.nullTransform\n }\n }\n- var url = this.url;\n- var s = \"\" + x + y + z;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(s, url)\n- }\n- if (this.useArcGISServer) {\n- url = url + \"/tile/${z}/${y}/${x}\"\n- } else {\n- x = \"C\" + OpenLayers.Number.zeroPad(x, 8, 16);\n- y = \"R\" + OpenLayers.Number.zeroPad(y, 8, 16);\n- z = \"L\" + OpenLayers.Number.zeroPad(z, 2, 10);\n- url = url + \"/${z}/${y}/${x}.\" + this.type\n- }\n- url = OpenLayers.String.format(url, {\n- x: x,\n- y: y,\n- z: z\n- });\n- return OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(this.params))\n+ return equals\n },\n- CLASS_NAME: \"OpenLayers.Layer.ArcGISCache\"\n+ destroy: function() {\n+ delete this.proj;\n+ delete this.projCode\n+ },\n+ CLASS_NAME: \"OpenLayers.Projection\"\n });\n-OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {\n- fontStyleKeys: [\"antialiasing\", \"blockout\", \"font\", \"fontcolor\", \"fontsize\", \"fontstyle\", \"glowing\", \"interval\", \"outline\", \"printmode\", \"shadow\", \"transparency\"],\n- request: null,\n- response: null,\n- initialize: function(options) {\n- this.request = new OpenLayers.Format.ArcXML.Request;\n- this.response = new OpenLayers.Format.ArcXML.Response;\n- if (options) {\n- if (options.requesttype == \"feature\") {\n- this.request.get_image = null;\n- var qry = this.request.get_feature.query;\n- this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);\n- this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);\n- if (options.polygon) {\n- qry.isspatial = true;\n- qry.spatialfilter.polygon = options.polygon\n- } else if (options.envelope) {\n- qry.isspatial = true;\n- qry.spatialfilter.envelope = {\n- minx: 0,\n- miny: 0,\n- maxx: 0,\n- maxy: 0\n- };\n- this.parseEnvelope(qry.spatialfilter.envelope, options.envelope)\n- }\n- } else if (options.requesttype == \"image\") {\n- this.request.get_feature = null;\n- var props = this.request.get_image.properties;\n- this.parseEnvelope(props.envelope, options.envelope);\n- this.addLayers(props.layerlist, options.layers);\n- this.addImageSize(props.imagesize, options.tileSize);\n- this.addCoordSys(props.featurecoordsys, options.featureCoordSys);\n- this.addCoordSys(props.filtercoordsys, options.filterCoordSys)\n- } else {\n- this.request = null\n- }\n- }\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n+OpenLayers.Projection.transforms = {};\n+OpenLayers.Projection.defaults = {\n+ \"EPSG:4326\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90],\n+ yx: true\n },\n- parseEnvelope: function(env, arr) {\n- if (arr && arr.length == 4) {\n- env.minx = arr[0];\n- env.miny = arr[1];\n- env.maxx = arr[2];\n- env.maxy = arr[3]\n- }\n+ \"CRS:84\": {\n+ units: \"degrees\",\n+ maxExtent: [-180, -90, 180, 90]\n },\n- addLayers: function(ll, lyrs) {\n- for (var lind = 0, len = lyrs.length; lind < len; lind++) {\n- ll.push(lyrs[lind])\n+ \"EPSG:900913\": {\n+ units: \"m\",\n+ maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]\n+ }\n+};\n+OpenLayers.Projection.addTransform = function(from, to, method) {\n+ if (method === OpenLayers.Projection.nullTransform) {\n+ var defaults = OpenLayers.Projection.defaults[from];\n+ if (defaults && !OpenLayers.Projection.defaults[to]) {\n+ OpenLayers.Projection.defaults[to] = defaults\n }\n- },\n- addImageSize: function(imsize, olsize) {\n- if (olsize !== null) {\n- imsize.width = olsize.w;\n- imsize.height = olsize.h;\n- imsize.printwidth = olsize.w;\n- imsize.printheight = olsize.h\n+ }\n+ if (!OpenLayers.Projection.transforms[from]) {\n+ OpenLayers.Projection.transforms[from] = {}\n+ }\n+ OpenLayers.Projection.transforms[from][to] = method\n+};\n+OpenLayers.Projection.transform = function(point, source, dest) {\n+ if (source && dest) {\n+ if (!(source instanceof OpenLayers.Projection)) {\n+ source = new OpenLayers.Projection(source)\n }\n- },\n- addCoordSys: function(featOrFilt, fsys) {\n- if (typeof fsys == \"string\") {\n- featOrFilt.id = parseInt(fsys);\n- featOrFilt.string = fsys\n- } else if (typeof fsys == \"object\" && fsys.proj !== null) {\n- featOrFilt.id = fsys.proj.srsProjNumber;\n- featOrFilt.string = fsys.proj.srsCode\n- } else {\n- featOrFilt = fsys\n+ if (!(dest instanceof OpenLayers.Projection)) {\n+ dest = new OpenLayers.Projection(dest)\n }\n- },\n- iserror: function(data) {\n- var ret = null;\n- if (!data) {\n- ret = this.response.error !== \"\"\n+ if (source.proj && dest.proj) {\n+ point = Proj4js.transform(source.proj, dest.proj, point)\n } else {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- var errorNodes = data.documentElement.getElementsByTagName(\"ERROR\");\n- ret = errorNodes !== null && errorNodes.length > 0\n- }\n- return ret\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- var arcNode = null;\n- if (data && data.documentElement) {\n- if (data.documentElement.nodeName == \"ARCXML\") {\n- arcNode = data.documentElement\n- } else {\n- arcNode = data.documentElement.getElementsByTagName(\"ARCXML\")[0]\n+ var sourceCode = source.getCode();\n+ var destCode = dest.getCode();\n+ var transforms = OpenLayers.Projection.transforms;\n+ if (transforms[sourceCode] && transforms[sourceCode][destCode]) {\n+ transforms[sourceCode][destCode](point)\n }\n }\n- if (!arcNode || arcNode.firstChild.nodeName === \"parsererror\") {\n- var error, source;\n- try {\n- error = data.firstChild.nodeValue;\n- source = data.firstChild.childNodes[1].firstChild.nodeValue\n- } catch (err) {}\n- throw {\n- message: \"Error parsing the ArcXML request\",\n- error: error,\n- source: source\n+ }\n+ return point\n+};\n+OpenLayers.Projection.nullTransform = function(point) {\n+ return point\n+};\n+(function() {\n+ var pole = 20037508.34;\n+\n+ function inverseMercator(xy) {\n+ xy.x = 180 * xy.x / pole;\n+ xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2);\n+ return xy\n+ }\n+\n+ function forwardMercator(xy) {\n+ xy.x = xy.x * pole / 180;\n+ var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;\n+ xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));\n+ return xy\n+ }\n+\n+ function map(base, codes) {\n+ var add = OpenLayers.Projection.addTransform;\n+ var same = OpenLayers.Projection.nullTransform;\n+ var i, len, code, other, j;\n+ for (i = 0, len = codes.length; i < len; ++i) {\n+ code = codes[i];\n+ add(base, code, forwardMercator);\n+ add(code, base, inverseMercator);\n+ for (j = i + 1; j < len; ++j) {\n+ other = codes[j];\n+ add(code, other, same);\n+ add(other, code, same)\n }\n }\n- var response = this.parseResponse(arcNode);\n- return response\n+ }\n+ var mercator = [\"EPSG:900913\", \"EPSG:3857\", \"EPSG:102113\", \"EPSG:102100\"],\n+ geographic = [\"CRS:84\", \"urn:ogc:def:crs:EPSG:6.6:4326\", \"EPSG:4326\"],\n+ i;\n+ for (i = mercator.length - 1; i >= 0; --i) {\n+ map(mercator[i], geographic)\n+ }\n+ for (i = geographic.length - 1; i >= 0; --i) {\n+ map(geographic[i], mercator)\n+ }\n+})();\n+OpenLayers.Map = OpenLayers.Class({\n+ Z_INDEX_BASE: {\n+ BaseLayer: 100,\n+ Overlay: 325,\n+ Feature: 725,\n+ Popup: 750,\n+ Control: 1e3\n },\n- write: function(request) {\n- if (!request) {\n- request = this.request\n+ id: null,\n+ fractionalZoom: false,\n+ events: null,\n+ allOverlays: false,\n+ div: null,\n+ dragging: false,\n+ size: null,\n+ viewPortDiv: null,\n+ layerContainerOrigin: null,\n+ layerContainerDiv: null,\n+ layers: null,\n+ controls: null,\n+ popups: null,\n+ baseLayer: null,\n+ center: null,\n+ resolution: null,\n+ zoom: 0,\n+ panRatio: 1.5,\n+ options: null,\n+ tileSize: null,\n+ projection: \"EPSG:4326\",\n+ units: null,\n+ resolutions: null,\n+ maxResolution: null,\n+ minResolution: null,\n+ maxScale: null,\n+ minScale: null,\n+ maxExtent: null,\n+ minExtent: null,\n+ restrictedExtent: null,\n+ numZoomLevels: 16,\n+ theme: null,\n+ displayProjection: null,\n+ fallThrough: false,\n+ autoUpdateSize: true,\n+ eventListeners: null,\n+ panTween: null,\n+ panMethod: OpenLayers.Easing.Expo.easeOut,\n+ panDuration: 50,\n+ zoomTween: null,\n+ zoomMethod: OpenLayers.Easing.Quad.easeOut,\n+ zoomDuration: 20,\n+ paddingForPopups: null,\n+ layerContainerOriginPx: null,\n+ minPx: null,\n+ maxPx: null,\n+ initialize: function(div, options) {\n+ if (arguments.length === 1 && typeof div === \"object\") {\n+ options = div;\n+ div = options && options.div\n }\n- var root = this.createElementNS(\"\", \"ARCXML\");\n- root.setAttribute(\"version\", \"1.1\");\n- var reqElem = this.createElementNS(\"\", \"REQUEST\");\n- if (request.get_image != null) {\n- var getElem = this.createElementNS(\"\", \"GET_IMAGE\");\n- reqElem.appendChild(getElem);\n- var propElem = this.createElementNS(\"\", \"PROPERTIES\");\n- getElem.appendChild(propElem);\n- var props = request.get_image.properties;\n- if (props.featurecoordsys != null) {\n- var feat = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n- propElem.appendChild(feat);\n- if (props.featurecoordsys.id === 0) {\n- feat.setAttribute(\"string\", props.featurecoordsys[\"string\"])\n- } else {\n- feat.setAttribute(\"id\", props.featurecoordsys.id)\n- }\n+ this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, OpenLayers.Map.TILE_HEIGHT);\n+ this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);\n+ this.theme = OpenLayers._getScriptLocation() + \"theme/default/style.css\";\n+ this.options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.extend(this, options);\n+ var projCode = this.projection instanceof OpenLayers.Projection ? this.projection.projCode : this.projection;\n+ OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);\n+ if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {\n+ this.maxExtent = new OpenLayers.Bounds(this.maxExtent)\n+ }\n+ if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {\n+ this.minExtent = new OpenLayers.Bounds(this.minExtent)\n+ }\n+ if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {\n+ this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent)\n+ }\n+ if (this.center && !(this.center instanceof OpenLayers.LonLat)) {\n+ this.center = new OpenLayers.LonLat(this.center)\n+ }\n+ this.layers = [];\n+ this.id = OpenLayers.Util.createUniqueID(\"OpenLayers.Map_\");\n+ this.div = OpenLayers.Util.getElement(div);\n+ if (!this.div) {\n+ this.div = document.createElement(\"div\");\n+ this.div.style.height = \"1px\";\n+ this.div.style.width = \"1px\"\n+ }\n+ OpenLayers.Element.addClass(this.div, \"olMap\");\n+ var id = this.id + \"_OpenLayers_ViewPort\";\n+ this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null, \"relative\", null, \"hidden\");\n+ this.viewPortDiv.style.width = \"100%\";\n+ this.viewPortDiv.style.height = \"100%\";\n+ this.viewPortDiv.className = \"olMapViewport\";\n+ this.div.appendChild(this.viewPortDiv);\n+ this.events = new OpenLayers.Events(this, this.viewPortDiv, null, this.fallThrough, {\n+ includeXY: true\n+ });\n+ if (OpenLayers.TileManager && this.tileManager !== null) {\n+ if (!(this.tileManager instanceof OpenLayers.TileManager)) {\n+ this.tileManager = new OpenLayers.TileManager(this.tileManager)\n }\n- if (props.filtercoordsys != null) {\n- var filt = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n- propElem.appendChild(filt);\n- if (props.filtercoordsys.id === 0) {\n- filt.setAttribute(\"string\", props.filtercoordsys.string)\n- } else {\n- filt.setAttribute(\"id\", props.filtercoordsys.id)\n+ this.tileManager.addMap(this)\n+ }\n+ id = this.id + \"_OpenLayers_Container\";\n+ this.layerContainerDiv = OpenLayers.Util.createDiv(id);\n+ this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE[\"Popup\"] - 1;\n+ this.layerContainerOriginPx = {\n+ x: 0,\n+ y: 0\n+ };\n+ this.applyTransform();\n+ this.viewPortDiv.appendChild(this.layerContainerDiv);\n+ this.updateSize();\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n+ }\n+ if (this.autoUpdateSize === true) {\n+ this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, this);\n+ OpenLayers.Event.observe(window, \"resize\", this.updateSizeDestroy)\n+ }\n+ if (this.theme) {\n+ var addNode = true;\n+ var nodes = document.getElementsByTagName(\"link\");\n+ for (var i = 0, len = nodes.length; i < len; ++i) {\n+ if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href, this.theme)) {\n+ addNode = false;\n+ break\n }\n }\n- if (props.envelope != null) {\n- var env = this.createElementNS(\"\", \"ENVELOPE\");\n- propElem.appendChild(env);\n- env.setAttribute(\"minx\", props.envelope.minx);\n- env.setAttribute(\"miny\", props.envelope.miny);\n- env.setAttribute(\"maxx\", props.envelope.maxx);\n- env.setAttribute(\"maxy\", props.envelope.maxy)\n- }\n- var imagesz = this.createElementNS(\"\", \"IMAGESIZE\");\n- propElem.appendChild(imagesz);\n- imagesz.setAttribute(\"height\", props.imagesize.height);\n- imagesz.setAttribute(\"width\", props.imagesize.width);\n- if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) {\n- imagesz.setAttribute(\"printheight\", props.imagesize.printheight);\n- imagesz.setArrtibute(\"printwidth\", props.imagesize.printwidth)\n+ if (addNode) {\n+ var cssNode = document.createElement(\"link\");\n+ cssNode.setAttribute(\"rel\", \"stylesheet\");\n+ cssNode.setAttribute(\"type\", \"text/css\");\n+ cssNode.setAttribute(\"href\", this.theme);\n+ document.getElementsByTagName(\"head\")[0].appendChild(cssNode)\n }\n- if (props.background != null) {\n- var backgrnd = this.createElementNS(\"\", \"BACKGROUND\");\n- propElem.appendChild(backgrnd);\n- backgrnd.setAttribute(\"color\", props.background.color.r + \",\" + props.background.color.g + \",\" + props.background.color.b);\n- if (props.background.transcolor !== null) {\n- backgrnd.setAttribute(\"transcolor\", props.background.transcolor.r + \",\" + props.background.transcolor.g + \",\" + props.background.transcolor.b)\n+ }\n+ if (this.controls == null) {\n+ this.controls = [];\n+ if (OpenLayers.Control != null) {\n+ if (OpenLayers.Control.Navigation) {\n+ this.controls.push(new OpenLayers.Control.Navigation)\n+ } else if (OpenLayers.Control.TouchNavigation) {\n+ this.controls.push(new OpenLayers.Control.TouchNavigation)\n }\n- }\n- if (props.layerlist != null && props.layerlist.length > 0) {\n- var layerlst = this.createElementNS(\"\", \"LAYERLIST\");\n- propElem.appendChild(layerlst);\n- for (var ld = 0; ld < props.layerlist.length; ld++) {\n- var ldef = this.createElementNS(\"\", \"LAYERDEF\");\n- layerlst.appendChild(ldef);\n- ldef.setAttribute(\"id\", props.layerlist[ld].id);\n- ldef.setAttribute(\"visible\", props.layerlist[ld].visible);\n- if (typeof props.layerlist[ld].query == \"object\") {\n- var query = props.layerlist[ld].query;\n- if (query.where.length < 0) {\n- continue\n- }\n- var queryElem = null;\n- if (typeof query.spatialfilter == \"boolean\" && query.spatialfilter) {\n- queryElem = this.createElementNS(\"\", \"SPATIALQUERY\")\n- } else {\n- queryElem = this.createElementNS(\"\", \"QUERY\")\n- }\n- queryElem.setAttribute(\"where\", query.where);\n- if (typeof query.accuracy == \"number\" && query.accuracy > 0) {\n- queryElem.setAttribute(\"accuracy\", query.accuracy)\n- }\n- if (typeof query.featurelimit == \"number\" && query.featurelimit < 2e3) {\n- queryElem.setAttribute(\"featurelimit\", query.featurelimit)\n- }\n- if (typeof query.subfields == \"string\" && query.subfields != \"#ALL#\") {\n- queryElem.setAttribute(\"subfields\", query.subfields)\n- }\n- if (typeof query.joinexpression == \"string\" && query.joinexpression.length > 0) {\n- queryElem.setAttribute(\"joinexpression\", query.joinexpression)\n- }\n- if (typeof query.jointables == \"string\" && query.jointables.length > 0) {\n- queryElem.setAttribute(\"jointables\", query.jointables)\n- }\n- ldef.appendChild(queryElem)\n- }\n- if (typeof props.layerlist[ld].renderer == \"object\") {\n- this.addRenderer(ldef, props.layerlist[ld].renderer)\n- }\n+ if (OpenLayers.Control.Zoom) {\n+ this.controls.push(new OpenLayers.Control.Zoom)\n+ } else if (OpenLayers.Control.PanZoom) {\n+ this.controls.push(new OpenLayers.Control.PanZoom)\n+ }\n+ if (OpenLayers.Control.ArgParser) {\n+ this.controls.push(new OpenLayers.Control.ArgParser)\n+ }\n+ if (OpenLayers.Control.Attribution) {\n+ this.controls.push(new OpenLayers.Control.Attribution)\n }\n }\n- } else if (request.get_feature != null) {\n- var getElem = this.createElementNS(\"\", \"GET_FEATURES\");\n- getElem.setAttribute(\"outputmode\", \"newxml\");\n- getElem.setAttribute(\"checkesc\", \"true\");\n- if (request.get_feature.geometry) {\n- getElem.setAttribute(\"geometry\", request.get_feature.geometry)\n- } else {\n- getElem.setAttribute(\"geometry\", \"false\")\n- }\n- if (request.get_feature.compact) {\n- getElem.setAttribute(\"compact\", request.get_feature.compact)\n+ }\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.addControlToMap(this.controls[i])\n+ }\n+ this.popups = [];\n+ this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);\n+ OpenLayers.Event.observe(window, \"unload\", this.unloadDestroy);\n+ if (options && options.layers) {\n+ delete this.center;\n+ delete this.zoom;\n+ this.addLayers(options.layers);\n+ if (options.center && !this.getCenter()) {\n+ this.setCenter(options.center, options.zoom)\n }\n- if (request.get_feature.featurelimit == \"number\") {\n- getElem.setAttribute(\"featurelimit\", request.get_feature.featurelimit)\n+ }\n+ if (this.panMethod) {\n+ this.panTween = new OpenLayers.Tween(this.panMethod)\n+ }\n+ if (this.zoomMethod && this.applyTransform.transform) {\n+ this.zoomTween = new OpenLayers.Tween(this.zoomMethod)\n+ }\n+ },\n+ getViewport: function() {\n+ return this.viewPortDiv\n+ },\n+ render: function(div) {\n+ this.div = OpenLayers.Util.getElement(div);\n+ OpenLayers.Element.addClass(this.div, \"olMap\");\n+ this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);\n+ this.div.appendChild(this.viewPortDiv);\n+ this.updateSize()\n+ },\n+ unloadDestroy: null,\n+ updateSizeDestroy: null,\n+ destroy: function() {\n+ if (!this.unloadDestroy) {\n+ return false\n+ }\n+ if (this.panTween) {\n+ this.panTween.stop();\n+ this.panTween = null\n+ }\n+ if (this.zoomTween) {\n+ this.zoomTween.stop();\n+ this.zoomTween = null\n+ }\n+ OpenLayers.Event.stopObserving(window, \"unload\", this.unloadDestroy);\n+ this.unloadDestroy = null;\n+ if (this.updateSizeDestroy) {\n+ OpenLayers.Event.stopObserving(window, \"resize\", this.updateSizeDestroy)\n+ }\n+ this.paddingForPopups = null;\n+ if (this.controls != null) {\n+ for (var i = this.controls.length - 1; i >= 0; --i) {\n+ this.controls[i].destroy()\n }\n- getElem.setAttribute(\"globalenvelope\", \"true\");\n- reqElem.appendChild(getElem);\n- if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {\n- var lyrElem = this.createElementNS(\"\", \"LAYER\");\n- lyrElem.setAttribute(\"id\", request.get_feature.layer);\n- getElem.appendChild(lyrElem)\n+ this.controls = null\n+ }\n+ if (this.layers != null) {\n+ for (var i = this.layers.length - 1; i >= 0; --i) {\n+ this.layers[i].destroy(false)\n }\n- var fquery = request.get_feature.query;\n- if (fquery != null) {\n- var qElem = null;\n- if (fquery.isspatial) {\n- qElem = this.createElementNS(\"\", \"SPATIALQUERY\")\n- } else {\n- qElem = this.createElementNS(\"\", \"QUERY\")\n- }\n- getElem.appendChild(qElem);\n- if (typeof fquery.accuracy == \"number\") {\n- qElem.setAttribute(\"accuracy\", fquery.accuracy)\n- }\n- if (fquery.featurecoordsys != null) {\n- var fcsElem1 = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n- if (fquery.featurecoordsys.id == 0) {\n- fcsElem1.setAttribute(\"string\", fquery.featurecoordsys.string)\n- } else {\n- fcsElem1.setAttribute(\"id\", fquery.featurecoordsys.id)\n- }\n- qElem.appendChild(fcsElem1)\n- }\n- if (fquery.filtercoordsys != null) {\n- var fcsElem2 = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n- if (fquery.filtercoordsys.id === 0) {\n- fcsElem2.setAttribute(\"string\", fquery.filtercoordsys.string)\n- } else {\n- fcsElem2.setAttribute(\"id\", fquery.filtercoordsys.id)\n- }\n- qElem.appendChild(fcsElem2)\n- }\n- if (fquery.buffer > 0) {\n- var bufElem = this.createElementNS(\"\", \"BUFFER\");\n- bufElem.setAttribute(\"distance\", fquery.buffer);\n- qElem.appendChild(bufElem)\n- }\n- if (fquery.isspatial) {\n- var spfElem = this.createElementNS(\"\", \"SPATIALFILTER\");\n- spfElem.setAttribute(\"relation\", fquery.spatialfilter.relation);\n- qElem.appendChild(spfElem);\n- if (fquery.spatialfilter.envelope) {\n- var envElem = this.createElementNS(\"\", \"ENVELOPE\");\n- envElem.setAttribute(\"minx\", fquery.spatialfilter.envelope.minx);\n- envElem.setAttribute(\"miny\", fquery.spatialfilter.envelope.miny);\n- envElem.setAttribute(\"maxx\", fquery.spatialfilter.envelope.maxx);\n- envElem.setAttribute(\"maxy\", fquery.spatialfilter.envelope.maxy);\n- spfElem.appendChild(envElem)\n- } else if (typeof fquery.spatialfilter.polygon == \"object\") {\n- spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon))\n- }\n- }\n- if (fquery.where != null && fquery.where.length > 0) {\n- qElem.setAttribute(\"where\", fquery.where)\n- }\n+ this.layers = null\n+ }\n+ if (this.viewPortDiv && this.viewPortDiv.parentNode) {\n+ this.viewPortDiv.parentNode.removeChild(this.viewPortDiv)\n+ }\n+ this.viewPortDiv = null;\n+ if (this.tileManager) {\n+ this.tileManager.removeMap(this);\n+ this.tileManager = null\n+ }\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners);\n+ this.eventListeners = null\n+ }\n+ this.events.destroy();\n+ this.events = null;\n+ this.options = null\n+ },\n+ setOptions: function(options) {\n+ var updatePxExtent = this.minPx && options.restrictedExtent != this.restrictedExtent;\n+ OpenLayers.Util.extend(this, options);\n+ updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {\n+ forceZoomChange: true\n+ })\n+ },\n+ getTileSize: function() {\n+ return this.tileSize\n+ },\n+ getBy: function(array, property, match) {\n+ var test = typeof match.test == \"function\";\n+ var found = OpenLayers.Array.filter(this[array], function(item) {\n+ return item[property] == match || test && match.test(item[property])\n+ });\n+ return found\n+ },\n+ getLayersBy: function(property, match) {\n+ return this.getBy(\"layers\", property, match)\n+ },\n+ getLayersByName: function(match) {\n+ return this.getLayersBy(\"name\", match)\n+ },\n+ getLayersByClass: function(match) {\n+ return this.getLayersBy(\"CLASS_NAME\", match)\n+ },\n+ getControlsBy: function(property, match) {\n+ return this.getBy(\"controls\", property, match)\n+ },\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match)\n+ },\n+ getLayer: function(id) {\n+ var foundLayer = null;\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer.id == id) {\n+ foundLayer = layer;\n+ break\n }\n }\n- root.appendChild(reqElem);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [root])\n+ return foundLayer\n },\n- addGroupRenderer: function(ldef, toprenderer) {\n- var topRelem = this.createElementNS(\"\", \"GROUPRENDERER\");\n- ldef.appendChild(topRelem);\n- for (var rind = 0; rind < toprenderer.length; rind++) {\n- var renderer = toprenderer[rind];\n- this.addRenderer(topRelem, renderer)\n+ setLayerZIndex: function(layer, zIdx) {\n+ layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer ? \"BaseLayer\" : \"Overlay\"] + zIdx * 5)\n+ },\n+ resetLayersZIndex: function() {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ this.setLayerZIndex(layer, i)\n }\n },\n- addRenderer: function(topRelem, renderer) {\n- if (OpenLayers.Util.isArray(renderer)) {\n- this.addGroupRenderer(topRelem, renderer)\n+ addLayer: function(layer) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ if (this.layers[i] == layer) {\n+ return false\n+ }\n+ }\n+ if (this.events.triggerEvent(\"preaddlayer\", {\n+ layer: layer\n+ }) === false) {\n+ return false\n+ }\n+ if (this.allOverlays) {\n+ layer.isBaseLayer = false\n+ }\n+ layer.div.className = \"olLayerDiv\";\n+ layer.div.style.overflow = \"\";\n+ this.setLayerZIndex(layer, this.layers.length);\n+ if (layer.isFixed) {\n+ this.viewPortDiv.appendChild(layer.div)\n } else {\n- var renderElem = this.createElementNS(\"\", renderer.type.toUpperCase() + \"RENDERER\");\n- topRelem.appendChild(renderElem);\n- if (renderElem.tagName == \"VALUEMAPRENDERER\") {\n- this.addValueMapRenderer(renderElem, renderer)\n- } else if (renderElem.tagName == \"VALUEMAPLABELRENDERER\") {\n- this.addValueMapLabelRenderer(renderElem, renderer)\n- } else if (renderElem.tagName == \"SIMPLELABELRENDERER\") {\n- this.addSimpleLabelRenderer(renderElem, renderer)\n- } else if (renderElem.tagName == \"SCALEDEPENDENTRENDERER\") {\n- this.addScaleDependentRenderer(renderElem, renderer)\n+ this.layerContainerDiv.appendChild(layer.div)\n+ }\n+ this.layers.push(layer);\n+ layer.setMap(this);\n+ if (layer.isBaseLayer || this.allOverlays && !this.baseLayer) {\n+ if (this.baseLayer == null) {\n+ this.setBaseLayer(layer)\n+ } else {\n+ layer.setVisibility(false)\n }\n+ } else {\n+ layer.redraw()\n }\n+ this.events.triggerEvent(\"addlayer\", {\n+ layer: layer\n+ });\n+ layer.events.triggerEvent(\"added\", {\n+ map: this,\n+ layer: layer\n+ });\n+ layer.afterAdd();\n+ return true\n },\n- addScaleDependentRenderer: function(renderElem, renderer) {\n- if (typeof renderer.lower == \"string\" || typeof renderer.lower == \"number\") {\n- renderElem.setAttribute(\"lower\", renderer.lower)\n- }\n- if (typeof renderer.upper == \"string\" || typeof renderer.upper == \"number\") {\n- renderElem.setAttribute(\"upper\", renderer.upper)\n+ addLayers: function(layers) {\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ this.addLayer(layers[i])\n }\n- this.addRenderer(renderElem, renderer.renderer)\n },\n- addValueMapLabelRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n- renderElem.setAttribute(\"labelfield\", renderer.labelfield);\n- if (typeof renderer.exacts == \"object\") {\n- for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n- var exact = renderer.exacts[ext];\n- var eelem = this.createElementNS(\"\", \"EXACT\");\n- if (typeof exact.value == \"string\") {\n- eelem.setAttribute(\"value\", exact.value)\n- }\n- if (typeof exact.label == \"string\") {\n- eelem.setAttribute(\"label\", exact.label)\n- }\n- if (typeof exact.method == \"string\") {\n- eelem.setAttribute(\"method\", exact.method)\n- }\n- renderElem.appendChild(eelem);\n- if (typeof exact.symbol == \"object\") {\n- var selem = null;\n- if (exact.symbol.type == \"text\") {\n- selem = this.createElementNS(\"\", \"TEXTSYMBOL\")\n- }\n- if (selem != null) {\n- var keys = this.fontStyleKeys;\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (exact.symbol[key]) {\n- selem.setAttribute(key, exact.symbol[key])\n- }\n- }\n- eelem.appendChild(selem)\n+ removeLayer: function(layer, setNewBaseLayer) {\n+ if (this.events.triggerEvent(\"preremovelayer\", {\n+ layer: layer\n+ }) === false) {\n+ return\n+ }\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true\n+ }\n+ if (layer.isFixed) {\n+ this.viewPortDiv.removeChild(layer.div)\n+ } else {\n+ this.layerContainerDiv.removeChild(layer.div)\n+ }\n+ OpenLayers.Util.removeItem(this.layers, layer);\n+ layer.removeMap(this);\n+ layer.map = null;\n+ if (this.baseLayer == layer) {\n+ this.baseLayer = null;\n+ if (setNewBaseLayer) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var iLayer = this.layers[i];\n+ if (iLayer.isBaseLayer || this.allOverlays) {\n+ this.setBaseLayer(iLayer);\n+ break\n }\n }\n }\n }\n+ this.resetLayersZIndex();\n+ this.events.triggerEvent(\"removelayer\", {\n+ layer: layer\n+ });\n+ layer.events.triggerEvent(\"removed\", {\n+ map: this,\n+ layer: layer\n+ })\n },\n- addValueMapRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n- if (typeof renderer.ranges == \"object\") {\n- for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) {\n- var range = renderer.ranges[rng];\n- var relem = this.createElementNS(\"\", \"RANGE\");\n- relem.setAttribute(\"lower\", range.lower);\n- relem.setAttribute(\"upper\", range.upper);\n- renderElem.appendChild(relem);\n- if (typeof range.symbol == \"object\") {\n- var selem = null;\n- if (range.symbol.type == \"simplepolygon\") {\n- selem = this.createElementNS(\"\", \"SIMPLEPOLYGONSYMBOL\")\n- }\n- if (selem != null) {\n- if (typeof range.symbol.boundarycolor == \"string\") {\n- selem.setAttribute(\"boundarycolor\", range.symbol.boundarycolor)\n- }\n- if (typeof range.symbol.fillcolor == \"string\") {\n- selem.setAttribute(\"fillcolor\", range.symbol.fillcolor)\n- }\n- if (typeof range.symbol.filltransparency == \"number\") {\n- selem.setAttribute(\"filltransparency\", range.symbol.filltransparency)\n- }\n- relem.appendChild(selem)\n- }\n- }\n+ getNumLayers: function() {\n+ return this.layers.length\n+ },\n+ getLayerIndex: function(layer) {\n+ return OpenLayers.Util.indexOf(this.layers, layer)\n+ },\n+ setLayerIndex: function(layer, idx) {\n+ var base = this.getLayerIndex(layer);\n+ if (idx < 0) {\n+ idx = 0\n+ } else if (idx > this.layers.length) {\n+ idx = this.layers.length\n+ }\n+ if (base != idx) {\n+ this.layers.splice(base, 1);\n+ this.layers.splice(idx, 0, layer);\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ this.setLayerZIndex(this.layers[i], i)\n }\n- } else if (typeof renderer.exacts == \"object\") {\n- for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n- var exact = renderer.exacts[ext];\n- var eelem = this.createElementNS(\"\", \"EXACT\");\n- if (typeof exact.value == \"string\") {\n- eelem.setAttribute(\"value\", exact.value)\n- }\n- if (typeof exact.label == \"string\") {\n- eelem.setAttribute(\"label\", exact.label)\n+ this.events.triggerEvent(\"changelayer\", {\n+ layer: layer,\n+ property: \"order\"\n+ });\n+ if (this.allOverlays) {\n+ if (idx === 0) {\n+ this.setBaseLayer(layer)\n+ } else if (this.baseLayer !== this.layers[0]) {\n+ this.setBaseLayer(this.layers[0])\n }\n- if (typeof exact.method == \"string\") {\n- eelem.setAttribute(\"method\", exact.method)\n+ }\n+ }\n+ },\n+ raiseLayer: function(layer, delta) {\n+ var idx = this.getLayerIndex(layer) + delta;\n+ this.setLayerIndex(layer, idx)\n+ },\n+ setBaseLayer: function(newBaseLayer) {\n+ if (newBaseLayer != this.baseLayer) {\n+ if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {\n+ var center = this.getCachedCenter();\n+ var newResolution = OpenLayers.Util.getResolutionFromScale(this.getScale(), newBaseLayer.units);\n+ if (this.baseLayer != null && !this.allOverlays) {\n+ this.baseLayer.setVisibility(false)\n }\n- renderElem.appendChild(eelem);\n- if (typeof exact.symbol == \"object\") {\n- var selem = null;\n- if (exact.symbol.type == \"simplemarker\") {\n- selem = this.createElementNS(\"\", \"SIMPLEMARKERSYMBOL\")\n- }\n- if (selem != null) {\n- if (typeof exact.symbol.antialiasing == \"string\") {\n- selem.setAttribute(\"antialiasing\", exact.symbol.antialiasing)\n- }\n- if (typeof exact.symbol.color == \"string\") {\n- selem.setAttribute(\"color\", exact.symbol.color)\n- }\n- if (typeof exact.symbol.outline == \"string\") {\n- selem.setAttribute(\"outline\", exact.symbol.outline)\n- }\n- if (typeof exact.symbol.overlap == \"string\") {\n- selem.setAttribute(\"overlap\", exact.symbol.overlap)\n- }\n- if (typeof exact.symbol.shadow == \"string\") {\n- selem.setAttribute(\"shadow\", exact.symbol.shadow)\n- }\n- if (typeof exact.symbol.transparency == \"number\") {\n- selem.setAttribute(\"transparency\", exact.symbol.transparency)\n- }\n- if (typeof exact.symbol.usecentroid == \"string\") {\n- selem.setAttribute(\"usecentroid\", exact.symbol.usecentroid)\n- }\n- if (typeof exact.symbol.width == \"number\") {\n- selem.setAttribute(\"width\", exact.symbol.width)\n- }\n- eelem.appendChild(selem)\n+ this.baseLayer = newBaseLayer;\n+ if (!this.allOverlays || this.baseLayer.visibility) {\n+ this.baseLayer.setVisibility(true);\n+ if (this.baseLayer.inRange === false) {\n+ this.baseLayer.redraw()\n }\n }\n+ if (center != null) {\n+ var newZoom = this.getZoomForResolution(newResolution || this.resolution, true);\n+ this.setCenter(center, newZoom, false, true)\n+ }\n+ this.events.triggerEvent(\"changebaselayer\", {\n+ layer: this.baseLayer\n+ })\n }\n }\n },\n- addSimpleLabelRenderer: function(renderElem, renderer) {\n- renderElem.setAttribute(\"field\", renderer.field);\n- var keys = [\"featureweight\", \"howmanylabels\", \"labelbufferratio\", \"labelpriorities\", \"labelweight\", \"linelabelposition\", \"rotationalangles\"];\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (renderer[key]) {\n- renderElem.setAttribute(key, renderer[key])\n+ addControl: function(control, px) {\n+ this.controls.push(control);\n+ this.addControlToMap(control, px)\n+ },\n+ addControls: function(controls, pixels) {\n+ var pxs = arguments.length === 1 ? [] : pixels;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var ctrl = controls[i];\n+ var px = pxs[i] ? pxs[i] : null;\n+ this.addControl(ctrl, px)\n+ }\n+ },\n+ addControlToMap: function(control, px) {\n+ control.outsideViewport = control.div != null;\n+ if (this.displayProjection && !control.displayProjection) {\n+ control.displayProjection = this.displayProjection\n+ }\n+ control.setMap(this);\n+ var div = control.draw(px);\n+ if (div) {\n+ if (!control.outsideViewport) {\n+ div.style.zIndex = this.Z_INDEX_BASE[\"Control\"] + this.controls.length;\n+ this.viewPortDiv.appendChild(div)\n }\n }\n- if (renderer.symbol.type == \"text\") {\n- var symbol = renderer.symbol;\n- var selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n- renderElem.appendChild(selem);\n- var keys = this.fontStyleKeys;\n- for (var i = 0, len = keys.length; i < len; i++) {\n- var key = keys[i];\n- if (symbol[key]) {\n- selem.setAttribute(key, renderer[key])\n- }\n+ if (control.autoActivate) {\n+ control.activate()\n+ }\n+ },\n+ getControl: function(id) {\n+ var returnControl = null;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ var control = this.controls[i];\n+ if (control.id == id) {\n+ returnControl = control;\n+ break\n }\n }\n+ return returnControl\n },\n- writePolygonGeometry: function(polygon) {\n- if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {\n- throw {\n- message: \"Cannot write polygon geometry to ArcXML with an \" + polygon.CLASS_NAME + \" object.\",\n- geometry: polygon\n+ removeControl: function(control) {\n+ if (control && control == this.getControl(control.id)) {\n+ if (control.div && control.div.parentNode == this.viewPortDiv) {\n+ this.viewPortDiv.removeChild(control.div)\n }\n+ OpenLayers.Util.removeItem(this.controls, control)\n }\n- var polyElem = this.createElementNS(\"\", \"POLYGON\");\n- for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) {\n- var ring = polygon.components[ln];\n- var ringElem = this.createElementNS(\"\", \"RING\");\n- for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) {\n- var point = ring.components[rn];\n- var pointElem = this.createElementNS(\"\", \"POINT\");\n- pointElem.setAttribute(\"x\", point.x);\n- pointElem.setAttribute(\"y\", point.y);\n- ringElem.appendChild(pointElem)\n+ },\n+ addPopup: function(popup, exclusive) {\n+ if (exclusive) {\n+ for (var i = this.popups.length - 1; i >= 0; --i) {\n+ this.removePopup(this.popups[i])\n }\n- polyElem.appendChild(ringElem)\n }\n- return polyElem\n+ popup.map = this;\n+ this.popups.push(popup);\n+ var popupDiv = popup.draw();\n+ if (popupDiv) {\n+ popupDiv.style.zIndex = this.Z_INDEX_BASE[\"Popup\"] + this.popups.length;\n+ this.layerContainerDiv.appendChild(popupDiv)\n+ }\n },\n- parseResponse: function(data) {\n- if (typeof data == \"string\") {\n- var newData = new OpenLayers.Format.XML;\n- data = newData.read(data)\n+ removePopup: function(popup) {\n+ OpenLayers.Util.removeItem(this.popups, popup);\n+ if (popup.div) {\n+ try {\n+ this.layerContainerDiv.removeChild(popup.div)\n+ } catch (e) {}\n }\n- var response = new OpenLayers.Format.ArcXML.Response;\n- var errorNode = data.getElementsByTagName(\"ERROR\");\n- if (errorNode != null && errorNode.length > 0) {\n- response.error = this.getChildValue(errorNode, \"Unknown error.\")\n- } else {\n- var responseNode = data.getElementsByTagName(\"RESPONSE\");\n- if (responseNode == null || responseNode.length == 0) {\n- response.error = \"No RESPONSE tag found in ArcXML response.\";\n- return response\n+ popup.map = null\n+ },\n+ getSize: function() {\n+ var size = null;\n+ if (this.size != null) {\n+ size = this.size.clone()\n+ }\n+ return size\n+ },\n+ updateSize: function() {\n+ var newSize = this.getCurrentSize();\n+ if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {\n+ this.events.clearMouseCache();\n+ var oldSize = this.getSize();\n+ if (oldSize == null) {\n+ this.size = oldSize = newSize\n }\n- var rtype = responseNode[0].firstChild.nodeName;\n- if (rtype == \"#text\") {\n- rtype = responseNode[0].firstChild.nextSibling.nodeName\n+ if (!newSize.equals(oldSize)) {\n+ this.size = newSize;\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ this.layers[i].onMapResize()\n+ }\n+ var center = this.getCachedCenter();\n+ if (this.baseLayer != null && center != null) {\n+ var zoom = this.getZoom();\n+ this.zoom = null;\n+ this.setCenter(center, zoom)\n+ }\n }\n- if (rtype == \"IMAGE\") {\n- var envelopeNode = data.getElementsByTagName(\"ENVELOPE\");\n- var outputNode = data.getElementsByTagName(\"OUTPUT\");\n- if (envelopeNode == null || envelopeNode.length == 0) {\n- response.error = \"No ENVELOPE tag found in ArcXML response.\"\n- } else if (outputNode == null || outputNode.length == 0) {\n- response.error = \"No OUTPUT tag found in ArcXML response.\"\n+ }\n+ this.events.triggerEvent(\"updatesize\")\n+ },\n+ getCurrentSize: function() {\n+ var size = new OpenLayers.Size(this.div.clientWidth, this.div.clientHeight);\n+ if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n+ size.w = this.div.offsetWidth;\n+ size.h = this.div.offsetHeight\n+ }\n+ if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {\n+ size.w = parseInt(this.div.style.width);\n+ size.h = parseInt(this.div.style.height)\n+ }\n+ return size\n+ },\n+ calculateBounds: function(center, resolution) {\n+ var extent = null;\n+ if (center == null) {\n+ center = this.getCachedCenter()\n+ }\n+ if (resolution == null) {\n+ resolution = this.getResolution()\n+ }\n+ if (center != null && resolution != null) {\n+ var halfWDeg = this.size.w * resolution / 2;\n+ var halfHDeg = this.size.h * resolution / 2;\n+ extent = new OpenLayers.Bounds(center.lon - halfWDeg, center.lat - halfHDeg, center.lon + halfWDeg, center.lat + halfHDeg)\n+ }\n+ return extent\n+ },\n+ getCenter: function() {\n+ var center = null;\n+ var cachedCenter = this.getCachedCenter();\n+ if (cachedCenter) {\n+ center = cachedCenter.clone()\n+ }\n+ return center\n+ },\n+ getCachedCenter: function() {\n+ if (!this.center && this.size) {\n+ this.center = this.getLonLatFromViewPortPx({\n+ x: this.size.w / 2,\n+ y: this.size.h / 2\n+ })\n+ }\n+ return this.center\n+ },\n+ getZoom: function() {\n+ return this.zoom\n+ },\n+ pan: function(dx, dy, options) {\n+ options = OpenLayers.Util.applyDefaults(options, {\n+ animate: true,\n+ dragging: false\n+ });\n+ if (options.dragging) {\n+ if (dx != 0 || dy != 0) {\n+ this.moveByPx(dx, dy)\n+ }\n+ } else {\n+ var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());\n+ var newCenterPx = centerPx.add(dx, dy);\n+ if (this.dragging || !newCenterPx.equals(centerPx)) {\n+ var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);\n+ if (options.animate) {\n+ this.panTo(newCenterLonLat)\n } else {\n- var envAttr = this.parseAttributes(envelopeNode[0]);\n- var outputAttr = this.parseAttributes(outputNode[0]);\n- if (typeof outputAttr.type == \"string\") {\n- response.image = {\n- envelope: envAttr,\n- output: {\n- type: outputAttr.type,\n- data: this.getChildValue(outputNode[0])\n- }\n- }\n- } else {\n- response.image = {\n- envelope: envAttr,\n- output: outputAttr\n- }\n- }\n- }\n- } else if (rtype == \"FEATURES\") {\n- var features = responseNode[0].getElementsByTagName(\"FEATURES\");\n- var featureCount = features[0].getElementsByTagName(\"FEATURECOUNT\");\n- response.features.featurecount = featureCount[0].getAttribute(\"count\");\n- if (response.features.featurecount > 0) {\n- var envelope = features[0].getElementsByTagName(\"ENVELOPE\");\n- response.features.envelope = this.parseAttributes(envelope[0], typeof 0);\n- var featureList = features[0].getElementsByTagName(\"FEATURE\");\n- for (var fn = 0; fn < featureList.length; fn++) {\n- var feature = new OpenLayers.Feature.Vector;\n- var fields = featureList[fn].getElementsByTagName(\"FIELD\");\n- for (var fdn = 0; fdn < fields.length; fdn++) {\n- var fieldName = fields[fdn].getAttribute(\"name\");\n- var fieldValue = fields[fdn].getAttribute(\"value\");\n- feature.attributes[fieldName] = fieldValue\n- }\n- var geom = featureList[fn].getElementsByTagName(\"POLYGON\");\n- if (geom.length > 0) {\n- var ring = geom[0].getElementsByTagName(\"RING\");\n- var polys = [];\n- for (var rn = 0; rn < ring.length; rn++) {\n- var linearRings = [];\n- linearRings.push(this.parsePointGeometry(ring[rn]));\n- var holes = ring[rn].getElementsByTagName(\"HOLE\");\n- for (var hn = 0; hn < holes.length; hn++) {\n- linearRings.push(this.parsePointGeometry(holes[hn]))\n- }\n- holes = null;\n- polys.push(new OpenLayers.Geometry.Polygon(linearRings));\n- linearRings = null\n- }\n- ring = null;\n- if (polys.length == 1) {\n- feature.geometry = polys[0]\n- } else {\n- feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys)\n- }\n- }\n- response.features.feature.push(feature)\n+ this.moveTo(newCenterLonLat);\n+ if (this.dragging) {\n+ this.dragging = false;\n+ this.events.triggerEvent(\"moveend\")\n }\n }\n- } else {\n- response.error = \"Unidentified response type.\"\n }\n }\n- return response\n },\n- parseAttributes: function(node, type) {\n- var attributes = {};\n- for (var attr = 0; attr < node.attributes.length; attr++) {\n- if (type == \"number\") {\n- attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue)\n- } else {\n- attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue\n+ panTo: function(lonlat) {\n+ if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {\n+ var center = this.getCachedCenter();\n+ if (lonlat.equals(center)) {\n+ return\n }\n+ var from = this.getPixelFromLonLat(center);\n+ var to = this.getPixelFromLonLat(lonlat);\n+ var vector = {\n+ x: to.x - from.x,\n+ y: to.y - from.y\n+ };\n+ var last = {\n+ x: 0,\n+ y: 0\n+ };\n+ this.panTween.start({\n+ x: 0,\n+ y: 0\n+ }, vector, this.panDuration, {\n+ callbacks: {\n+ eachStep: OpenLayers.Function.bind(function(px) {\n+ var x = px.x - last.x,\n+ y = px.y - last.y;\n+ this.moveByPx(x, y);\n+ last.x = Math.round(px.x);\n+ last.y = Math.round(px.y)\n+ }, this),\n+ done: OpenLayers.Function.bind(function(px) {\n+ this.moveTo(lonlat);\n+ this.dragging = false;\n+ this.events.triggerEvent(\"moveend\")\n+ }, this)\n+ }\n+ })\n+ } else {\n+ this.setCenter(lonlat)\n }\n- return attributes\n },\n- parsePointGeometry: function(node) {\n- var ringPoints = [];\n- var coords = node.getElementsByTagName(\"COORDS\");\n- if (coords.length > 0) {\n- var coordArr = this.getChildValue(coords[0]);\n- coordArr = coordArr.split(/;/);\n- for (var cn = 0; cn < coordArr.length; cn++) {\n- var coordItems = coordArr[cn].split(/ /);\n- ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1]))\n+ setCenter: function(lonlat, zoom, dragging, forceZoomChange) {\n+ if (this.panTween) {\n+ this.panTween.stop()\n+ }\n+ if (this.zoomTween) {\n+ this.zoomTween.stop()\n+ }\n+ this.moveTo(lonlat, zoom, {\n+ dragging: dragging,\n+ forceZoomChange: forceZoomChange\n+ })\n+ },\n+ moveByPx: function(dx, dy) {\n+ var hw = this.size.w / 2;\n+ var hh = this.size.h / 2;\n+ var x = hw + dx;\n+ var y = hh + dy;\n+ var wrapDateLine = this.baseLayer.wrapDateLine;\n+ var xRestriction = 0;\n+ var yRestriction = 0;\n+ if (this.restrictedExtent) {\n+ xRestriction = hw;\n+ yRestriction = hh;\n+ wrapDateLine = false\n+ }\n+ dx = wrapDateLine || x <= this.maxPx.x - xRestriction && x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;\n+ dy = y <= this.maxPx.y - yRestriction && y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;\n+ if (dx || dy) {\n+ if (!this.dragging) {\n+ this.dragging = true;\n+ this.events.triggerEvent(\"movestart\")\n }\n- coords = null\n- } else {\n- var point = node.getElementsByTagName(\"POINT\");\n- if (point.length > 0) {\n- for (var pn = 0; pn < point.length; pn++) {\n- ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(point[pn].getAttribute(\"x\")), parseFloat(point[pn].getAttribute(\"y\"))))\n+ this.center = null;\n+ if (dx) {\n+ this.layerContainerOriginPx.x -= dx;\n+ this.minPx.x -= dx;\n+ this.maxPx.x -= dx\n+ }\n+ if (dy) {\n+ this.layerContainerOriginPx.y -= dy;\n+ this.minPx.y -= dy;\n+ this.maxPx.y -= dy\n+ }\n+ this.applyTransform();\n+ var layer, i, len;\n+ for (i = 0, len = this.layers.length; i < len; ++i) {\n+ layer = this.layers[i];\n+ if (layer.visibility && (layer === this.baseLayer || layer.inRange)) {\n+ layer.moveByPx(dx, dy);\n+ layer.events.triggerEvent(\"move\")\n }\n }\n- point = null\n+ this.events.triggerEvent(\"move\")\n }\n- return new OpenLayers.Geometry.LinearRing(ringPoints)\n },\n- CLASS_NAME: \"OpenLayers.Format.ArcXML\"\n-});\n-OpenLayers.Format.ArcXML.Request = OpenLayers.Class({\n- initialize: function(params) {\n- var defaults = {\n- get_image: {\n- properties: {\n- background: null,\n- draw: true,\n- envelope: {\n- minx: 0,\n- miny: 0,\n- maxx: 0,\n- maxy: 0\n- },\n- featurecoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- filtercoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- imagesize: {\n- height: 0,\n- width: 0,\n- dpi: 96,\n- printheight: 0,\n- printwidth: 0,\n- scalesymbols: false\n- },\n- layerlist: [],\n- output: {\n- baseurl: \"\",\n- legendbaseurl: \"\",\n- legendname: \"\",\n- legendpath: \"\",\n- legendurl: \"\",\n- name: \"\",\n- path: \"\",\n- type: \"jpg\",\n- url: \"\"\n- }\n- }\n- },\n- get_feature: {\n- layer: \"\",\n- query: {\n- isspatial: false,\n- featurecoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- filtercoordsys: {\n- id: 0,\n- string: \"\",\n- datumtransformid: 0,\n- datumtransformstring: \"\"\n- },\n- buffer: 0,\n- where: \"\",\n- spatialfilter: {\n- relation: \"envelope_intersection\",\n- envelope: null\n+ adjustZoom: function(zoom) {\n+ if (this.baseLayer && this.baseLayer.wrapDateLine) {\n+ var resolution, resolutions = this.baseLayer.resolutions,\n+ maxResolution = this.getMaxExtent().getWidth() / this.size.w;\n+ if (this.getResolutionForZoom(zoom) > maxResolution) {\n+ if (this.fractionalZoom) {\n+ zoom = this.getZoomForResolution(maxResolution)\n+ } else {\n+ for (var i = zoom | 0, ii = resolutions.length; i < ii; ++i) {\n+ if (resolutions[i] <= maxResolution) {\n+ zoom = i;\n+ break\n+ }\n }\n }\n- },\n- environment: {\n- separators: {\n- cs: \" \",\n- ts: \";\"\n- }\n- },\n- layer: [],\n- workspaces: []\n- };\n- return OpenLayers.Util.extend(this, defaults)\n- },\n- CLASS_NAME: \"OpenLayers.Format.ArcXML.Request\"\n-});\n-OpenLayers.Format.ArcXML.Response = OpenLayers.Class({\n- initialize: function(params) {\n- var defaults = {\n- image: {\n- envelope: null,\n- output: \"\"\n- },\n- features: {\n- featurecount: 0,\n- envelope: null,\n- feature: []\n- },\n- error: \"\"\n- };\n- return OpenLayers.Util.extend(this, defaults)\n- },\n- CLASS_NAME: \"OpenLayers.Format.ArcXML.Response\"\n-});\n-OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- ClientVersion: \"9.2\",\n- ServiceName: \"\"\n+ }\n+ }\n+ return zoom\n },\n- featureCoordSys: \"4326\",\n- filterCoordSys: \"4326\",\n- layers: null,\n- async: true,\n- name: \"ArcIMS\",\n- isBaseLayer: true,\n- DEFAULT_OPTIONS: {\n- tileSize: new OpenLayers.Size(512, 512),\n- featureCoordSys: \"4326\",\n- filterCoordSys: \"4326\",\n- layers: null,\n- isBaseLayer: true,\n- async: true,\n- name: \"ArcIMS\"\n+ getMinZoom: function() {\n+ return this.adjustZoom(0)\n },\n- initialize: function(name, url, options) {\n- this.tileSize = new OpenLayers.Size(512, 512);\n- this.params = OpenLayers.Util.applyDefaults({\n- ServiceName: options.serviceName\n- }, this.DEFAULT_PARAMS);\n- this.options = OpenLayers.Util.applyDefaults(options, this.DEFAULT_OPTIONS);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, this.params, options]);\n- if (this.transparent) {\n- if (!this.isBaseLayer) {\n- this.isBaseLayer = false\n- }\n- if (this.format == \"image/jpeg\") {\n- this.format = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n+ moveTo: function(lonlat, zoom, options) {\n+ if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {\n+ lonlat = new OpenLayers.LonLat(lonlat)\n+ }\n+ if (!options) {\n+ options = {}\n+ }\n+ if (zoom != null) {\n+ zoom = parseFloat(zoom);\n+ if (!this.fractionalZoom) {\n+ zoom = Math.round(zoom)\n }\n }\n- if (this.options.layers === null) {\n- this.options.layers = []\n+ var requestedZoom = zoom;\n+ zoom = this.adjustZoom(zoom);\n+ if (zoom !== requestedZoom) {\n+ lonlat = this.getCenter()\n }\n- },\n- getURL: function(bounds) {\n- var url = \"\";\n- bounds = this.adjustBounds(bounds);\n- var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- }));\n- var req = new OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- data: axlReq.write(),\n- async: false\n- });\n- if (req != null) {\n- var doc = req.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText\n+ var dragging = options.dragging || this.dragging;\n+ var forceZoomChange = options.forceZoomChange;\n+ if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {\n+ lonlat = this.maxExtent.getCenterLonLat();\n+ this.center = lonlat.clone()\n+ }\n+ if (this.restrictedExtent != null) {\n+ if (lonlat == null) {\n+ lonlat = this.center\n+ }\n+ if (zoom == null) {\n+ zoom = this.getZoom()\n+ }\n+ var resolution = this.getResolutionForZoom(zoom);\n+ var extent = this.calculateBounds(lonlat, resolution);\n+ if (!this.restrictedExtent.containsBounds(extent)) {\n+ var maxCenter = this.restrictedExtent.getCenterLonLat();\n+ if (extent.getWidth() > this.restrictedExtent.getWidth()) {\n+ lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat)\n+ } else if (extent.left < this.restrictedExtent.left) {\n+ lonlat = lonlat.add(this.restrictedExtent.left - extent.left, 0)\n+ } else if (extent.right > this.restrictedExtent.right) {\n+ lonlat = lonlat.add(this.restrictedExtent.right - extent.right, 0)\n+ }\n+ if (extent.getHeight() > this.restrictedExtent.getHeight()) {\n+ lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat)\n+ } else if (extent.bottom < this.restrictedExtent.bottom) {\n+ lonlat = lonlat.add(0, this.restrictedExtent.bottom - extent.bottom)\n+ } else if (extent.top > this.restrictedExtent.top) {\n+ lonlat = lonlat.add(0, this.restrictedExtent.top - extent.top)\n+ }\n }\n- var axlResp = new OpenLayers.Format.ArcXML;\n- var arcxml = axlResp.read(doc);\n- url = this.getUrlOrImage(arcxml.image.output)\n }\n- return url\n- },\n- getURLasync: function(bounds, callback, scope) {\n- bounds = this.adjustBounds(bounds);\n- var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- }));\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- async: true,\n- data: axlReq.write(),\n- callback: function(req) {\n- var doc = req.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText\n+ var zoomChanged = forceZoomChange || this.isValidZoomLevel(zoom) && zoom != this.getZoom();\n+ var centerChanged = this.isValidLonLat(lonlat) && !lonlat.equals(this.center);\n+ if (zoomChanged || centerChanged || dragging) {\n+ dragging || this.events.triggerEvent(\"movestart\", {\n+ zoomChanged: zoomChanged\n+ });\n+ if (centerChanged) {\n+ if (!zoomChanged && this.center) {\n+ this.centerLayerContainer(lonlat)\n }\n- var axlResp = new OpenLayers.Format.ArcXML;\n- var arcxml = axlResp.read(doc);\n- callback.call(scope, this.getUrlOrImage(arcxml.image.output))\n- },\n- scope: this\n- })\n+ this.center = lonlat.clone()\n+ }\n+ var res = zoomChanged ? this.getResolutionForZoom(zoom) : this.getResolution();\n+ if (zoomChanged || this.layerContainerOrigin == null) {\n+ this.layerContainerOrigin = this.getCachedCenter();\n+ this.layerContainerOriginPx.x = 0;\n+ this.layerContainerOriginPx.y = 0;\n+ this.applyTransform();\n+ var maxExtent = this.getMaxExtent({\n+ restricted: true\n+ });\n+ var maxExtentCenter = maxExtent.getCenterLonLat();\n+ var lonDelta = this.center.lon - maxExtentCenter.lon;\n+ var latDelta = maxExtentCenter.lat - this.center.lat;\n+ var extentWidth = Math.round(maxExtent.getWidth() / res);\n+ var extentHeight = Math.round(maxExtent.getHeight() / res);\n+ this.minPx = {\n+ x: (this.size.w - extentWidth) / 2 - lonDelta / res,\n+ y: (this.size.h - extentHeight) / 2 - latDelta / res\n+ };\n+ this.maxPx = {\n+ x: this.minPx.x + Math.round(maxExtent.getWidth() / res),\n+ y: this.minPx.y + Math.round(maxExtent.getHeight() / res)\n+ }\n+ }\n+ if (zoomChanged) {\n+ this.zoom = zoom;\n+ this.resolution = res\n+ }\n+ var bounds = this.getExtent();\n+ if (this.baseLayer.visibility) {\n+ this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);\n+ options.dragging || this.baseLayer.events.triggerEvent(\"moveend\", {\n+ zoomChanged: zoomChanged\n+ })\n+ }\n+ bounds = this.baseLayer.getExtent();\n+ for (var i = this.layers.length - 1; i >= 0; --i) {\n+ var layer = this.layers[i];\n+ if (layer !== this.baseLayer && !layer.isBaseLayer) {\n+ var inRange = layer.calculateInRange();\n+ if (layer.inRange != inRange) {\n+ layer.inRange = inRange;\n+ if (!inRange) {\n+ layer.display(false)\n+ }\n+ this.events.triggerEvent(\"changelayer\", {\n+ layer: layer,\n+ property: \"visibility\"\n+ })\n+ }\n+ if (inRange && layer.visibility) {\n+ layer.moveTo(bounds, zoomChanged, options.dragging);\n+ options.dragging || layer.events.triggerEvent(\"moveend\", {\n+ zoomChanged: zoomChanged\n+ })\n+ }\n+ }\n+ }\n+ this.events.triggerEvent(\"move\");\n+ dragging || this.events.triggerEvent(\"moveend\");\n+ if (zoomChanged) {\n+ for (var i = 0, len = this.popups.length; i < len; i++) {\n+ this.popups[i].updatePosition()\n+ }\n+ this.events.triggerEvent(\"zoomend\")\n+ }\n+ }\n },\n- getUrlOrImage: function(output) {\n- var ret = \"\";\n- if (output.url) {\n- ret = output.url\n- } else if (output.data) {\n- ret = \"data:image/\" + output.type + \";base64,\" + output.data\n+ centerLayerContainer: function(lonlat) {\n+ var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);\n+ var newPx = this.getViewPortPxFromLonLat(lonlat);\n+ if (originPx != null && newPx != null) {\n+ var oldLeft = this.layerContainerOriginPx.x;\n+ var oldTop = this.layerContainerOriginPx.y;\n+ var newLeft = Math.round(originPx.x - newPx.x);\n+ var newTop = Math.round(originPx.y - newPx.y);\n+ this.applyTransform(this.layerContainerOriginPx.x = newLeft, this.layerContainerOriginPx.y = newTop);\n+ var dx = oldLeft - newLeft;\n+ var dy = oldTop - newTop;\n+ this.minPx.x -= dx;\n+ this.maxPx.x -= dx;\n+ this.minPx.y -= dy;\n+ this.maxPx.y -= dy\n }\n- return ret\n },\n- setLayerQuery: function(id, querydef) {\n- for (var lyr = 0; lyr < this.options.layers.length; lyr++) {\n- if (id == this.options.layers[lyr].id) {\n- this.options.layers[lyr].query = querydef;\n- return\n- }\n+ isValidZoomLevel: function(zoomLevel) {\n+ return zoomLevel != null && zoomLevel >= 0 && zoomLevel < this.getNumZoomLevels()\n+ },\n+ isValidLonLat: function(lonlat) {\n+ var valid = false;\n+ if (lonlat != null) {\n+ var maxExtent = this.getMaxExtent();\n+ var worldBounds = this.baseLayer.wrapDateLine && maxExtent;\n+ valid = maxExtent.containsLonLat(lonlat, {\n+ worldBounds: worldBounds\n+ })\n }\n- this.options.layers.push({\n- id: id,\n- visible: true,\n- query: querydef\n- })\n+ return valid\n },\n- getFeatureInfo: function(geometry, layer, options) {\n- var buffer = options.buffer || 1;\n- var callback = options.callback || function() {};\n- var scope = options.scope || window;\n- var requestOptions = {};\n- OpenLayers.Util.extend(requestOptions, this.options);\n- requestOptions.requesttype = \"feature\";\n- if (geometry instanceof OpenLayers.LonLat) {\n- requestOptions.polygon = null;\n- requestOptions.envelope = [geometry.lon - buffer, geometry.lat - buffer, geometry.lon + buffer, geometry.lat + buffer]\n- } else if (geometry instanceof OpenLayers.Geometry.Polygon) {\n- requestOptions.envelope = null;\n- requestOptions.polygon = geometry\n+ getProjection: function() {\n+ var projection = this.getProjectionObject();\n+ return projection ? projection.getCode() : null\n+ },\n+ getProjectionObject: function() {\n+ var projection = null;\n+ if (this.baseLayer != null) {\n+ projection = this.baseLayer.projection\n }\n- var arcxml = new OpenLayers.Format.ArcXML(requestOptions);\n- OpenLayers.Util.extend(arcxml.request.get_feature, options);\n- arcxml.request.get_feature.layer = layer.id;\n- if (typeof layer.query.accuracy == \"number\") {\n- arcxml.request.get_feature.query.accuracy = layer.query.accuracy\n- } else {\n- var mapCenter = this.map.getCenter();\n- var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);\n- viewPx.x++;\n- var mapOffCenter = this.map.getLonLatFromPixel(viewPx);\n- arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon\n+ return projection\n+ },\n+ getMaxResolution: function() {\n+ var maxResolution = null;\n+ if (this.baseLayer != null) {\n+ maxResolution = this.baseLayer.maxResolution\n }\n- arcxml.request.get_feature.query.where = layer.query.where;\n- arcxml.request.get_feature.query.spatialfilter.relation = \"area_intersection\";\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString({\n- CustomService: \"Query\"\n- }),\n- data: arcxml.write(),\n- callback: function(request) {\n- var response = arcxml.parseResponse(request.responseText);\n- if (!arcxml.iserror()) {\n- callback.call(scope, response.features)\n- } else {\n- callback.call(scope, null)\n- }\n- }\n- })\n+ return maxResolution\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcIMS(this.name, this.url, this.getOptions())\n+ getMaxExtent: function(options) {\n+ var maxExtent = null;\n+ if (options && options.restricted && this.restrictedExtent) {\n+ maxExtent = this.restrictedExtent\n+ } else if (this.baseLayer != null) {\n+ maxExtent = this.baseLayer.maxExtent\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ return maxExtent\n },\n- CLASS_NAME: \"OpenLayers.Layer.ArcIMS\"\n-});\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- name: \"OpenStreetMap\",\n- url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n- sphericalMercator: true,\n- wrapDateLine: true,\n- tileOptions: null,\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options && this.options.tileOptions)\n+ getNumZoomLevels: function() {\n+ var numZoomLevels = null;\n+ if (this.baseLayer != null) {\n+ numZoomLevels = this.baseLayer.numZoomLevels\n+ }\n+ return numZoomLevels\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.baseLayer != null) {\n+ extent = this.baseLayer.getExtent()\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj\n+ return extent\n },\n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- useHttpTile: false,\n- singleTile: false,\n- useOverlay: false,\n- useAsyncOverlay: true,\n- TILE_PARAMS: {\n- operation: \"GETTILEIMAGE\",\n- version: \"1.2.0\"\n+ getResolution: function() {\n+ var resolution = null;\n+ if (this.baseLayer != null) {\n+ resolution = this.baseLayer.getResolution()\n+ } else if (this.allOverlays === true && this.layers.length > 0) {\n+ resolution = this.layers[0].getResolution()\n+ }\n+ return resolution\n },\n- SINGLE_TILE_PARAMS: {\n- operation: \"GETMAPIMAGE\",\n- format: \"PNG\",\n- locale: \"en\",\n- clip: \"1\",\n- version: \"1.0.0\"\n+ getUnits: function() {\n+ var units = null;\n+ if (this.baseLayer != null) {\n+ units = this.baseLayer.units\n+ }\n+ return units\n },\n- OVERLAY_PARAMS: {\n- operation: \"GETDYNAMICMAPOVERLAYIMAGE\",\n- format: \"PNG\",\n- locale: \"en\",\n- clip: \"1\",\n- version: \"2.0.0\"\n+ getScale: function() {\n+ var scale = null;\n+ if (this.baseLayer != null) {\n+ var res = this.getResolution();\n+ var units = this.baseLayer.units;\n+ scale = OpenLayers.Util.getScaleFromResolution(res, units)\n+ }\n+ return scale\n },\n- FOLDER_PARAMS: {\n- tileColumnsPerFolder: 30,\n- tileRowsPerFolder: 30,\n- format: \"png\",\n- querystring: null\n+ getZoomForExtent: function(bounds, closest) {\n+ var zoom = null;\n+ if (this.baseLayer != null) {\n+ zoom = this.baseLayer.getZoomForExtent(bounds, closest)\n+ }\n+ return zoom\n },\n- defaultSize: new OpenLayers.Size(300, 300),\n- tileOriginCorner: \"tl\",\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n- if (options == null || options.isBaseLayer == null) {\n- this.isBaseLayer = this.transparent != \"true\" && this.transparent != true\n+ getResolutionForZoom: function(zoom) {\n+ var resolution = null;\n+ if (this.baseLayer) {\n+ resolution = this.baseLayer.getResolutionForZoom(zoom)\n }\n- if (options && options.useOverlay != null) {\n- this.useOverlay = options.useOverlay\n+ return resolution\n+ },\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom = null;\n+ if (this.baseLayer != null) {\n+ zoom = this.baseLayer.getZoomForResolution(resolution, closest)\n }\n- if (this.singleTile) {\n- if (this.useOverlay) {\n- OpenLayers.Util.applyDefaults(this.params, this.OVERLAY_PARAMS);\n- if (!this.useAsyncOverlay) {\n- this.params.version = \"1.0.0\"\n- }\n- } else {\n- OpenLayers.Util.applyDefaults(this.params, this.SINGLE_TILE_PARAMS)\n+ return zoom\n+ },\n+ zoomTo: function(zoom, xy) {\n+ var map = this;\n+ if (map.isValidZoomLevel(zoom)) {\n+ if (map.baseLayer.wrapDateLine) {\n+ zoom = map.adjustZoom(zoom)\n }\n- } else {\n- if (this.useHttpTile) {\n- OpenLayers.Util.applyDefaults(this.params, this.FOLDER_PARAMS)\n+ if (map.zoomTween) {\n+ var currentRes = map.getResolution(),\n+ targetRes = map.getResolutionForZoom(zoom),\n+ start = {\n+ scale: 1\n+ },\n+ end = {\n+ scale: currentRes / targetRes\n+ };\n+ if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {\n+ map.zoomTween.finish = {\n+ scale: map.zoomTween.finish.scale * end.scale\n+ }\n+ } else {\n+ if (!xy) {\n+ var size = map.getSize();\n+ xy = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ }\n+ }\n+ map.zoomTween.start(start, end, map.zoomDuration, {\n+ minFrameRate: 50,\n+ callbacks: {\n+ eachStep: function(data) {\n+ var containerOrigin = map.layerContainerOriginPx,\n+ scale = data.scale,\n+ dx = (scale - 1) * (containerOrigin.x - xy.x) | 0,\n+ dy = (scale - 1) * (containerOrigin.y - xy.y) | 0;\n+ map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale)\n+ },\n+ done: function(data) {\n+ map.applyTransform();\n+ var resolution = map.getResolution() / data.scale,\n+ zoom = map.getZoomForResolution(resolution, true);\n+ map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true)\n+ }\n+ }\n+ })\n+ }\n } else {\n- OpenLayers.Util.applyDefaults(this.params, this.TILE_PARAMS)\n+ var center = xy ? map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) : null;\n+ map.setCenter(center, zoom)\n }\n- this.setTileSize(this.defaultSize)\n }\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.MapGuide(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ zoomIn: function() {\n+ this.zoomTo(this.getZoom() + 1)\n },\n- getURL: function(bounds) {\n- var url;\n+ zoomOut: function() {\n+ this.zoomTo(this.getZoom() - 1)\n+ },\n+ zoomToExtent: function(bounds, closest) {\n+ if (!(bounds instanceof OpenLayers.Bounds)) {\n+ bounds = new OpenLayers.Bounds(bounds)\n+ }\n var center = bounds.getCenterLonLat();\n- var mapSize = this.map.getSize();\n- if (this.singleTile) {\n- var params = {\n- setdisplaydpi: OpenLayers.DOTS_PER_INCH,\n- setdisplayheight: mapSize.h * this.ratio,\n- setdisplaywidth: mapSize.w * this.ratio,\n- setviewcenterx: center.lon,\n- setviewcentery: center.lat,\n- setviewscale: this.map.getScale()\n- };\n- if (this.useOverlay && !this.useAsyncOverlay) {\n- var getVisParams = {};\n- getVisParams = OpenLayers.Util.extend(getVisParams, params);\n- getVisParams.operation = \"GETVISIBLEMAPEXTENT\";\n- getVisParams.version = \"1.0.0\";\n- getVisParams.session = this.params.session;\n- getVisParams.mapName = this.params.mapName;\n- getVisParams.format = \"text/xml\";\n- url = this.getFullRequestString(getVisParams);\n- OpenLayers.Request.GET({\n- url: url,\n- async: false\n- })\n- }\n- url = this.getFullRequestString(params)\n- } else {\n- var currentRes = this.map.getResolution();\n- var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);\n- colidx = Math.round(colidx / this.tileSize.w);\n- var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);\n- rowidx = Math.round(rowidx / this.tileSize.h);\n- if (this.useHttpTile) {\n- url = this.getImageFilePath({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- })\n- } else {\n- url = this.getFullRequestString({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- })\n+ if (this.baseLayer.wrapDateLine) {\n+ var maxExtent = this.getMaxExtent();\n+ bounds = bounds.clone();\n+ while (bounds.right < bounds.left) {\n+ bounds.right += maxExtent.getWidth()\n }\n+ center = bounds.getCenterLonLat().wrapDateLine(maxExtent)\n }\n- return url\n+ this.setCenter(center, this.getZoomForExtent(bounds, closest))\n },\n- getFullRequestString: function(newParams, altUrl) {\n- var url = altUrl == null ? this.url : altUrl;\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)]\n+ zoomToMaxExtent: function(options) {\n+ var restricted = options ? options.restricted : true;\n+ var maxExtent = this.getMaxExtent({\n+ restricted: restricted\n+ });\n+ this.zoomToExtent(maxExtent)\n+ },\n+ zoomToScale: function(scale, closest) {\n+ var res = OpenLayers.Util.getResolutionFromScale(scale, this.baseLayer.units);\n+ var halfWDeg = this.size.w * res / 2;\n+ var halfHDeg = this.size.h * res / 2;\n+ var center = this.getCachedCenter();\n+ var extent = new OpenLayers.Bounds(center.lon - halfWDeg, center.lat - halfHDeg, center.lon + halfWDeg, center.lat + halfHDeg);\n+ this.zoomToExtent(extent, closest)\n+ },\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.baseLayer != null) {\n+ lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx)\n }\n- var requestString = url;\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key]\n- }\n+ return lonlat\n+ },\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var px = null;\n+ if (this.baseLayer != null) {\n+ px = this.baseLayer.getViewPortPxFromLonLat(lonlat)\n }\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n- paramsString = paramsString.replace(/,/g, \"+\");\n- if (paramsString != \"\") {\n- var lastServerChar = url.charAt(url.length - 1);\n- if (lastServerChar == \"&\" || lastServerChar == \"?\") {\n- requestString += paramsString\n- } else {\n- if (url.indexOf(\"?\") == -1) {\n- requestString += \"?\" + paramsString\n- } else {\n- requestString += \"&\" + paramsString\n- }\n- }\n+ return px\n+ },\n+ getZoomTargetCenter: function(xy, resolution) {\n+ var lonlat = null,\n+ size = this.getSize(),\n+ deltaX = size.w / 2 - xy.x,\n+ deltaY = xy.y - size.h / 2,\n+ zoomPoint = this.getLonLatFromPixel(xy);\n+ if (zoomPoint) {\n+ lonlat = new OpenLayers.LonLat(zoomPoint.lon + deltaX * resolution, zoomPoint.lat + deltaY * resolution)\n }\n- return requestString\n+ return lonlat\n },\n- getImageFilePath: function(newParams, altUrl) {\n- var url = altUrl == null ? this.url : altUrl;\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)]\n+ getLonLatFromPixel: function(px) {\n+ return this.getLonLatFromViewPortPx(px)\n+ },\n+ getPixelFromLonLat: function(lonlat) {\n+ var px = this.getViewPortPxFromLonLat(lonlat);\n+ px.x = Math.round(px.x);\n+ px.y = Math.round(px.y);\n+ return px\n+ },\n+ getGeodesicPixelSize: function(px) {\n+ var lonlat = px ? this.getLonLatFromPixel(px) : this.getCachedCenter() || new OpenLayers.LonLat(0, 0);\n+ var res = this.getResolution();\n+ var left = lonlat.add(-res / 2, 0);\n+ var right = lonlat.add(res / 2, 0);\n+ var bottom = lonlat.add(0, -res / 2);\n+ var top = lonlat.add(0, res / 2);\n+ var dest = new OpenLayers.Projection(\"EPSG:4326\");\n+ var source = this.getProjectionObject() || dest;\n+ if (!source.equals(dest)) {\n+ left.transform(source, dest);\n+ right.transform(source, dest);\n+ bottom.transform(source, dest);\n+ top.transform(source, dest)\n }\n- var requestString = url;\n- var tileRowGroup = \"\";\n- var tileColGroup = \"\";\n- if (newParams.tilerow < 0) {\n- tileRowGroup = \"-\"\n+ return new OpenLayers.Size(OpenLayers.Util.distVincenty(left, right), OpenLayers.Util.distVincenty(bottom, top))\n+ },\n+ getViewPortPxFromLayerPx: function(layerPx) {\n+ var viewPortPx = null;\n+ if (layerPx != null) {\n+ var dX = this.layerContainerOriginPx.x;\n+ var dY = this.layerContainerOriginPx.y;\n+ viewPortPx = layerPx.add(dX, dY)\n }\n- if (newParams.tilerow == 0) {\n- tileRowGroup += \"0\"\n- } else {\n- tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder\n+ return viewPortPx\n+ },\n+ getLayerPxFromViewPortPx: function(viewPortPx) {\n+ var layerPx = null;\n+ if (viewPortPx != null) {\n+ var dX = -this.layerContainerOriginPx.x;\n+ var dY = -this.layerContainerOriginPx.y;\n+ layerPx = viewPortPx.add(dX, dY);\n+ if (isNaN(layerPx.x) || isNaN(layerPx.y)) {\n+ layerPx = null\n+ }\n }\n- if (newParams.tilecol < 0) {\n- tileColGroup = \"-\"\n+ return layerPx\n+ },\n+ getLonLatFromLayerPx: function(px) {\n+ px = this.getViewPortPxFromLayerPx(px);\n+ return this.getLonLatFromViewPortPx(px)\n+ },\n+ getLayerPxFromLonLat: function(lonlat) {\n+ var px = this.getPixelFromLonLat(lonlat);\n+ return this.getLayerPxFromViewPortPx(px)\n+ },\n+ applyTransform: function(x, y, scale) {\n+ scale = scale || 1;\n+ var origin = this.layerContainerOriginPx,\n+ needTransform = scale !== 1;\n+ x = x || origin.x;\n+ y = y || origin.y;\n+ var style = this.layerContainerDiv.style,\n+ transform = this.applyTransform.transform,\n+ template = this.applyTransform.template;\n+ if (transform === undefined) {\n+ transform = OpenLayers.Util.vendorPrefix.style(\"transform\");\n+ this.applyTransform.transform = transform;\n+ if (transform) {\n+ var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv, OpenLayers.Util.vendorPrefix.css(\"transform\"));\n+ if (!computedStyle || computedStyle !== \"none\") {\n+ template = [\"translate3d(\", \",0) \", \"scale3d(\", \",1)\"];\n+ style[transform] = [template[0], \"0,0\", template[1]].join(\"\")\n+ }\n+ if (!template || !~style[transform].indexOf(template[0])) {\n+ template = [\"translate(\", \") \", \"scale(\", \")\"]\n+ }\n+ this.applyTransform.template = template\n+ }\n }\n- if (newParams.tilecol == 0) {\n- tileColGroup += \"0\"\n+ if (transform !== null && (template[0] === \"translate3d(\" || needTransform === true)) {\n+ if (needTransform === true && template[0] === \"translate(\") {\n+ x -= origin.x;\n+ y -= origin.y;\n+ style.left = origin.x + \"px\";\n+ style.top = origin.y + \"px\"\n+ }\n+ style[transform] = [template[0], x, \"px,\", y, \"px\", template[1], template[2], scale, \",\", scale, template[3]].join(\"\")\n } else {\n- tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder\n- }\n- var tilePath = \"/S\" + Math.floor(newParams.scaleindex) + \"/\" + this.params.basemaplayergroupname + \"/R\" + tileRowGroup + \"/C\" + tileColGroup + \"/\" + newParams.tilerow % this.params.tileRowsPerFolder + \"_\" + newParams.tilecol % this.params.tileColumnsPerFolder + \".\" + this.params.format;\n- if (this.params.querystring) {\n- tilePath += \"?\" + this.params.querystring\n+ style.left = x + \"px\";\n+ style.top = y + \"px\";\n+ if (transform !== null) {\n+ style[transform] = \"\"\n+ }\n }\n- requestString += tilePath;\n- return requestString\n },\n- CLASS_NAME: \"OpenLayers.Layer.MapGuide\"\n+ CLASS_NAME: \"OpenLayers.Map\"\n });\n-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+OpenLayers.Map.TILE_WIDTH = 256;\n+OpenLayers.Map.TILE_HEIGHT = 256;\n+OpenLayers.Layer = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ div: null,\n+ opacity: 1,\n+ alwaysInRange: null,\n+ RESOLUTION_PROPERTIES: [\"scales\", \"resolutions\", \"maxScale\", \"minScale\", \"maxResolution\", \"minResolution\", \"numZoomLevels\", \"maxZoomLevel\"],\n+ events: null,\n+ map: null,\n isBaseLayer: false,\n- isFixed: false,\n- features: null,\n- filter: null,\n- selectedFeatures: null,\n- unrenderedFeatures: null,\n- reportError: true,\n- style: null,\n- styleMap: null,\n- strategies: null,\n- protocol: null,\n- renderers: [\"SVG\", \"VML\", \"Canvas\"],\n- renderer: null,\n- rendererOptions: null,\n- geometryType: null,\n- drawn: false,\n- ratio: 1,\n+ alpha: false,\n+ displayInLayerSwitcher: true,\n+ visibility: true,\n+ attribution: null,\n+ inRange: false,\n+ imageSize: null,\n+ options: null,\n+ eventListeners: null,\n+ gutter: 0,\n+ projection: null,\n+ units: null,\n+ scales: null,\n+ resolutions: null,\n+ maxExtent: null,\n+ minExtent: null,\n+ maxResolution: null,\n+ minResolution: null,\n+ numZoomLevels: null,\n+ minScale: null,\n+ maxScale: null,\n+ displayOutsideMaxExtent: false,\n+ wrapDateLine: false,\n+ metadata: null,\n initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (!this.renderer || !this.renderer.supported()) {\n- this.assignRenderer()\n- }\n- if (!this.renderer || !this.renderer.supported()) {\n- this.renderer = null;\n- this.displayError()\n- }\n- if (!this.styleMap) {\n- this.styleMap = new OpenLayers.StyleMap\n+ this.metadata = {};\n+ options = OpenLayers.Util.extend({}, options);\n+ if (this.alwaysInRange != null) {\n+ options.alwaysInRange = this.alwaysInRange\n }\n- this.features = [];\n- this.selectedFeatures = [];\n- this.unrenderedFeatures = {};\n- if (this.strategies) {\n- for (var i = 0, len = this.strategies.length; i < len; i++) {\n- this.strategies[i].setLayer(this)\n+ this.addOptions(options);\n+ this.name = name;\n+ if (this.id == null) {\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ this.div = OpenLayers.Util.createDiv(this.id);\n+ this.div.style.width = \"100%\";\n+ this.div.style.height = \"100%\";\n+ this.div.dir = \"ltr\";\n+ this.events = new OpenLayers.Events(this, this.div);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n }\n }\n },\n- destroy: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoDestroy) {\n- strategy.destroy()\n- }\n- }\n- this.strategies = null\n+ destroy: function(setNewBaseLayer) {\n+ if (setNewBaseLayer == null) {\n+ setNewBaseLayer = true\n }\n- if (this.protocol) {\n- if (this.protocol.autoDestroy) {\n- this.protocol.destroy()\n- }\n- this.protocol = null\n+ if (this.map != null) {\n+ this.map.removeLayer(this, setNewBaseLayer)\n }\n- this.destroyFeatures();\n- this.features = null;\n- this.selectedFeatures = null;\n- this.unrenderedFeatures = null;\n- if (this.renderer) {\n- this.renderer.destroy()\n+ this.projection = null;\n+ this.map = null;\n+ this.name = null;\n+ this.div = null;\n+ this.options = null;\n+ if (this.events) {\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n+ }\n+ this.events.destroy()\n }\n- this.renderer = null;\n- this.geometryType = null;\n- this.drawn = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+ this.eventListeners = null;\n+ this.events = null\n },\n clone: function(obj) {\n if (obj == null) {\n- obj = new OpenLayers.Layer.Vector(this.name, this.getOptions())\n- }\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n- var features = this.features;\n- var len = features.length;\n- var clonedFeatures = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- clonedFeatures[i] = features[i].clone()\n+ obj = new OpenLayers.Layer(this.name, this.getOptions())\n }\n- obj.features = clonedFeatures;\n+ OpenLayers.Util.applyDefaults(obj, this);\n+ obj.map = null;\n return obj\n },\n- refresh: function(obj) {\n- if (this.calculateInRange() && this.visibility) {\n- this.events.triggerEvent(\"refresh\", obj)\n+ getOptions: function() {\n+ var options = {};\n+ for (var o in this.options) {\n+ options[o] = this[o]\n }\n+ return options\n },\n- assignRenderer: function() {\n- for (var i = 0, len = this.renderers.length; i < len; i++) {\n- var rendererClass = this.renderers[i];\n- var renderer = typeof rendererClass == \"function\" ? rendererClass : OpenLayers.Renderer[rendererClass];\n- if (renderer && renderer.prototype.supported()) {\n- this.renderer = new renderer(this.div, this.rendererOptions);\n- break\n+ setName: function(newName) {\n+ if (newName != this.name) {\n+ this.name = newName;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"name\"\n+ })\n }\n }\n },\n- displayError: function() {\n- if (this.reportError) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n- renderers: this.renderers.join(\"\\n\")\n- }))\n+ addOptions: function(newOptions, reinitialize) {\n+ if (this.options == null) {\n+ this.options = {}\n }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n- if (!this.renderer) {\n- this.map.removeLayer(this)\n- } else {\n- this.renderer.map = this.map;\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize)\n+ if (newOptions) {\n+ if (typeof newOptions.projection == \"string\") {\n+ newOptions.projection = new OpenLayers.Projection(newOptions.projection)\n+ }\n+ if (newOptions.projection) {\n+ OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()])\n+ }\n+ if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent)\n+ }\n+ if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {\n+ newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent)\n+ }\n }\n- },\n- afterAdd: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.activate()\n+ OpenLayers.Util.extend(this.options, newOptions);\n+ OpenLayers.Util.extend(this, newOptions);\n+ if (this.projection && this.projection.getUnits()) {\n+ this.units = this.projection.getUnits()\n+ }\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ var properties = this.RESOLUTION_PROPERTIES.concat([\"projection\", \"units\", \"minExtent\", \"maxExtent\"]);\n+ for (var o in newOptions) {\n+ if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) {\n+ this.initResolutions();\n+ if (reinitialize && this.map.baseLayer === this) {\n+ this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true);\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ })\n+ }\n+ break\n }\n }\n }\n },\n- removeMap: function(map) {\n- this.drawn = false;\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.deactivate()\n- }\n+ onMapResize: function() {},\n+ redraw: function() {\n+ var redrawn = false;\n+ if (this.map) {\n+ this.inRange = this.calculateInRange();\n+ var extent = this.getExtent();\n+ if (extent && this.inRange && this.visibility) {\n+ var zoomChanged = true;\n+ this.moveTo(extent, zoomChanged, false);\n+ this.events.triggerEvent(\"moveend\", {\n+ zoomChanged: zoomChanged\n+ });\n+ redrawn = true\n }\n }\n- },\n- onMapResize: function() {\n- OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize)\n+ return redrawn\n },\n moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- var coordSysUnchanged = true;\n- if (!dragging) {\n- this.renderer.root.style.visibility = \"hidden\";\n- var viewSize = this.map.getSize(),\n- viewWidth = viewSize.w,\n- viewHeight = viewSize.h,\n- offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2,\n- offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2;\n- offsetLeft += this.map.layerContainerOriginPx.x;\n- offsetLeft = -Math.round(offsetLeft);\n- offsetTop += this.map.layerContainerOriginPx.y;\n- offsetTop = -Math.round(offsetTop);\n- this.div.style.left = offsetLeft + \"px\";\n- this.div.style.top = offsetTop + \"px\";\n- var extent = this.map.getExtent().scale(this.ratio);\n- coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n- this.renderer.root.style.visibility = \"visible\";\n- if (OpenLayers.IS_GECKO === true) {\n- this.div.scrollLeft = this.div.scrollLeft\n+ var display = this.visibility;\n+ if (!this.isBaseLayer) {\n+ display = display && this.inRange\n+ }\n+ this.display(display)\n+ },\n+ moveByPx: function(dx, dy) {},\n+ setMap: function(map) {\n+ if (this.map == null) {\n+ this.map = map;\n+ this.maxExtent = this.maxExtent || this.map.maxExtent;\n+ this.minExtent = this.minExtent || this.map.minExtent;\n+ this.projection = this.projection || this.map.projection;\n+ if (typeof this.projection == \"string\") {\n+ this.projection = new OpenLayers.Projection(this.projection)\n }\n- if (!zoomChanged && coordSysUnchanged) {\n- for (var i in this.unrenderedFeatures) {\n- var feature = this.unrenderedFeatures[i];\n- this.drawFeature(feature)\n- }\n+ this.units = this.projection.getUnits() || this.units || this.map.units;\n+ this.initResolutions();\n+ if (!this.isBaseLayer) {\n+ this.inRange = this.calculateInRange();\n+ var show = this.visibility && this.inRange;\n+ this.div.style.display = show ? \"\" : \"none\"\n }\n+ this.setTileSize()\n }\n- if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n- this.drawn = true;\n- var feature;\n- for (var i = 0, len = this.features.length; i < len; i++) {\n- this.renderer.locked = i !== len - 1;\n- feature = this.features[i];\n- this.drawFeature(feature)\n+ },\n+ afterAdd: function() {},\n+ removeMap: function(map) {},\n+ getImageSize: function(bounds) {\n+ return this.imageSize || this.tileSize\n+ },\n+ setTileSize: function(size) {\n+ var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize();\n+ this.tileSize = tileSize;\n+ if (this.gutter) {\n+ this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter)\n+ }\n+ },\n+ getVisibility: function() {\n+ return this.visibility\n+ },\n+ setVisibility: function(visibility) {\n+ if (visibility != this.visibility) {\n+ this.visibility = visibility;\n+ this.display(visibility);\n+ this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"visibility\"\n+ })\n }\n+ this.events.triggerEvent(\"visibilitychanged\")\n }\n },\n display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- var currentDisplay = this.div.style.display;\n- if (currentDisplay != this.renderer.root.style.display) {\n- this.renderer.root.style.display = currentDisplay\n+ if (display != (this.div.style.display != \"none\")) {\n+ this.div.style.display = display && this.calculateInRange() ? \"block\" : \"none\"\n }\n },\n- addFeatures: function(features, options) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- var notify = !options || !options.silent;\n- if (notify) {\n- var event = {\n- features: features\n- };\n- var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n- if (ret === false) {\n- return\n+ calculateInRange: function() {\n+ var inRange = false;\n+ if (this.alwaysInRange) {\n+ inRange = true\n+ } else {\n+ if (this.map) {\n+ var resolution = this.map.getResolution();\n+ inRange = resolution >= this.minResolution && resolution <= this.maxResolution\n }\n- features = event.features\n }\n- var featuresAdded = [];\n- for (var i = 0, len = features.length; i < len; i++) {\n- if (i != features.length - 1) {\n- this.renderer.locked = true\n- } else {\n- this.renderer.locked = false\n- }\n- var feature = features[i];\n- if (this.geometryType && !(feature.geometry instanceof this.geometryType)) {\n- throw new TypeError(\"addFeatures: component should be an \" + this.geometryType.prototype.CLASS_NAME)\n- }\n- feature.layer = this;\n- if (!feature.style && this.style) {\n- feature.style = OpenLayers.Util.extend({}, this.style)\n- }\n- if (notify) {\n- if (this.events.triggerEvent(\"beforefeatureadded\", {\n- feature: feature\n- }) === false) {\n- continue\n- }\n- this.preFeatureInsert(feature)\n- }\n- featuresAdded.push(feature);\n- this.features.push(feature);\n- this.drawFeature(feature);\n- if (notify) {\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- this.onFeatureInsert(feature)\n+ return inRange\n+ },\n+ setIsBaseLayer: function(isBaseLayer) {\n+ if (isBaseLayer != this.isBaseLayer) {\n+ this.isBaseLayer = isBaseLayer;\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changebaselayer\", {\n+ layer: this\n+ })\n }\n }\n- if (notify) {\n- this.events.triggerEvent(\"featuresadded\", {\n- features: featuresAdded\n- })\n- }\n },\n- removeFeatures: function(features, options) {\n- if (!features || features.length === 0) {\n- return\n- }\n- if (features === this.features) {\n- return this.removeAllFeatures(options)\n+ initResolutions: function() {\n+ var i, len, p;\n+ var props = {},\n+ alwaysInRange = true;\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p];\n+ if (alwaysInRange && this.options[p]) {\n+ alwaysInRange = false\n+ }\n }\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n+ if (this.options.alwaysInRange == null) {\n+ this.alwaysInRange = alwaysInRange\n }\n- if (features === this.selectedFeatures) {\n- features = features.slice()\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales)\n }\n- var notify = !options || !options.silent;\n- if (notify) {\n- this.events.triggerEvent(\"beforefeaturesremoved\", {\n- features: features\n- })\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props)\n }\n- for (var i = features.length - 1; i >= 0; i--) {\n- if (i != 0 && features[i - 1].geometry) {\n- this.renderer.locked = true\n- } else {\n- this.renderer.locked = false\n- }\n- var feature = features[i];\n- delete this.unrenderedFeatures[feature.id];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- })\n- }\n- this.features = OpenLayers.Util.removeItem(this.features, feature);\n- feature.layer = null;\n- if (feature.geometry) {\n- this.renderer.eraseFeatures(feature)\n+ if (props.resolutions == null) {\n+ for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {\n+ p = this.RESOLUTION_PROPERTIES[i];\n+ props[p] = this.options[p] != null ? this.options[p] : this.map[p]\n }\n- if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n- OpenLayers.Util.removeItem(this.selectedFeatures, feature)\n+ if (props.resolutions == null) {\n+ props.resolutions = this.resolutionsFromScales(props.scales)\n }\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- })\n+ if (props.resolutions == null) {\n+ props.resolutions = this.calculateResolutions(props)\n }\n }\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- })\n+ var maxResolution;\n+ if (this.options.maxResolution && this.options.maxResolution !== \"auto\") {\n+ maxResolution = this.options.maxResolution\n }\n- },\n- removeAllFeatures: function(options) {\n- var notify = !options || !options.silent;\n- var features = this.features;\n- if (notify) {\n- this.events.triggerEvent(\"beforefeaturesremoved\", {\n- features: features\n- })\n+ if (this.options.minScale) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(this.options.minScale, this.units)\n }\n- var feature;\n- for (var i = features.length - 1; i >= 0; i--) {\n- feature = features[i];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- })\n+ var minResolution;\n+ if (this.options.minResolution && this.options.minResolution !== \"auto\") {\n+ minResolution = this.options.minResolution\n+ }\n+ if (this.options.maxScale) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units)\n+ }\n+ if (props.resolutions) {\n+ props.resolutions.sort(function(a, b) {\n+ return b - a\n+ });\n+ if (!maxResolution) {\n+ maxResolution = props.resolutions[0]\n }\n- feature.layer = null;\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- })\n+ if (!minResolution) {\n+ var lastIdx = props.resolutions.length - 1;\n+ minResolution = props.resolutions[lastIdx]\n }\n }\n- this.renderer.clear();\n- this.features = [];\n- this.unrenderedFeatures = {};\n- this.selectedFeatures = [];\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- })\n+ this.resolutions = props.resolutions;\n+ if (this.resolutions) {\n+ len = this.resolutions.length;\n+ this.scales = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units)\n+ }\n+ this.numZoomLevels = len\n }\n- },\n- destroyFeatures: function(features, options) {\n- var all = features == undefined;\n- if (all) {\n- features = this.features\n+ this.minResolution = minResolution;\n+ if (minResolution) {\n+ this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units)\n }\n- if (features) {\n- this.removeFeatures(features, options);\n- for (var i = features.length - 1; i >= 0; i--) {\n- features[i].destroy()\n- }\n+ this.maxResolution = maxResolution;\n+ if (maxResolution) {\n+ this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units)\n }\n },\n- drawFeature: function(feature, style) {\n- if (!this.drawn) {\n+ resolutionsFromScales: function(scales) {\n+ if (scales == null) {\n return\n }\n- if (typeof style != \"object\") {\n- if (!style && feature.state === OpenLayers.State.DELETE) {\n- style = \"delete\"\n- }\n- var renderIntent = style || feature.renderIntent;\n- style = feature.style || this.style;\n- if (!style) {\n- style = this.styleMap.createSymbolizer(feature, renderIntent)\n- }\n- }\n- var drawn = this.renderer.drawFeature(feature, style);\n- if (drawn === false || drawn === null) {\n- this.unrenderedFeatures[feature.id] = feature\n- } else {\n- delete this.unrenderedFeatures[feature.id]\n+ var resolutions, i, len;\n+ len = scales.length;\n+ resolutions = new Array(len);\n+ for (i = 0; i < len; i++) {\n+ resolutions[i] = OpenLayers.Util.getResolutionFromScale(scales[i], this.units)\n }\n+ return resolutions\n },\n- eraseFeatures: function(features) {\n- this.renderer.eraseFeatures(features)\n- },\n- getFeatureFromEvent: function(evt) {\n- if (!this.renderer) {\n- throw new Error(\"getFeatureFromEvent called on layer with no \" + \"renderer. This usually means you destroyed a \" + \"layer, but not some handler which is associated \" + \"with it.\")\n+ calculateResolutions: function(props) {\n+ var viewSize, wRes, hRes;\n+ var maxResolution = props.maxResolution;\n+ if (props.minScale != null) {\n+ maxResolution = OpenLayers.Util.getResolutionFromScale(props.minScale, this.units)\n+ } else if (maxResolution == \"auto\" && this.maxExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.maxExtent.getWidth() / viewSize.w;\n+ hRes = this.maxExtent.getHeight() / viewSize.h;\n+ maxResolution = Math.max(wRes, hRes)\n }\n- var feature = null;\n- var featureId = this.renderer.getFeatureIdFromEvent(evt);\n- if (featureId) {\n- if (typeof featureId === \"string\") {\n- feature = this.getFeatureById(featureId)\n- } else {\n- feature = featureId\n- }\n+ var minResolution = props.minResolution;\n+ if (props.maxScale != null) {\n+ minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units)\n+ } else if (props.minResolution == \"auto\" && this.minExtent != null) {\n+ viewSize = this.map.getSize();\n+ wRes = this.minExtent.getWidth() / viewSize.w;\n+ hRes = this.minExtent.getHeight() / viewSize.h;\n+ minResolution = Math.max(wRes, hRes)\n }\n- return feature\n- },\n- getFeatureBy: function(property, value) {\n- var feature = null;\n- for (var i = 0, len = this.features.length; i < len; ++i) {\n- if (this.features[i][property] == value) {\n- feature = this.features[i];\n- break\n+ if (typeof maxResolution !== \"number\" && typeof minResolution !== \"number\" && this.maxExtent != null) {\n+ var tileSize = this.map.getTileSize();\n+ maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h)\n+ }\n+ var maxZoomLevel = props.maxZoomLevel;\n+ var numZoomLevels = props.numZoomLevels;\n+ if (typeof minResolution === \"number\" && typeof maxResolution === \"number\" && numZoomLevels === undefined) {\n+ var ratio = maxResolution / minResolution;\n+ numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1\n+ } else if (numZoomLevels === undefined && maxZoomLevel != null) {\n+ numZoomLevels = maxZoomLevel + 1\n+ }\n+ if (typeof numZoomLevels !== \"number\" || numZoomLevels <= 0 || typeof maxResolution !== \"number\" && typeof minResolution !== \"number\") {\n+ return\n+ }\n+ var resolutions = new Array(numZoomLevels);\n+ var base = 2;\n+ if (typeof minResolution == \"number\" && typeof maxResolution == \"number\") {\n+ base = Math.pow(maxResolution / minResolution, 1 / (numZoomLevels - 1))\n+ }\n+ var i;\n+ if (typeof maxResolution === \"number\") {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[i] = maxResolution / Math.pow(base, i)\n+ }\n+ } else {\n+ for (i = 0; i < numZoomLevels; i++) {\n+ resolutions[numZoomLevels - 1 - i] = minResolution * Math.pow(base, i)\n }\n }\n- return feature\n+ return resolutions\n },\n- getFeatureById: function(featureId) {\n- return this.getFeatureBy(\"id\", featureId)\n+ getResolution: function() {\n+ var zoom = this.map.getZoom();\n+ return this.getResolutionForZoom(zoom)\n },\n- getFeatureByFid: function(featureFid) {\n- return this.getFeatureBy(\"fid\", featureFid)\n+ getExtent: function() {\n+ return this.map.calculateBounds()\n },\n- getFeaturesByAttribute: function(attrName, attrValue) {\n- var i, feature, len = this.features.length,\n- foundFeatures = [];\n- for (i = 0; i < len; i++) {\n- feature = this.features[i];\n- if (feature && feature.attributes) {\n- if (feature.attributes[attrName] === attrValue) {\n- foundFeatures.push(feature)\n- }\n- }\n+ getZoomForExtent: function(extent, closest) {\n+ var viewSize = this.map.getSize();\n+ var idealResolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);\n+ return this.getZoomForResolution(idealResolution, closest)\n+ },\n+ getDataExtent: function() {},\n+ getResolutionForZoom: function(zoom) {\n+ zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));\n+ var resolution;\n+ if (this.map.fractionalZoom) {\n+ var low = Math.floor(zoom);\n+ var high = Math.ceil(zoom);\n+ resolution = this.resolutions[low] - (zoom - low) * (this.resolutions[low] - this.resolutions[high])\n+ } else {\n+ resolution = this.resolutions[Math.round(zoom)]\n }\n- return foundFeatures\n+ return resolution\n },\n- onFeatureInsert: function(feature) {},\n- preFeatureInsert: function(feature) {},\n- getDataExtent: function() {\n- var maxExtent = null;\n- var features = this.features;\n- if (features && features.length > 0) {\n- var geometry = null;\n- for (var i = 0, len = features.length; i < len; i++) {\n- geometry = features[i].geometry;\n- if (geometry) {\n- if (maxExtent === null) {\n- maxExtent = new OpenLayers.Bounds\n+ getZoomForResolution: function(resolution, closest) {\n+ var zoom, i, len;\n+ if (this.map.fractionalZoom) {\n+ var lowZoom = 0;\n+ var highZoom = this.resolutions.length - 1;\n+ var highRes = this.resolutions[lowZoom];\n+ var lowRes = this.resolutions[highZoom];\n+ var res;\n+ for (i = 0, len = this.resolutions.length; i < len; ++i) {\n+ res = this.resolutions[i];\n+ if (res >= resolution) {\n+ highRes = res;\n+ lowZoom = i\n+ }\n+ if (res <= resolution) {\n+ lowRes = res;\n+ highZoom = i;\n+ break\n+ }\n+ }\n+ var dRes = highRes - lowRes;\n+ if (dRes > 0) {\n+ zoom = lowZoom + (highRes - resolution) / dRes\n+ } else {\n+ zoom = lowZoom\n+ }\n+ } else {\n+ var diff;\n+ var minDiff = Number.POSITIVE_INFINITY;\n+ for (i = 0, len = this.resolutions.length; i < len; i++) {\n+ if (closest) {\n+ diff = Math.abs(this.resolutions[i] - resolution);\n+ if (diff > minDiff) {\n+ break\n+ }\n+ minDiff = diff\n+ } else {\n+ if (this.resolutions[i] < resolution) {\n+ break\n }\n- maxExtent.extend(geometry.getBounds())\n }\n }\n+ zoom = Math.max(0, i - 1)\n }\n- return maxExtent\n+ return zoom\n },\n- CLASS_NAME: \"OpenLayers.Layer.Vector\"\n-});\n-OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {\n- dataFrom: null,\n- styleFrom: null,\n- addNodes: function(pointFeatures, options) {\n- if (pointFeatures.length < 2) {\n- throw new Error(\"At least two point features have to be added to \" + \"create a line from\")\n- }\n- var lines = new Array(pointFeatures.length - 1);\n- var pointFeature, startPoint, endPoint;\n- for (var i = 0, len = pointFeatures.length; i < len; i++) {\n- pointFeature = pointFeatures[i];\n- endPoint = pointFeature.geometry;\n- if (!endPoint) {\n- var lonlat = pointFeature.lonlat;\n- endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)\n- } else if (endPoint.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n- throw new TypeError(\"Only features with point geometries are supported.\")\n- }\n- if (i > 0) {\n- var attributes = this.dataFrom != null ? pointFeatures[i + this.dataFrom].data || pointFeatures[i + this.dataFrom].attributes : null;\n- var style = this.styleFrom != null ? pointFeatures[i + this.styleFrom].style : null;\n- var line = new OpenLayers.Geometry.LineString([startPoint, endPoint]);\n- lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, style)\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ var map = this.map;\n+ if (viewPortPx != null && map.minPx) {\n+ var res = map.getResolution();\n+ var maxExtent = map.getMaxExtent({\n+ restricted: true\n+ });\n+ var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;\n+ var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;\n+ lonlat = new OpenLayers.LonLat(lon, lat);\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent)\n }\n- startPoint = endPoint\n }\n- this.addFeatures(lines, options)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.PointTrack\"\n-});\n-OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;\n-OpenLayers.Layer.PointTrack.TARGET_NODE = 0;\n-OpenLayers.Layer.PointTrack.dataFrom = {\n- SOURCE_NODE: -1,\n- TARGET_NODE: 0\n-};\n-OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {\n- isBaseLayer: false,\n- markers: null,\n- drawn: false,\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- this.markers = []\n+ return lonlat\n },\n- destroy: function() {\n- this.clearMarkers();\n- this.markers = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+ getViewPortPxFromLonLat: function(lonlat, resolution) {\n+ var px = null;\n+ if (lonlat != null) {\n+ resolution = resolution || this.map.getResolution();\n+ var extent = this.map.calculateBounds(null, resolution);\n+ px = new OpenLayers.Pixel(1 / resolution * (lonlat.lon - extent.left), 1 / resolution * (extent.top - lonlat.lat))\n+ }\n+ return px\n },\n setOpacity: function(opacity) {\n if (opacity != this.opacity) {\n this.opacity = opacity;\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- this.markers[i].setOpacity(this.opacity)\n- }\n- }\n- },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- if (zoomChanged || !this.drawn) {\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- this.drawMarker(this.markers[i])\n+ var childNodes = this.div.childNodes;\n+ for (var i = 0, len = childNodes.length; i < len; ++i) {\n+ var element = childNodes[i].firstChild || childNodes[i];\n+ var lastChild = childNodes[i].lastChild;\n+ if (lastChild && lastChild.nodeName.toLowerCase() === \"iframe\") {\n+ element = lastChild.parentNode\n+ }\n+ OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity)\n }\n- this.drawn = true\n- }\n- },\n- addMarker: function(marker) {\n- this.markers.push(marker);\n- if (this.opacity < 1) {\n- marker.setOpacity(this.opacity)\n- }\n- if (this.map && this.map.getExtent()) {\n- marker.map = this.map;\n- this.drawMarker(marker)\n- }\n- },\n- removeMarker: function(marker) {\n- if (this.markers && this.markers.length) {\n- OpenLayers.Util.removeItem(this.markers, marker);\n- marker.erase()\n- }\n- },\n- clearMarkers: function() {\n- if (this.markers != null) {\n- while (this.markers.length > 0) {\n- this.removeMarker(this.markers[0])\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ })\n }\n }\n },\n- drawMarker: function(marker) {\n- var px = this.map.getLayerPxFromLonLat(marker.lonlat);\n- if (px == null) {\n- marker.display(false)\n- } else {\n- if (!marker.isDrawn()) {\n- var markerImg = marker.draw(px);\n- this.div.appendChild(markerImg)\n- } else if (marker.icon) {\n- marker.icon.moveTo(px)\n- }\n- }\n+ getZIndex: function() {\n+ return this.div.style.zIndex\n },\n- getDataExtent: function() {\n- var maxExtent = null;\n- if (this.markers && this.markers.length > 0) {\n- var maxExtent = new OpenLayers.Bounds;\n- for (var i = 0, len = this.markers.length; i < len; i++) {\n- var marker = this.markers[i];\n- maxExtent.extend(marker.lonlat)\n- }\n- }\n- return maxExtent\n+ setZIndex: function(zIndex) {\n+ this.div.style.zIndex = zIndex\n },\n- CLASS_NAME: \"OpenLayers.Layer.Markers\"\n-});\n-OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {\n- drawMarker: function(marker) {\n- var topleft = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.left,\n- lat: marker.bounds.top\n- });\n- var botright = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.right,\n- lat: marker.bounds.bottom\n- });\n- if (botright == null || topleft == null) {\n- marker.display(false)\n- } else {\n- var markerDiv = marker.draw(topleft, {\n- w: Math.max(1, botright.x - topleft.x),\n- h: Math.max(1, botright.y - topleft.y)\n- });\n- if (!marker.drawn) {\n- this.div.appendChild(markerDiv);\n- marker.drawn = true\n- }\n+ adjustBounds: function(bounds) {\n+ if (this.gutter) {\n+ var mapGutter = this.gutter * this.map.getResolution();\n+ bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter)\n }\n- },\n- removeMarker: function(marker) {\n- OpenLayers.Util.removeItem(this.markers, marker);\n- if (marker.div != null && marker.div.parentNode == this.div) {\n- this.div.removeChild(marker.div)\n+ if (this.wrapDateLine) {\n+ var wrappingOptions = {\n+ rightTolerance: this.getResolution(),\n+ leftTolerance: this.getResolution()\n+ };\n+ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions)\n }\n+ return bounds\n },\n- CLASS_NAME: \"OpenLayers.Layer.Boxes\"\n+ CLASS_NAME: \"OpenLayers.Layer\"\n });\n-OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {\n- isBaseLayer: true,\n+OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {\n+ URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,\n url: null,\n- extent: null,\n- size: null,\n- tile: null,\n- aspectRatio: null,\n- initialize: function(name, url, extent, size, options) {\n- this.url = url;\n- this.extent = extent;\n- this.maxExtent = extent;\n- this.size = size;\n+ params: null,\n+ reproject: false,\n+ initialize: function(name, url, params, options) {\n OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n- this.aspectRatio = this.extent.getHeight() / this.size.h / (this.extent.getWidth() / this.size.w)\n+ this.url = url;\n+ if (!this.params) {\n+ this.params = OpenLayers.Util.extend({}, params)\n+ }\n },\n destroy: function() {\n- if (this.tile) {\n- this.removeTileMonitoringHooks(this.tile);\n- this.tile.destroy();\n- this.tile = null\n- }\n+ this.url = null;\n+ this.params = null;\n OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n },\n clone: function(obj) {\n if (obj == null) {\n- obj = new OpenLayers.Layer.Image(this.name, this.url, this.extent, this.size, this.getOptions())\n+ obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions())\n }\n obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n return obj\n },\n- setMap: function(map) {\n- if (this.options.maxResolution == null) {\n- this.options.maxResolution = this.aspectRatio * this.extent.getWidth() / this.size.w\n- }\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments)\n+ setUrl: function(newUrl) {\n+ this.url = newUrl\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- var firstRendering = this.tile == null;\n- if (zoomChanged || firstRendering) {\n- this.setTileSize();\n- var ulPx = this.map.getLayerPxFromLonLat({\n- lon: this.extent.left,\n- lat: this.extent.top\n- });\n- if (firstRendering) {\n- this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize);\n- this.addTileMonitoringHooks(this.tile)\n- } else {\n- this.tile.size = this.tileSize.clone();\n- this.tile.position = ulPx.clone()\n- }\n- this.tile.draw()\n+ mergeNewParams: function(newParams) {\n+ this.params = OpenLayers.Util.extend(this.params, newParams);\n+ var ret = this.redraw();\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"params\"\n+ })\n }\n+ return ret\n },\n- setTileSize: function() {\n- var tileWidth = this.extent.getWidth() / this.map.getResolution();\n- var tileHeight = this.extent.getHeight() / this.map.getResolution();\n- this.tileSize = new OpenLayers.Size(tileWidth, tileHeight)\n- },\n- addTileMonitoringHooks: function(tile) {\n- tile.onLoadStart = function() {\n- this.events.triggerEvent(\"loadstart\")\n- };\n- tile.events.register(\"loadstart\", this, tile.onLoadStart);\n- tile.onLoadEnd = function() {\n- this.events.triggerEvent(\"loadend\")\n- };\n- tile.events.register(\"loadend\", this, tile.onLoadEnd);\n- tile.events.register(\"unload\", this, tile.onLoadEnd)\n- },\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- loadstart: tile.onLoadStart,\n- loadend: tile.onLoadEnd,\n- unload: tile.onLoadEnd,\n- scope: this\n- })\n+ redraw: function(force) {\n+ if (force) {\n+ return this.mergeNewParams({\n+ _olSalt: Math.random()\n+ })\n+ } else {\n+ return OpenLayers.Layer.prototype.redraw.apply(this, [])\n+ }\n },\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n- this.tile.draw()\n+ selectUrl: function(paramString, urls) {\n+ var product = 1;\n+ for (var i = 0, len = paramString.length; i < len; i++) {\n+ product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;\n+ product -= Math.floor(product)\n+ }\n+ return urls[Math.floor(product * urls.length)]\n },\n- getURL: function(bounds) {\n- return this.url\n+ getFullRequestString: function(newParams, altUrl) {\n+ var url = altUrl || this.url;\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url)\n+ }\n+ var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key]\n+ }\n+ }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n+ return OpenLayers.Util.urlAppend(url, paramsString)\n },\n- CLASS_NAME: \"OpenLayers.Layer.Image\"\n+ CLASS_NAME: \"OpenLayers.Layer.HTTPRequest\"\n });\n-OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {\n- dx: null,\n- dy: null,\n- ratio: 1.5,\n- maxFeatures: 250,\n- rotation: 0,\n- origin: null,\n- gridBounds: null,\n- initialize: function(config) {\n- config = config || {};\n- OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config])\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- map.events.register(\"moveend\", this, this.onMoveEnd)\n- },\n- removeMap: function(map) {\n- map.events.unregister(\"moveend\", this, this.onMoveEnd);\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n- },\n- setRatio: function(ratio) {\n- this.ratio = ratio;\n- this.updateGrid(true)\n- },\n- setMaxFeatures: function(maxFeatures) {\n- this.maxFeatures = maxFeatures;\n- this.updateGrid(true)\n- },\n- setSpacing: function(dx, dy) {\n- this.dx = dx;\n- this.dy = dy || dx;\n- this.updateGrid(true)\n- },\n- setOrigin: function(origin) {\n- this.origin = origin;\n- this.updateGrid(true)\n- },\n- getOrigin: function() {\n- if (!this.origin) {\n- this.origin = this.map.getExtent().getCenterLonLat()\n+OpenLayers.Tile = OpenLayers.Class({\n+ events: null,\n+ eventListeners: null,\n+ id: null,\n+ layer: null,\n+ url: null,\n+ bounds: null,\n+ size: null,\n+ position: null,\n+ isLoading: false,\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ this.layer = layer;\n+ this.position = position.clone();\n+ this.setBounds(bounds);\n+ this.url = url;\n+ if (size) {\n+ this.size = size.clone()\n+ }\n+ this.id = OpenLayers.Util.createUniqueID(\"Tile_\");\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ if (this.eventListeners instanceof Object) {\n+ this.events.on(this.eventListeners)\n }\n- return this.origin\n },\n- setRotation: function(rotation) {\n- this.rotation = rotation;\n- this.updateGrid(true)\n+ unload: function() {\n+ if (this.isLoading) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"unload\")\n+ }\n },\n- onMoveEnd: function() {\n- this.updateGrid()\n+ destroy: function() {\n+ this.layer = null;\n+ this.bounds = null;\n+ this.size = null;\n+ this.position = null;\n+ if (this.eventListeners) {\n+ this.events.un(this.eventListeners)\n+ }\n+ this.events.destroy();\n+ this.eventListeners = null;\n+ this.events = null\n },\n- getViewBounds: function() {\n- var bounds = this.map.getExtent();\n- if (this.rotation) {\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var rect = bounds.toGeometry();\n- rect.rotate(-this.rotation, rotationOrigin);\n- bounds = rect.getBounds()\n+ draw: function(force) {\n+ if (!force) {\n+ this.clear()\n }\n- return bounds\n+ var draw = this.shouldDraw();\n+ if (draw && !force && this.events.triggerEvent(\"beforedraw\") === false) {\n+ draw = null\n+ }\n+ return draw\n },\n- updateGrid: function(force) {\n- if (force || this.invalidBounds()) {\n- var viewBounds = this.getViewBounds();\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var viewBoundsWidth = viewBounds.getWidth();\n- var viewBoundsHeight = viewBounds.getHeight();\n- var aspectRatio = viewBoundsWidth / viewBoundsHeight;\n- var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);\n- var maxWidth = maxHeight * aspectRatio;\n- var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);\n- var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);\n- var center = viewBounds.getCenterLonLat();\n- this.gridBounds = new OpenLayers.Bounds(center.lon - gridWidth / 2, center.lat - gridHeight / 2, center.lon + gridWidth / 2, center.lat + gridHeight / 2);\n- var rows = Math.floor(gridHeight / this.dy);\n- var cols = Math.floor(gridWidth / this.dx);\n- var gridLeft = origin.lon + this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx);\n- var gridBottom = origin.lat + this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy);\n- var features = new Array(rows * cols);\n- var x, y, point;\n- for (var i = 0; i < cols; ++i) {\n- x = gridLeft + i * this.dx;\n- for (var j = 0; j < rows; ++j) {\n- y = gridBottom + j * this.dy;\n- point = new OpenLayers.Geometry.Point(x, y);\n- if (this.rotation) {\n- point.rotate(this.rotation, rotationOrigin)\n- }\n- features[i * rows + j] = new OpenLayers.Feature.Vector(point)\n- }\n+ shouldDraw: function() {\n+ var withinMaxExtent = false,\n+ maxExtent = this.layer.maxExtent;\n+ if (maxExtent) {\n+ var map = this.layer.map;\n+ var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();\n+ if (this.bounds.intersectsBounds(maxExtent, {\n+ inclusive: false,\n+ worldBounds: worldBounds\n+ })) {\n+ withinMaxExtent = true\n }\n- this.destroyFeatures(this.features, {\n- silent: true\n- });\n- this.addFeatures(features, {\n- silent: true\n+ }\n+ return withinMaxExtent || this.layer.displayOutsideMaxExtent\n+ },\n+ setBounds: function(bounds) {\n+ bounds = bounds.clone();\n+ if (this.layer.map.baseLayer.wrapDateLine) {\n+ var worldExtent = this.layer.map.getMaxExtent(),\n+ tolerance = this.layer.map.getResolution();\n+ bounds = bounds.wrapDateLine(worldExtent, {\n+ leftTolerance: tolerance,\n+ rightTolerance: tolerance\n })\n }\n+ this.bounds = bounds\n },\n- invalidBounds: function() {\n- return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds())\n+ moveTo: function(bounds, position, redraw) {\n+ if (redraw == null) {\n+ redraw = true\n+ }\n+ this.setBounds(bounds);\n+ this.position = position.clone();\n+ if (redraw) {\n+ this.draw()\n+ }\n },\n- CLASS_NAME: \"OpenLayers.Layer.PointGrid\"\n+ clear: function(draw) {},\n+ CLASS_NAME: \"OpenLayers.Tile\"\n });\n-OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {\n- location: null,\n- features: null,\n- formatOptions: null,\n- selectedFeature: null,\n- icon: null,\n- popupSize: null,\n- useFeedTitle: true,\n- initialize: function(name, location, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);\n- this.location = location;\n- this.features = []\n- },\n- destroy: function() {\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null\n- },\n- loadRSS: function() {\n- if (!this.loaded) {\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- scope: this\n- });\n- this.loaded = true\n+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {\n+ url: null,\n+ imgDiv: null,\n+ frame: null,\n+ imageReloadAttempts: null,\n+ layerAlphaHack: null,\n+ asyncRequestId: null,\n+ maxGetUrlLength: null,\n+ canvasContext: null,\n+ crossOriginKeyword: null,\n+ initialize: function(layer, position, bounds, url, size, options) {\n+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);\n+ this.url = url;\n+ this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();\n+ if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {\n+ this.frame = document.createElement(\"div\");\n+ this.frame.style.position = \"absolute\";\n+ this.frame.style.overflow = \"hidden\"\n }\n- },\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadRSS()\n+ if (this.maxGetUrlLength != null) {\n+ OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame)\n }\n },\n- parseData: function(ajaxRequest) {\n- var doc = ajaxRequest.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText)\n+ destroy: function() {\n+ if (this.imgDiv) {\n+ this.clear();\n+ this.imgDiv = null;\n+ this.frame = null\n }\n- if (this.useFeedTitle) {\n- var name = null;\n- try {\n- name = doc.getElementsByTagNameNS(\"*\", \"title\")[0].firstChild.nodeValue\n- } catch (e) {\n- try {\n- name = doc.getElementsByTagName(\"title\")[0].firstChild.nodeValue\n- } catch (e) {}\n+ this.asyncRequestId = null;\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n+ },\n+ draw: function() {\n+ var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (shouldDraw) {\n+ if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {\n+ this.bounds = this.getBoundsFromBaseLayer(this.position)\n }\n- if (name) {\n- this.setName(name)\n+ if (this.isLoading) {\n+ this._loadEvent = \"reload\"\n+ } else {\n+ this.isLoading = true;\n+ this._loadEvent = \"loadstart\"\n }\n+ this.renderTile();\n+ this.positionTile()\n+ } else if (shouldDraw === false) {\n+ this.unload()\n }\n- var options = {};\n- OpenLayers.Util.extend(options, this.formatOptions);\n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject()\n- }\n- var format = new OpenLayers.Format.GeoRSS(options);\n- var features = format.read(doc);\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n- if (!feature.geometry) {\n- continue\n- }\n- var title = feature.attributes.title ? feature.attributes.title : \"Untitled\";\n- var description = feature.attributes.description ? feature.attributes.description : \"No description.\";\n- var link = feature.attributes.link ? feature.attributes.link : \"\";\n- var location = feature.geometry.getBounds().getCenterLonLat();\n- data.icon = this.icon == null ? OpenLayers.Marker.defaultIcon() : this.icon.clone();\n- data.popupSize = this.popupSize ? this.popupSize.clone() : new OpenLayers.Size(250, 120);\n- if (title || description) {\n- data.title = title;\n- data.description = description;\n- var contentHTML = '<div class=\"olLayerGeoRSSClose\">[x]</div>';\n- contentHTML += '<div class=\"olLayerGeoRSSTitle\">';\n- if (link) {\n- contentHTML += '<a class=\"link\" href=\"' + link + '\" target=\"_blank\">'\n- }\n- contentHTML += title;\n- if (link) {\n- contentHTML += \"</a>\"\n+ return shouldDraw\n+ },\n+ renderTile: function() {\n+ if (this.layer.async) {\n+ var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;\n+ this.layer.getURLasync(this.bounds, function(url) {\n+ if (id == this.asyncRequestId) {\n+ this.url = url;\n+ this.initImage()\n }\n- contentHTML += \"</div>\";\n- contentHTML += '<div style=\"\" class=\"olLayerGeoRSSDescription\">';\n- contentHTML += description;\n- contentHTML += \"</div>\";\n- data[\"popupContentHTML\"] = contentHTML\n- }\n- var feature = new OpenLayers.Feature(this, location, data);\n- this.features.push(feature);\n- var marker = feature.createMarker();\n- marker.events.register(\"click\", feature, this.markerClick);\n- this.addMarker(marker)\n+ }, this)\n+ } else {\n+ this.url = this.layer.getURL(this.bounds);\n+ this.initImage()\n }\n- this.events.triggerEvent(\"loadend\")\n },\n- markerClick: function(evt) {\n- var sameMarkerClicked = this == this.layer.selectedFeature;\n- this.layer.selectedFeature = !sameMarkerClicked ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i])\n- }\n- if (!sameMarkerClicked) {\n- var popup = this.createPopup();\n- OpenLayers.Event.observe(popup.div, \"click\", OpenLayers.Function.bind(function() {\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i])\n- }\n- }, this));\n- this.layer.map.addPopup(popup)\n+ positionTile: function() {\n+ var style = this.getTile().style,\n+ size = this.frame ? this.size : this.layer.getImageSize(this.bounds),\n+ ratio = 1;\n+ if (this.layer instanceof OpenLayers.Layer.Grid) {\n+ ratio = this.layer.getServerResolution() / this.layer.map.getResolution()\n }\n- OpenLayers.Event.stop(evt)\n+ style.left = this.position.x + \"px\";\n+ style.top = this.position.y + \"px\";\n+ style.width = Math.round(ratio * size.w) + \"px\";\n+ style.height = Math.round(ratio * size.h) + \"px\"\n },\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy()\n+ clear: function() {\n+ OpenLayers.Tile.prototype.clear.apply(this, arguments);\n+ var img = this.imgDiv;\n+ if (img) {\n+ var tile = this.getTile();\n+ if (tile.parentNode === this.layer.div) {\n+ this.layer.div.removeChild(tile)\n }\n+ this.setImgSrc();\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"\"\n+ }\n+ OpenLayers.Element.removeClass(img, \"olImageLoadError\")\n }\n+ this.canvasContext = null\n },\n- CLASS_NAME: \"OpenLayers.Layer.GeoRSS\"\n-});\n-OpenLayers.Layer.SphericalMercator = {\n- getExtent: function() {\n- var extent = null;\n- if (this.sphericalMercator) {\n- extent = this.map.calculateBounds()\n- } else {\n- extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)\n+ getImage: function() {\n+ if (!this.imgDiv) {\n+ this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);\n+ var style = this.imgDiv.style;\n+ if (this.frame) {\n+ var left = 0,\n+ top = 0;\n+ if (this.layer.gutter) {\n+ left = this.layer.gutter / this.layer.tileSize.w * 100;\n+ top = this.layer.gutter / this.layer.tileSize.h * 100\n+ }\n+ style.left = -left + \"%\";\n+ style.top = -top + \"%\";\n+ style.width = 2 * left + 100 + \"%\";\n+ style.height = 2 * top + 100 + \"%\"\n+ }\n+ style.visibility = \"hidden\";\n+ style.opacity = 0;\n+ if (this.layer.opacity < 1) {\n+ style.filter = \"alpha(opacity=\" + this.layer.opacity * 100 + \")\"\n+ }\n+ style.position = \"absolute\";\n+ if (this.layerAlphaHack) {\n+ style.paddingTop = style.height;\n+ style.height = \"0\";\n+ style.width = \"100%\"\n+ }\n+ if (this.frame) {\n+ this.frame.appendChild(this.imgDiv)\n+ }\n }\n- return extent\n- },\n- getLonLatFromViewPortPx: function(viewPortPx) {\n- return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments)\n- },\n- getViewPortPxFromLonLat: function(lonlat) {\n- return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments)\n+ return this.imgDiv\n },\n- initMercatorParameters: function() {\n- this.RESOLUTIONS = [];\n- var maxResolution = 156543.03390625;\n- for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n- this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom)\n- }\n- this.units = \"m\";\n- this.projection = this.projection || \"EPSG:900913\"\n+ setImage: function(img) {\n+ this.imgDiv = img\n },\n- forwardMercator: function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(lon, lat) {\n- var point = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- }, gg, sm);\n- return new OpenLayers.LonLat(point.x, point.y)\n- }\n- }(),\n- inverseMercator: function() {\n- var gg = new OpenLayers.Projection(\"EPSG:4326\");\n- var sm = new OpenLayers.Projection(\"EPSG:900913\");\n- return function(x, y) {\n- var point = OpenLayers.Projection.transform({\n- x: x,\n- y: y\n- }, sm, gg);\n- return new OpenLayers.LonLat(point.x, point.y)\n- }\n- }()\n-};\n-OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n- initialize: function() {},\n- initResolutions: function() {\n- var props = [\"minZoomLevel\", \"maxZoomLevel\", \"numZoomLevels\"];\n- for (var i = 0, len = props.length; i < len; i++) {\n- var property = props[i];\n- this[property] = this.options[property] != null ? this.options[property] : this.map[property]\n- }\n- if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) {\n- this.minZoomLevel = this.MIN_ZOOM_LEVEL\n- }\n- var desiredZoomLevels;\n- var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n- if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) {\n- desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1\n- } else {\n- desiredZoomLevels = this.numZoomLevels\n+ initImage: function() {\n+ if (!this.url && !this.imgDiv) {\n+ this.isLoading = false;\n+ return\n }\n- if (desiredZoomLevels != null) {\n- this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels)\n+ this.events.triggerEvent(\"beforeload\");\n+ this.layer.div.appendChild(this.getTile());\n+ this.events.triggerEvent(this._loadEvent);\n+ var img = this.getImage();\n+ var src = img.getAttribute(\"src\") || \"\";\n+ if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {\n+ this._loadTimeout = window.setTimeout(OpenLayers.Function.bind(this.onImageLoad, this), 0)\n } else {\n- this.numZoomLevels = limitZoomLevels\n- }\n- this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n- if (this.RESOLUTIONS != null) {\n- var resolutionsIndex = 0;\n- this.resolutions = [];\n- for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n- this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]\n+ this.stopLoading();\n+ if (this.crossOriginKeyword) {\n+ img.removeAttribute(\"crossorigin\")\n }\n- this.maxResolution = this.resolutions[0];\n- this.minResolution = this.resolutions[this.resolutions.length - 1]\n+ OpenLayers.Event.observe(img, \"load\", OpenLayers.Function.bind(this.onImageLoad, this));\n+ OpenLayers.Event.observe(img, \"error\", OpenLayers.Function.bind(this.onImageError, this));\n+ this.imageReloadAttempts = 0;\n+ this.setImgSrc(this.url)\n }\n },\n- getResolution: function() {\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getResolution.apply(this, arguments)\n+ setImgSrc: function(url) {\n+ var img = this.imgDiv;\n+ if (url) {\n+ img.style.visibility = \"hidden\";\n+ img.style.opacity = 0;\n+ if (this.crossOriginKeyword) {\n+ if (url.substr(0, 5) !== \"data:\") {\n+ img.setAttribute(\"crossorigin\", this.crossOriginKeyword)\n+ } else {\n+ img.removeAttribute(\"crossorigin\")\n+ }\n+ }\n+ img.src = url\n } else {\n- var resolution = null;\n- var viewSize = this.map.getSize();\n- var extent = this.getExtent();\n- if (viewSize != null && extent != null) {\n- resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h)\n+ this.stopLoading();\n+ this.imgDiv = null;\n+ if (img.parentNode) {\n+ img.parentNode.removeChild(img)\n }\n- return resolution\n }\n },\n- getExtent: function() {\n- var size = this.map.getSize();\n- var tl = this.getLonLatFromViewPortPx({\n- x: 0,\n- y: 0\n- });\n- var br = this.getLonLatFromViewPortPx({\n- x: size.w,\n- y: size.h\n- });\n- if (tl != null && br != null) {\n- return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat)\n+ getTile: function() {\n+ return this.frame ? this.frame : this.getImage()\n+ },\n+ createBackBuffer: function() {\n+ if (!this.imgDiv || this.isLoading) {\n+ return\n+ }\n+ var backBuffer;\n+ if (this.frame) {\n+ backBuffer = this.frame.cloneNode(false);\n+ backBuffer.appendChild(this.imgDiv)\n } else {\n- return null\n+ backBuffer = this.imgDiv\n }\n+ this.imgDiv = null;\n+ return backBuffer\n },\n- getZoomForResolution: function(resolution) {\n- if (this.resolutions != null) {\n- return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments)\n- } else {\n- var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n- return this.getZoomForExtent(extent)\n+ onImageLoad: function() {\n+ var img = this.imgDiv;\n+ this.stopLoading();\n+ img.style.visibility = \"inherit\";\n+ img.style.opacity = this.layer.opacity;\n+ this.isLoading = false;\n+ this.canvasContext = null;\n+ this.events.triggerEvent(\"loadend\");\n+ if (this.layerAlphaHack === true) {\n+ img.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\" + img.src + \"', sizingMethod='scale')\"\n }\n },\n- getOLZoomFromMapObjectZoom: function(moZoom) {\n- var zoom = null;\n- if (moZoom != null) {\n- zoom = moZoom - this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom))\n+ onImageError: function() {\n+ var img = this.imgDiv;\n+ if (img.src != null) {\n+ this.imageReloadAttempts++;\n+ if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {\n+ this.setImgSrc(this.layer.getURL(this.bounds))\n+ } else {\n+ OpenLayers.Element.addClass(img, \"olImageLoadError\");\n+ this.events.triggerEvent(\"loaderror\");\n+ this.onImageLoad()\n }\n }\n- return zoom\n },\n- getMapObjectZoomFromOLZoom: function(olZoom) {\n- var zoom = null;\n- if (olZoom != null) {\n- zoom = olZoom + this.minZoomLevel;\n- if (this.map.baseLayer !== this) {\n- zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom))\n+ stopLoading: function() {\n+ OpenLayers.Event.stopObservingElement(this.imgDiv);\n+ window.clearTimeout(this._loadTimeout);\n+ delete this._loadTimeout\n+ },\n+ getCanvasContext: function() {\n+ if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {\n+ if (!this.canvasContext) {\n+ var canvas = document.createElement(\"canvas\");\n+ canvas.width = this.size.w;\n+ canvas.height = this.size.h;\n+ this.canvasContext = canvas.getContext(\"2d\");\n+ this.canvasContext.drawImage(this.imgDiv, 0, 0)\n }\n+ return this.canvasContext\n }\n- return zoom\n },\n- CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n+ CLASS_NAME: \"OpenLayers.Tile.Image\"\n });\n-OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {\n- MIN_ZOOM_LEVEL: 0,\n- MAX_ZOOM_LEVEL: 21,\n- RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7],\n- type: null,\n- wrapDateLine: true,\n- sphericalMercator: false,\n- version: null,\n- initialize: function(name, options) {\n- options = options || {};\n- if (!options.version) {\n- options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\"\n- }\n- var mixin = OpenLayers.Layer.Google[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (mixin) {\n- OpenLayers.Util.applyDefaults(options, mixin)\n- } else {\n- throw \"Unsupported Google Maps API version: \" + options.version\n- }\n- OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n- if (options.maxExtent) {\n- options.maxExtent = options.maxExtent.clone()\n+OpenLayers.Tile.Image.IMAGE = function() {\n+ var img = new Image;\n+ img.className = \"olTileImage\";\n+ img.galleryImg = \"no\";\n+ return img\n+}();\n+OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {\n+ tileSize: null,\n+ tileOriginCorner: \"bl\",\n+ tileOrigin: null,\n+ tileOptions: null,\n+ tileClass: OpenLayers.Tile.Image,\n+ grid: null,\n+ singleTile: false,\n+ ratio: 1.5,\n+ buffer: 0,\n+ transitionEffect: \"resize\",\n+ numLoadingTiles: 0,\n+ serverResolutions: null,\n+ loading: false,\n+ backBuffer: null,\n+ gridResolution: null,\n+ backBufferResolution: null,\n+ backBufferLonLat: null,\n+ backBufferTimerId: null,\n+ removeBackBufferDelay: null,\n+ className: null,\n+ gridLayout: null,\n+ rowSign: null,\n+ transitionendEvents: [\"transitionend\", \"webkitTransitionEnd\", \"otransitionend\", \"oTransitionEnd\"],\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments);\n+ this.grid = [];\n+ this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);\n+ this.initProperties();\n+ this.rowSign = this.tileOriginCorner.substr(0, 1) === \"t\" ? 1 : -1\n+ },\n+ initProperties: function() {\n+ if (this.options.removeBackBufferDelay === undefined) {\n+ this.removeBackBufferDelay = this.singleTile ? 0 : 2500\n }\n- OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]);\n- OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]);\n- if (this.sphericalMercator) {\n- OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n- this.initMercatorParameters()\n+ if (this.options.className === undefined) {\n+ this.className = this.singleTile ? \"olLayerGridSingleTile\" : \"olLayerGrid\"\n }\n },\n- clone: function() {\n- return new OpenLayers.Layer.Google(this.name, this.getOptions())\n- },\n- setVisibility: function(visible) {\n- var opacity = this.opacity == null ? 1 : this.opacity;\n- OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n- this.setOpacity(opacity)\n+ setMap: function(map) {\n+ OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);\n+ OpenLayers.Element.addClass(this.div, this.className)\n },\n- display: function(visible) {\n- if (!this._dragging) {\n- this.setGMapVisibility(visible)\n- }\n- OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments)\n+ removeMap: function(map) {\n+ this.removeBackBuffer()\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- this._dragging = dragging;\n- OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n- delete this._dragging\n+ destroy: function() {\n+ this.removeBackBuffer();\n+ this.clearGrid();\n+ this.grid = null;\n+ this.tileSize = null;\n+ OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments)\n },\n- setOpacity: function(opacity) {\n- if (opacity !== this.opacity) {\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- })\n+ clearGrid: function() {\n+ if (this.grid) {\n+ for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {\n+ var row = this.grid[iRow];\n+ for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {\n+ var tile = row[iCol];\n+ this.destroyTile(tile)\n+ }\n }\n- this.opacity = opacity\n- }\n- if (this.getVisibility()) {\n- var container = this.getMapContainer();\n- OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity)\n+ this.grid = [];\n+ this.gridResolution = null;\n+ this.gridLayout = null\n }\n },\n- destroy: function() {\n- if (this.map) {\n- this.setGMapVisibility(false);\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache && cache.count <= 1) {\n- this.removeGMapElements()\n- }\n+ addOptions: function(newOptions, reinitialize) {\n+ var singleTileChanged = newOptions.singleTile !== undefined && newOptions.singleTile !== this.singleTile;\n+ OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);\n+ if (this.map && singleTileChanged) {\n+ this.initProperties();\n+ this.clearGrid();\n+ this.tileSize = this.options.tileSize;\n+ this.setTileSize();\n+ this.moveTo(null, true)\n }\n- OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments)\n },\n- removeGMapElements: function() {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject && this.getMapContainer();\n- if (container && container.parentNode) {\n- container.parentNode.removeChild(container)\n- }\n- var termsOfUse = cache.termsOfUse;\n- if (termsOfUse && termsOfUse.parentNode) {\n- termsOfUse.parentNode.removeChild(termsOfUse)\n- }\n- var poweredBy = cache.poweredBy;\n- if (poweredBy && poweredBy.parentNode) {\n- poweredBy.parentNode.removeChild(poweredBy)\n- }\n- if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) {\n- google.maps.event.clearListeners(this.mapObject, \"tilesloaded\")\n- }\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions())\n }\n- },\n- removeMap: function(map) {\n- if (this.visibility && this.mapObject) {\n- this.setGMapVisibility(false)\n+ obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone()\n }\n- var cache = OpenLayers.Layer.Google.cache[map.id];\n- if (cache) {\n- if (cache.count <= 1) {\n- this.removeGMapElements();\n- delete OpenLayers.Layer.Google.cache[map.id]\n+ obj.grid = [];\n+ obj.gridResolution = null;\n+ obj.backBuffer = null;\n+ obj.backBufferTimerId = null;\n+ obj.loading = false;\n+ obj.numLoadingTiles = 0;\n+ return obj\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);\n+ bounds = bounds || this.map.getExtent();\n+ if (bounds != null) {\n+ var forceReTile = !this.grid.length || zoomChanged;\n+ var tilesBounds = this.getTilesBounds();\n+ var resolution = this.map.getResolution();\n+ var serverResolution = this.getServerResolution(resolution);\n+ if (this.singleTile) {\n+ if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) {\n+ if (zoomChanged && this.transitionEffect !== \"resize\") {\n+ this.removeBackBuffer()\n+ }\n+ if (!zoomChanged || this.transitionEffect === \"resize\") {\n+ this.applyBackBuffer(resolution)\n+ }\n+ this.initSingleTile(bounds)\n+ }\n } else {\n- --cache.count\n+ forceReTile = forceReTile || !tilesBounds.intersectsBounds(bounds, {\n+ worldBounds: this.map.baseLayer.wrapDateLine && this.map.getMaxExtent()\n+ });\n+ if (forceReTile) {\n+ if (zoomChanged && (this.transitionEffect === \"resize\" || this.gridResolution === resolution)) {\n+ this.applyBackBuffer(resolution)\n+ }\n+ this.initGriddedTiles(bounds)\n+ } else {\n+ this.moveGriddedTiles()\n+ }\n }\n }\n- delete this.termsOfUse;\n- delete this.poweredBy;\n- delete this.mapObject;\n- delete this.dragObject;\n- OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments)\n },\n- getOLBoundsFromMapObjectBounds: function(moBounds) {\n- var olBounds = null;\n- if (moBounds != null) {\n- var sw = moBounds.getSouthWest();\n- var ne = moBounds.getNorthEast();\n- if (this.sphericalMercator) {\n- sw = this.forwardMercator(sw.lng(), sw.lat());\n- ne = this.forwardMercator(ne.lng(), ne.lat())\n- } else {\n- sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n- ne = new OpenLayers.LonLat(ne.lng(), ne.lat())\n+ getTileData: function(loc) {\n+ var data = null,\n+ x = loc.lon,\n+ y = loc.lat,\n+ numRows = this.grid.length;\n+ if (this.map && numRows) {\n+ var res = this.map.getResolution(),\n+ tileWidth = this.tileSize.w,\n+ tileHeight = this.tileSize.h,\n+ bounds = this.grid[0][0].bounds,\n+ left = bounds.left,\n+ top = bounds.top;\n+ if (x < left) {\n+ if (this.map.baseLayer.wrapDateLine) {\n+ var worldWidth = this.map.getMaxExtent().getWidth();\n+ var worldsAway = Math.ceil((left - x) / worldWidth);\n+ x += worldWidth * worldsAway\n+ }\n+ }\n+ var dtx = (x - left) / (res * tileWidth);\n+ var dty = (top - y) / (res * tileHeight);\n+ var col = Math.floor(dtx);\n+ var row = Math.floor(dty);\n+ if (row >= 0 && row < numRows) {\n+ var tile = this.grid[row][col];\n+ if (tile) {\n+ data = {\n+ tile: tile,\n+ i: Math.floor((dtx - col) * tileWidth),\n+ j: Math.floor((dty - row) * tileHeight)\n+ }\n+ }\n }\n- olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat)\n }\n- return olBounds\n- },\n- getWarningHTML: function() {\n- return OpenLayers.i18n(\"googleWarning\")\n- },\n- getMapObjectCenter: function() {\n- return this.mapObject.getCenter()\n- },\n- getMapObjectZoom: function() {\n- return this.mapObject.getZoom()\n- },\n- getLongitudeFromMapObjectLonLat: function(moLonLat) {\n- return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng()\n+ return data\n },\n- getLatitudeFromMapObjectLonLat: function(moLonLat) {\n- var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat();\n- return lat\n+ destroyTile: function(tile) {\n+ this.removeTileMonitoringHooks(tile);\n+ tile.destroy()\n },\n- getXFromMapObjectPixel: function(moPixel) {\n- return moPixel.x\n+ getServerResolution: function(resolution) {\n+ var distance = Number.POSITIVE_INFINITY;\n+ resolution = resolution || this.map.getResolution();\n+ if (this.serverResolutions && OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {\n+ var i, newDistance, newResolution, serverResolution;\n+ for (i = this.serverResolutions.length - 1; i >= 0; i--) {\n+ newResolution = this.serverResolutions[i];\n+ newDistance = Math.abs(newResolution - resolution);\n+ if (newDistance > distance) {\n+ break\n+ }\n+ distance = newDistance;\n+ serverResolution = newResolution\n+ }\n+ resolution = serverResolution\n+ }\n+ return resolution\n },\n- getYFromMapObjectPixel: function(moPixel) {\n- return moPixel.y\n+ getServerZoom: function() {\n+ var resolution = this.getServerResolution();\n+ return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0)\n },\n- CLASS_NAME: \"OpenLayers.Layer.Google\"\n-});\n-OpenLayers.Layer.Google.cache = {};\n-OpenLayers.Layer.Google.v2 = {\n- termsOfUse: null,\n- poweredBy: null,\n- dragObject: null,\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = G_NORMAL_MAP\n+ applyBackBuffer: function(resolution) {\n+ if (this.backBufferTimerId !== null) {\n+ this.removeBackBuffer()\n }\n- var mapObject, termsOfUse, poweredBy;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- mapObject = cache.mapObject;\n- termsOfUse = cache.termsOfUse;\n- poweredBy = cache.poweredBy;\n- ++cache.count\n- } else {\n- var container = this.map.viewPortDiv;\n- var div = document.createElement(\"div\");\n- div.id = this.map.id + \"_GMap2Container\";\n- div.style.position = \"absolute\";\n- div.style.width = \"100%\";\n- div.style.height = \"100%\";\n- container.appendChild(div);\n- try {\n- mapObject = new GMap2(div);\n- termsOfUse = div.lastChild;\n- container.appendChild(termsOfUse);\n- termsOfUse.style.zIndex = \"1100\";\n- termsOfUse.style.right = \"\";\n- termsOfUse.style.bottom = \"\";\n- termsOfUse.className = \"olLayerGoogleCopyright\";\n- poweredBy = div.lastChild;\n- container.appendChild(poweredBy);\n- poweredBy.style.zIndex = \"1100\";\n- poweredBy.style.right = \"\";\n- poweredBy.style.bottom = \"\";\n- poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\"\n- } catch (e) {\n- throw e\n+ var backBuffer = this.backBuffer;\n+ if (!backBuffer) {\n+ backBuffer = this.createBackBuffer();\n+ if (!backBuffer) {\n+ return\n }\n- OpenLayers.Layer.Google.cache[this.map.id] = {\n- mapObject: mapObject,\n- termsOfUse: termsOfUse,\n- poweredBy: poweredBy,\n- count: 1\n+ if (resolution === this.gridResolution) {\n+ this.div.insertBefore(backBuffer, this.div.firstChild)\n+ } else {\n+ this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div)\n }\n+ this.backBuffer = backBuffer;\n+ var topLeftTileBounds = this.grid[0][0].bounds;\n+ this.backBufferLonLat = {\n+ lon: topLeftTileBounds.left,\n+ lat: topLeftTileBounds.top\n+ };\n+ this.backBufferResolution = this.gridResolution\n }\n- this.mapObject = mapObject;\n- this.termsOfUse = termsOfUse;\n- this.poweredBy = poweredBy;\n- if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) {\n- this.mapObject.addMapType(this.type)\n- }\n- if (typeof mapObject.getDragObject == \"function\") {\n- this.dragObject = mapObject.getDragObject()\n- } else {\n- this.dragPanMapObject = null\n- }\n- if (this.isBaseLayer === false) {\n- this.setGMapVisibility(this.div.style.display !== \"none\")\n+ var ratio = this.backBufferResolution / resolution;\n+ var tiles = backBuffer.childNodes,\n+ tile;\n+ for (var i = tiles.length - 1; i >= 0; --i) {\n+ tile = tiles[i];\n+ tile.style.top = (ratio * tile._i * tile._h | 0) + \"px\";\n+ tile.style.left = (ratio * tile._j * tile._w | 0) + \"px\";\n+ tile.style.width = Math.round(ratio * tile._w) + \"px\";\n+ tile.style.height = Math.round(ratio * tile._h) + \"px\"\n }\n+ var position = this.getViewPortPxFromLonLat(this.backBufferLonLat, resolution);\n+ var leftOffset = this.map.layerContainerOriginPx.x;\n+ var topOffset = this.map.layerContainerOriginPx.y;\n+ backBuffer.style.left = Math.round(position.x - leftOffset) + \"px\";\n+ backBuffer.style.top = Math.round(position.y - topOffset) + \"px\"\n },\n- onMapResize: function() {\n- if (this.visibility && this.mapObject.isLoaded()) {\n- this.mapObject.checkResize()\n- } else {\n- if (!this._resized) {\n- var layer = this;\n- var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n- GEvent.removeListener(handle);\n- delete layer._resized;\n- layer.mapObject.checkResize();\n- layer.moveTo(layer.map.getCenter(), layer.map.getZoom())\n- })\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.grid.length > 0) {\n+ backBuffer = document.createElement(\"div\");\n+ backBuffer.id = this.div.id + \"_bb\";\n+ backBuffer.className = \"olBackBuffer\";\n+ backBuffer.style.position = \"absolute\";\n+ var map = this.map;\n+ backBuffer.style.zIndex = this.transitionEffect === \"resize\" ? this.getZIndex() - 1 : map.Z_INDEX_BASE.BaseLayer - (map.getNumLayers() - map.getLayerIndex(this));\n+ for (var i = 0, lenI = this.grid.length; i < lenI; i++) {\n+ for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) {\n+ var tile = this.grid[i][j],\n+ markup = this.grid[i][j].createBackBuffer();\n+ if (markup) {\n+ markup._i = i;\n+ markup._j = j;\n+ markup._w = tile.size.w;\n+ markup._h = tile.size.h;\n+ markup.id = tile.id + \"_bb\";\n+ backBuffer.appendChild(markup)\n+ }\n+ }\n }\n- this._resized = true\n }\n+ return backBuffer\n },\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject.getContainer();\n- if (visible === true) {\n- this.mapObject.setMapType(this.type);\n- container.style.display = \"\";\n- this.termsOfUse.style.left = \"\";\n- this.termsOfUse.style.display = \"\";\n- this.poweredBy.style.display = \"\";\n- cache.displayed = this.id\n- } else {\n- if (cache.displayed === this.id) {\n- delete cache.displayed\n- }\n- if (!cache.displayed) {\n- container.style.display = \"none\";\n- this.termsOfUse.style.display = \"none\";\n- this.termsOfUse.style.left = \"-9999px\";\n- this.poweredBy.style.display = \"none\"\n- }\n+ removeBackBuffer: function() {\n+ if (this._transitionElement) {\n+ for (var i = this.transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.stopObserving(this._transitionElement, this.transitionendEvents[i], this._removeBackBuffer)\n }\n+ delete this._transitionElement\n }\n- },\n- getMapContainer: function() {\n- return this.mapObject.getContainer()\n- },\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon))\n+ if (this.backBuffer) {\n+ if (this.backBuffer.parentNode) {\n+ this.backBuffer.parentNode.removeChild(this.backBuffer)\n+ }\n+ this.backBuffer = null;\n+ this.backBufferResolution = null;\n+ if (this.backBufferTimerId !== null) {\n+ window.clearTimeout(this.backBufferTimerId);\n+ this.backBufferTimerId = null\n+ }\n }\n- return moBounds\n- },\n- setMapObjectCenter: function(center, zoom) {\n- this.mapObject.setCenter(center, zoom)\n },\n- dragPanMapObject: function(dX, dY) {\n- this.dragObject.moveBy(new GSize(-dX, dY))\n- },\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- return this.mapObject.fromContainerPixelToLatLng(moPixel)\n+ moveByPx: function(dx, dy) {\n+ if (!this.singleTile) {\n+ this.moveGriddedTiles()\n+ }\n },\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- return this.mapObject.fromLatLngToContainerPixel(moLonLat)\n+ setTileSize: function(size) {\n+ if (this.singleTile) {\n+ size = this.map.getSize();\n+ size.h = parseInt(size.h * this.ratio, 10);\n+ size.w = parseInt(size.w * this.ratio, 10)\n+ }\n+ OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size])\n },\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds)\n+ getTilesBounds: function() {\n+ var bounds = null;\n+ var length = this.grid.length;\n+ if (length) {\n+ var bottomLeftTileBounds = this.grid[length - 1][0].bounds,\n+ width = this.grid[0].length * bottomLeftTileBounds.getWidth(),\n+ height = this.grid.length * bottomLeftTileBounds.getHeight();\n+ bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height)\n+ }\n+ return bounds\n },\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new GLatLng(lonlat.lat, lonlat.lon)\n+ initSingleTile: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+ var center = bounds.getCenterLonLat();\n+ var tileWidth = bounds.getWidth() * this.ratio;\n+ var tileHeight = bounds.getHeight() * this.ratio;\n+ var tileBounds = new OpenLayers.Bounds(center.lon - tileWidth / 2, center.lat - tileHeight / 2, center.lon + tileWidth / 2, center.lat + tileHeight / 2);\n+ var px = this.map.getLayerPxFromLonLat({\n+ lon: tileBounds.left,\n+ lat: tileBounds.top\n+ });\n+ if (!this.grid.length) {\n+ this.grid[0] = []\n+ }\n+ var tile = this.grid[0][0];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ tile.draw();\n+ this.grid[0][0] = tile\n } else {\n- gLatLng = new GLatLng(lat, lon)\n+ tile.moveTo(tileBounds, px)\n }\n- return gLatLng\n- },\n- getMapObjectPixelFromXY: function(x, y) {\n- return new GPoint(x, y)\n- }\n-};\n-OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- DEFAULT_PARAMS: {\n- i: \"jpeg\",\n- map: \"\"\n- },\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n- this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS)\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round(this.map.getScale() * 1e4) / 1e4;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n- return this.getFullRequestString({\n- t: pY,\n- l: pX,\n- s: scale\n- })\n+ this.removeExcessTiles(1, 1);\n+ this.gridResolution = this.getServerResolution()\n },\n calculateGridLayout: function(bounds, origin, resolution) {\n var tilelon = resolution * this.tileSize.w;\n var tilelat = resolution * this.tileSize.h;\n- var offsetlon = bounds.left;\n+ var offsetlon = bounds.left - origin.lon;\n var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n- var offsetlat = bounds.top;\n- var tilerow = Math.floor(offsetlat / tilelat) + this.buffer;\n+ var rowSign = this.rowSign;\n+ var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);\n+ var tilerow = Math[~rowSign ? \"floor\" : \"ceil\"](offsetlat / tilelat) - this.buffer * rowSign;\n return {\n tilelon: tilelon,\n tilelat: tilelat,\n startcol: tilecol,\n startrow: tilerow\n }\n },\n+ getTileOrigin: function() {\n+ var origin = this.tileOrigin;\n+ if (!origin) {\n+ var extent = this.getMaxExtent();\n+ var edges = {\n+ tl: [\"left\", \"top\"],\n+ tr: [\"right\", \"top\"],\n+ bl: [\"left\", \"bottom\"],\n+ br: [\"right\", \"bottom\"]\n+ } [this.tileOriginCorner];\n+ origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]])\n+ }\n+ return origin\n+ },\n getTileBoundsForGridIndex: function(row, col) {\n var origin = this.getTileOrigin();\n var tileLayout = this.gridLayout;\n var tilelon = tileLayout.tilelon;\n var tilelat = tileLayout.tilelat;\n- var minX = (tileLayout.startcol + col) * tilelon;\n- var minY = (tileLayout.startrow - row) * tilelat;\n- return new OpenLayers.Bounds(minX, minY, minX + tilelon, minY + tilelat)\n+ var startcol = tileLayout.startcol;\n+ var startrow = tileLayout.startrow;\n+ var rowSign = this.rowSign;\n+ return new OpenLayers.Bounds(origin.lon + (startcol + col) * tilelon, origin.lat - (startrow + row * rowSign) * tilelat * rowSign, origin.lon + (startcol + col + 1) * tilelon, origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign)\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.KaMap(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone()\n+ initGriddedTiles: function(bounds) {\n+ this.events.triggerEvent(\"retile\");\n+ var viewSize = this.map.getSize();\n+ var origin = this.getTileOrigin();\n+ var resolution = this.map.getResolution(),\n+ serverResolution = this.getServerResolution(),\n+ ratio = resolution / serverResolution,\n+ tileSize = {\n+ w: this.tileSize.w / ratio,\n+ h: this.tileSize.h / ratio\n+ };\n+ var minRows = Math.ceil(viewSize.h / tileSize.h) + 2 * this.buffer + 1;\n+ var minCols = Math.ceil(viewSize.w / tileSize.w) + 2 * this.buffer + 1;\n+ var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);\n+ this.gridLayout = tileLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var layerContainerDivLeft = this.map.layerContainerOriginPx.x;\n+ var layerContainerDivTop = this.map.layerContainerOriginPx.y;\n+ var tileBounds = this.getTileBoundsForGridIndex(0, 0);\n+ var startPx = this.map.getViewPortPxFromLonLat(new OpenLayers.LonLat(tileBounds.left, tileBounds.top));\n+ startPx.x = Math.round(startPx.x) - layerContainerDivLeft;\n+ startPx.y = Math.round(startPx.y) - layerContainerDivTop;\n+ var tileData = [],\n+ center = this.map.getCenter();\n+ var rowidx = 0;\n+ do {\n+ var row = this.grid[rowidx];\n+ if (!row) {\n+ row = [];\n+ this.grid.push(row)\n+ }\n+ var colidx = 0;\n+ do {\n+ tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);\n+ var px = startPx.clone();\n+ px.x = px.x + colidx * Math.round(tileSize.w);\n+ px.y = px.y + rowidx * Math.round(tileSize.h);\n+ var tile = row[colidx];\n+ if (!tile) {\n+ tile = this.addTile(tileBounds, px);\n+ this.addTileMonitoringHooks(tile);\n+ row.push(tile)\n+ } else {\n+ tile.moveTo(tileBounds, px, false)\n+ }\n+ var tileCenter = tileBounds.getCenterLonLat();\n+ tileData.push({\n+ tile: tile,\n+ distance: Math.pow(tileCenter.lon - center.lon, 2) + Math.pow(tileCenter.lat - center.lat, 2)\n+ });\n+ colidx += 1\n+ } while (tileBounds.right <= bounds.right + tilelon * this.buffer || colidx < minCols);\n+ rowidx += 1\n+ } while (tileBounds.bottom >= bounds.bottom - tilelat * this.buffer || rowidx < minRows);\n+ this.removeExcessTiles(rowidx, colidx);\n+ var resolution = this.getServerResolution();\n+ this.gridResolution = resolution;\n+ tileData.sort(function(a, b) {\n+ return a.distance - b.distance\n+ });\n+ for (var i = 0, ii = tileData.length; i < ii; ++i) {\n+ tileData[i].tile.draw()\n }\n- obj.grid = [];\n- return obj\n },\n- getTileBounds: function(viewPortPx) {\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);\n- var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);\n- return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n+ getMaxExtent: function() {\n+ return this.maxExtent\n },\n- CLASS_NAME: \"OpenLayers.Layer.KaMap\"\n-});\n-OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n- defaultStyle: null,\n- extractStyles: true,\n- initialize: function(options) {\n- options = options || {};\n- if (options.extractStyles !== false) {\n- options.defaultStyle = {\n- externalGraphic: OpenLayers.Util.getImageLocation(\"marker.png\"),\n- graphicWidth: 21,\n- graphicHeight: 25,\n- graphicXOffset: -10.5,\n- graphicYOffset: -12.5\n- }\n- }\n- OpenLayers.Format.prototype.initialize.apply(this, [options])\n+ addTile: function(bounds, position) {\n+ var tile = new this.tileClass(this, position, bounds, null, this.tileSize, this.tileOptions);\n+ this.events.triggerEvent(\"addtile\", {\n+ tile: tile\n+ });\n+ return tile\n },\n- read: function(text) {\n- var lines = text.split(\"\\n\");\n- var columns;\n- var features = [];\n- for (var lcv = 0; lcv < lines.length - 1; lcv++) {\n- var currLine = lines[lcv].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\");\n- if (currLine.charAt(0) != \"#\") {\n- if (!columns) {\n- columns = currLine.split(\"\\t\")\n- } else {\n- var vals = currLine.split(\"\\t\");\n- var geometry = new OpenLayers.Geometry.Point(0, 0);\n- var attributes = {};\n- var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null;\n- var icon, iconSize, iconOffset, overflow;\n- var set = false;\n- for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n- if (vals[valIndex]) {\n- if (columns[valIndex] == \"point\") {\n- var coords = vals[valIndex].split(\",\");\n- geometry.y = parseFloat(coords[0]);\n- geometry.x = parseFloat(coords[1]);\n- set = true\n- } else if (columns[valIndex] == \"lat\") {\n- geometry.y = parseFloat(vals[valIndex]);\n- set = true\n- } else if (columns[valIndex] == \"lon\") {\n- geometry.x = parseFloat(vals[valIndex]);\n- set = true\n- } else if (columns[valIndex] == \"title\") attributes[\"title\"] = vals[valIndex];\n- else if (columns[valIndex] == \"image\" || columns[valIndex] == \"icon\" && style) {\n- style[\"externalGraphic\"] = vals[valIndex]\n- } else if (columns[valIndex] == \"iconSize\" && style) {\n- var size = vals[valIndex].split(\",\");\n- style[\"graphicWidth\"] = parseFloat(size[0]);\n- style[\"graphicHeight\"] = parseFloat(size[1])\n- } else if (columns[valIndex] == \"iconOffset\" && style) {\n- var offset = vals[valIndex].split(\",\");\n- style[\"graphicXOffset\"] = parseFloat(offset[0]);\n- style[\"graphicYOffset\"] = parseFloat(offset[1])\n- } else if (columns[valIndex] == \"description\") {\n- attributes[\"description\"] = vals[valIndex]\n- } else if (columns[valIndex] == \"overflow\") {\n- attributes[\"overflow\"] = vals[valIndex]\n- } else {\n- attributes[columns[valIndex]] = vals[valIndex]\n- }\n- }\n+ addTileMonitoringHooks: function(tile) {\n+ var replacingCls = \"olTileReplacing\";\n+ tile.onLoadStart = function() {\n+ if (this.loading === false) {\n+ this.loading = true;\n+ this.events.triggerEvent(\"loadstart\")\n+ }\n+ this.events.triggerEvent(\"tileloadstart\", {\n+ tile: tile\n+ });\n+ this.numLoadingTiles++;\n+ if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ OpenLayers.Element.addClass(tile.getTile(), replacingCls)\n+ }\n+ };\n+ tile.onLoadEnd = function(evt) {\n+ this.numLoadingTiles--;\n+ var aborted = evt.type === \"unload\";\n+ this.events.triggerEvent(\"tileloaded\", {\n+ tile: tile,\n+ aborted: aborted\n+ });\n+ if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {\n+ var tileDiv = tile.getTile();\n+ if (OpenLayers.Element.getStyle(tileDiv, \"display\") === \"none\") {\n+ var bufferTile = document.getElementById(tile.id + \"_bb\");\n+ if (bufferTile) {\n+ bufferTile.parentNode.removeChild(bufferTile)\n }\n- if (set) {\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection, this.internalProjection)\n+ }\n+ OpenLayers.Element.removeClass(tileDiv, replacingCls)\n+ }\n+ if (this.numLoadingTiles === 0) {\n+ if (this.backBuffer) {\n+ if (this.backBuffer.childNodes.length === 0) {\n+ this.removeBackBuffer()\n+ } else {\n+ this._transitionElement = aborted ? this.div.lastChild : tile.imgDiv;\n+ var transitionendEvents = this.transitionendEvents;\n+ for (var i = transitionendEvents.length - 1; i >= 0; --i) {\n+ OpenLayers.Event.observe(this._transitionElement, transitionendEvents[i], this._removeBackBuffer)\n }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n- features.push(feature)\n+ this.backBufferTimerId = window.setTimeout(this._removeBackBuffer, this.removeBackBufferDelay)\n }\n }\n+ this.loading = false;\n+ this.events.triggerEvent(\"loadend\")\n }\n- }\n- return features\n- },\n- CLASS_NAME: \"OpenLayers.Format.Text\"\n-});\n-OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {\n- location: null,\n- features: null,\n- formatOptions: null,\n- selectedFeature: null,\n- initialize: function(name, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);\n- this.features = []\n+ };\n+ tile.onLoadError = function() {\n+ this.events.triggerEvent(\"tileerror\", {\n+ tile: tile\n+ })\n+ };\n+ tile.events.on({\n+ loadstart: tile.onLoadStart,\n+ loadend: tile.onLoadEnd,\n+ unload: tile.onLoadEnd,\n+ loaderror: tile.onLoadError,\n+ scope: this\n+ })\n },\n- destroy: function() {\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ loadstart: tile.onLoadStart,\n+ loadend: tile.onLoadEnd,\n+ unload: tile.onLoadEnd,\n+ loaderror: tile.onLoadError,\n+ scope: this\n+ })\n },\n- loadText: function() {\n- if (!this.loaded) {\n- if (this.location != null) {\n- var onFail = function(e) {\n- this.events.triggerEvent(\"loadend\")\n- };\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- failure: onFail,\n- scope: this\n- });\n- this.loaded = true\n+ moveGriddedTiles: function() {\n+ var buffer = this.buffer + 1;\n+ while (true) {\n+ var tlTile = this.grid[0][0];\n+ var tlViewPort = {\n+ x: tlTile.position.x + this.map.layerContainerOriginPx.x,\n+ y: tlTile.position.y + this.map.layerContainerOriginPx.y\n+ };\n+ var ratio = this.getServerResolution() / this.map.getResolution();\n+ var tileSize = {\n+ w: Math.round(this.tileSize.w * ratio),\n+ h: Math.round(this.tileSize.h * ratio)\n+ };\n+ if (tlViewPort.x > -tileSize.w * (buffer - 1)) {\n+ this.shiftColumn(true, tileSize)\n+ } else if (tlViewPort.x < -tileSize.w * buffer) {\n+ this.shiftColumn(false, tileSize)\n+ } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {\n+ this.shiftRow(true, tileSize)\n+ } else if (tlViewPort.y < -tileSize.h * buffer) {\n+ this.shiftRow(false, tileSize)\n+ } else {\n+ break\n }\n }\n },\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadText()\n+ shiftRow: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var rowIndex = prepend ? 0 : grid.length - 1;\n+ var sign = prepend ? -1 : 1;\n+ var rowSign = this.rowSign;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startrow += sign * rowSign;\n+ var modelRow = grid[rowIndex];\n+ var row = grid[prepend ? \"pop\" : \"shift\"]();\n+ for (var i = 0, len = row.length; i < len; i++) {\n+ var tile = row[i];\n+ var position = modelRow[i].position.clone();\n+ position.y += tileSize.h * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position)\n }\n+ grid[prepend ? \"unshift\" : \"push\"](row)\n },\n- parseData: function(ajaxRequest) {\n- var text = ajaxRequest.responseText;\n- var options = {};\n- OpenLayers.Util.extend(options, this.formatOptions);\n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject()\n+ shiftColumn: function(prepend, tileSize) {\n+ var grid = this.grid;\n+ var colIndex = prepend ? 0 : grid[0].length - 1;\n+ var sign = prepend ? -1 : 1;\n+ var tileLayout = this.gridLayout;\n+ tileLayout.startcol += sign;\n+ for (var i = 0, len = grid.length; i < len; i++) {\n+ var row = grid[i];\n+ var position = row[colIndex].position.clone();\n+ var tile = row[prepend ? \"pop\" : \"shift\"]();\n+ position.x += tileSize.w * sign;\n+ tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);\n+ row[prepend ? \"unshift\" : \"push\"](tile)\n }\n- var parser = new OpenLayers.Format.Text(options);\n- var features = parser.read(text);\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n- var location;\n- var iconSize, iconOffset;\n- location = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y);\n- if (feature.style.graphicWidth && feature.style.graphicHeight) {\n- iconSize = new OpenLayers.Size(feature.style.graphicWidth, feature.style.graphicHeight)\n- }\n- if (feature.style.graphicXOffset !== undefined && feature.style.graphicYOffset !== undefined) {\n- iconOffset = new OpenLayers.Pixel(feature.style.graphicXOffset, feature.style.graphicYOffset)\n- }\n- if (feature.style.externalGraphic != null) {\n- data.icon = new OpenLayers.Icon(feature.style.externalGraphic, iconSize, iconOffset)\n- } else {\n- data.icon = OpenLayers.Marker.defaultIcon();\n- if (iconSize != null) {\n- data.icon.setSize(iconSize)\n- }\n- }\n- if (feature.attributes.title != null && feature.attributes.description != null) {\n- data[\"popupContentHTML\"] = \"<h2>\" + feature.attributes.title + \"</h2>\" + \"<p>\" + feature.attributes.description + \"</p>\"\n+ },\n+ removeExcessTiles: function(rows, columns) {\n+ var i, l;\n+ while (this.grid.length > rows) {\n+ var row = this.grid.pop();\n+ for (i = 0, l = row.length; i < l; i++) {\n+ var tile = row[i];\n+ this.destroyTile(tile)\n }\n- data[\"overflow\"] = feature.attributes.overflow || \"auto\";\n- var markerFeature = new OpenLayers.Feature(this, location, data);\n- this.features.push(markerFeature);\n- var marker = markerFeature.createMarker();\n- if (feature.attributes.title != null && feature.attributes.description != null) {\n- marker.events.register(\"click\", markerFeature, this.markerClick)\n+ }\n+ for (i = 0, l = this.grid.length; i < l; i++) {\n+ while (this.grid[i].length > columns) {\n+ var row = this.grid[i];\n+ var tile = row.pop();\n+ this.destroyTile(tile)\n }\n- this.addMarker(marker)\n }\n- this.events.triggerEvent(\"loadend\")\n },\n- markerClick: function(evt) {\n- var sameMarkerClicked = this == this.layer.selectedFeature;\n- this.layer.selectedFeature = !sameMarkerClicked ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i])\n- }\n- if (!sameMarkerClicked) {\n- this.layer.map.addPopup(this.createPopup())\n+ onMapResize: function() {\n+ if (this.singleTile) {\n+ this.clearGrid();\n+ this.setTileSize()\n }\n- OpenLayers.Event.stop(evt)\n },\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy()\n- }\n- }\n+ getTileBounds: function(viewPortPx) {\n+ var maxExtent = this.maxExtent;\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth);\n+ var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight);\n+ return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n },\n- CLASS_NAME: \"OpenLayers.Layer.Text\"\n+ CLASS_NAME: \"OpenLayers.Layer.Grid\"\n });\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- key: null,\n- serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n- metadata: null,\n- protocolRegex: /^http:/i,\n- type: \"Road\",\n- culture: \"en-US\",\n- metadataParams: null,\n- tileOptions: null,\n- protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n+OpenLayers.TileManager = OpenLayers.Class({\n+ cacheSize: 256,\n+ tilesPerFrame: 2,\n+ frameDelay: 16,\n+ moveDelay: 100,\n+ zoomDelay: 200,\n+ maps: null,\n+ tileQueueId: null,\n+ tileQueue: null,\n+ tileCache: null,\n+ tileCacheIndex: null,\n initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options.tileOptions);\n- this.loadMetadata()\n- },\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script)\n- },\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n- }\n- this.addOptions({\n- maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n- numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw()\n- }\n- this.updateAttribution()\n+ OpenLayers.Util.extend(this, options);\n+ this.maps = [];\n+ this.tileQueueId = {};\n+ this.tileQueue = {};\n+ this.tileCache = {};\n+ this.tileCacheIndex = []\n },\n- getURL: function(bounds) {\n- if (!this.url) {\n+ addMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n return\n }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = \"0\";\n- var mask = 1 << i - 1;\n- if ((x & mask) != 0) {\n- digit++\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++\n- }\n- quadDigits.push(digit)\n+ this.maps.push(map);\n+ this.tileQueue[map.id] = [];\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.addLayer({\n+ layer: map.layers[i]\n+ })\n }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl(\"\" + x + y + z, this.url);\n- return OpenLayers.String.format(url, {\n- quadkey: quadKey\n+ map.events.on({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n })\n },\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ removeMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n return\n }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \"\n- }\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ if (map.layers) {\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.removeLayer({\n+ layer: map.layers[i]\n+ })\n }\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- })\n- },\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options)\n+ if (map.events) {\n+ map.events.un({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ })\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj\n+ delete this.tileQueue[map.id];\n+ delete this.tileQueueId[map.id];\n+ OpenLayers.Util.removeItem(this.maps, map)\n },\n- destroy: function() {\n- this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n+ move: function(evt) {\n+ this.updateTimeout(evt.object, this.moveDelay, true)\n },\n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n-});\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined;\n- delete this._callbackId\n-};\n-OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n- url: null,\n- utfgridResolution: 2,\n- json: null,\n- format: null,\n- destroy: function() {\n- this.clear();\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n+ zoomEnd: function(evt) {\n+ this.updateTimeout(evt.object, this.zoomDelay)\n },\n- draw: function() {\n- var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (drawn) {\n- if (this.isLoading) {\n- this.abortLoading();\n- this.events.triggerEvent(\"reload\")\n- } else {\n- this.isLoading = true;\n- this.events.triggerEvent(\"loadstart\")\n+ changeLayer: function(evt) {\n+ if (evt.property === \"visibility\" || evt.property === \"params\") {\n+ this.updateTimeout(evt.object, 0)\n+ }\n+ },\n+ addLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ layer.events.on({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.addTile({\n+ tile: tile\n+ });\n+ if (tile.url && !tile.imgDiv) {\n+ this.manageTileCache({\n+ object: tile\n+ })\n+ }\n+ }\n }\n- this.url = this.layer.getURL(this.bounds);\n- if (this.layer.useJSONP) {\n- var ols = new OpenLayers.Protocol.Script({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- this.json = response.data\n- },\n- scope: this\n- });\n- ols.read();\n- this.request = ols\n- } else {\n- this.request = OpenLayers.Request.GET({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- if (response.status === 200) {\n- this.parseData(response.responseText)\n- }\n- },\n+ }\n+ },\n+ removeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ this.clearTileQueue({\n+ object: layer\n+ });\n+ if (layer.events) {\n+ layer.events.un({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n scope: this\n })\n }\n- } else {\n- this.unload()\n+ if (layer.grid) {\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.unloadTile({\n+ object: tile\n+ })\n+ }\n+ }\n+ }\n }\n- return drawn\n },\n- abortLoading: function() {\n- if (this.request) {\n- this.request.abort();\n- delete this.request\n+ updateTimeout: function(map, delay, nice) {\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ var tileQueue = this.tileQueue[map.id];\n+ if (!nice || tileQueue.length) {\n+ this.tileQueueId[map.id] = window.setTimeout(OpenLayers.Function.bind(function() {\n+ this.drawTilesFromQueue(map);\n+ if (tileQueue.length) {\n+ this.updateTimeout(map, this.frameDelay)\n+ }\n+ }, this), delay)\n }\n- this.isLoading = false\n },\n- getFeatureInfo: function(i, j) {\n- var info = null;\n- if (this.json) {\n- var id = this.getFeatureId(i, j);\n- if (id !== null) {\n- info = {\n- id: id,\n- data: this.json.data[id]\n- }\n- }\n+ addTile: function(evt) {\n+ if (evt.tile instanceof OpenLayers.Tile.Image) {\n+ evt.tile.events.on({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ })\n+ } else {\n+ this.removeLayer({\n+ layer: evt.tile.layer\n+ })\n }\n- return info\n },\n- getFeatureId: function(i, j) {\n- var id = null;\n- if (this.json) {\n- var resolution = this.utfgridResolution;\n- var row = Math.floor(j / resolution);\n- var col = Math.floor(i / resolution);\n- var charCode = this.json.grid[row].charCodeAt(col);\n- var index = this.indexFromCharCode(charCode);\n- var keys = this.json.keys;\n- if (!isNaN(index) && index in keys) {\n- id = keys[index]\n+ unloadTile: function(evt) {\n+ var tile = evt.object;\n+ tile.events.un({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile)\n+ },\n+ queueTileDraw: function(evt) {\n+ var tile = evt.object;\n+ var queued = false;\n+ var layer = tile.layer;\n+ var url = layer.getURL(tile.bounds);\n+ var img = this.tileCache[url];\n+ if (img && img.className !== \"olTileImage\") {\n+ delete this.tileCache[url];\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n+ img = null\n+ }\n+ if (layer.url && (layer.async || !img)) {\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n+ tileQueue.push(tile)\n }\n+ queued = true\n }\n- return id\n+ return !queued\n },\n- indexFromCharCode: function(charCode) {\n- if (charCode >= 93) {\n- charCode--\n+ drawTilesFromQueue: function(map) {\n+ var tileQueue = this.tileQueue[map.id];\n+ var limit = this.tilesPerFrame;\n+ var animating = map.zoomTween && map.zoomTween.playing;\n+ while (!animating && tileQueue.length && limit) {\n+ tileQueue.shift().draw(true);\n+ --limit\n }\n- if (charCode >= 35) {\n- charCode--\n+ },\n+ manageTileCache: function(evt) {\n+ var tile = evt.object;\n+ var img = this.tileCache[tile.url];\n+ if (img) {\n+ if (img.parentNode && OpenLayers.Element.hasClass(img.parentNode, \"olBackBuffer\")) {\n+ img.parentNode.removeChild(img);\n+ img.id = null\n+ }\n+ if (!img.parentNode) {\n+ img.style.visibility = \"hidden\";\n+ img.style.opacity = 0;\n+ tile.setImage(img);\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n+ this.tileCacheIndex.push(tile.url)\n+ }\n }\n- return charCode - 32\n },\n- parseData: function(str) {\n- if (!this.format) {\n- this.format = new OpenLayers.Format.JSON\n+ addToCache: function(evt) {\n+ var tile = evt.object;\n+ if (!this.tileCache[tile.url]) {\n+ if (!OpenLayers.Element.hasClass(tile.imgDiv, \"olImageLoadError\")) {\n+ if (this.tileCacheIndex.length >= this.cacheSize) {\n+ delete this.tileCache[this.tileCacheIndex[0]];\n+ this.tileCacheIndex.shift()\n+ }\n+ this.tileCache[tile.url] = tile.imgDiv;\n+ this.tileCacheIndex.push(tile.url)\n+ }\n }\n- this.json = this.format.read(str)\n },\n- clear: function() {\n- this.json = null\n+ clearTileQueue: function(evt) {\n+ var layer = evt.object;\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ for (var i = tileQueue.length - 1; i >= 0; --i) {\n+ if (tileQueue[i].layer === layer) {\n+ tileQueue.splice(i, 1)\n+ }\n+ }\n },\n- CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n+ destroy: function() {\n+ for (var i = this.maps.length - 1; i >= 0; --i) {\n+ this.removeMap(this.maps[i])\n+ }\n+ this.maps = null;\n+ this.tileQueue = null;\n+ this.tileQueueId = null;\n+ this.tileCache = null;\n+ this.tileCacheIndex = null;\n+ this._destroyed = true\n+ }\n });\n-OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- isBaseLayer: false,\n- projection: new OpenLayers.Projection(\"EPSG:900913\"),\n- useJSONP: false,\n- tileClass: OpenLayers.Tile.UTFGrid,\n+OpenLayers.Protocol = OpenLayers.Class({\n+ format: null,\n+ options: null,\n+ autoDestroy: true,\n+ defaultFilter: null,\n initialize: function(options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [options.name, options.url, {}, options]);\n- this.tileOptions = OpenLayers.Util.extend({\n- utfgridResolution: this.utfgridResolution\n- }, this.tileOptions)\n+ options = options || {};\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options\n },\n- createBackBuffer: function() {},\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.UTFGrid(this.getOptions())\n+ mergeWithDefaultFilter: function(filter) {\n+ var merged;\n+ if (filter && this.defaultFilter) {\n+ merged = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.defaultFilter, filter]\n+ })\n+ } else {\n+ merged = filter || this.defaultFilter || undefined\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ return merged\n },\n- getFeatureInfo: function(location) {\n- var info = null;\n- var tileInfo = this.getTileData(location);\n- if (tileInfo && tileInfo.tile) {\n- info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j)\n- }\n- return info\n+ destroy: function() {\n+ this.options = null;\n+ this.format = null\n },\n- getFeatureId: function(location) {\n- var id = null;\n- var info = this.getTileData(location);\n- if (info.tile) {\n- id = info.tile.getFeatureId(info.i, info.j)\n- }\n- return id\n+ read: function(options) {\n+ options = options || {};\n+ options.filter = this.mergeWithDefaultFilter(options.filter)\n },\n- CLASS_NAME: \"OpenLayers.Layer.UTFGrid\"\n-});\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n+ create: function() {},\n+ update: function() {},\n+ delete: function() {},\n+ commit: function() {},\n+ abort: function(response) {},\n+ createCallback: function(method, response, options) {\n+ return OpenLayers.Function.bind(function() {\n+ method.apply(this, [response, options])\n+ }, this)\n },\n- isBaseLayer: true,\n- encodeBBOX: false,\n- noMagic: false,\n- yx: {},\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\"\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n- if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n- if (options == null || !options.isBaseLayer) {\n- this.isBaseLayer = false\n- }\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Protocol\"\n+});\n+OpenLayers.Protocol.Response = OpenLayers.Class({\n+ code: null,\n+ requestType: null,\n+ last: true,\n+ features: null,\n+ data: null,\n+ reqFeatures: null,\n+ priv: null,\n+ error: null,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options)\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ success: function() {\n+ return this.code > 0\n },\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n+ CLASS_NAME: \"OpenLayers.Protocol.Response\"\n+});\n+OpenLayers.Protocol.Response.SUCCESS = 1;\n+OpenLayers.Protocol.Response.FAILURE = 0;\n+OpenLayers.Strategy = OpenLayers.Class({\n+ layer: null,\n+ options: null,\n+ active: null,\n+ autoActivate: true,\n+ autoDestroy: true,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ this.active = false\n },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null\n },\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ setLayer: function(layer) {\n+ this.layer = layer\n },\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n- var value = projectionCode == \"none\" ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value\n- } else {\n- this.params.SRS = value\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true\n }\n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n+ return false\n+ },\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true\n }\n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n+ return false\n },\n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n });\n-OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {\n- IMAGE_EXTENSIONS: {\n- jpeg: \"jpg\",\n- gif: \"gif\",\n- png: \"png\",\n- png8: \"png\",\n- png24: \"png\",\n- dithered: \"png\"\n- },\n- DEFAULT_FORMAT: \"jpeg\",\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);\n- this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT]\n+OpenLayers.Popup = OpenLayers.Class({\n+ events: null,\n+ id: \"\",\n+ lonlat: null,\n+ div: null,\n+ contentSize: null,\n+ size: null,\n+ contentHTML: null,\n+ backgroundColor: \"\",\n+ opacity: \"\",\n+ border: \"\",\n+ contentDiv: null,\n+ groupDiv: null,\n+ closeDiv: null,\n+ autoSize: false,\n+ minSize: null,\n+ maxSize: null,\n+ displayClass: \"olPopup\",\n+ contentDisplayClass: \"olPopupContent\",\n+ padding: 0,\n+ disableFirefoxOverflowHack: false,\n+ fixPadding: function() {\n+ if (typeof this.padding == \"number\") {\n+ this.padding = new OpenLayers.Bounds(this.padding, this.padding, this.padding, this.padding)\n+ }\n },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round(this.map.getScale() * 1e4) / 1e4;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n- var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;\n- var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;\n- var components = [\"/\", this.params.map, \"/\", scale, \"/\", this.params.g.replace(/\\s/g, \"_\"), \"/def/t\", metaY, \"/l\", metaX, \"/t\", pY, \"l\", pX, \".\", this.extension];\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(components.join(\"\"), url)\n+ panMapIfOutOfView: false,\n+ keepInMap: false,\n+ closeOnMove: false,\n+ map: null,\n+ initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {\n+ if (id == null) {\n+ id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n }\n- return url + components.join(\"\")\n+ this.id = id;\n+ this.lonlat = lonlat;\n+ this.contentSize = contentSize != null ? contentSize : new OpenLayers.Size(OpenLayers.Popup.WIDTH, OpenLayers.Popup.HEIGHT);\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML\n+ }\n+ this.backgroundColor = OpenLayers.Popup.COLOR;\n+ this.opacity = OpenLayers.Popup.OPACITY;\n+ this.border = OpenLayers.Popup.BORDER;\n+ this.div = OpenLayers.Util.createDiv(this.id, null, null, null, null, null, \"hidden\");\n+ this.div.className = this.displayClass;\n+ var groupDivId = this.id + \"_GroupDiv\";\n+ this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, null, \"relative\", null, \"hidden\");\n+ var id = this.div.id + \"_contentDiv\";\n+ this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), null, \"relative\");\n+ this.contentDiv.className = this.contentDisplayClass;\n+ this.groupDiv.appendChild(this.contentDiv);\n+ this.div.appendChild(this.groupDiv);\n+ if (closeBox) {\n+ this.addCloseBox(closeBoxCallback)\n+ }\n+ this.registerEvents()\n },\n- CLASS_NAME: \"OpenLayers.Layer.KaMapCache\"\n-});\n-OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- version: \"1.0.0\",\n- requestEncoding: \"KVP\",\n- url: null,\n- layer: null,\n- matrixSet: null,\n- style: null,\n- format: \"image/jpeg\",\n- tileOrigin: null,\n- tileFullExtent: null,\n- formatSuffix: null,\n- matrixIds: null,\n- dimensions: null,\n- params: null,\n- zoomOffset: 0,\n- serverResolutions: null,\n- formatSuffixMap: {\n- \"image/png\": \"png\",\n- \"image/png8\": \"png\",\n- \"image/png24\": \"png\",\n- \"image/png32\": \"png\",\n- png: \"png\",\n- \"image/jpeg\": \"jpg\",\n- \"image/jpg\": \"jpg\",\n- jpeg: \"jpg\",\n- jpg: \"jpg\"\n+ destroy: function() {\n+ this.id = null;\n+ this.lonlat = null;\n+ this.size = null;\n+ this.contentHTML = null;\n+ this.backgroundColor = null;\n+ this.opacity = null;\n+ this.border = null;\n+ if (this.closeOnMove && this.map) {\n+ this.map.events.unregister(\"movestart\", this, this.hide)\n+ }\n+ this.events.destroy();\n+ this.events = null;\n+ if (this.closeDiv) {\n+ OpenLayers.Event.stopObservingElement(this.closeDiv);\n+ this.groupDiv.removeChild(this.closeDiv)\n+ }\n+ this.closeDiv = null;\n+ this.div.removeChild(this.groupDiv);\n+ this.groupDiv = null;\n+ if (this.map != null) {\n+ this.map.removePopup(this)\n+ }\n+ this.map = null;\n+ this.div = null;\n+ this.autoSize = null;\n+ this.minSize = null;\n+ this.maxSize = null;\n+ this.padding = null;\n+ this.panMapIfOutOfView = null\n },\n- matrix: null,\n- initialize: function(config) {\n- var required = {\n- url: true,\n- layer: true,\n- style: true,\n- matrixSet: true\n- };\n- for (var prop in required) {\n- if (!(prop in config)) {\n- throw new Error(\"Missing property '\" + prop + \"' in layer configuration.\")\n+ draw: function(px) {\n+ if (px == null) {\n+ if (this.lonlat != null && this.map != null) {\n+ px = this.map.getLayerPxFromLonLat(this.lonlat)\n }\n }\n- config.params = OpenLayers.Util.upperCaseObject(config.params);\n- var args = [config.name, config.url, config.params, config];\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);\n- if (!this.formatSuffix) {\n- this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split(\"/\").pop()\n+ if (this.closeOnMove) {\n+ this.map.events.register(\"movestart\", this, this.hide)\n }\n- if (this.matrixIds) {\n- var len = this.matrixIds.length;\n- if (len && typeof this.matrixIds[0] === \"string\") {\n- var ids = this.matrixIds;\n- this.matrixIds = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- this.matrixIds[i] = {\n- identifier: ids[i]\n- }\n+ if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == \"firefox\") {\n+ this.map.events.register(\"movestart\", this, function() {\n+ var style = document.defaultView.getComputedStyle(this.contentDiv, null);\n+ var currentOverflow = style.getPropertyValue(\"overflow\");\n+ if (currentOverflow != \"hidden\") {\n+ this.contentDiv._oldOverflow = currentOverflow;\n+ this.contentDiv.style.overflow = \"hidden\"\n }\n- }\n+ });\n+ this.map.events.register(\"moveend\", this, function() {\n+ var oldOverflow = this.contentDiv._oldOverflow;\n+ if (oldOverflow) {\n+ this.contentDiv.style.overflow = oldOverflow;\n+ this.contentDiv._oldOverflow = null\n+ }\n+ })\n }\n- },\n- setMap: function() {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments)\n- },\n- updateMatrixProperties: function() {\n- this.matrix = this.getMatrix();\n- if (this.matrix) {\n- if (this.matrix.topLeftCorner) {\n- this.tileOrigin = this.matrix.topLeftCorner\n- }\n- if (this.matrix.tileWidth && this.matrix.tileHeight) {\n- this.tileSize = new OpenLayers.Size(this.matrix.tileWidth, this.matrix.tileHeight)\n- }\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top)\n- }\n- if (!this.tileFullExtent) {\n- this.tileFullExtent = this.maxExtent\n- }\n+ this.moveTo(px);\n+ if (!this.autoSize && !this.size) {\n+ this.setSize(this.contentSize)\n }\n+ this.setBackgroundColor();\n+ this.setOpacity();\n+ this.setBorder();\n+ this.setContentHTML();\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView()\n+ }\n+ return this.div\n },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- if (zoomChanged || !this.matrix) {\n- this.updateMatrixProperties()\n+ updatePosition: function() {\n+ if (this.lonlat && this.map) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ if (px) {\n+ this.moveTo(px)\n+ }\n }\n- return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments)\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMTS(this.options)\n+ moveTo: function(px) {\n+ if (px != null && this.div != null) {\n+ this.div.style.left = px.x + \"px\";\n+ this.div.style.top = px.y + \"px\"\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n },\n- getIdentifier: function() {\n- return this.getServerZoom()\n+ visible: function() {\n+ return OpenLayers.Element.visible(this.div)\n },\n- getMatrix: function() {\n- var matrix;\n- if (!this.matrixIds || this.matrixIds.length === 0) {\n- matrix = {\n- identifier: this.getIdentifier()\n- }\n+ toggle: function() {\n+ if (this.visible()) {\n+ this.hide()\n } else {\n- if (\"scaleDenominator\" in this.matrixIds[0]) {\n- var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * this.getServerResolution() / 28e-5;\n- var diff = Number.POSITIVE_INFINITY;\n- var delta;\n- for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {\n- delta = Math.abs(1 - this.matrixIds[i].scaleDenominator / denom);\n- if (delta < diff) {\n- diff = delta;\n- matrix = this.matrixIds[i]\n- }\n- }\n- } else {\n- matrix = this.matrixIds[this.getIdentifier()]\n- }\n+ this.show()\n }\n- return matrix\n },\n- getTileInfo: function(loc) {\n- var res = this.getServerResolution();\n- var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);\n- var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);\n- var col = Math.floor(fx);\n- var row = Math.floor(fy);\n- return {\n- col: col,\n- row: row,\n- i: Math.floor((fx - col) * this.tileSize.w),\n- j: Math.floor((fy - row) * this.tileSize.h)\n+ show: function() {\n+ this.div.style.display = \"\";\n+ if (this.panMapIfOutOfView) {\n+ this.panIntoView()\n }\n },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var url = \"\";\n- if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {\n- var center = bounds.getCenterLonLat();\n- var info = this.getTileInfo(center);\n- var matrixId = this.matrix.identifier;\n- var dimensions = this.dimensions,\n- params;\n- if (OpenLayers.Util.isArray(this.url)) {\n- url = this.selectUrl([this.version, this.style, this.matrixSet, this.matrix.identifier, info.row, info.col].join(\",\"), this.url)\n+ hide: function() {\n+ this.div.style.display = \"none\"\n+ },\n+ setSize: function(contentSize) {\n+ this.size = contentSize.clone();\n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right\n+ }\n+ this.size.w += wPadding;\n+ this.size.h += hPadding;\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.contentSize.w += contentDivPadding.left + contentDivPadding.right;\n+ this.contentSize.h += contentDivPadding.bottom + contentDivPadding.top\n+ }\n+ if (this.div != null) {\n+ this.div.style.width = this.size.w + \"px\";\n+ this.div.style.height = this.size.h + \"px\"\n+ }\n+ if (this.contentDiv != null) {\n+ this.contentDiv.style.width = contentSize.w + \"px\";\n+ this.contentDiv.style.height = contentSize.h + \"px\"\n+ }\n+ },\n+ updateSize: function() {\n+ var preparedHTML = \"<div class='\" + this.contentDisplayClass + \"'>\" + this.contentDiv.innerHTML + \"</div>\";\n+ var containerElement = this.map ? this.map.div : document.body;\n+ var realSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, null, {\n+ displayClass: this.displayClass,\n+ containerElement: containerElement\n+ });\n+ var safeSize = this.getSafeContentSize(realSize);\n+ var newSize = null;\n+ if (safeSize.equals(realSize)) {\n+ newSize = realSize\n+ } else {\n+ var fixedSize = {\n+ w: safeSize.w < realSize.w ? safeSize.w : null,\n+ h: safeSize.h < realSize.h ? safeSize.h : null\n+ };\n+ if (fixedSize.w && fixedSize.h) {\n+ newSize = safeSize\n } else {\n- url = this.url\n- }\n- if (this.requestEncoding.toUpperCase() === \"REST\") {\n- params = this.params;\n- if (url.indexOf(\"{\") !== -1) {\n- var template = url.replace(/\\{/g, \"${\");\n- var context = {\n- style: this.style,\n- Style: this.style,\n- TileMatrixSet: this.matrixSet,\n- TileMatrix: this.matrix.identifier,\n- TileRow: info.row,\n- TileCol: info.col\n- };\n- if (dimensions) {\n- var dimension, i;\n- for (i = dimensions.length - 1; i >= 0; --i) {\n- dimension = dimensions[i];\n- context[dimension] = params[dimension.toUpperCase()]\n- }\n- }\n- url = OpenLayers.String.format(template, context)\n- } else {\n- var path = this.version + \"/\" + this.layer + \"/\" + this.style + \"/\";\n- if (dimensions) {\n- for (var i = 0; i < dimensions.length; i++) {\n- if (params[dimensions[i]]) {\n- path = path + params[dimensions[i]] + \"/\"\n- }\n- }\n- }\n- path = path + this.matrixSet + \"/\" + this.matrix.identifier + \"/\" + info.row + \"/\" + info.col + \".\" + this.formatSuffix;\n- if (!url.match(/\\/$/)) {\n- url = url + \"/\"\n+ var clippedSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, fixedSize, {\n+ displayClass: this.contentDisplayClass,\n+ containerElement: containerElement\n+ });\n+ var currentOverflow = OpenLayers.Element.getStyle(this.contentDiv, \"overflow\");\n+ if (currentOverflow != \"hidden\" && clippedSize.equals(safeSize)) {\n+ var scrollBar = OpenLayers.Util.getScrollbarWidth();\n+ if (fixedSize.w) {\n+ clippedSize.h += scrollBar\n+ } else {\n+ clippedSize.w += scrollBar\n }\n- url = url + path\n }\n- } else if (this.requestEncoding.toUpperCase() === \"KVP\") {\n- params = {\n- SERVICE: \"WMTS\",\n- REQUEST: \"GetTile\",\n- VERSION: this.version,\n- LAYER: this.layer,\n- STYLE: this.style,\n- TILEMATRIXSET: this.matrixSet,\n- TILEMATRIX: this.matrix.identifier,\n- TILEROW: info.row,\n- TILECOL: info.col,\n- FORMAT: this.format\n- };\n- url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params])\n+ newSize = this.getSafeContentSize(clippedSize)\n }\n }\n- return url\n+ this.setSize(newSize)\n },\n- mergeNewParams: function(newParams) {\n- if (this.requestEncoding.toUpperCase() === \"KVP\") {\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, [OpenLayers.Util.upperCaseObject(newParams)])\n+ setBackgroundColor: function(color) {\n+ if (color != undefined) {\n+ this.backgroundColor = color\n+ }\n+ if (this.div != null) {\n+ this.div.style.backgroundColor = this.backgroundColor\n }\n },\n- CLASS_NAME: \"OpenLayers.Layer.WMTS\"\n-});\n-OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- mode: \"map\",\n- map_imagetype: \"png\"\n+ setOpacity: function(opacity) {\n+ if (opacity != undefined) {\n+ this.opacity = opacity\n+ }\n+ if (this.div != null) {\n+ this.div.style.opacity = this.opacity;\n+ this.div.style.filter = \"alpha(opacity=\" + this.opacity * 100 + \")\"\n+ }\n },\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n- this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS);\n- if (options == null || options.isBaseLayer == null) {\n- this.isBaseLayer = this.params.transparent != \"true\" && this.params.transparent != true\n+ setBorder: function(border) {\n+ if (border != undefined) {\n+ this.border = border\n+ }\n+ if (this.div != null) {\n+ this.div.style.border = this.border\n }\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.MapServer(this.name, this.url, this.params, this.getOptions())\n+ setContentHTML: function(contentHTML) {\n+ if (contentHTML != null) {\n+ this.contentHTML = contentHTML\n+ }\n+ if (this.contentDiv != null && this.contentHTML != null && this.contentHTML != this.contentDiv.innerHTML) {\n+ this.contentDiv.innerHTML = this.contentHTML;\n+ if (this.autoSize) {\n+ this.registerImageListeners();\n+ this.updateSize()\n+ }\n }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top];\n- var imageSize = this.getImageSize();\n- var url = this.getFullRequestString({\n- mapext: extent,\n- imgext: extent,\n- map_size: [imageSize.w, imageSize.h],\n- imgx: imageSize.w / 2,\n- imgy: imageSize.h / 2,\n- imgxy: [imageSize.w, imageSize.h]\n- });\n- return url\n+ registerImageListeners: function() {\n+ var onImgLoad = function() {\n+ if (this.popup.id === null) {\n+ return\n+ }\n+ this.popup.updateSize();\n+ if (this.popup.visible() && this.popup.panMapIfOutOfView) {\n+ this.popup.panIntoView()\n+ }\n+ OpenLayers.Event.stopObserving(this.img, \"load\", this.img._onImgLoad)\n+ };\n+ var images = this.contentDiv.getElementsByTagName(\"img\");\n+ for (var i = 0, len = images.length; i < len; i++) {\n+ var img = images[i];\n+ if (img.width == 0 || img.height == 0) {\n+ var context = {\n+ popup: this,\n+ img: img\n+ };\n+ img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);\n+ OpenLayers.Event.observe(img, \"load\", img._onImgLoad)\n+ }\n+ }\n },\n- getFullRequestString: function(newParams, altUrl) {\n- var url = altUrl == null ? this.url : altUrl;\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(paramsString, url)\n+ getSafeContentSize: function(size) {\n+ var safeContentSize = size.clone();\n+ var contentDivPadding = this.getContentDivPadding();\n+ var wPadding = contentDivPadding.left + contentDivPadding.right;\n+ var hPadding = contentDivPadding.top + contentDivPadding.bottom;\n+ this.fixPadding();\n+ wPadding += this.padding.left + this.padding.right;\n+ hPadding += this.padding.top + this.padding.bottom;\n+ if (this.closeDiv) {\n+ var closeDivWidth = parseInt(this.closeDiv.style.width);\n+ wPadding += closeDivWidth + contentDivPadding.right\n }\n- var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key]\n- }\n+ if (this.minSize) {\n+ safeContentSize.w = Math.max(safeContentSize.w, this.minSize.w - wPadding);\n+ safeContentSize.h = Math.max(safeContentSize.h, this.minSize.h - hPadding)\n }\n- paramsString = OpenLayers.Util.getParameterString(allParams);\n- var requestString = url;\n- paramsString = paramsString.replace(/,/g, \"+\");\n- if (paramsString != \"\") {\n- var lastServerChar = url.charAt(url.length - 1);\n- if (lastServerChar == \"&\" || lastServerChar == \"?\") {\n- requestString += paramsString\n- } else {\n- if (url.indexOf(\"?\") == -1) {\n- requestString += \"?\" + paramsString\n- } else {\n- requestString += \"&\" + paramsString\n+ if (this.maxSize) {\n+ safeContentSize.w = Math.min(safeContentSize.w, this.maxSize.w - wPadding);\n+ safeContentSize.h = Math.min(safeContentSize.h, this.maxSize.h - hPadding)\n+ }\n+ if (this.map && this.map.size) {\n+ var extraX = 0,\n+ extraY = 0;\n+ if (this.keepInMap && !this.panMapIfOutOfView) {\n+ var px = this.map.getPixelFromLonLat(this.lonlat);\n+ switch (this.relativePosition) {\n+ case \"tr\":\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"tl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = this.map.size.h - px.y;\n+ break;\n+ case \"bl\":\n+ extraX = this.map.size.w - px.x;\n+ extraY = px.y;\n+ break;\n+ case \"br\":\n+ extraX = px.x;\n+ extraY = px.y;\n+ break;\n+ default:\n+ extraX = px.x;\n+ extraY = this.map.size.h - px.y;\n+ break\n }\n }\n+ var maxY = this.map.size.h - this.map.paddingForPopups.top - this.map.paddingForPopups.bottom - hPadding - extraY;\n+ var maxX = this.map.size.w - this.map.paddingForPopups.left - this.map.paddingForPopups.right - wPadding - extraX;\n+ safeContentSize.w = Math.min(safeContentSize.w, maxX);\n+ safeContentSize.h = Math.min(safeContentSize.h, maxY)\n }\n- return requestString\n- },\n- CLASS_NAME: \"OpenLayers.Layer.MapServer\"\n-});\n-OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- format: \"png\"\n+ return safeContentSize\n },\n- isBaseLayer: true,\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- params = OpenLayers.Util.upperCaseObject(params);\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n- if (this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n- if (options == null || !options.isBaseLayer) {\n- this.isBaseLayer = false\n+ getContentDivPadding: function() {\n+ var contentDivPadding = this._contentDivPadding;\n+ if (!contentDivPadding) {\n+ if (this.div.parentNode == null) {\n+ this.div.style.display = \"none\";\n+ document.body.appendChild(this.div)\n }\n- if (this.params.FORMAT == \"jpg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"gif\" : \"png\"\n+ contentDivPadding = new OpenLayers.Bounds(OpenLayers.Element.getStyle(this.contentDiv, \"padding-left\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-bottom\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-right\"), OpenLayers.Element.getStyle(this.contentDiv, \"padding-top\"));\n+ this._contentDivPadding = contentDivPadding;\n+ if (this.div.parentNode == document.body) {\n+ document.body.removeChild(this.div);\n+ this.div.style.display = \"\"\n }\n }\n+ return contentDivPadding\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var projWords = this.projection.getCode().split(\":\");\n- var srid = projWords[projWords.length - 1];\n- var imageSize = this.getImageSize();\n- var newParams = {\n- BBOX: bounds.toBBOX(),\n- SIZE: imageSize.w + \",\" + imageSize.h,\n- F: \"image\",\n- BBOXSR: srid,\n- IMAGESR: srid\n+ addCloseBox: function(callback) {\n+ this.closeDiv = OpenLayers.Util.createDiv(this.id + \"_close\", null, {\n+ w: 17,\n+ h: 17\n+ });\n+ this.closeDiv.className = \"olPopupCloseBox\";\n+ var contentDivPadding = this.getContentDivPadding();\n+ this.closeDiv.style.right = contentDivPadding.right + \"px\";\n+ this.closeDiv.style.top = contentDivPadding.top + \"px\";\n+ this.groupDiv.appendChild(this.closeDiv);\n+ var closePopup = callback || function(e) {\n+ this.hide();\n+ OpenLayers.Event.stop(e)\n };\n- if (this.layerDefs) {\n- var layerDefStrList = [];\n- var layerID;\n- for (layerID in this.layerDefs) {\n- if (this.layerDefs.hasOwnProperty(layerID)) {\n- if (this.layerDefs[layerID]) {\n- layerDefStrList.push(layerID);\n- layerDefStrList.push(\":\");\n- layerDefStrList.push(this.layerDefs[layerID]);\n- layerDefStrList.push(\";\")\n- }\n- }\n- }\n- if (layerDefStrList.length > 0) {\n- newParams[\"LAYERDEFS\"] = layerDefStrList.join(\"\")\n- }\n+ OpenLayers.Event.observe(this.closeDiv, \"touchend\", OpenLayers.Function.bindAsEventListener(closePopup, this));\n+ OpenLayers.Event.observe(this.closeDiv, \"click\", OpenLayers.Function.bindAsEventListener(closePopup, this))\n+ },\n+ panIntoView: function() {\n+ var mapSize = this.map.getSize();\n+ var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(parseInt(this.div.style.left), parseInt(this.div.style.top)));\n+ var newTL = origTL.clone();\n+ if (origTL.x < this.map.paddingForPopups.left) {\n+ newTL.x = this.map.paddingForPopups.left\n+ } else if (origTL.x + this.size.w > mapSize.w - this.map.paddingForPopups.right) {\n+ newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w\n }\n- var requestString = this.getFullRequestString(newParams);\n- return requestString\n+ if (origTL.y < this.map.paddingForPopups.top) {\n+ newTL.y = this.map.paddingForPopups.top\n+ } else if (origTL.y + this.size.h > mapSize.h - this.map.paddingForPopups.bottom) {\n+ newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h\n+ }\n+ var dx = origTL.x - newTL.x;\n+ var dy = origTL.y - newTL.y;\n+ this.map.pan(dx, dy)\n },\n- setLayerFilter: function(id, queryDef) {\n- if (!this.layerDefs) {\n- this.layerDefs = {}\n+ registerEvents: function() {\n+ this.events = new OpenLayers.Events(this, this.div, null, true);\n+\n+ function onTouchstart(evt) {\n+ OpenLayers.Event.stop(evt, true)\n }\n- if (queryDef) {\n- this.layerDefs[id] = queryDef\n- } else {\n- delete this.layerDefs[id]\n+ this.events.on({\n+ mousedown: this.onmousedown,\n+ mousemove: this.onmousemove,\n+ mouseup: this.onmouseup,\n+ click: this.onclick,\n+ mouseout: this.onmouseout,\n+ dblclick: this.ondblclick,\n+ touchstart: onTouchstart,\n+ scope: this\n+ })\n+ },\n+ onmousedown: function(evt) {\n+ this.mousedown = true;\n+ OpenLayers.Event.stop(evt, true)\n+ },\n+ onmousemove: function(evt) {\n+ if (this.mousedown) {\n+ OpenLayers.Event.stop(evt, true)\n }\n },\n- clearLayerFilter: function(id) {\n- if (id) {\n- delete this.layerDefs[id]\n- } else {\n- delete this.layerDefs\n+ onmouseup: function(evt) {\n+ if (this.mousedown) {\n+ this.mousedown = false;\n+ OpenLayers.Event.stop(evt, true)\n }\n },\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ onclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true)\n },\n- CLASS_NAME: \"OpenLayers.Layer.ArcGIS93Rest\"\n+ onmouseout: function(evt) {\n+ this.mousedown = false\n+ },\n+ ondblclick: function(evt) {\n+ OpenLayers.Event.stop(evt, true)\n+ },\n+ CLASS_NAME: \"OpenLayers.Popup\"\n });\n-OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Popup.WIDTH = 200;\n+OpenLayers.Popup.HEIGHT = 200;\n+OpenLayers.Popup.COLOR = \"white\";\n+OpenLayers.Popup.OPACITY = 1;\n+OpenLayers.Popup.BORDER = \"0px\";\n+OpenLayers.Spherical = OpenLayers.Spherical || {};\n+OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n+OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n+ var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n+ var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n+ var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n+ var a = sinHalfDeltaLat * sinHalfDeltaLat + sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n+ return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))\n+};\n+OpenLayers.Spherical.computeHeading = function(from, to) {\n+ var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n+ var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) - Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n+ return 180 * Math.atan2(y, x) / Math.PI\n+};\n+OpenLayers.Renderer = OpenLayers.Class({\n+ container: null,\n+ root: null,\n+ extent: null,\n+ locked: false,\n size: null,\n- isBaseLayer: true,\n- standardTileSize: 256,\n- tileOriginCorner: \"tl\",\n- numberOfTiers: 0,\n- tileCountUpToTier: null,\n- tierSizeInTiles: null,\n- tierImageSize: null,\n- initialize: function(name, url, size, options) {\n- this.initializeZoomify(size);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, size, {}, options])\n+ resolution: null,\n+ map: null,\n+ featureDx: 0,\n+ initialize: function(containerID, options) {\n+ this.container = OpenLayers.Util.getElement(containerID);\n+ OpenLayers.Util.extend(this, options)\n },\n- initializeZoomify: function(size) {\n- var imageSize = size.clone();\n- this.size = size.clone();\n- var tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));\n- this.tierSizeInTiles = [tiles];\n- this.tierImageSize = [imageSize];\n- while (imageSize.w > this.standardTileSize || imageSize.h > this.standardTileSize) {\n- imageSize = new OpenLayers.Size(Math.floor(imageSize.w / 2), Math.floor(imageSize.h / 2));\n- tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));\n- this.tierSizeInTiles.push(tiles);\n- this.tierImageSize.push(imageSize)\n- }\n- this.tierSizeInTiles.reverse();\n- this.tierImageSize.reverse();\n- this.numberOfTiers = this.tierSizeInTiles.length;\n- var resolutions = [1];\n- this.tileCountUpToTier = [0];\n- for (var i = 1; i < this.numberOfTiers; i++) {\n- resolutions.unshift(Math.pow(2, i));\n- this.tileCountUpToTier.push(this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + this.tileCountUpToTier[i - 1])\n+ destroy: function() {\n+ this.container = null;\n+ this.extent = null;\n+ this.size = null;\n+ this.resolution = null;\n+ this.map = null\n+ },\n+ supported: function() {\n+ return false\n+ },\n+ setExtent: function(extent, resolutionChanged) {\n+ this.extent = extent.clone();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio);\n+ this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio)\n }\n- if (!this.serverResolutions) {\n- this.serverResolutions = resolutions\n+ if (resolutionChanged) {\n+ this.resolution = null\n }\n+ return true\n },\n- destroy: function() {\n- OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);\n- this.tileCountUpToTier.length = 0;\n- this.tierSizeInTiles.length = 0;\n- this.tierImageSize.length = 0\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ this.resolution = null\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Zoomify(this.name, this.url, this.size, this.options)\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n+ getResolution: function() {\n+ this.resolution = this.resolution || this.map.getResolution();\n+ return this.resolution\n },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n- var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];\n- var path = \"TileGroup\" + Math.floor(tileIndex / 256) + \"/\" + z + \"-\" + x + \"-\" + y + \".jpg\";\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url)\n+ drawFeature: function(feature, style) {\n+ if (style == null) {\n+ style = feature.style\n }\n- return url + path\n- },\n- getImageSize: function() {\n- if (arguments.length > 0) {\n- var bounds = this.adjustBounds(arguments[0]);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n- var w = this.standardTileSize;\n- var h = this.standardTileSize;\n- if (x == this.tierSizeInTiles[z].w - 1) {\n- var w = this.tierImageSize[z].w % this.standardTileSize\n- }\n- if (y == this.tierSizeInTiles[z].h - 1) {\n- var h = this.tierImageSize[z].h % this.standardTileSize\n+ if (feature.geometry) {\n+ var bounds = feature.geometry.getBounds();\n+ if (bounds) {\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent()\n+ }\n+ if (!bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ })) {\n+ style = {\n+ display: \"none\"\n+ }\n+ } else {\n+ this.calculateFeatureDx(bounds, worldBounds)\n+ }\n+ var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n+ if (style.display != \"none\" && style.label && rendered !== false) {\n+ var location = feature.geometry.getCentroid();\n+ if (style.labelXOffset || style.labelYOffset) {\n+ var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n+ var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n+ var res = this.getResolution();\n+ location.move(xOffset * res, yOffset * res)\n+ }\n+ this.drawText(feature.id, style, location)\n+ } else {\n+ this.removeText(feature.id)\n+ }\n+ return rendered\n }\n- return new OpenLayers.Size(w, h)\n- } else {\n- return this.tileSize\n }\n },\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.top)\n+ calculateFeatureDx: function(bounds, worldBounds) {\n+ this.featureDx = 0;\n+ if (worldBounds) {\n+ var worldWidth = worldBounds.getWidth(),\n+ rendererCenterX = (this.extent.left + this.extent.right) / 2,\n+ featureCenterX = (bounds.left + bounds.right) / 2,\n+ worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n+ this.featureDx = worldsAway * worldWidth\n+ }\n },\n- CLASS_NAME: \"OpenLayers.Layer.Zoomify\"\n-});\n-OpenLayers.Layer.Google.v3 = {\n- DEFAULTS: {\n- sphericalMercator: true,\n- projection: \"EPSG:900913\"\n+ drawGeometry: function(geometry, style, featureId) {},\n+ drawText: function(featureId, style, location) {},\n+ removeText: function(featureId) {},\n+ clear: function() {},\n+ getFeatureIdFromEvent: function(evt) {},\n+ eraseFeatures: function(features) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ var feature = features[i];\n+ this.eraseGeometry(feature.geometry, feature.id);\n+ this.removeText(feature.id)\n+ }\n },\n- animationEnabled: true,\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = google.maps.MapTypeId.ROADMAP\n+ eraseGeometry: function(geometry, featureId) {},\n+ moveRoot: function(renderer) {},\n+ getRenderLayerId: function() {\n+ return this.container.id\n+ },\n+ applyDefaultSymbolizer: function(symbolizer) {\n+ var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer);\n+ if (symbolizer.stroke === false) {\n+ delete result.strokeWidth;\n+ delete result.strokeColor\n }\n- var mapObject;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- mapObject = cache.mapObject;\n- ++cache.count\n- } else {\n- var center = this.map.getCenter();\n- var container = document.createElement(\"div\");\n- container.className = \"olForeignContainer\";\n- container.style.width = \"100%\";\n- container.style.height = \"100%\";\n- mapObject = new google.maps.Map(container, {\n- center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n- zoom: this.map.getZoom() || 0,\n- mapTypeId: this.type,\n- disableDefaultUI: true,\n- keyboardShortcuts: false,\n- draggable: false,\n- disableDoubleClickZoom: true,\n- scrollwheel: false,\n- streetViewControl: false\n- });\n- var googleControl = document.createElement(\"div\");\n- googleControl.style.width = \"100%\";\n- googleControl.style.height = \"100%\";\n- mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n- cache = {\n- googleControl: googleControl,\n- mapObject: mapObject,\n- count: 1\n- };\n- OpenLayers.Layer.Google.cache[this.map.id] = cache\n+ if (symbolizer.fill === false) {\n+ delete result.fillColor\n+ }\n+ OpenLayers.Util.extend(result, symbolizer);\n+ return result\n+ },\n+ CLASS_NAME: \"OpenLayers.Renderer\"\n+});\n+OpenLayers.Renderer.defaultSymbolizer = {\n+ fillColor: \"#000000\",\n+ strokeColor: \"#000000\",\n+ strokeWidth: 2,\n+ fillOpacity: 1,\n+ strokeOpacity: 1,\n+ pointRadius: 0,\n+ labelAlign: \"cm\"\n+};\n+OpenLayers.Renderer.symbol = {\n+ star: [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75],\n+ cross: [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0],\n+ x: [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n+ square: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n+ triangle: [0, 10, 10, 10, 5, 0, 0, 10]\n+};\n+OpenLayers.Handler = OpenLayers.Class({\n+ id: null,\n+ control: null,\n+ map: null,\n+ keyMask: null,\n+ active: false,\n+ evt: null,\n+ touch: false,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.control = control;\n+ this.callbacks = callbacks;\n+ var map = this.map || control.map;\n+ if (map) {\n+ this.setMap(map)\n }\n- this.mapObject = mapObject;\n- this.setGMapVisibility(this.visibility)\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n- onMapResize: function() {\n- if (this.visibility) {\n- google.maps.event.trigger(this.mapObject, \"resize\")\n+ setMap: function(map) {\n+ this.map = map\n+ },\n+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true\n }\n+ var keyModifiers = (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n+ return keyModifiers == this.keyMask\n },\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- var map = this.map;\n- if (cache) {\n- var type = this.type;\n- var layers = map.layers;\n- var layer;\n- for (var i = layers.length - 1; i >= 0; --i) {\n- layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) {\n- type = layer.type;\n- visible = true;\n- break\n- }\n- }\n- var container = this.mapObject.getDiv();\n- if (visible === true) {\n- if (container.parentNode !== map.div) {\n- if (!cache.rendered) {\n- var me = this;\n- google.maps.event.addListenerOnce(this.mapObject, \"tilesloaded\", function() {\n- cache.rendered = true;\n- me.setGMapVisibility(me.getVisibility());\n- me.moveTo(me.map.getCenter())\n- })\n- } else {\n- map.div.appendChild(container);\n- cache.googleControl.appendChild(map.viewPortDiv);\n- google.maps.event.trigger(this.mapObject, \"resize\")\n- }\n- }\n- this.mapObject.setMapTypeId(type)\n- } else if (cache.googleControl.hasChildNodes()) {\n- map.div.appendChild(map.viewPortDiv);\n- map.div.removeChild(container)\n+ activate: function() {\n+ if (this.active) {\n+ return false\n+ }\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.register(events[i], this[events[i]])\n }\n }\n+ this.active = true;\n+ return true\n },\n- getMapContainer: function() {\n- return this.mapObject.getDiv()\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false\n+ }\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]])\n+ }\n+ }\n+ this.touch = false;\n+ this.active = false;\n+ return true\n },\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon))\n+ startTouch: function() {\n+ if (!this.touch) {\n+ this.touch = true;\n+ var events = [\"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\", \"mouseout\"];\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]])\n+ }\n+ }\n }\n- return moBounds\n },\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- var size = this.map.getSize();\n- var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n- var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n- var res = this.map.getResolution();\n- var delta_x = moPixel.x - size.w / 2;\n- var delta_y = moPixel.y - size.h / 2;\n- var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res);\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent)\n+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args)\n }\n- return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat)\n },\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- var res = this.map.getResolution();\n- var extent = this.map.getExtent();\n- return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat))\n+ register: function(name, method) {\n+ this.map.events.registerPriority(name, this, method);\n+ this.map.events.registerPriority(name, this, this.setEvent)\n },\n- setMapObjectCenter: function(center, zoom) {\n- if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n- var mapContainer = this.getMapContainer();\n- google.maps.event.addListenerOnce(this.mapObject, \"idle\", function() {\n- mapContainer.style.visibility = \"\"\n- });\n- mapContainer.style.visibility = \"hidden\"\n- }\n- this.mapObject.setOptions({\n- center: center,\n- zoom: zoom\n- })\n+ unregister: function(name, method) {\n+ this.map.events.unregister(name, this, method);\n+ this.map.events.unregister(name, this, this.setEvent)\n },\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds)\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true\n },\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon)\n- } else {\n- gLatLng = new google.maps.LatLng(lat, lon)\n- }\n- return gLatLng\n+ destroy: function() {\n+ this.deactivate();\n+ this.control = this.map = null\n },\n- getMapObjectPixelFromXY: function(x, y) {\n- return new google.maps.Point(x, y)\n- }\n-};\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n- displayInLayerSwitcher: false,\n- layers: null,\n- display: function() {},\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature\n- }\n+ CLASS_NAME: \"OpenLayers.Handler\"\n+});\n+OpenLayers.Handler.MOD_NONE = 0;\n+OpenLayers.Handler.MOD_SHIFT = 1;\n+OpenLayers.Handler.MOD_CTRL = 2;\n+OpenLayers.Handler.MOD_ALT = 4;\n+OpenLayers.Handler.MOD_META = 8;\n+OpenLayers.Icon = OpenLayers.Class({\n+ url: null,\n+ size: null,\n+ offset: null,\n+ calculateOffset: null,\n+ imageDiv: null,\n+ px: null,\n+ initialize: function(url, size, offset, calculateOffset) {\n+ this.url = url;\n+ this.size = size || {\n+ w: 20,\n+ h: 20\n+ };\n+ this.offset = offset || {\n+ x: -(this.size.w / 2),\n+ y: -(this.size.h / 2)\n+ };\n+ this.calculateOffset = calculateOffset;\n+ var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n+ this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id)\n+ },\n+ destroy: function() {\n+ this.erase();\n+ OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n+ this.imageDiv.innerHTML = \"\";\n+ this.imageDiv = null\n+ },\n+ clone: function() {\n+ return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset)\n+ },\n+ setSize: function(size) {\n+ if (size != null) {\n+ this.size = size\n }\n+ this.draw()\n },\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer)\n+ setUrl: function(url) {\n+ if (url != null) {\n+ this.url = url\n+ }\n+ this.draw()\n },\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n+ draw: function(px) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, \"absolute\");\n+ this.moveTo(px);\n+ return this.imageDiv\n },\n- collectRoots: function() {\n- var layer;\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer)\n- }\n+ erase: function() {\n+ if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n+ OpenLayers.Element.remove(this.imageDiv)\n }\n },\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer)\n+ setOpacity: function(opacity) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity)\n+ },\n+ moveTo: function(px) {\n+ if (px != null) {\n+ this.px = px\n+ }\n+ if (this.imageDiv != null) {\n+ if (this.px == null) {\n+ this.display(false)\n+ } else {\n+ if (this.calculateOffset) {\n+ this.offset = this.calculateOffset(this.size)\n+ }\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n+ x: this.px.x + this.offset.x,\n+ y: this.px.y + this.offset.y\n+ })\n }\n }\n },\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots()\n- }\n+ display: function(display) {\n+ this.imageDiv.style.display = display ? \"\" : \"none\"\n },\n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n+ isDrawn: function() {\n+ var isDrawn = this.imageDiv && this.imageDiv.parentNode && this.imageDiv.parentNode.nodeType != 11;\n+ return isDrawn\n+ },\n+ CLASS_NAME: \"OpenLayers.Icon\"\n });\n-OpenLayers.Popup.Anchored = OpenLayers.Class(OpenLayers.Popup, {\n- relativePosition: null,\n- keepInMap: true,\n- anchor: null,\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n- var newArguments = [id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback];\n- OpenLayers.Popup.prototype.initialize.apply(this, newArguments);\n- this.anchor = anchor != null ? anchor : {\n- size: new OpenLayers.Size(0, 0),\n- offset: new OpenLayers.Pixel(0, 0)\n+OpenLayers.Marker = OpenLayers.Class({\n+ icon: null,\n+ lonlat: null,\n+ events: null,\n+ map: null,\n+ initialize: function(lonlat, icon) {\n+ this.lonlat = lonlat;\n+ var newIcon = icon ? icon : OpenLayers.Marker.defaultIcon();\n+ if (this.icon == null) {\n+ this.icon = newIcon\n+ } else {\n+ this.icon.url = newIcon.url;\n+ this.icon.size = newIcon.size;\n+ this.icon.offset = newIcon.offset;\n+ this.icon.calculateOffset = newIcon.calculateOffset\n }\n+ this.events = new OpenLayers.Events(this, this.icon.imageDiv)\n },\n destroy: function() {\n- this.anchor = null;\n- this.relativePosition = null;\n- OpenLayers.Popup.prototype.destroy.apply(this, arguments)\n+ this.erase();\n+ this.map = null;\n+ this.events.destroy();\n+ this.events = null;\n+ if (this.icon != null) {\n+ this.icon.destroy();\n+ this.icon = null\n+ }\n },\n- show: function() {\n- this.updatePosition();\n- OpenLayers.Popup.prototype.show.apply(this, arguments)\n+ draw: function(px) {\n+ return this.icon.draw(px)\n+ },\n+ erase: function() {\n+ if (this.icon != null) {\n+ this.icon.erase()\n+ }\n },\n moveTo: function(px) {\n- var oldRelativePosition = this.relativePosition;\n- this.relativePosition = this.calculateRelativePosition(px);\n- OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));\n- if (this.relativePosition != oldRelativePosition) {\n- this.updateRelativePosition()\n+ if (px != null && this.icon != null) {\n+ this.icon.moveTo(px)\n }\n+ this.lonlat = this.map.getLonLatFromLayerPx(px)\n },\n- setSize: function(contentSize) {\n- OpenLayers.Popup.prototype.setSize.apply(this, arguments);\n- if (this.lonlat && this.map) {\n- var px = this.map.getLayerPxFromLonLat(this.lonlat);\n- this.moveTo(px)\n+ isDrawn: function() {\n+ var isDrawn = this.icon && this.icon.isDrawn();\n+ return isDrawn\n+ },\n+ onScreen: function() {\n+ var onScreen = false;\n+ if (this.map) {\n+ var screenBounds = this.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat)\n }\n+ return onScreen\n },\n- calculateRelativePosition: function(px) {\n- var lonlat = this.map.getLonLatFromLayerPx(px);\n- var extent = this.map.getExtent();\n- var quadrant = extent.determineQuadrant(lonlat);\n- return OpenLayers.Bounds.oppositeQuadrant(quadrant)\n+ inflate: function(inflate) {\n+ if (this.icon) {\n+ this.icon.setSize({\n+ w: this.icon.size.w * inflate,\n+ h: this.icon.size.h * inflate\n+ })\n+ }\n },\n- updateRelativePosition: function() {},\n- calculateNewPx: function(px) {\n- var newPx = px.offset(this.anchor.offset);\n- var size = this.size || this.contentSize;\n- var top = this.relativePosition.charAt(0) == \"t\";\n- newPx.y += top ? -size.h : this.anchor.size.h;\n- var left = this.relativePosition.charAt(1) == \"l\";\n- newPx.x += left ? -size.w : this.anchor.size.w;\n- return newPx\n+ setOpacity: function(opacity) {\n+ this.icon.setOpacity(opacity)\n },\n- CLASS_NAME: \"OpenLayers.Popup.Anchored\"\n+ setUrl: function(url) {\n+ this.icon.setUrl(url)\n+ },\n+ display: function(display) {\n+ this.icon.display(display)\n+ },\n+ CLASS_NAME: \"OpenLayers.Marker\"\n });\n-OpenLayers.Popup.Framed = OpenLayers.Class(OpenLayers.Popup.Anchored, {\n- imageSrc: null,\n- imageSize: null,\n- isAlphaImage: false,\n- positionBlocks: null,\n- blocks: null,\n- fixedRelativePosition: false,\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n- OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);\n- if (this.fixedRelativePosition) {\n- this.updateRelativePosition();\n- this.calculateRelativePosition = function(px) {\n- return this.relativePosition\n- }\n+OpenLayers.Marker.defaultIcon = function() {\n+ return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n+ w: 21,\n+ h: 25\n+ }, {\n+ x: -10.5,\n+ y: -25\n+ })\n+};\n+OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(OpenLayers.Format.XML, {\n+ VERSION: \"1.0.0\",\n+ namespaces: {\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+ schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n+ defaultPrefix: \"wps\",\n+ regExes: {\n+ trimSpace: /^\\s*|\\s*$/g,\n+ removeSpace: /\\s*/g,\n+ splitSpace: /\\s+/,\n+ trimComma: /\\s*,\\s*/g\n+ },\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n }\n- this.contentDiv.style.position = \"absolute\";\n- this.contentDiv.style.zIndex = 1;\n- if (closeBox) {\n- this.closeDiv.style.zIndex = 1\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement\n }\n- this.groupDiv.style.position = \"absolute\";\n- this.groupDiv.style.top = \"0px\";\n- this.groupDiv.style.left = \"0px\";\n- this.groupDiv.style.height = \"100%\";\n- this.groupDiv.style.width = \"100%\"\n+ var info = {};\n+ this.readNode(data, info);\n+ return info\n },\n- destroy: function() {\n- this.imageSrc = null;\n- this.imageSize = null;\n- this.isAlphaImage = null;\n- this.fixedRelativePosition = false;\n- this.positionBlocks = null;\n- for (var i = 0; i < this.blocks.length; i++) {\n- var block = this.blocks[i];\n- if (block.image) {\n- block.div.removeChild(block.image)\n- }\n- block.image = null;\n- if (block.div) {\n- this.groupDiv.removeChild(block.div)\n+ readers: {\n+ wps: {\n+ ProcessDescriptions: function(node, obj) {\n+ obj.processDescriptions = {};\n+ this.readChildNodes(node, obj.processDescriptions)\n+ },\n+ ProcessDescription: function(node, processDescriptions) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var processDescription = {\n+ processVersion: processVersion,\n+ statusSupported: node.getAttribute(\"statusSupported\") === \"true\",\n+ storeSupported: node.getAttribute(\"storeSupported\") === \"true\"\n+ };\n+ this.readChildNodes(node, processDescription);\n+ processDescriptions[processDescription.identifier] = processDescription\n+ },\n+ DataInputs: function(node, processDescription) {\n+ processDescription.dataInputs = [];\n+ this.readChildNodes(node, processDescription.dataInputs)\n+ },\n+ ProcessOutputs: function(node, processDescription) {\n+ processDescription.processOutputs = [];\n+ this.readChildNodes(node, processDescription.processOutputs)\n+ },\n+ Output: function(node, processOutputs) {\n+ var output = {};\n+ this.readChildNodes(node, output);\n+ processOutputs.push(output)\n+ },\n+ ComplexOutput: function(node, output) {\n+ output.complexOutput = {};\n+ this.readChildNodes(node, output.complexOutput)\n+ },\n+ LiteralOutput: function(node, output) {\n+ output.literalOutput = {};\n+ this.readChildNodes(node, output.literalOutput)\n+ },\n+ Input: function(node, dataInputs) {\n+ var input = {\n+ maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n+ minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n+ };\n+ this.readChildNodes(node, input);\n+ dataInputs.push(input)\n+ },\n+ BoundingBoxData: function(node, input) {\n+ input.boundingBoxData = {};\n+ this.readChildNodes(node, input.boundingBoxData)\n+ },\n+ CRS: function(node, obj) {\n+ if (!obj.CRSs) {\n+ obj.CRSs = {}\n+ }\n+ obj.CRSs[this.getChildValue(node)] = true\n+ },\n+ LiteralData: function(node, input) {\n+ input.literalData = {};\n+ this.readChildNodes(node, input.literalData)\n+ },\n+ ComplexData: function(node, input) {\n+ input.complexData = {};\n+ this.readChildNodes(node, input.complexData)\n+ },\n+ Default: function(node, complexData) {\n+ complexData[\"default\"] = {};\n+ this.readChildNodes(node, complexData[\"default\"])\n+ },\n+ Supported: function(node, complexData) {\n+ complexData[\"supported\"] = {};\n+ this.readChildNodes(node, complexData[\"supported\"])\n+ },\n+ Format: function(node, obj) {\n+ var format = {};\n+ this.readChildNodes(node, format);\n+ if (!obj.formats) {\n+ obj.formats = {}\n+ }\n+ obj.formats[format.mimeType] = true\n+ },\n+ MimeType: function(node, format) {\n+ format.mimeType = this.getChildValue(node)\n }\n- block.div = null\n- }\n- this.blocks = null;\n- OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments)\n- },\n- setBackgroundColor: function(color) {},\n- setBorder: function() {},\n- setOpacity: function(opacity) {},\n- setSize: function(contentSize) {\n- OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);\n- this.updateBlocks()\n+ },\n+ ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n- updateRelativePosition: function() {\n- this.padding = this.positionBlocks[this.relativePosition].padding;\n- if (this.closeDiv) {\n- var contentDivPadding = this.getContentDivPadding();\n- this.closeDiv.style.right = contentDivPadding.right + this.padding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top + this.padding.top + \"px\"\n+ CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n+});\n+OpenLayers.WPSClient = OpenLayers.Class({\n+ servers: null,\n+ version: \"1.0.0\",\n+ lazy: false,\n+ events: null,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ this.servers = {};\n+ for (var s in options.servers) {\n+ this.servers[s] = typeof options.servers[s] == \"string\" ? {\n+ url: options.servers[s],\n+ version: this.version,\n+ processDescription: {}\n+ } : options.servers[s]\n }\n- this.updateBlocks()\n },\n- calculateNewPx: function(px) {\n- var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(this, arguments);\n- newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);\n- return newPx\n+ execute: function(options) {\n+ var process = this.getProcess(options.server, options.process);\n+ process.execute({\n+ inputs: options.inputs,\n+ success: options.success,\n+ scope: options.scope\n+ })\n },\n- createBlocks: function() {\n- this.blocks = [];\n- var firstPosition = null;\n- for (var key in this.positionBlocks) {\n- firstPosition = key;\n- break\n- }\n- var position = this.positionBlocks[firstPosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n- var block = {};\n- this.blocks.push(block);\n- var divId = this.id + \"_FrameDecorationDiv_\" + i;\n- block.div = OpenLayers.Util.createDiv(divId, null, null, null, \"absolute\", null, \"hidden\", null);\n- var imgId = this.id + \"_FrameDecorationImg_\" + i;\n- var imageCreator = this.isAlphaImage ? OpenLayers.Util.createAlphaImageDiv : OpenLayers.Util.createImage;\n- block.image = imageCreator(imgId, null, this.imageSize, this.imageSrc, \"absolute\", null, null, null);\n- block.div.appendChild(block.image);\n- this.groupDiv.appendChild(block.div)\n+ getProcess: function(serverID, processID) {\n+ var process = new OpenLayers.WPSProcess({\n+ client: this,\n+ server: serverID,\n+ identifier: processID\n+ });\n+ if (!this.lazy) {\n+ process.describe()\n }\n+ return process\n },\n- updateBlocks: function() {\n- if (!this.blocks) {\n- this.createBlocks()\n- }\n- if (this.size && this.relativePosition) {\n- var position = this.positionBlocks[this.relativePosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n- var positionBlock = position.blocks[i];\n- var block = this.blocks[i];\n- var l = positionBlock.anchor.left;\n- var b = positionBlock.anchor.bottom;\n- var r = positionBlock.anchor.right;\n- var t = positionBlock.anchor.top;\n- var w = isNaN(positionBlock.size.w) ? this.size.w - (r + l) : positionBlock.size.w;\n- var h = isNaN(positionBlock.size.h) ? this.size.h - (b + t) : positionBlock.size.h;\n- block.div.style.width = (w < 0 ? 0 : w) + \"px\";\n- block.div.style.height = (h < 0 ? 0 : h) + \"px\";\n- block.div.style.left = l != null ? l + \"px\" : \"\";\n- block.div.style.bottom = b != null ? b + \"px\" : \"\";\n- block.div.style.right = r != null ? r + \"px\" : \"\";\n- block.div.style.top = t != null ? t + \"px\" : \"\";\n- block.image.style.left = positionBlock.position.x + \"px\";\n- block.image.style.top = positionBlock.position.y + \"px\"\n+ describeProcess: function(serverID, processID, callback, scope) {\n+ var server = this.servers[serverID];\n+ if (!server.processDescription[processID]) {\n+ if (!(processID in server.processDescription)) {\n+ server.processDescription[processID] = null;\n+ OpenLayers.Request.GET({\n+ url: server.url,\n+ params: {\n+ SERVICE: \"WPS\",\n+ VERSION: server.version,\n+ REQUEST: \"DescribeProcess\",\n+ IDENTIFIER: processID\n+ },\n+ success: function(response) {\n+ server.processDescription[processID] = response.responseText;\n+ this.events.triggerEvent(\"describeprocess\", {\n+ identifier: processID,\n+ raw: response.responseText\n+ })\n+ },\n+ scope: this\n+ })\n+ } else {\n+ this.events.register(\"describeprocess\", this, function describe(evt) {\n+ if (evt.identifier === processID) {\n+ this.events.unregister(\"describeprocess\", this, describe);\n+ callback.call(scope, evt.raw)\n+ }\n+ })\n }\n- this.contentDiv.style.left = this.padding.left + \"px\";\n- this.contentDiv.style.top = this.padding.top + \"px\"\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Popup.Framed\"\n-});\n-OpenLayers.Popup.FramedCloud = OpenLayers.Class(OpenLayers.Popup.Framed, {\n- contentDisplayClass: \"olFramedCloudPopupContent\",\n- autoSize: true,\n- panMapIfOutOfView: true,\n- imageSize: new OpenLayers.Size(1276, 736),\n- isAlphaImage: false,\n- fixedRelativePosition: false,\n- positionBlocks: {\n- tl: {\n- offset: new OpenLayers.Pixel(44, 0),\n- padding: new OpenLayers.Bounds(8, 40, 8, 9),\n- blocks: [{\n- size: new OpenLayers.Size(\"auto\", \"auto\"),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, {\n- size: new OpenLayers.Size(22, \"auto\"),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, {\n- size: new OpenLayers.Size(\"auto\", 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, {\n- size: new OpenLayers.Size(22, 18),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -632)\n- }, {\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(0, -688)\n- }]\n- },\n- tr: {\n- offset: new OpenLayers.Pixel(-45, 0),\n- padding: new OpenLayers.Bounds(8, 40, 8, 9),\n- blocks: [{\n- size: new OpenLayers.Size(\"auto\", \"auto\"),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, {\n- size: new OpenLayers.Size(22, \"auto\"),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, {\n- size: new OpenLayers.Size(\"auto\", 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, {\n- size: new OpenLayers.Size(22, 19),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -631)\n- }, {\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(0, 0, null, null),\n- position: new OpenLayers.Pixel(-215, -687)\n- }]\n- },\n- bl: {\n- offset: new OpenLayers.Pixel(45, 0),\n- padding: new OpenLayers.Bounds(8, 9, 8, 40),\n- blocks: [{\n- size: new OpenLayers.Size(\"auto\", \"auto\"),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, {\n- size: new OpenLayers.Size(22, \"auto\"),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, {\n- size: new OpenLayers.Size(\"auto\", 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, {\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, {\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(null, null, 0, 0),\n- position: new OpenLayers.Pixel(-101, -674)\n- }]\n- },\n- br: {\n- offset: new OpenLayers.Pixel(-44, 0),\n- padding: new OpenLayers.Bounds(8, 9, 8, 40),\n- blocks: [{\n- size: new OpenLayers.Size(\"auto\", \"auto\"),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, {\n- size: new OpenLayers.Size(22, \"auto\"),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, {\n- size: new OpenLayers.Size(\"auto\", 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, {\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, {\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(0, null, null, 0),\n- position: new OpenLayers.Pixel(-311, -674)\n- }]\n+ } else {\n+ window.setTimeout(function() {\n+ callback.call(scope, server.processDescription[processID])\n+ }, 0)\n }\n },\n- minSize: new OpenLayers.Size(105, 10),\n- maxSize: new OpenLayers.Size(1200, 660),\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n- this.imageSrc = OpenLayers.Util.getImageLocation(\"cloud-popup-relative.png\");\n- OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n- this.contentDiv.className = this.contentDisplayClass\n+ destroy: function() {\n+ this.events.destroy();\n+ this.events = null;\n+ this.servers = null\n },\n- CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n-});\n-OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.1.0\",\n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n-});\n-OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.0.0\",\n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n+ CLASS_NAME: \"OpenLayers.WPSClient\"\n });\n-OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n+ defaultDesc: \"No description available\",\n+ extractWaypoints: true,\n+ extractTracks: true,\n+ extractRoutes: true,\n+ extractAttributes: true,\n namespaces: {\n- atom: \"http://www.w3.org/2005/Atom\",\n- georss: \"http://www.georss.org/georss\"\n+ gpx: \"http://www.topografix.com/GPX/1/1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+ schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n+ creator: \"OpenLayers\",\n+ initialize: function(options) {\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n },\n- feedTitle: \"untitled\",\n- defaultEntryTitle: \"untitled\",\n- gmlParser: null,\n- xy: false,\n read: function(doc) {\n if (typeof doc == \"string\") {\n doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc])\n }\n- return this.parseFeatures(doc)\n- },\n- write: function(features) {\n- var doc;\n- if (OpenLayers.Util.isArray(features)) {\n- doc = this.createElementNSPlus(\"atom:feed\");\n- doc.appendChild(this.createElementNSPlus(\"atom:title\", {\n- value: this.feedTitle\n- }));\n- for (var i = 0, ii = features.length; i < ii; i++) {\n- doc.appendChild(this.buildEntryNode(features[i]))\n+ var features = [];\n+ if (this.extractTracks) {\n+ var tracks = doc.getElementsByTagName(\"trk\");\n+ for (var i = 0, len = tracks.length; i < len; i++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(tracks[i])\n+ }\n+ var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n+ for (var j = 0, seglen = segs.length; j < seglen; j++) {\n+ var track = this.extractSegment(segs[j], \"trkpt\");\n+ features.push(new OpenLayers.Feature.Vector(track, attrs))\n+ }\n }\n- } else {\n- doc = this.buildEntryNode(features)\n }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [doc])\n- },\n- buildContentNode: function(content) {\n- var node = this.createElementNSPlus(\"atom:content\", {\n- attributes: {\n- type: content.type || null\n+ if (this.extractRoutes) {\n+ var routes = doc.getElementsByTagName(\"rte\");\n+ for (var k = 0, klen = routes.length; k < klen; k++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(routes[k])\n+ }\n+ var route = this.extractSegment(routes[k], \"rtept\");\n+ features.push(new OpenLayers.Feature.Vector(route, attrs))\n }\n- });\n- if (content.src) {\n- node.setAttribute(\"src\", content.src)\n- } else {\n- if (content.type == \"text\" || content.type == null) {\n- node.appendChild(this.createTextNode(content.value))\n- } else if (content.type == \"html\") {\n- if (typeof content.value != \"string\") {\n- throw \"HTML content must be in form of an escaped string\"\n+ }\n+ if (this.extractWaypoints) {\n+ var waypoints = doc.getElementsByTagName(\"wpt\");\n+ for (var l = 0, len = waypoints.length; l < len; l++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(waypoints[l])\n }\n- node.appendChild(this.createTextNode(content.value))\n- } else if (content.type == \"xhtml\") {\n- node.appendChild(content.value)\n- } else if (content.type == \"xhtml\" || content.type.match(/(\\+|\\/)xml$/)) {\n- node.appendChild(content.value)\n- } else {\n- node.appendChild(this.createTextNode(content.value))\n+ var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n+ features.push(new OpenLayers.Feature.Vector(wpt, attrs))\n }\n }\n- return node\n- },\n- buildEntryNode: function(feature) {\n- var attrib = feature.attributes;\n- var atomAttrib = attrib.atom || {};\n- var entryNode = this.createElementNSPlus(\"atom:entry\");\n- if (atomAttrib.authors) {\n- var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? atomAttrib.authors : [atomAttrib.authors];\n- for (var i = 0, ii = authors.length; i < ii; i++) {\n- entryNode.appendChild(this.buildPersonConstructNode(\"author\", authors[i]))\n+ if (this.internalProjection && this.externalProjection) {\n+ for (var g = 0, featLength = features.length; g < featLength; g++) {\n+ features[g].geometry.transform(this.externalProjection, this.internalProjection)\n }\n }\n- if (atomAttrib.categories) {\n- var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? atomAttrib.categories : [atomAttrib.categories];\n- var category;\n- for (var i = 0, ii = categories.length; i < ii; i++) {\n- category = categories[i];\n- entryNode.appendChild(this.createElementNSPlus(\"atom:category\", {\n- attributes: {\n- term: category.term,\n- scheme: category.scheme || null,\n- label: category.label || null\n+ return features\n+ },\n+ extractSegment: function(segment, segmentType) {\n+ var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n+ var point_features = [];\n+ for (var i = 0, len = points.length; i < len; i++) {\n+ point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")))\n+ }\n+ return new OpenLayers.Geometry.LineString(point_features)\n+ },\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+ var attrNode = node.firstChild,\n+ value, name;\n+ while (attrNode) {\n+ if (attrNode.nodeType == 1 && attrNode.firstChild) {\n+ value = attrNode.firstChild;\n+ if (value.nodeType == 3 || value.nodeType == 4) {\n+ name = attrNode.prefix ? attrNode.nodeName.split(\":\")[1] : attrNode.nodeName;\n+ if (name != \"trkseg\" && name != \"rtept\") {\n+ attributes[name] = value.nodeValue\n }\n- }))\n+ }\n }\n+ attrNode = attrNode.nextSibling\n }\n- if (atomAttrib.content) {\n- entryNode.appendChild(this.buildContentNode(atomAttrib.content))\n+ return attributes\n+ },\n+ write: function(features, metadata) {\n+ features = OpenLayers.Util.isArray(features) ? features : [features];\n+ var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n+ gpx.setAttribute(\"version\", \"1.1\");\n+ gpx.setAttribute(\"creator\", this.creator);\n+ this.setAttributes(gpx, {\n+ \"xsi:schemaLocation\": this.schemaLocation\n+ });\n+ if (metadata && typeof metadata == \"object\") {\n+ gpx.appendChild(this.buildMetadataNode(metadata))\n }\n- if (atomAttrib.contributors) {\n- var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? atomAttrib.contributors : [atomAttrib.contributors];\n- for (var i = 0, ii = contributors.length; i < ii; i++) {\n- entryNode.appendChild(this.buildPersonConstructNode(\"contributor\", contributors[i]))\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ gpx.appendChild(this.buildFeatureNode(features[i]))\n+ }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [gpx])\n+ },\n+ buildMetadataNode: function(metadata) {\n+ var types = [\"name\", \"desc\", \"author\"],\n+ node = this.createElementNS(this.namespaces.gpx, \"metadata\");\n+ for (var i = 0; i < types.length; i++) {\n+ var type = types[i];\n+ if (metadata[type]) {\n+ var n = this.createElementNS(this.namespaces.gpx, type);\n+ n.appendChild(this.createTextNode(metadata[type]));\n+ node.appendChild(n)\n }\n }\n- if (feature.fid) {\n- entryNode.appendChild(this.createElementNSPlus(\"atom:id\", {\n- value: feature.fid\n- }))\n+ return node\n+ },\n+ buildFeatureNode: function(feature) {\n+ var geometry = feature.geometry;\n+ geometry = geometry.clone();\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.internalProjection, this.externalProjection)\n }\n- if (atomAttrib.links) {\n- var links = OpenLayers.Util.isArray(atomAttrib.links) ? atomAttrib.links : [atomAttrib.links];\n- var link;\n- for (var i = 0, ii = links.length; i < ii; i++) {\n- link = links[i];\n- entryNode.appendChild(this.createElementNSPlus(\"atom:link\", {\n- attributes: {\n- href: link.href,\n- rel: link.rel || null,\n- type: link.type || null,\n- hreflang: link.hreflang || null,\n- title: link.title || null,\n- length: link.length || null\n- }\n- }))\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ var wpt = this.buildWptNode(geometry);\n+ this.appendAttributesNode(wpt, feature);\n+ return wpt\n+ } else {\n+ var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n+ this.appendAttributesNode(trkNode, feature);\n+ var trkSegNodes = this.buildTrkSegNode(geometry);\n+ trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? trkSegNodes : [trkSegNodes];\n+ for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n+ trkNode.appendChild(trkSegNodes[i])\n }\n+ return trkNode\n }\n- if (atomAttrib.published) {\n- entryNode.appendChild(this.createElementNSPlus(\"atom:published\", {\n- value: atomAttrib.published\n- }))\n- }\n- if (atomAttrib.rights) {\n- entryNode.appendChild(this.createElementNSPlus(\"atom:rights\", {\n- value: atomAttrib.rights\n- }))\n+ },\n+ buildTrkSegNode: function(geometry) {\n+ var node, i, len, point, nodes;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ point = geometry.components[i];\n+ node.appendChild(this.buildTrkPtNode(point))\n+ }\n+ return node\n+ } else {\n+ nodes = [];\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ nodes.push(this.buildTrkSegNode(geometry.components[i]))\n+ }\n+ return nodes\n }\n- if (atomAttrib.summary || attrib.description) {\n- entryNode.appendChild(this.createElementNSPlus(\"atom:summary\", {\n- value: atomAttrib.summary || attrib.description\n- }))\n+ },\n+ buildTrkPtNode: function(point) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n+ node.setAttribute(\"lon\", point.x);\n+ node.setAttribute(\"lat\", point.y);\n+ return node\n+ },\n+ buildWptNode: function(geometry) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n+ node.setAttribute(\"lon\", geometry.x);\n+ node.setAttribute(\"lat\", geometry.y);\n+ return node\n+ },\n+ appendAttributesNode: function(node, feature) {\n+ var name = this.createElementNS(this.namespaces.gpx, \"name\");\n+ name.appendChild(this.createTextNode(feature.attributes.name || feature.id));\n+ node.appendChild(name);\n+ var desc = this.createElementNS(this.namespaces.gpx, \"desc\");\n+ desc.appendChild(this.createTextNode(feature.attributes.description || this.defaultDesc));\n+ node.appendChild(desc)\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.GPX\"\n+});\n+OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n+ geometryType: \"linestring\",\n+ initialize: function(options) {\n+ OpenLayers.Format.prototype.initialize.apply(this, [options])\n+ },\n+ read: function(encoded) {\n+ var geomType;\n+ if (this.geometryType == \"linestring\") geomType = OpenLayers.Geometry.LineString;\n+ else if (this.geometryType == \"linearring\") geomType = OpenLayers.Geometry.LinearRing;\n+ else if (this.geometryType == \"multipoint\") geomType = OpenLayers.Geometry.MultiPoint;\n+ else if (this.geometryType != \"point\" && this.geometryType != \"polygon\") return null;\n+ var flatPoints = this.decodeDeltas(encoded, 2);\n+ var flatPointsLength = flatPoints.length;\n+ var pointGeometries = [];\n+ for (var i = 0; i + 1 < flatPointsLength;) {\n+ var y = flatPoints[i++],\n+ x = flatPoints[i++];\n+ pointGeometries.push(new OpenLayers.Geometry.Point(x, y))\n }\n- entryNode.appendChild(this.createElementNSPlus(\"atom:title\", {\n- value: atomAttrib.title || attrib.title || this.defaultEntryTitle\n- }));\n- if (atomAttrib.updated) {\n- entryNode.appendChild(this.createElementNSPlus(\"atom:updated\", {\n- value: atomAttrib.updated\n- }))\n+ if (this.geometryType == \"point\") return new OpenLayers.Feature.Vector(pointGeometries[0]);\n+ if (this.geometryType == \"polygon\") return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(pointGeometries)]));\n+ return new OpenLayers.Feature.Vector(new geomType(pointGeometries))\n+ },\n+ decode: function(encoded, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = this.decodeDeltas(encoded, dims, factor);\n+ var flatPointsLength = flatPoints.length;\n+ var points = [];\n+ for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n+ var point = [];\n+ for (var dim = 0; dim < dims; ++dim) {\n+ point.push(flatPoints[i++])\n+ }\n+ points.push(point)\n }\n- if (feature.geometry) {\n- var whereNode = this.createElementNSPlus(\"georss:where\");\n- whereNode.appendChild(this.buildGeometryNode(feature.geometry));\n- entryNode.appendChild(whereNode)\n+ return points\n+ },\n+ write: function(features) {\n+ var feature;\n+ if (features.constructor == Array) feature = features[0];\n+ else feature = features;\n+ var geometry = feature.geometry;\n+ var type = geometry.CLASS_NAME.split(\".\")[2].toLowerCase();\n+ var pointGeometries;\n+ if (type == \"point\") pointGeometries = new Array(geometry);\n+ else if (type == \"linestring\" || type == \"linearring\" || type == \"multipoint\") pointGeometries = geometry.components;\n+ else if (type == \"polygon\") pointGeometries = geometry.components[0].components;\n+ else return null;\n+ var flatPoints = [];\n+ var pointGeometriesLength = pointGeometries.length;\n+ for (var i = 0; i < pointGeometriesLength; ++i) {\n+ var pointGeometry = pointGeometries[i];\n+ flatPoints.push(pointGeometry.y);\n+ flatPoints.push(pointGeometry.x)\n }\n- return entryNode\n+ return this.encodeDeltas(flatPoints, 2)\n },\n- initGmlParser: function() {\n- this.gmlParser = new OpenLayers.Format.GML.v3({\n- xy: this.xy,\n- featureNS: \"http://example.com#feature\",\n- internalProjection: this.internalProjection,\n- externalProjection: this.externalProjection\n- })\n+ encode: function(points, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = [];\n+ var pointsLength = points.length;\n+ for (var i = 0; i < pointsLength; ++i) {\n+ var point = points[i];\n+ for (var dim = 0; dim < dims; ++dim) {\n+ flatPoints.push(point[dim])\n+ }\n+ }\n+ return this.encodeDeltas(flatPoints, dims, factor)\n },\n- buildGeometryNode: function(geometry) {\n- if (!this.gmlParser) {\n- this.initGmlParser()\n+ encodeDeltas: function(numbers, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0\n }\n- var node = this.gmlParser.writeNode(\"feature:_geometry\", geometry);\n- return node.firstChild\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ var num = numbers[i];\n+ var delta = num - lastNumbers[d];\n+ lastNumbers[d] = num;\n+ numbers[i] = delta\n+ }\n+ }\n+ return this.encodeFloats(numbers, factor)\n },\n- buildPersonConstructNode: function(name, value) {\n- var oNames = [\"uri\", \"email\"];\n- var personNode = this.createElementNSPlus(\"atom:\" + name);\n- personNode.appendChild(this.createElementNSPlus(\"atom:name\", {\n- value: value.name\n- }));\n- for (var i = 0, ii = oNames.length; i < ii; i++) {\n- if (value[oNames[i]]) {\n- personNode.appendChild(this.createElementNSPlus(\"atom:\" + oNames[i], {\n- value: value[oNames[i]]\n- }))\n+ decodeDeltas: function(encoded, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0\n+ }\n+ var numbers = this.decodeFloats(encoded, factor);\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ lastNumbers[d] += numbers[i];\n+ numbers[i] = lastNumbers[d]\n }\n }\n- return personNode\n+ return numbers\n },\n- getFirstChildValue: function(node, nsuri, name, def) {\n- var value;\n- var nodes = this.getElementsByTagNameNS(node, nsuri, name);\n- if (nodes && nodes.length > 0) {\n- value = this.getChildValue(nodes[0], def)\n- } else {\n- value = def\n+ encodeFloats: function(numbers, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] = Math.round(numbers[i] * factor)\n }\n- return value\n+ return this.encodeSignedIntegers(numbers)\n },\n- parseFeature: function(node) {\n- var atomAttrib = {};\n- var value = null;\n- var nodes = null;\n- var attval = null;\n- var atomns = this.namespaces.atom;\n- this.parsePersonConstructs(node, \"author\", atomAttrib);\n- nodes = this.getElementsByTagNameNS(node, atomns, \"category\");\n- if (nodes.length > 0) {\n- atomAttrib.categories = []\n+ decodeFloats: function(encoded, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var numbers = this.decodeSignedIntegers(encoded);\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] /= factor\n }\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- value = {};\n- value.term = nodes[i].getAttribute(\"term\");\n- attval = nodes[i].getAttribute(\"scheme\");\n- if (attval) {\n- value.scheme = attval\n- }\n- attval = nodes[i].getAttribute(\"label\");\n- if (attval) {\n- value.label = attval\n+ return numbers\n+ },\n+ encodeSignedIntegers: function(numbers) {\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~signedNum\n }\n- atomAttrib.categories.push(value)\n+ numbers[i] = signedNum\n }\n- nodes = this.getElementsByTagNameNS(node, atomns, \"content\");\n- if (nodes.length > 0) {\n- value = {};\n- attval = nodes[0].getAttribute(\"type\");\n- if (attval) {\n- value.type = attval\n- }\n- attval = nodes[0].getAttribute(\"src\");\n- if (attval) {\n- value.src = attval\n- } else {\n- if (value.type == \"text\" || value.type == \"html\" || value.type == null) {\n- value.value = this.getFirstChildValue(node, atomns, \"content\", null)\n- } else if (value.type == \"xhtml\" || value.type.match(/(\\+|\\/)xml$/)) {\n- value.value = this.getChildEl(nodes[0])\n- } else {\n- value.value = this.getFirstChildValue(node, atomns, \"content\", null)\n- }\n- atomAttrib.content = value\n- }\n+ return this.encodeUnsignedIntegers(numbers)\n+ },\n+ decodeSignedIntegers: function(encoded) {\n+ var numbers = this.decodeUnsignedIntegers(encoded);\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+ numbers[i] = num & 1 ? ~(num >> 1) : num >> 1\n }\n- this.parsePersonConstructs(node, \"contributor\", atomAttrib);\n- atomAttrib.id = this.getFirstChildValue(node, atomns, \"id\", null);\n- nodes = this.getElementsByTagNameNS(node, atomns, \"link\");\n- if (nodes.length > 0) {\n- atomAttrib.links = new Array(nodes.length)\n+ return numbers\n+ },\n+ encodeUnsignedIntegers: function(numbers) {\n+ var encoded = \"\";\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ encoded += this.encodeUnsignedInteger(numbers[i])\n }\n- var oAtts = [\"rel\", \"type\", \"hreflang\", \"title\", \"length\"];\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- value = {};\n- value.href = nodes[i].getAttribute(\"href\");\n- for (var j = 0, jj = oAtts.length; j < jj; j++) {\n- attval = nodes[i].getAttribute(oAtts[j]);\n- if (attval) {\n- value[oAtts[j]] = attval\n- }\n+ return encoded\n+ },\n+ decodeUnsignedIntegers: function(encoded) {\n+ var numbers = [];\n+ var current = 0;\n+ var shift = 0;\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n+ current |= (b & 31) << shift;\n+ if (b < 32) {\n+ numbers.push(current);\n+ current = 0;\n+ shift = 0\n+ } else {\n+ shift += 5\n }\n- atomAttrib.links[i] = value\n }\n- value = this.getFirstChildValue(node, atomns, \"published\", null);\n- if (value) {\n- atomAttrib.published = value\n- }\n- value = this.getFirstChildValue(node, atomns, \"rights\", null);\n- if (value) {\n- atomAttrib.rights = value\n+ return numbers\n+ },\n+ encodeFloat: function(num, opt_factor) {\n+ num = Math.round(num * (opt_factor || 1e5));\n+ return this.encodeSignedInteger(num)\n+ },\n+ decodeFloat: function(encoded, opt_factor) {\n+ var result = this.decodeSignedInteger(encoded);\n+ return result / (opt_factor || 1e5)\n+ },\n+ encodeSignedInteger: function(num) {\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~signedNum\n }\n- value = this.getFirstChildValue(node, atomns, \"summary\", null);\n- if (value) {\n- atomAttrib.summary = value\n+ return this.encodeUnsignedInteger(signedNum)\n+ },\n+ decodeSignedInteger: function(encoded) {\n+ var result = this.decodeUnsignedInteger(encoded);\n+ return result & 1 ? ~(result >> 1) : result >> 1\n+ },\n+ encodeUnsignedInteger: function(num) {\n+ var value, encoded = \"\";\n+ while (num >= 32) {\n+ value = (32 | num & 31) + 63;\n+ encoded += String.fromCharCode(value);\n+ num >>= 5\n }\n- atomAttrib.title = this.getFirstChildValue(node, atomns, \"title\", null);\n- atomAttrib.updated = this.getFirstChildValue(node, atomns, \"updated\", null);\n- var featureAttrib = {\n- title: atomAttrib.title,\n- description: atomAttrib.summary,\n- atom: atomAttrib\n- };\n- var geometry = this.parseLocations(node)[0];\n- var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);\n- feature.fid = atomAttrib.id;\n- return feature\n+ value = num + 63;\n+ encoded += String.fromCharCode(value);\n+ return encoded\n },\n- parseFeatures: function(node) {\n- var features = [];\n- var entries = this.getElementsByTagNameNS(node, this.namespaces.atom, \"entry\");\n- if (entries.length == 0) {\n- entries = [node]\n+ decodeUnsignedInteger: function(encoded) {\n+ var result = 0;\n+ var shift = 0;\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n+ result |= (b & 31) << shift;\n+ if (b < 32) break;\n+ shift += 5\n }\n- for (var i = 0, ii = entries.length; i < ii; i++) {\n- features.push(this.parseFeature(entries[i]))\n+ return result\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n+});\n+OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ layerOptions: null,\n+ layerParams: null,\n+ read: function(data, options) {\n+ var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, arguments);\n+ var map;\n+ if (options && options.map) {\n+ this.context = context;\n+ if (options.map instanceof OpenLayers.Map) {\n+ map = this.mergeContextToMap(context, options.map)\n+ } else {\n+ var mapOptions = options.map;\n+ if (OpenLayers.Util.isElement(mapOptions) || typeof mapOptions == \"string\") {\n+ mapOptions = {\n+ div: mapOptions\n+ }\n+ }\n+ map = this.contextToMap(context, mapOptions)\n+ }\n+ } else {\n+ map = context\n }\n- return features\n+ return map\n },\n- parseLocations: function(node) {\n- var georssns = this.namespaces.georss;\n- var locations = {\n- components: []\n+ getLayerFromContext: function(layerContext) {\n+ var i, len;\n+ var options = {\n+ queryable: layerContext.queryable,\n+ visibility: layerContext.visibility,\n+ maxExtent: layerContext.maxExtent,\n+ metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n+ styles: layerContext.styles,\n+ formats: layerContext.formats,\n+ abstract: layerContext[\"abstract\"],\n+ dataURL: layerContext.dataURL\n+ }),\n+ numZoomLevels: layerContext.numZoomLevels,\n+ units: layerContext.units,\n+ isBaseLayer: layerContext.isBaseLayer,\n+ opacity: layerContext.opacity,\n+ displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n+ singleTile: layerContext.singleTile,\n+ tileSize: layerContext.tileSize ? new OpenLayers.Size(layerContext.tileSize.width, layerContext.tileSize.height) : undefined,\n+ minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n+ maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n+ srs: layerContext.srs,\n+ dimensions: layerContext.dimensions,\n+ metadataURL: layerContext.metadataURL\n };\n- var where = this.getElementsByTagNameNS(node, georssns, \"where\");\n- if (where && where.length > 0) {\n- if (!this.gmlParser) {\n- this.initGmlParser()\n- }\n- for (var i = 0, ii = where.length; i < ii; i++) {\n- this.gmlParser.readChildNodes(where[i], locations)\n- }\n+ if (this.layerOptions) {\n+ OpenLayers.Util.applyDefaults(options, this.layerOptions)\n }\n- var components = locations.components;\n- var point = this.getElementsByTagNameNS(node, georssns, \"point\");\n- if (point && point.length > 0) {\n- for (var i = 0, ii = point.length; i < ii; i++) {\n- var xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\\s+/);\n- if (xy.length != 2) {\n- xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\\s*,\\s*/)\n+ var params = {\n+ layers: layerContext.name,\n+ transparent: layerContext.transparent,\n+ version: layerContext.version\n+ };\n+ if (layerContext.formats && layerContext.formats.length > 0) {\n+ params.format = layerContext.formats[0].value;\n+ for (i = 0, len = layerContext.formats.length; i < len; i++) {\n+ var format = layerContext.formats[i];\n+ if (format.current == true) {\n+ params.format = format.value;\n+ break\n }\n- components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]))\n }\n }\n- var line = this.getElementsByTagNameNS(node, georssns, \"line\");\n- if (line && line.length > 0) {\n- var coords;\n- var p;\n- var points;\n- for (var i = 0, ii = line.length; i < ii; i++) {\n- coords = OpenLayers.String.trim(line[i].firstChild.nodeValue).split(/\\s+/);\n- points = [];\n- for (var j = 0, jj = coords.length; j < jj; j += 2) {\n- p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n- points.push(p)\n+ if (layerContext.styles && layerContext.styles.length > 0) {\n+ for (i = 0, len = layerContext.styles.length; i < len; i++) {\n+ var style = layerContext.styles[i];\n+ if (style.current == true) {\n+ if (style.href) {\n+ params.sld = style.href\n+ } else if (style.body) {\n+ params.sld_body = style.body\n+ } else {\n+ params.styles = style.name\n+ }\n+ break\n }\n- components.push(new OpenLayers.Geometry.LineString(points))\n }\n }\n- var polygon = this.getElementsByTagNameNS(node, georssns, \"polygon\");\n- if (polygon && polygon.length > 0) {\n- var coords;\n- var p;\n- var points;\n- for (var i = 0, ii = polygon.length; i < ii; i++) {\n- coords = OpenLayers.String.trim(polygon[i].firstChild.nodeValue).split(/\\s+/);\n- points = [];\n- for (var j = 0, jj = coords.length; j < jj; j += 2) {\n- p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n- points.push(p)\n- }\n- components.push(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(points)]))\n+ if (this.layerParams) {\n+ OpenLayers.Util.applyDefaults(params, this.layerParams)\n+ }\n+ var layer = null;\n+ var service = layerContext.service;\n+ if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n+ options.strategies = [new OpenLayers.Strategy.BBOX];\n+ options.protocol = new OpenLayers.Protocol.WFS({\n+ url: layerContext.url,\n+ featurePrefix: layerContext.name.split(\":\")[0],\n+ featureType: layerContext.name.split(\":\").pop()\n+ });\n+ layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n+ options.strategies = [new OpenLayers.Strategy.Fixed];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.KML\n+ });\n+ layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n+ options.strategies = [new OpenLayers.Strategy.Fixed];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.GML\n+ });\n+ layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n+ } else if (layerContext.features) {\n+ layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);\n+ layer.addFeatures(layerContext.features)\n+ } else if (layerContext.categoryLayer !== true) {\n+ layer = new OpenLayers.Layer.WMS(layerContext.title || layerContext.name, layerContext.url, params, options)\n+ }\n+ return layer\n+ },\n+ getLayersFromContext: function(layersContext) {\n+ var layers = [];\n+ for (var i = 0, len = layersContext.length; i < len; i++) {\n+ var layer = this.getLayerFromContext(layersContext[i]);\n+ if (layer !== null) {\n+ layers.push(layer)\n }\n }\n- if (this.internalProjection && this.externalProjection) {\n- for (var i = 0, ii = components.length; i < ii; i++) {\n- if (components[i]) {\n- components[i].transform(this.externalProjection, this.internalProjection)\n- }\n+ return layers\n+ },\n+ contextToMap: function(context, options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ maxExtent: context.maxExtent,\n+ projection: context.projection,\n+ units: context.units\n+ }, options);\n+ if (options.maxExtent) {\n+ options.maxResolution = options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH\n+ }\n+ var metadata = {\n+ contactInformation: context.contactInformation,\n+ abstract: context[\"abstract\"],\n+ keywords: context.keywords,\n+ logo: context.logo,\n+ descriptionURL: context.descriptionURL\n+ };\n+ options.metadata = metadata;\n+ var map = new OpenLayers.Map(options);\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ map.setCenter(context.bounds.getCenterLonLat(), map.getZoomForExtent(context.bounds, true));\n+ return map\n+ },\n+ mergeContextToMap: function(context, map) {\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ return map\n+ },\n+ write: function(obj, options) {\n+ obj = this.toContext(obj);\n+ return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.Context\"\n+});\n+OpenLayers.Format.Context.serviceTypes = {\n+ WMS: \"urn:ogc:serviceType:WMS\",\n+ WFS: \"urn:ogc:serviceType:WFS\",\n+ WCS: \"urn:ogc:serviceType:WCS\",\n+ GML: \"urn:ogc:serviceType:GML\",\n+ SLD: \"urn:ogc:serviceType:SLD\",\n+ FES: \"urn:ogc:serviceType:FES\",\n+ KML: \"urn:ogc:serviceType:KML\"\n+};\n+OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n+ defaultVersion: \"0.3.1\",\n+ getVersion: function(root, options) {\n+ var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this, arguments);\n+ if (version === \"0.3.0\") {\n+ version = this.defaultVersion\n+ }\n+ return version\n+ },\n+ toContext: function(obj) {\n+ var context = {};\n+ if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n+ context.bounds = obj.getExtent();\n+ context.maxExtent = obj.maxExtent;\n+ context.projection = obj.projection;\n+ context.size = obj.getSize();\n+ context.layers = obj.layers\n+ }\n+ return context\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n+});\n+OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.1.0\",\n+ stringifyOutput: true,\n+ CLASS_NAME: \"OpenLayers.Format.XLS\"\n+});\n+OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n+ defaultStyle: null,\n+ extractStyles: true,\n+ initialize: function(options) {\n+ options = options || {};\n+ if (options.extractStyles !== false) {\n+ options.defaultStyle = {\n+ externalGraphic: OpenLayers.Util.getImageLocation(\"marker.png\"),\n+ graphicWidth: 21,\n+ graphicHeight: 25,\n+ graphicXOffset: -10.5,\n+ graphicYOffset: -12.5\n }\n }\n- return components\n+ OpenLayers.Format.prototype.initialize.apply(this, [options])\n },\n- parsePersonConstructs: function(node, name, data) {\n- var persons = [];\n- var atomns = this.namespaces.atom;\n- var nodes = this.getElementsByTagNameNS(node, atomns, name);\n- var oAtts = [\"uri\", \"email\"];\n- for (var i = 0, ii = nodes.length; i < ii; i++) {\n- var value = {};\n- value.name = this.getFirstChildValue(nodes[i], atomns, \"name\", null);\n- for (var j = 0, jj = oAtts.length; j < jj; j++) {\n- var attval = this.getFirstChildValue(nodes[i], atomns, oAtts[j], null);\n- if (attval) {\n- value[oAtts[j]] = attval\n+ read: function(text) {\n+ var lines = text.split(\"\\n\");\n+ var columns;\n+ var features = [];\n+ for (var lcv = 0; lcv < lines.length - 1; lcv++) {\n+ var currLine = lines[lcv].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\");\n+ if (currLine.charAt(0) != \"#\") {\n+ if (!columns) {\n+ columns = currLine.split(\"\\t\")\n+ } else {\n+ var vals = currLine.split(\"\\t\");\n+ var geometry = new OpenLayers.Geometry.Point(0, 0);\n+ var attributes = {};\n+ var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null;\n+ var icon, iconSize, iconOffset, overflow;\n+ var set = false;\n+ for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n+ if (vals[valIndex]) {\n+ if (columns[valIndex] == \"point\") {\n+ var coords = vals[valIndex].split(\",\");\n+ geometry.y = parseFloat(coords[0]);\n+ geometry.x = parseFloat(coords[1]);\n+ set = true\n+ } else if (columns[valIndex] == \"lat\") {\n+ geometry.y = parseFloat(vals[valIndex]);\n+ set = true\n+ } else if (columns[valIndex] == \"lon\") {\n+ geometry.x = parseFloat(vals[valIndex]);\n+ set = true\n+ } else if (columns[valIndex] == \"title\") attributes[\"title\"] = vals[valIndex];\n+ else if (columns[valIndex] == \"image\" || columns[valIndex] == \"icon\" && style) {\n+ style[\"externalGraphic\"] = vals[valIndex]\n+ } else if (columns[valIndex] == \"iconSize\" && style) {\n+ var size = vals[valIndex].split(\",\");\n+ style[\"graphicWidth\"] = parseFloat(size[0]);\n+ style[\"graphicHeight\"] = parseFloat(size[1])\n+ } else if (columns[valIndex] == \"iconOffset\" && style) {\n+ var offset = vals[valIndex].split(\",\");\n+ style[\"graphicXOffset\"] = parseFloat(offset[0]);\n+ style[\"graphicYOffset\"] = parseFloat(offset[1])\n+ } else if (columns[valIndex] == \"description\") {\n+ attributes[\"description\"] = vals[valIndex]\n+ } else if (columns[valIndex] == \"overflow\") {\n+ attributes[\"overflow\"] = vals[valIndex]\n+ } else {\n+ attributes[columns[valIndex]] = vals[valIndex]\n+ }\n+ }\n+ }\n+ if (set) {\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection, this.internalProjection)\n+ }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n+ features.push(feature)\n+ }\n }\n }\n- persons.push(value)\n- }\n- if (persons.length > 0) {\n- data[name + \"s\"] = persons\n }\n+ return features\n },\n- CLASS_NAME: \"OpenLayers.Format.Atom\"\n+ CLASS_NAME: \"OpenLayers.Format.Text\"\n });\n OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n defaultVersion: \"1.0.0\",\n CLASS_NAME: \"OpenLayers.Format.SOSCapabilities\"\n });\n+OpenLayers.Format.QueryStringFilter = function() {\n+ var cmpToStr = {};\n+ cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n+ cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n+\n+ function regex2value(value) {\n+ value = value.replace(/%/g, \"\\\\%\");\n+ value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n+ return $1 ? $0 : \"\\\\\\\\_\"\n+ });\n+ value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n+ value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n+ return $1 || $2 ? $0 : \"_\"\n+ });\n+ value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"%\"\n+ });\n+ value = value.replace(/\\\\\\./g, \".\");\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"*\"\n+ });\n+ return value\n+ }\n+ return OpenLayers.Class(OpenLayers.Format, {\n+ wildcarded: false,\n+ srsInBBOX: false,\n+ write: function(filter, params) {\n+ params = params || {};\n+ var className = filter.CLASS_NAME;\n+ var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n+ switch (filterType) {\n+ case \"Spatial\":\n+ switch (filter.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ params.bbox = filter.value.toArray();\n+ if (this.srsInBBOX && filter.projection) {\n+ params.bbox.push(filter.projection.getCode())\n+ }\n+ break;\n+ case OpenLayers.Filter.Spatial.DWITHIN:\n+ params.tolerance = filter.distance;\n+ case OpenLayers.Filter.Spatial.WITHIN:\n+ params.lon = filter.value.x;\n+ params.lat = filter.value.y;\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\"Unknown spatial filter type \" + filter.type)\n+ }\n+ break;\n+ case \"Comparison\":\n+ var op = cmpToStr[filter.type];\n+ if (op !== undefined) {\n+ var value = filter.value;\n+ if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n+ value = regex2value(value);\n+ if (this.wildcarded) {\n+ value = \"%\" + value + \"%\"\n+ }\n+ }\n+ params[filter.property + \"__\" + op] = value;\n+ params.queryable = params.queryable || [];\n+ params.queryable.push(filter.property)\n+ } else {\n+ OpenLayers.Console.warn(\"Unknown comparison filter type \" + filter.type)\n+ }\n+ break;\n+ case \"Logical\":\n+ if (filter.type === OpenLayers.Filter.Logical.AND) {\n+ for (var i = 0, len = filter.filters.length; i < len; i++) {\n+ params = this.write(filter.filters[i], params)\n+ }\n+ } else {\n+ OpenLayers.Console.warn(\"Unsupported logical filter type \" + filter.type)\n+ }\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\"Unknown filter type \" + filterType)\n+ }\n+ return params\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n+ })\n+}();\n OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(OpenLayers.Format.XML, {\n VERSION: \"1.0.0\",\n namespaces: {\n sos: \"http://www.opengis.net/sos/1.0\",\n gml: \"http://www.opengis.net/gml\",\n sa: \"http://www.opengis.net/sampling/1.0\",\n xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n@@ -18765,18 +14682,14 @@\n });\n return node\n }\n }\n },\n CLASS_NAME: \"OpenLayers.Format.SOSGetFeatureOfInterest\"\n });\n-OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.1.1\",\n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n-});\n OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, {\n namespaces: {\n ows: \"http://www.opengis.net/ows\",\n gml: \"http://www.opengis.net/gml\",\n sos: \"http://www.opengis.net/sos/1.0\",\n ogc: \"http://www.opengis.net/ogc\",\n om: \"http://www.opengis.net/om/1.0\",\n@@ -18993,792 +14906,14 @@\n });\n return node\n }\n }\n },\n CLASS_NAME: \"OpenLayers.Format.SOSGetObservation\"\n });\n-OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- layerOptions: null,\n- layerParams: null,\n- read: function(data, options) {\n- var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, arguments);\n- var map;\n- if (options && options.map) {\n- this.context = context;\n- if (options.map instanceof OpenLayers.Map) {\n- map = this.mergeContextToMap(context, options.map)\n- } else {\n- var mapOptions = options.map;\n- if (OpenLayers.Util.isElement(mapOptions) || typeof mapOptions == \"string\") {\n- mapOptions = {\n- div: mapOptions\n- }\n- }\n- map = this.contextToMap(context, mapOptions)\n- }\n- } else {\n- map = context\n- }\n- return map\n- },\n- getLayerFromContext: function(layerContext) {\n- var i, len;\n- var options = {\n- queryable: layerContext.queryable,\n- visibility: layerContext.visibility,\n- maxExtent: layerContext.maxExtent,\n- metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n- styles: layerContext.styles,\n- formats: layerContext.formats,\n- abstract: layerContext[\"abstract\"],\n- dataURL: layerContext.dataURL\n- }),\n- numZoomLevels: layerContext.numZoomLevels,\n- units: layerContext.units,\n- isBaseLayer: layerContext.isBaseLayer,\n- opacity: layerContext.opacity,\n- displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n- singleTile: layerContext.singleTile,\n- tileSize: layerContext.tileSize ? new OpenLayers.Size(layerContext.tileSize.width, layerContext.tileSize.height) : undefined,\n- minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n- maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n- srs: layerContext.srs,\n- dimensions: layerContext.dimensions,\n- metadataURL: layerContext.metadataURL\n- };\n- if (this.layerOptions) {\n- OpenLayers.Util.applyDefaults(options, this.layerOptions)\n- }\n- var params = {\n- layers: layerContext.name,\n- transparent: layerContext.transparent,\n- version: layerContext.version\n- };\n- if (layerContext.formats && layerContext.formats.length > 0) {\n- params.format = layerContext.formats[0].value;\n- for (i = 0, len = layerContext.formats.length; i < len; i++) {\n- var format = layerContext.formats[i];\n- if (format.current == true) {\n- params.format = format.value;\n- break\n- }\n- }\n- }\n- if (layerContext.styles && layerContext.styles.length > 0) {\n- for (i = 0, len = layerContext.styles.length; i < len; i++) {\n- var style = layerContext.styles[i];\n- if (style.current == true) {\n- if (style.href) {\n- params.sld = style.href\n- } else if (style.body) {\n- params.sld_body = style.body\n- } else {\n- params.styles = style.name\n- }\n- break\n- }\n- }\n- }\n- if (this.layerParams) {\n- OpenLayers.Util.applyDefaults(params, this.layerParams)\n- }\n- var layer = null;\n- var service = layerContext.service;\n- if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n- options.strategies = [new OpenLayers.Strategy.BBOX];\n- options.protocol = new OpenLayers.Protocol.WFS({\n- url: layerContext.url,\n- featurePrefix: layerContext.name.split(\":\")[0],\n- featureType: layerContext.name.split(\":\").pop()\n- });\n- layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n- } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n- options.strategies = [new OpenLayers.Strategy.Fixed];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.KML\n- });\n- layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n- } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n- options.strategies = [new OpenLayers.Strategy.Fixed];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.GML\n- });\n- layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options)\n- } else if (layerContext.features) {\n- layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);\n- layer.addFeatures(layerContext.features)\n- } else if (layerContext.categoryLayer !== true) {\n- layer = new OpenLayers.Layer.WMS(layerContext.title || layerContext.name, layerContext.url, params, options)\n- }\n- return layer\n- },\n- getLayersFromContext: function(layersContext) {\n- var layers = [];\n- for (var i = 0, len = layersContext.length; i < len; i++) {\n- var layer = this.getLayerFromContext(layersContext[i]);\n- if (layer !== null) {\n- layers.push(layer)\n- }\n- }\n- return layers\n- },\n- contextToMap: function(context, options) {\n- options = OpenLayers.Util.applyDefaults({\n- maxExtent: context.maxExtent,\n- projection: context.projection,\n- units: context.units\n- }, options);\n- if (options.maxExtent) {\n- options.maxResolution = options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH\n- }\n- var metadata = {\n- contactInformation: context.contactInformation,\n- abstract: context[\"abstract\"],\n- keywords: context.keywords,\n- logo: context.logo,\n- descriptionURL: context.descriptionURL\n- };\n- options.metadata = metadata;\n- var map = new OpenLayers.Map(options);\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- map.setCenter(context.bounds.getCenterLonLat(), map.getZoomForExtent(context.bounds, true));\n- return map\n- },\n- mergeContextToMap: function(context, map) {\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- return map\n- },\n- write: function(obj, options) {\n- obj = this.toContext(obj);\n- return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, arguments)\n- },\n- CLASS_NAME: \"OpenLayers.Format.Context\"\n-});\n-OpenLayers.Format.Context.serviceTypes = {\n- WMS: \"urn:ogc:serviceType:WMS\",\n- WFS: \"urn:ogc:serviceType:WFS\",\n- WCS: \"urn:ogc:serviceType:WCS\",\n- GML: \"urn:ogc:serviceType:GML\",\n- SLD: \"urn:ogc:serviceType:SLD\",\n- FES: \"urn:ogc:serviceType:FES\",\n- KML: \"urn:ogc:serviceType:KML\"\n-};\n-OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, {\n- defaultVersion: \"1.1.0\",\n- layerToContext: function(layer) {\n- var parser = this.getParser();\n- var layerContext = {\n- queryable: layer.queryable,\n- visibility: layer.visibility,\n- name: layer.params[\"LAYERS\"],\n- title: layer.name,\n- abstract: layer.metadata[\"abstract\"],\n- dataURL: layer.metadata.dataURL,\n- metadataURL: layer.metadataURL,\n- server: {\n- version: layer.params[\"VERSION\"],\n- url: layer.url\n- },\n- maxExtent: layer.maxExtent,\n- transparent: layer.params[\"TRANSPARENT\"],\n- numZoomLevels: layer.numZoomLevels,\n- units: layer.units,\n- isBaseLayer: layer.isBaseLayer,\n- opacity: layer.opacity == 1 ? undefined : layer.opacity,\n- displayInLayerSwitcher: layer.displayInLayerSwitcher,\n- singleTile: layer.singleTile,\n- tileSize: layer.singleTile || !layer.tileSize ? undefined : {\n- width: layer.tileSize.w,\n- height: layer.tileSize.h\n- },\n- minScale: layer.options.resolutions || layer.options.scales || layer.options.maxResolution || layer.options.minScale ? layer.minScale : undefined,\n- maxScale: layer.options.resolutions || layer.options.scales || layer.options.minResolution || layer.options.maxScale ? layer.maxScale : undefined,\n- formats: [],\n- styles: [],\n- srs: layer.srs,\n- dimensions: layer.dimensions\n- };\n- if (layer.metadata.servertitle) {\n- layerContext.server.title = layer.metadata.servertitle\n- }\n- if (layer.metadata.formats && layer.metadata.formats.length > 0) {\n- for (var i = 0, len = layer.metadata.formats.length; i < len; i++) {\n- var format = layer.metadata.formats[i];\n- layerContext.formats.push({\n- value: format.value,\n- current: format.value == layer.params[\"FORMAT\"]\n- })\n- }\n- } else {\n- layerContext.formats.push({\n- value: layer.params[\"FORMAT\"],\n- current: true\n- })\n- }\n- if (layer.metadata.styles && layer.metadata.styles.length > 0) {\n- for (var i = 0, len = layer.metadata.styles.length; i < len; i++) {\n- var style = layer.metadata.styles[i];\n- if (style.href == layer.params[\"SLD\"] || style.body == layer.params[\"SLD_BODY\"] || style.name == layer.params[\"STYLES\"]) {\n- style.current = true\n- } else {\n- style.current = false\n- }\n- layerContext.styles.push(style)\n- }\n- } else {\n- layerContext.styles.push({\n- href: layer.params[\"SLD\"],\n- body: layer.params[\"SLD_BODY\"],\n- name: layer.params[\"STYLES\"] || parser.defaultStyleName,\n- title: parser.defaultStyleTitle,\n- current: true\n- })\n- }\n- return layerContext\n- },\n- toContext: function(obj) {\n- var context = {};\n- var layers = obj.layers;\n- if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n- var metadata = obj.metadata || {};\n- context.size = obj.getSize();\n- context.bounds = obj.getExtent();\n- context.projection = obj.projection;\n- context.title = obj.title;\n- context.keywords = metadata.keywords;\n- context[\"abstract\"] = metadata[\"abstract\"];\n- context.logo = metadata.logo;\n- context.descriptionURL = metadata.descriptionURL;\n- context.contactInformation = metadata.contactInformation;\n- context.maxExtent = obj.maxExtent\n- } else {\n- OpenLayers.Util.applyDefaults(context, obj);\n- if (context.layers != undefined) {\n- delete context.layers\n- }\n- }\n- if (context.layersContext == undefined) {\n- context.layersContext = []\n- }\n- if (layers != undefined && OpenLayers.Util.isArray(layers)) {\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.WMS) {\n- context.layersContext.push(this.layerToContext(layer))\n- }\n- }\n- }\n- return context\n- },\n- CLASS_NAME: \"OpenLayers.Format.WMC\"\n-});\n-OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {\n- checkTags: false,\n- interestingTagsExclude: null,\n- areaTags: null,\n- initialize: function(options) {\n- var layer_defaults = {\n- interestingTagsExclude: [\"source\", \"source_ref\", \"source:ref\", \"history\", \"attribution\", \"created_by\"],\n- areaTags: [\"area\", \"building\", \"leisure\", \"tourism\", \"ruins\", \"historic\", \"landuse\", \"military\", \"natural\", \"sport\"]\n- };\n- layer_defaults = OpenLayers.Util.extend(layer_defaults, options);\n- var interesting = {};\n- for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {\n- interesting[layer_defaults.interestingTagsExclude[i]] = true\n- }\n- layer_defaults.interestingTagsExclude = interesting;\n- var area = {};\n- for (var i = 0; i < layer_defaults.areaTags.length; i++) {\n- area[layer_defaults.areaTags[i]] = true\n- }\n- layer_defaults.areaTags = area;\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults])\n- },\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc])\n- }\n- var nodes = this.getNodes(doc);\n- var ways = this.getWays(doc);\n- var feat_list = new Array(ways.length);\n- for (var i = 0; i < ways.length; i++) {\n- var point_list = new Array(ways[i].nodes.length);\n- var poly = this.isWayArea(ways[i]) ? 1 : 0;\n- for (var j = 0; j < ways[i].nodes.length; j++) {\n- var node = nodes[ways[i].nodes[j]];\n- var point = new OpenLayers.Geometry.Point(node.lon, node.lat);\n- point.osm_id = parseInt(ways[i].nodes[j]);\n- point_list[j] = point;\n- node.used = true\n- }\n- var geometry = null;\n- if (poly) {\n- geometry = new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(point_list))\n- } else {\n- geometry = new OpenLayers.Geometry.LineString(point_list)\n- }\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection, this.internalProjection)\n- }\n- var feat = new OpenLayers.Feature.Vector(geometry, ways[i].tags);\n- feat.osm_id = parseInt(ways[i].id);\n- feat.fid = \"way.\" + feat.osm_id;\n- feat_list[i] = feat\n- }\n- for (var node_id in nodes) {\n- var node = nodes[node_id];\n- if (!node.used || this.checkTags) {\n- var tags = null;\n- if (this.checkTags) {\n- var result = this.getTags(node.node, true);\n- if (node.used && !result[1]) {\n- continue\n- }\n- tags = result[0]\n- } else {\n- tags = this.getTags(node.node)\n- }\n- var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(node[\"lon\"], node[\"lat\"]), tags);\n- if (this.internalProjection && this.externalProjection) {\n- feat.geometry.transform(this.externalProjection, this.internalProjection)\n- }\n- feat.osm_id = parseInt(node_id);\n- feat.fid = \"node.\" + feat.osm_id;\n- feat_list.push(feat)\n- }\n- node.node = null\n- }\n- return feat_list\n- },\n- getNodes: function(doc) {\n- var node_list = doc.getElementsByTagName(\"node\");\n- var nodes = {};\n- for (var i = 0; i < node_list.length; i++) {\n- var node = node_list[i];\n- var id = node.getAttribute(\"id\");\n- nodes[id] = {\n- lat: node.getAttribute(\"lat\"),\n- lon: node.getAttribute(\"lon\"),\n- node: node\n- }\n- }\n- return nodes\n- },\n- getWays: function(doc) {\n- var way_list = doc.getElementsByTagName(\"way\");\n- var return_ways = [];\n- for (var i = 0; i < way_list.length; i++) {\n- var way = way_list[i];\n- var way_object = {\n- id: way.getAttribute(\"id\")\n- };\n- way_object.tags = this.getTags(way);\n- var node_list = way.getElementsByTagName(\"nd\");\n- way_object.nodes = new Array(node_list.length);\n- for (var j = 0; j < node_list.length; j++) {\n- way_object.nodes[j] = node_list[j].getAttribute(\"ref\")\n- }\n- return_ways.push(way_object)\n- }\n- return return_ways\n- },\n- getTags: function(dom_node, interesting_tags) {\n- var tag_list = dom_node.getElementsByTagName(\"tag\");\n- var tags = {};\n- var interesting = false;\n- for (var j = 0; j < tag_list.length; j++) {\n- var key = tag_list[j].getAttribute(\"k\");\n- tags[key] = tag_list[j].getAttribute(\"v\");\n- if (interesting_tags) {\n- if (!this.interestingTagsExclude[key]) {\n- interesting = true\n- }\n- }\n- }\n- return interesting_tags ? [tags, interesting] : tags\n- },\n- isWayArea: function(way) {\n- var poly_shaped = false;\n- var poly_tags = false;\n- if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {\n- poly_shaped = true\n- }\n- if (this.checkTags) {\n- for (var key in way.tags) {\n- if (this.areaTags[key]) {\n- poly_tags = true;\n- break\n- }\n- }\n- }\n- return poly_shaped && (this.checkTags ? poly_tags : true)\n- },\n- write: function(features) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- this.osm_id = 1;\n- this.created_nodes = {};\n- var root_node = this.createElementNS(null, \"osm\");\n- root_node.setAttribute(\"version\", \"0.5\");\n- root_node.setAttribute(\"generator\", \"OpenLayers \" + OpenLayers.VERSION_NUMBER);\n- for (var i = features.length - 1; i >= 0; i--) {\n- var nodes = this.createFeatureNodes(features[i]);\n- for (var j = 0; j < nodes.length; j++) {\n- root_node.appendChild(nodes[j])\n- }\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [root_node])\n- },\n- createFeatureNodes: function(feature) {\n- var nodes = [];\n- var className = feature.geometry.CLASS_NAME;\n- var type = className.substring(className.lastIndexOf(\".\") + 1);\n- type = type.toLowerCase();\n- var builder = this.createXML[type];\n- if (builder) {\n- nodes = builder.apply(this, [feature])\n- }\n- return nodes\n- },\n- createXML: {\n- point: function(point) {\n- var id = null;\n- var geometry = point.geometry ? point.geometry : point;\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection, this.externalProjection)\n- }\n- var already_exists = false;\n- if (point.osm_id) {\n- id = point.osm_id;\n- if (this.created_nodes[id]) {\n- already_exists = true\n- }\n- } else {\n- id = -this.osm_id;\n- this.osm_id++\n- }\n- if (already_exists) {\n- node = this.created_nodes[id]\n- } else {\n- var node = this.createElementNS(null, \"node\")\n- }\n- this.created_nodes[id] = node;\n- node.setAttribute(\"id\", id);\n- node.setAttribute(\"lon\", geometry.x);\n- node.setAttribute(\"lat\", geometry.y);\n- if (point.attributes) {\n- this.serializeTags(point, node)\n- }\n- this.setState(point, node);\n- return already_exists ? [] : [node]\n- },\n- linestring: function(feature) {\n- var id;\n- var nodes = [];\n- var geometry = feature.geometry;\n- if (feature.osm_id) {\n- id = feature.osm_id\n- } else {\n- id = -this.osm_id;\n- this.osm_id++\n- }\n- var way = this.createElementNS(null, \"way\");\n- way.setAttribute(\"id\", id);\n- for (var i = 0; i < geometry.components.length; i++) {\n- var node = this.createXML[\"point\"].apply(this, [geometry.components[i]]);\n- if (node.length) {\n- node = node[0];\n- var node_ref = node.getAttribute(\"id\");\n- nodes.push(node)\n- } else {\n- node_ref = geometry.components[i].osm_id;\n- node = this.created_nodes[node_ref]\n- }\n- this.setState(feature, node);\n- var nd_dom = this.createElementNS(null, \"nd\");\n- nd_dom.setAttribute(\"ref\", node_ref);\n- way.appendChild(nd_dom)\n- }\n- this.serializeTags(feature, way);\n- nodes.push(way);\n- return nodes\n- },\n- polygon: function(feature) {\n- var attrs = OpenLayers.Util.extend({\n- area: \"yes\"\n- }, feature.attributes);\n- var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);\n- feat.osm_id = feature.osm_id;\n- return this.createXML[\"linestring\"].apply(this, [feat])\n- }\n- },\n- serializeTags: function(feature, node) {\n- for (var key in feature.attributes) {\n- var tag = this.createElementNS(null, \"tag\");\n- tag.setAttribute(\"k\", key);\n- tag.setAttribute(\"v\", feature.attributes[key]);\n- node.appendChild(tag)\n- }\n- },\n- setState: function(feature, node) {\n- if (feature.state) {\n- var state = null;\n- switch (feature.state) {\n- case OpenLayers.State.UPDATE:\n- state = \"modify\";\n- case OpenLayers.State.DELETE:\n- state = \"delete\"\n- }\n- if (state) {\n- node.setAttribute(\"action\", state)\n- }\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Format.OSM\"\n-});\n-OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n- defaultVersion: \"0.3.1\",\n- getVersion: function(root, options) {\n- var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this, arguments);\n- if (version === \"0.3.0\") {\n- version = this.defaultVersion\n- }\n- return version\n- },\n- toContext: function(obj) {\n- var context = {};\n- if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n- context.bounds = obj.getExtent();\n- context.maxExtent = obj.maxExtent;\n- context.projection = obj.projection;\n- context.size = obj.getSize();\n- context.layers = obj.layers\n- }\n- return context\n- },\n- CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n-});\n-OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n- geometryType: \"linestring\",\n- initialize: function(options) {\n- OpenLayers.Format.prototype.initialize.apply(this, [options])\n- },\n- read: function(encoded) {\n- var geomType;\n- if (this.geometryType == \"linestring\") geomType = OpenLayers.Geometry.LineString;\n- else if (this.geometryType == \"linearring\") geomType = OpenLayers.Geometry.LinearRing;\n- else if (this.geometryType == \"multipoint\") geomType = OpenLayers.Geometry.MultiPoint;\n- else if (this.geometryType != \"point\" && this.geometryType != \"polygon\") return null;\n- var flatPoints = this.decodeDeltas(encoded, 2);\n- var flatPointsLength = flatPoints.length;\n- var pointGeometries = [];\n- for (var i = 0; i + 1 < flatPointsLength;) {\n- var y = flatPoints[i++],\n- x = flatPoints[i++];\n- pointGeometries.push(new OpenLayers.Geometry.Point(x, y))\n- }\n- if (this.geometryType == \"point\") return new OpenLayers.Feature.Vector(pointGeometries[0]);\n- if (this.geometryType == \"polygon\") return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(pointGeometries)]));\n- return new OpenLayers.Feature.Vector(new geomType(pointGeometries))\n- },\n- decode: function(encoded, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = this.decodeDeltas(encoded, dims, factor);\n- var flatPointsLength = flatPoints.length;\n- var points = [];\n- for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n- var point = [];\n- for (var dim = 0; dim < dims; ++dim) {\n- point.push(flatPoints[i++])\n- }\n- points.push(point)\n- }\n- return points\n- },\n- write: function(features) {\n- var feature;\n- if (features.constructor == Array) feature = features[0];\n- else feature = features;\n- var geometry = feature.geometry;\n- var type = geometry.CLASS_NAME.split(\".\")[2].toLowerCase();\n- var pointGeometries;\n- if (type == \"point\") pointGeometries = new Array(geometry);\n- else if (type == \"linestring\" || type == \"linearring\" || type == \"multipoint\") pointGeometries = geometry.components;\n- else if (type == \"polygon\") pointGeometries = geometry.components[0].components;\n- else return null;\n- var flatPoints = [];\n- var pointGeometriesLength = pointGeometries.length;\n- for (var i = 0; i < pointGeometriesLength; ++i) {\n- var pointGeometry = pointGeometries[i];\n- flatPoints.push(pointGeometry.y);\n- flatPoints.push(pointGeometry.x)\n- }\n- return this.encodeDeltas(flatPoints, 2)\n- },\n- encode: function(points, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = [];\n- var pointsLength = points.length;\n- for (var i = 0; i < pointsLength; ++i) {\n- var point = points[i];\n- for (var dim = 0; dim < dims; ++dim) {\n- flatPoints.push(point[dim])\n- }\n- }\n- return this.encodeDeltas(flatPoints, dims, factor)\n- },\n- encodeDeltas: function(numbers, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0\n- }\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- var num = numbers[i];\n- var delta = num - lastNumbers[d];\n- lastNumbers[d] = num;\n- numbers[i] = delta\n- }\n- }\n- return this.encodeFloats(numbers, factor)\n- },\n- decodeDeltas: function(encoded, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0\n- }\n- var numbers = this.decodeFloats(encoded, factor);\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- lastNumbers[d] += numbers[i];\n- numbers[i] = lastNumbers[d]\n- }\n- }\n- return numbers\n- },\n- encodeFloats: function(numbers, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] = Math.round(numbers[i] * factor)\n- }\n- return this.encodeSignedIntegers(numbers)\n- },\n- decodeFloats: function(encoded, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var numbers = this.decodeSignedIntegers(encoded);\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] /= factor\n- }\n- return numbers\n- },\n- encodeSignedIntegers: function(numbers) {\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~signedNum\n- }\n- numbers[i] = signedNum\n- }\n- return this.encodeUnsignedIntegers(numbers)\n- },\n- decodeSignedIntegers: function(encoded) {\n- var numbers = this.decodeUnsignedIntegers(encoded);\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n- numbers[i] = num & 1 ? ~(num >> 1) : num >> 1\n- }\n- return numbers\n- },\n- encodeUnsignedIntegers: function(numbers) {\n- var encoded = \"\";\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- encoded += this.encodeUnsignedInteger(numbers[i])\n- }\n- return encoded\n- },\n- decodeUnsignedIntegers: function(encoded) {\n- var numbers = [];\n- var current = 0;\n- var shift = 0;\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n- current |= (b & 31) << shift;\n- if (b < 32) {\n- numbers.push(current);\n- current = 0;\n- shift = 0\n- } else {\n- shift += 5\n- }\n- }\n- return numbers\n- },\n- encodeFloat: function(num, opt_factor) {\n- num = Math.round(num * (opt_factor || 1e5));\n- return this.encodeSignedInteger(num)\n- },\n- decodeFloat: function(encoded, opt_factor) {\n- var result = this.decodeSignedInteger(encoded);\n- return result / (opt_factor || 1e5)\n- },\n- encodeSignedInteger: function(num) {\n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~signedNum\n- }\n- return this.encodeUnsignedInteger(signedNum)\n- },\n- decodeSignedInteger: function(encoded) {\n- var result = this.decodeUnsignedInteger(encoded);\n- return result & 1 ? ~(result >> 1) : result >> 1\n- },\n- encodeUnsignedInteger: function(num) {\n- var value, encoded = \"\";\n- while (num >= 32) {\n- value = (32 | num & 31) + 63;\n- encoded += String.fromCharCode(value);\n- num >>= 5\n- }\n- value = num + 63;\n- encoded += String.fromCharCode(value);\n- return encoded\n- },\n- decodeUnsignedInteger: function(encoded) {\n- var result = 0;\n- var shift = 0;\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n- result |= (b & 31) << shift;\n- if (b < 32) break;\n- shift += 5\n- }\n- return result\n- },\n- CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n-});\n OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n layerIdentifier: \"_layer\",\n featureIdentifier: \"_feature\",\n regExes: {\n trimSpace: /^\\s*|\\s*$/g,\n removeSpace: /\\s*/g,\n splitSpace: /\\s+/,\n@@ -19921,1075 +15056,1593 @@\n return {\n geometry: geometry,\n bounds: bounds\n }\n },\n CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n });\n-OpenLayers.Format.CSWGetDomain = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetDomain.DEFAULTS);\n- var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetDomain version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n- version: \"2.0.2\"\n-};\n-OpenLayers.Format.QueryStringFilter = function() {\n- var cmpToStr = {};\n- cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n- cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n- cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n-\n- function regex2value(value) {\n- value = value.replace(/%/g, \"\\\\%\");\n- value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n- return $1 ? $0 : \"\\\\\\\\_\"\n- });\n- value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n- value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n- return $1 || $2 ? $0 : \"_\"\n- });\n- value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n- return $1 ? $0 : \"%\"\n- });\n- value = value.replace(/\\\\\\./g, \".\");\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"*\"\n- });\n- return value\n- }\n- return OpenLayers.Class(OpenLayers.Format, {\n- wildcarded: false,\n- srsInBBOX: false,\n- write: function(filter, params) {\n- params = params || {};\n- var className = filter.CLASS_NAME;\n- var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n- switch (filterType) {\n- case \"Spatial\":\n- switch (filter.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- params.bbox = filter.value.toArray();\n- if (this.srsInBBOX && filter.projection) {\n- params.bbox.push(filter.projection.getCode())\n- }\n- break;\n- case OpenLayers.Filter.Spatial.DWITHIN:\n- params.tolerance = filter.distance;\n- case OpenLayers.Filter.Spatial.WITHIN:\n- params.lon = filter.value.x;\n- params.lat = filter.value.y;\n- break;\n- default:\n- OpenLayers.Console.warn(\"Unknown spatial filter type \" + filter.type)\n- }\n- break;\n- case \"Comparison\":\n- var op = cmpToStr[filter.type];\n- if (op !== undefined) {\n- var value = filter.value;\n- if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n- value = regex2value(value);\n- if (this.wildcarded) {\n- value = \"%\" + value + \"%\"\n- }\n- }\n- params[filter.property + \"__\" + op] = value;\n- params.queryable = params.queryable || [];\n- params.queryable.push(filter.property)\n- } else {\n- OpenLayers.Console.warn(\"Unknown comparison filter type \" + filter.type)\n- }\n- break;\n- case \"Logical\":\n- if (filter.type === OpenLayers.Filter.Logical.AND) {\n- for (var i = 0, len = filter.filters.length; i < len; i++) {\n- params = this.write(filter.filters[i], params)\n- }\n- } else {\n- OpenLayers.Console.warn(\"Unsupported logical filter type \" + filter.type)\n- }\n- break;\n- default:\n- OpenLayers.Console.warn(\"Unknown filter type \" + filterType)\n- }\n- return params\n- },\n- CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n- })\n-}();\n-OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {\n- namespaces: {\n- kml: \"http://www.opengis.net/kml/2.2\",\n- gx: \"http://www.google.com/kml/ext/2.2\"\n- },\n- kmlns: \"http://earth.google.com/kml/2.0\",\n- placemarksDesc: \"No description available\",\n- foldersName: \"OpenLayers export\",\n- foldersDesc: \"Exported on \" + new Date,\n- extractAttributes: true,\n- kvpAttributes: false,\n- extractStyles: false,\n- extractTracks: false,\n- trackAttributes: null,\n- internalns: null,\n- features: null,\n- styles: null,\n- styleBaseUrl: \"\",\n- fetched: null,\n- maxDepth: 0,\n+OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.0.0\",\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n+});\n+OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.1.0\",\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n+});\n+OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.1.1\",\n+ profile: null,\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n+});\n+OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.1.0\",\n+ CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n+});\n+OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {\n+ fontStyleKeys: [\"antialiasing\", \"blockout\", \"font\", \"fontcolor\", \"fontsize\", \"fontstyle\", \"glowing\", \"interval\", \"outline\", \"printmode\", \"shadow\", \"transparency\"],\n+ request: null,\n+ response: null,\n initialize: function(options) {\n- this.regExes = {\n- trimSpace: /^\\s*|\\s*$/g,\n- removeSpace: /\\s*/g,\n- splitSpace: /\\s+/,\n- trimComma: /\\s*,\\s*/g,\n- kmlColor: /(\\w{2})(\\w{2})(\\w{2})(\\w{2})/,\n- kmlIconPalette: /root:\\/\\/icons\\/palette-(\\d+)(\\.\\w+)/,\n- straightBracket: /\\$\\[(.*?)\\]/g\n- };\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+ this.request = new OpenLayers.Format.ArcXML.Request;\n+ this.response = new OpenLayers.Format.ArcXML.Response;\n+ if (options) {\n+ if (options.requesttype == \"feature\") {\n+ this.request.get_image = null;\n+ var qry = this.request.get_feature.query;\n+ this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);\n+ this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);\n+ if (options.polygon) {\n+ qry.isspatial = true;\n+ qry.spatialfilter.polygon = options.polygon\n+ } else if (options.envelope) {\n+ qry.isspatial = true;\n+ qry.spatialfilter.envelope = {\n+ minx: 0,\n+ miny: 0,\n+ maxx: 0,\n+ maxy: 0\n+ };\n+ this.parseEnvelope(qry.spatialfilter.envelope, options.envelope)\n+ }\n+ } else if (options.requesttype == \"image\") {\n+ this.request.get_feature = null;\n+ var props = this.request.get_image.properties;\n+ this.parseEnvelope(props.envelope, options.envelope);\n+ this.addLayers(props.layerlist, options.layers);\n+ this.addImageSize(props.imagesize, options.tileSize);\n+ this.addCoordSys(props.featurecoordsys, options.featureCoordSys);\n+ this.addCoordSys(props.filtercoordsys, options.filterCoordSys)\n+ } else {\n+ this.request = null\n+ }\n+ }\n OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n },\n- read: function(data) {\n- this.features = [];\n- this.styles = {};\n- this.fetched = {};\n- var options = {\n- depth: 0,\n- styleBaseUrl: this.styleBaseUrl\n- };\n- return this.parseData(data, options)\n+ parseEnvelope: function(env, arr) {\n+ if (arr && arr.length == 4) {\n+ env.minx = arr[0];\n+ env.miny = arr[1];\n+ env.maxx = arr[2];\n+ env.maxy = arr[3]\n+ }\n },\n- parseData: function(data, options) {\n+ addLayers: function(ll, lyrs) {\n+ for (var lind = 0, len = lyrs.length; lind < len; lind++) {\n+ ll.push(lyrs[lind])\n+ }\n+ },\n+ addImageSize: function(imsize, olsize) {\n+ if (olsize !== null) {\n+ imsize.width = olsize.w;\n+ imsize.height = olsize.h;\n+ imsize.printwidth = olsize.w;\n+ imsize.printheight = olsize.h\n+ }\n+ },\n+ addCoordSys: function(featOrFilt, fsys) {\n+ if (typeof fsys == \"string\") {\n+ featOrFilt.id = parseInt(fsys);\n+ featOrFilt.string = fsys\n+ } else if (typeof fsys == \"object\" && fsys.proj !== null) {\n+ featOrFilt.id = fsys.proj.srsProjNumber;\n+ featOrFilt.string = fsys.proj.srsCode\n+ } else {\n+ featOrFilt = fsys\n+ }\n+ },\n+ iserror: function(data) {\n+ var ret = null;\n+ if (!data) {\n+ ret = this.response.error !== \"\"\n+ } else {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ var errorNodes = data.documentElement.getElementsByTagName(\"ERROR\");\n+ ret = errorNodes !== null && errorNodes.length > 0\n+ }\n+ return ret\n+ },\n+ read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n }\n- var types = [\"Link\", \"NetworkLink\", \"Style\", \"StyleMap\", \"Placemark\"];\n- for (var i = 0, len = types.length; i < len; ++i) {\n- var type = types[i];\n- var nodes = this.getElementsByTagNameNS(data, \"*\", type);\n- if (nodes.length == 0) {\n- continue\n+ var arcNode = null;\n+ if (data && data.documentElement) {\n+ if (data.documentElement.nodeName == \"ARCXML\") {\n+ arcNode = data.documentElement\n+ } else {\n+ arcNode = data.documentElement.getElementsByTagName(\"ARCXML\")[0]\n }\n- switch (type.toLowerCase()) {\n- case \"link\":\n- case \"networklink\":\n- this.parseLinks(nodes, options);\n- break;\n- case \"style\":\n- if (this.extractStyles) {\n- this.parseStyles(nodes, options)\n- }\n- break;\n- case \"stylemap\":\n- if (this.extractStyles) {\n- this.parseStyleMaps(nodes, options)\n- }\n- break;\n- case \"placemark\":\n- this.parseFeatures(nodes, options);\n- break\n+ }\n+ if (!arcNode || arcNode.firstChild.nodeName === \"parsererror\") {\n+ var error, source;\n+ try {\n+ error = data.firstChild.nodeValue;\n+ source = data.firstChild.childNodes[1].firstChild.nodeValue\n+ } catch (err) {}\n+ throw {\n+ message: \"Error parsing the ArcXML request\",\n+ error: error,\n+ source: source\n }\n }\n- return this.features\n+ var response = this.parseResponse(arcNode);\n+ return response\n },\n- parseLinks: function(nodes, options) {\n- if (options.depth >= this.maxDepth) {\n- return false\n+ write: function(request) {\n+ if (!request) {\n+ request = this.request\n }\n- var newOptions = OpenLayers.Util.extend({}, options);\n- newOptions.depth++;\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var href = this.parseProperty(nodes[i], \"*\", \"href\");\n- if (href && !this.fetched[href]) {\n- this.fetched[href] = true;\n- var data = this.fetchLink(href);\n- if (data) {\n- this.parseData(data, newOptions)\n+ var root = this.createElementNS(\"\", \"ARCXML\");\n+ root.setAttribute(\"version\", \"1.1\");\n+ var reqElem = this.createElementNS(\"\", \"REQUEST\");\n+ if (request.get_image != null) {\n+ var getElem = this.createElementNS(\"\", \"GET_IMAGE\");\n+ reqElem.appendChild(getElem);\n+ var propElem = this.createElementNS(\"\", \"PROPERTIES\");\n+ getElem.appendChild(propElem);\n+ var props = request.get_image.properties;\n+ if (props.featurecoordsys != null) {\n+ var feat = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n+ propElem.appendChild(feat);\n+ if (props.featurecoordsys.id === 0) {\n+ feat.setAttribute(\"string\", props.featurecoordsys[\"string\"])\n+ } else {\n+ feat.setAttribute(\"id\", props.featurecoordsys.id)\n+ }\n+ }\n+ if (props.filtercoordsys != null) {\n+ var filt = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n+ propElem.appendChild(filt);\n+ if (props.filtercoordsys.id === 0) {\n+ filt.setAttribute(\"string\", props.filtercoordsys.string)\n+ } else {\n+ filt.setAttribute(\"id\", props.filtercoordsys.id)\n+ }\n+ }\n+ if (props.envelope != null) {\n+ var env = this.createElementNS(\"\", \"ENVELOPE\");\n+ propElem.appendChild(env);\n+ env.setAttribute(\"minx\", props.envelope.minx);\n+ env.setAttribute(\"miny\", props.envelope.miny);\n+ env.setAttribute(\"maxx\", props.envelope.maxx);\n+ env.setAttribute(\"maxy\", props.envelope.maxy)\n+ }\n+ var imagesz = this.createElementNS(\"\", \"IMAGESIZE\");\n+ propElem.appendChild(imagesz);\n+ imagesz.setAttribute(\"height\", props.imagesize.height);\n+ imagesz.setAttribute(\"width\", props.imagesize.width);\n+ if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) {\n+ imagesz.setAttribute(\"printheight\", props.imagesize.printheight);\n+ imagesz.setArrtibute(\"printwidth\", props.imagesize.printwidth)\n+ }\n+ if (props.background != null) {\n+ var backgrnd = this.createElementNS(\"\", \"BACKGROUND\");\n+ propElem.appendChild(backgrnd);\n+ backgrnd.setAttribute(\"color\", props.background.color.r + \",\" + props.background.color.g + \",\" + props.background.color.b);\n+ if (props.background.transcolor !== null) {\n+ backgrnd.setAttribute(\"transcolor\", props.background.transcolor.r + \",\" + props.background.transcolor.g + \",\" + props.background.transcolor.b)\n+ }\n+ }\n+ if (props.layerlist != null && props.layerlist.length > 0) {\n+ var layerlst = this.createElementNS(\"\", \"LAYERLIST\");\n+ propElem.appendChild(layerlst);\n+ for (var ld = 0; ld < props.layerlist.length; ld++) {\n+ var ldef = this.createElementNS(\"\", \"LAYERDEF\");\n+ layerlst.appendChild(ldef);\n+ ldef.setAttribute(\"id\", props.layerlist[ld].id);\n+ ldef.setAttribute(\"visible\", props.layerlist[ld].visible);\n+ if (typeof props.layerlist[ld].query == \"object\") {\n+ var query = props.layerlist[ld].query;\n+ if (query.where.length < 0) {\n+ continue\n+ }\n+ var queryElem = null;\n+ if (typeof query.spatialfilter == \"boolean\" && query.spatialfilter) {\n+ queryElem = this.createElementNS(\"\", \"SPATIALQUERY\")\n+ } else {\n+ queryElem = this.createElementNS(\"\", \"QUERY\")\n+ }\n+ queryElem.setAttribute(\"where\", query.where);\n+ if (typeof query.accuracy == \"number\" && query.accuracy > 0) {\n+ queryElem.setAttribute(\"accuracy\", query.accuracy)\n+ }\n+ if (typeof query.featurelimit == \"number\" && query.featurelimit < 2e3) {\n+ queryElem.setAttribute(\"featurelimit\", query.featurelimit)\n+ }\n+ if (typeof query.subfields == \"string\" && query.subfields != \"#ALL#\") {\n+ queryElem.setAttribute(\"subfields\", query.subfields)\n+ }\n+ if (typeof query.joinexpression == \"string\" && query.joinexpression.length > 0) {\n+ queryElem.setAttribute(\"joinexpression\", query.joinexpression)\n+ }\n+ if (typeof query.jointables == \"string\" && query.jointables.length > 0) {\n+ queryElem.setAttribute(\"jointables\", query.jointables)\n+ }\n+ ldef.appendChild(queryElem)\n+ }\n+ if (typeof props.layerlist[ld].renderer == \"object\") {\n+ this.addRenderer(ldef, props.layerlist[ld].renderer)\n+ }\n+ }\n+ }\n+ } else if (request.get_feature != null) {\n+ var getElem = this.createElementNS(\"\", \"GET_FEATURES\");\n+ getElem.setAttribute(\"outputmode\", \"newxml\");\n+ getElem.setAttribute(\"checkesc\", \"true\");\n+ if (request.get_feature.geometry) {\n+ getElem.setAttribute(\"geometry\", request.get_feature.geometry)\n+ } else {\n+ getElem.setAttribute(\"geometry\", \"false\")\n+ }\n+ if (request.get_feature.compact) {\n+ getElem.setAttribute(\"compact\", request.get_feature.compact)\n+ }\n+ if (request.get_feature.featurelimit == \"number\") {\n+ getElem.setAttribute(\"featurelimit\", request.get_feature.featurelimit)\n+ }\n+ getElem.setAttribute(\"globalenvelope\", \"true\");\n+ reqElem.appendChild(getElem);\n+ if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {\n+ var lyrElem = this.createElementNS(\"\", \"LAYER\");\n+ lyrElem.setAttribute(\"id\", request.get_feature.layer);\n+ getElem.appendChild(lyrElem)\n+ }\n+ var fquery = request.get_feature.query;\n+ if (fquery != null) {\n+ var qElem = null;\n+ if (fquery.isspatial) {\n+ qElem = this.createElementNS(\"\", \"SPATIALQUERY\")\n+ } else {\n+ qElem = this.createElementNS(\"\", \"QUERY\")\n+ }\n+ getElem.appendChild(qElem);\n+ if (typeof fquery.accuracy == \"number\") {\n+ qElem.setAttribute(\"accuracy\", fquery.accuracy)\n+ }\n+ if (fquery.featurecoordsys != null) {\n+ var fcsElem1 = this.createElementNS(\"\", \"FEATURECOORDSYS\");\n+ if (fquery.featurecoordsys.id == 0) {\n+ fcsElem1.setAttribute(\"string\", fquery.featurecoordsys.string)\n+ } else {\n+ fcsElem1.setAttribute(\"id\", fquery.featurecoordsys.id)\n+ }\n+ qElem.appendChild(fcsElem1)\n+ }\n+ if (fquery.filtercoordsys != null) {\n+ var fcsElem2 = this.createElementNS(\"\", \"FILTERCOORDSYS\");\n+ if (fquery.filtercoordsys.id === 0) {\n+ fcsElem2.setAttribute(\"string\", fquery.filtercoordsys.string)\n+ } else {\n+ fcsElem2.setAttribute(\"id\", fquery.filtercoordsys.id)\n+ }\n+ qElem.appendChild(fcsElem2)\n+ }\n+ if (fquery.buffer > 0) {\n+ var bufElem = this.createElementNS(\"\", \"BUFFER\");\n+ bufElem.setAttribute(\"distance\", fquery.buffer);\n+ qElem.appendChild(bufElem)\n+ }\n+ if (fquery.isspatial) {\n+ var spfElem = this.createElementNS(\"\", \"SPATIALFILTER\");\n+ spfElem.setAttribute(\"relation\", fquery.spatialfilter.relation);\n+ qElem.appendChild(spfElem);\n+ if (fquery.spatialfilter.envelope) {\n+ var envElem = this.createElementNS(\"\", \"ENVELOPE\");\n+ envElem.setAttribute(\"minx\", fquery.spatialfilter.envelope.minx);\n+ envElem.setAttribute(\"miny\", fquery.spatialfilter.envelope.miny);\n+ envElem.setAttribute(\"maxx\", fquery.spatialfilter.envelope.maxx);\n+ envElem.setAttribute(\"maxy\", fquery.spatialfilter.envelope.maxy);\n+ spfElem.appendChild(envElem)\n+ } else if (typeof fquery.spatialfilter.polygon == \"object\") {\n+ spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon))\n+ }\n+ }\n+ if (fquery.where != null && fquery.where.length > 0) {\n+ qElem.setAttribute(\"where\", fquery.where)\n }\n }\n }\n+ root.appendChild(reqElem);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root])\n },\n- fetchLink: function(href) {\n- var request = OpenLayers.Request.GET({\n- url: href,\n- async: false\n- });\n- if (request) {\n- return request.responseText\n+ addGroupRenderer: function(ldef, toprenderer) {\n+ var topRelem = this.createElementNS(\"\", \"GROUPRENDERER\");\n+ ldef.appendChild(topRelem);\n+ for (var rind = 0; rind < toprenderer.length; rind++) {\n+ var renderer = toprenderer[rind];\n+ this.addRenderer(topRelem, renderer)\n }\n },\n- parseStyles: function(nodes, options) {\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var style = this.parseStyle(nodes[i]);\n- if (style) {\n- var styleName = (options.styleBaseUrl || \"\") + \"#\" + style.id;\n- this.styles[styleName] = style\n+ addRenderer: function(topRelem, renderer) {\n+ if (OpenLayers.Util.isArray(renderer)) {\n+ this.addGroupRenderer(topRelem, renderer)\n+ } else {\n+ var renderElem = this.createElementNS(\"\", renderer.type.toUpperCase() + \"RENDERER\");\n+ topRelem.appendChild(renderElem);\n+ if (renderElem.tagName == \"VALUEMAPRENDERER\") {\n+ this.addValueMapRenderer(renderElem, renderer)\n+ } else if (renderElem.tagName == \"VALUEMAPLABELRENDERER\") {\n+ this.addValueMapLabelRenderer(renderElem, renderer)\n+ } else if (renderElem.tagName == \"SIMPLELABELRENDERER\") {\n+ this.addSimpleLabelRenderer(renderElem, renderer)\n+ } else if (renderElem.tagName == \"SCALEDEPENDENTRENDERER\") {\n+ this.addScaleDependentRenderer(renderElem, renderer)\n }\n }\n },\n- parseKmlColor: function(kmlColor) {\n- var color = null;\n- if (kmlColor) {\n- var matches = kmlColor.match(this.regExes.kmlColor);\n- if (matches) {\n- color = {\n- color: \"#\" + matches[4] + matches[3] + matches[2],\n- opacity: parseInt(matches[1], 16) / 255\n- }\n- }\n+ addScaleDependentRenderer: function(renderElem, renderer) {\n+ if (typeof renderer.lower == \"string\" || typeof renderer.lower == \"number\") {\n+ renderElem.setAttribute(\"lower\", renderer.lower)\n }\n- return color\n+ if (typeof renderer.upper == \"string\" || typeof renderer.upper == \"number\") {\n+ renderElem.setAttribute(\"upper\", renderer.upper)\n+ }\n+ this.addRenderer(renderElem, renderer.renderer)\n },\n- parseStyle: function(node) {\n- var style = {};\n- var types = [\"LineStyle\", \"PolyStyle\", \"IconStyle\", \"BalloonStyle\", \"LabelStyle\"];\n- var type, styleTypeNode, nodeList, geometry, parser;\n- for (var i = 0, len = types.length; i < len; ++i) {\n- type = types[i];\n- styleTypeNode = this.getElementsByTagNameNS(node, \"*\", type)[0];\n- if (!styleTypeNode) {\n- continue\n- }\n- switch (type.toLowerCase()) {\n- case \"linestyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"strokeColor\"] = color.color;\n- style[\"strokeOpacity\"] = color.opacity\n- }\n- var width = this.parseProperty(styleTypeNode, \"*\", \"width\");\n- if (width) {\n- style[\"strokeWidth\"] = width\n- }\n- break;\n- case \"polystyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"fillOpacity\"] = color.opacity;\n- style[\"fillColor\"] = color.color\n- }\n- var fill = this.parseProperty(styleTypeNode, \"*\", \"fill\");\n- if (fill == \"0\") {\n- style[\"fillColor\"] = \"none\"\n- }\n- var outline = this.parseProperty(styleTypeNode, \"*\", \"outline\");\n- if (outline == \"0\") {\n- style[\"strokeWidth\"] = \"0\"\n+ addValueMapLabelRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n+ renderElem.setAttribute(\"labelfield\", renderer.labelfield);\n+ if (typeof renderer.exacts == \"object\") {\n+ for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n+ var exact = renderer.exacts[ext];\n+ var eelem = this.createElementNS(\"\", \"EXACT\");\n+ if (typeof exact.value == \"string\") {\n+ eelem.setAttribute(\"value\", exact.value)\n+ }\n+ if (typeof exact.label == \"string\") {\n+ eelem.setAttribute(\"label\", exact.label)\n+ }\n+ if (typeof exact.method == \"string\") {\n+ eelem.setAttribute(\"method\", exact.method)\n+ }\n+ renderElem.appendChild(eelem);\n+ if (typeof exact.symbol == \"object\") {\n+ var selem = null;\n+ if (exact.symbol.type == \"text\") {\n+ selem = this.createElementNS(\"\", \"TEXTSYMBOL\")\n }\n- break;\n- case \"iconstyle\":\n- var scale = parseFloat(this.parseProperty(styleTypeNode, \"*\", \"scale\") || 1);\n- var width = 32 * scale;\n- var height = 32 * scale;\n- var iconNode = this.getElementsByTagNameNS(styleTypeNode, \"*\", \"Icon\")[0];\n- if (iconNode) {\n- var href = this.parseProperty(iconNode, \"*\", \"href\");\n- if (href) {\n- var w = this.parseProperty(iconNode, \"*\", \"w\");\n- var h = this.parseProperty(iconNode, \"*\", \"h\");\n- var google = \"http://maps.google.com/mapfiles/kml\";\n- if (OpenLayers.String.startsWith(href, google) && !w && !h) {\n- w = 64;\n- h = 64;\n- scale = scale / 2\n- }\n- w = w || h;\n- h = h || w;\n- if (w) {\n- width = parseInt(w) * scale\n- }\n- if (h) {\n- height = parseInt(h) * scale\n- }\n- var matches = href.match(this.regExes.kmlIconPalette);\n- if (matches) {\n- var palette = matches[1];\n- var file_extension = matches[2];\n- var x = this.parseProperty(iconNode, \"*\", \"x\");\n- var y = this.parseProperty(iconNode, \"*\", \"y\");\n- var posX = x ? x / 32 : 0;\n- var posY = y ? 7 - y / 32 : 7;\n- var pos = posY * 8 + posX;\n- href = \"http://maps.google.com/mapfiles/kml/pal\" + palette + \"/icon\" + pos + file_extension\n+ if (selem != null) {\n+ var keys = this.fontStyleKeys;\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (exact.symbol[key]) {\n+ selem.setAttribute(key, exact.symbol[key])\n }\n- style[\"graphicOpacity\"] = 1;\n- style[\"externalGraphic\"] = href\n }\n+ eelem.appendChild(selem)\n }\n- var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, \"*\", \"hotSpot\")[0];\n- if (hotSpotNode) {\n- var x = parseFloat(hotSpotNode.getAttribute(\"x\"));\n- var y = parseFloat(hotSpotNode.getAttribute(\"y\"));\n- var xUnits = hotSpotNode.getAttribute(\"xunits\");\n- if (xUnits == \"pixels\") {\n- style[\"graphicXOffset\"] = -x * scale\n- } else if (xUnits == \"insetPixels\") {\n- style[\"graphicXOffset\"] = -width + x * scale\n- } else if (xUnits == \"fraction\") {\n- style[\"graphicXOffset\"] = -width * x\n+ }\n+ }\n+ }\n+ },\n+ addValueMapRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"lookupfield\", renderer.lookupfield);\n+ if (typeof renderer.ranges == \"object\") {\n+ for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) {\n+ var range = renderer.ranges[rng];\n+ var relem = this.createElementNS(\"\", \"RANGE\");\n+ relem.setAttribute(\"lower\", range.lower);\n+ relem.setAttribute(\"upper\", range.upper);\n+ renderElem.appendChild(relem);\n+ if (typeof range.symbol == \"object\") {\n+ var selem = null;\n+ if (range.symbol.type == \"simplepolygon\") {\n+ selem = this.createElementNS(\"\", \"SIMPLEPOLYGONSYMBOL\")\n+ }\n+ if (selem != null) {\n+ if (typeof range.symbol.boundarycolor == \"string\") {\n+ selem.setAttribute(\"boundarycolor\", range.symbol.boundarycolor)\n }\n- var yUnits = hotSpotNode.getAttribute(\"yunits\");\n- if (yUnits == \"pixels\") {\n- style[\"graphicYOffset\"] = -height + y * scale + 1\n- } else if (yUnits == \"insetPixels\") {\n- style[\"graphicYOffset\"] = -(y * scale) + 1\n- } else if (yUnits == \"fraction\") {\n- style[\"graphicYOffset\"] = -height * (1 - y) + 1\n+ if (typeof range.symbol.fillcolor == \"string\") {\n+ selem.setAttribute(\"fillcolor\", range.symbol.fillcolor)\n+ }\n+ if (typeof range.symbol.filltransparency == \"number\") {\n+ selem.setAttribute(\"filltransparency\", range.symbol.filltransparency)\n }\n+ relem.appendChild(selem)\n }\n- style[\"graphicWidth\"] = width;\n- style[\"graphicHeight\"] = height;\n- break;\n- case \"balloonstyle\":\n- var balloonStyle = OpenLayers.Util.getXmlNodeValue(styleTypeNode);\n- if (balloonStyle) {\n- style[\"balloonStyle\"] = balloonStyle.replace(this.regExes.straightBracket, \"${$1}\")\n+ }\n+ }\n+ } else if (typeof renderer.exacts == \"object\") {\n+ for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {\n+ var exact = renderer.exacts[ext];\n+ var eelem = this.createElementNS(\"\", \"EXACT\");\n+ if (typeof exact.value == \"string\") {\n+ eelem.setAttribute(\"value\", exact.value)\n+ }\n+ if (typeof exact.label == \"string\") {\n+ eelem.setAttribute(\"label\", exact.label)\n+ }\n+ if (typeof exact.method == \"string\") {\n+ eelem.setAttribute(\"method\", exact.method)\n+ }\n+ renderElem.appendChild(eelem);\n+ if (typeof exact.symbol == \"object\") {\n+ var selem = null;\n+ if (exact.symbol.type == \"simplemarker\") {\n+ selem = this.createElementNS(\"\", \"SIMPLEMARKERSYMBOL\")\n }\n- break;\n- case \"labelstyle\":\n- var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n- var color = this.parseKmlColor(kmlColor);\n- if (color) {\n- style[\"fontColor\"] = color.color;\n- style[\"fontOpacity\"] = color.opacity\n+ if (selem != null) {\n+ if (typeof exact.symbol.antialiasing == \"string\") {\n+ selem.setAttribute(\"antialiasing\", exact.symbol.antialiasing)\n+ }\n+ if (typeof exact.symbol.color == \"string\") {\n+ selem.setAttribute(\"color\", exact.symbol.color)\n+ }\n+ if (typeof exact.symbol.outline == \"string\") {\n+ selem.setAttribute(\"outline\", exact.symbol.outline)\n+ }\n+ if (typeof exact.symbol.overlap == \"string\") {\n+ selem.setAttribute(\"overlap\", exact.symbol.overlap)\n+ }\n+ if (typeof exact.symbol.shadow == \"string\") {\n+ selem.setAttribute(\"shadow\", exact.symbol.shadow)\n+ }\n+ if (typeof exact.symbol.transparency == \"number\") {\n+ selem.setAttribute(\"transparency\", exact.symbol.transparency)\n+ }\n+ if (typeof exact.symbol.usecentroid == \"string\") {\n+ selem.setAttribute(\"usecentroid\", exact.symbol.usecentroid)\n+ }\n+ if (typeof exact.symbol.width == \"number\") {\n+ selem.setAttribute(\"width\", exact.symbol.width)\n+ }\n+ eelem.appendChild(selem)\n }\n- break;\n- default:\n+ }\n }\n }\n- if (!style[\"strokeColor\"] && style[\"fillColor\"]) {\n- style[\"strokeColor\"] = style[\"fillColor\"]\n+ },\n+ addSimpleLabelRenderer: function(renderElem, renderer) {\n+ renderElem.setAttribute(\"field\", renderer.field);\n+ var keys = [\"featureweight\", \"howmanylabels\", \"labelbufferratio\", \"labelpriorities\", \"labelweight\", \"linelabelposition\", \"rotationalangles\"];\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (renderer[key]) {\n+ renderElem.setAttribute(key, renderer[key])\n+ }\n }\n- var id = node.getAttribute(\"id\");\n- if (id && style) {\n- style.id = id\n+ if (renderer.symbol.type == \"text\") {\n+ var symbol = renderer.symbol;\n+ var selem = this.createElementNS(\"\", \"TEXTSYMBOL\");\n+ renderElem.appendChild(selem);\n+ var keys = this.fontStyleKeys;\n+ for (var i = 0, len = keys.length; i < len; i++) {\n+ var key = keys[i];\n+ if (symbol[key]) {\n+ selem.setAttribute(key, renderer[key])\n+ }\n+ }\n }\n- return style\n },\n- parseStyleMaps: function(nodes, options) {\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var node = nodes[i];\n- var pairs = this.getElementsByTagNameNS(node, \"*\", \"Pair\");\n- var id = node.getAttribute(\"id\");\n- for (var j = 0, jlen = pairs.length; j < jlen; j++) {\n- var pair = pairs[j];\n- var key = this.parseProperty(pair, \"*\", \"key\");\n- var styleUrl = this.parseProperty(pair, \"*\", \"styleUrl\");\n- if (styleUrl && key == \"normal\") {\n- this.styles[(options.styleBaseUrl || \"\") + \"#\" + id] = this.styles[(options.styleBaseUrl || \"\") + styleUrl]\n- }\n+ writePolygonGeometry: function(polygon) {\n+ if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {\n+ throw {\n+ message: \"Cannot write polygon geometry to ArcXML with an \" + polygon.CLASS_NAME + \" object.\",\n+ geometry: polygon\n+ }\n+ }\n+ var polyElem = this.createElementNS(\"\", \"POLYGON\");\n+ for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) {\n+ var ring = polygon.components[ln];\n+ var ringElem = this.createElementNS(\"\", \"RING\");\n+ for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) {\n+ var point = ring.components[rn];\n+ var pointElem = this.createElementNS(\"\", \"POINT\");\n+ pointElem.setAttribute(\"x\", point.x);\n+ pointElem.setAttribute(\"y\", point.y);\n+ ringElem.appendChild(pointElem)\n }\n+ polyElem.appendChild(ringElem)\n }\n+ return polyElem\n },\n- parseFeatures: function(nodes, options) {\n- var features = [];\n- for (var i = 0, len = nodes.length; i < len; i++) {\n- var featureNode = nodes[i];\n- var feature = this.parseFeature.apply(this, [featureNode]);\n- if (feature) {\n- if (this.extractStyles && feature.attributes && feature.attributes.styleUrl) {\n- feature.style = this.getStyle(feature.attributes.styleUrl, options)\n- }\n- if (this.extractStyles) {\n- var inlineStyleNode = this.getElementsByTagNameNS(featureNode, \"*\", \"Style\")[0];\n- if (inlineStyleNode) {\n- var inlineStyle = this.parseStyle(inlineStyleNode);\n- if (inlineStyle) {\n- feature.style = OpenLayers.Util.extend(feature.style, inlineStyle)\n+ parseResponse: function(data) {\n+ if (typeof data == \"string\") {\n+ var newData = new OpenLayers.Format.XML;\n+ data = newData.read(data)\n+ }\n+ var response = new OpenLayers.Format.ArcXML.Response;\n+ var errorNode = data.getElementsByTagName(\"ERROR\");\n+ if (errorNode != null && errorNode.length > 0) {\n+ response.error = this.getChildValue(errorNode, \"Unknown error.\")\n+ } else {\n+ var responseNode = data.getElementsByTagName(\"RESPONSE\");\n+ if (responseNode == null || responseNode.length == 0) {\n+ response.error = \"No RESPONSE tag found in ArcXML response.\";\n+ return response\n+ }\n+ var rtype = responseNode[0].firstChild.nodeName;\n+ if (rtype == \"#text\") {\n+ rtype = responseNode[0].firstChild.nextSibling.nodeName\n+ }\n+ if (rtype == \"IMAGE\") {\n+ var envelopeNode = data.getElementsByTagName(\"ENVELOPE\");\n+ var outputNode = data.getElementsByTagName(\"OUTPUT\");\n+ if (envelopeNode == null || envelopeNode.length == 0) {\n+ response.error = \"No ENVELOPE tag found in ArcXML response.\"\n+ } else if (outputNode == null || outputNode.length == 0) {\n+ response.error = \"No OUTPUT tag found in ArcXML response.\"\n+ } else {\n+ var envAttr = this.parseAttributes(envelopeNode[0]);\n+ var outputAttr = this.parseAttributes(outputNode[0]);\n+ if (typeof outputAttr.type == \"string\") {\n+ response.image = {\n+ envelope: envAttr,\n+ output: {\n+ type: outputAttr.type,\n+ data: this.getChildValue(outputNode[0])\n+ }\n+ }\n+ } else {\n+ response.image = {\n+ envelope: envAttr,\n+ output: outputAttr\n }\n }\n }\n- if (this.extractTracks) {\n- var tracks = this.getElementsByTagNameNS(featureNode, this.namespaces.gx, \"Track\");\n- if (tracks && tracks.length > 0) {\n- var track = tracks[0];\n- var container = {\n- features: [],\n- feature: feature\n- };\n- this.readNode(track, container);\n- if (container.features.length > 0) {\n- features.push.apply(features, container.features)\n+ } else if (rtype == \"FEATURES\") {\n+ var features = responseNode[0].getElementsByTagName(\"FEATURES\");\n+ var featureCount = features[0].getElementsByTagName(\"FEATURECOUNT\");\n+ response.features.featurecount = featureCount[0].getAttribute(\"count\");\n+ if (response.features.featurecount > 0) {\n+ var envelope = features[0].getElementsByTagName(\"ENVELOPE\");\n+ response.features.envelope = this.parseAttributes(envelope[0], typeof 0);\n+ var featureList = features[0].getElementsByTagName(\"FEATURE\");\n+ for (var fn = 0; fn < featureList.length; fn++) {\n+ var feature = new OpenLayers.Feature.Vector;\n+ var fields = featureList[fn].getElementsByTagName(\"FIELD\");\n+ for (var fdn = 0; fdn < fields.length; fdn++) {\n+ var fieldName = fields[fdn].getAttribute(\"name\");\n+ var fieldValue = fields[fdn].getAttribute(\"value\");\n+ feature.attributes[fieldName] = fieldValue\n+ }\n+ var geom = featureList[fn].getElementsByTagName(\"POLYGON\");\n+ if (geom.length > 0) {\n+ var ring = geom[0].getElementsByTagName(\"RING\");\n+ var polys = [];\n+ for (var rn = 0; rn < ring.length; rn++) {\n+ var linearRings = [];\n+ linearRings.push(this.parsePointGeometry(ring[rn]));\n+ var holes = ring[rn].getElementsByTagName(\"HOLE\");\n+ for (var hn = 0; hn < holes.length; hn++) {\n+ linearRings.push(this.parsePointGeometry(holes[hn]))\n+ }\n+ holes = null;\n+ polys.push(new OpenLayers.Geometry.Polygon(linearRings));\n+ linearRings = null\n+ }\n+ ring = null;\n+ if (polys.length == 1) {\n+ feature.geometry = polys[0]\n+ } else {\n+ feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys)\n+ }\n }\n+ response.features.feature.push(feature)\n }\n- } else {\n- features.push(feature)\n }\n } else {\n- throw \"Bad Placemark: \" + i\n+ response.error = \"Unidentified response type.\"\n }\n }\n- this.features = this.features.concat(features)\n+ return response\n },\n- readers: {\n- kml: {\n- when: function(node, container) {\n- container.whens.push(OpenLayers.Date.parse(this.getChildValue(node)))\n- },\n- _trackPointAttribute: function(node, container) {\n- var name = node.nodeName.split(\":\").pop();\n- container.attributes[name].push(this.getChildValue(node))\n+ parseAttributes: function(node, type) {\n+ var attributes = {};\n+ for (var attr = 0; attr < node.attributes.length; attr++) {\n+ if (type == \"number\") {\n+ attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue)\n+ } else {\n+ attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue\n }\n- },\n- gx: {\n- Track: function(node, container) {\n- var obj = {\n- whens: [],\n- points: [],\n- angles: []\n- };\n- if (this.trackAttributes) {\n- var name;\n- obj.attributes = {};\n- for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) {\n- name = this.trackAttributes[i];\n- obj.attributes[name] = [];\n- if (!(name in this.readers.kml)) {\n- this.readers.kml[name] = this.readers.kml._trackPointAttribute\n- }\n- }\n- }\n- this.readChildNodes(node, obj);\n- if (obj.whens.length !== obj.points.length) {\n- throw new Error(\"gx:Track with unequal number of when (\" + obj.whens.length + \") and gx:coord (\" + obj.points.length + \") elements.\")\n- }\n- var hasAngles = obj.angles.length > 0;\n- if (hasAngles && obj.whens.length !== obj.angles.length) {\n- throw new Error(\"gx:Track with unequal number of when (\" + obj.whens.length + \") and gx:angles (\" + obj.angles.length + \") elements.\")\n+ }\n+ return attributes\n+ },\n+ parsePointGeometry: function(node) {\n+ var ringPoints = [];\n+ var coords = node.getElementsByTagName(\"COORDS\");\n+ if (coords.length > 0) {\n+ var coordArr = this.getChildValue(coords[0]);\n+ coordArr = coordArr.split(/;/);\n+ for (var cn = 0; cn < coordArr.length; cn++) {\n+ var coordItems = coordArr[cn].split(/ /);\n+ ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1]))\n+ }\n+ coords = null\n+ } else {\n+ var point = node.getElementsByTagName(\"POINT\");\n+ if (point.length > 0) {\n+ for (var pn = 0; pn < point.length; pn++) {\n+ ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(point[pn].getAttribute(\"x\")), parseFloat(point[pn].getAttribute(\"y\"))))\n }\n- var feature, point, angles;\n- for (var i = 0, ii = obj.whens.length; i < ii; ++i) {\n- feature = container.feature.clone();\n- feature.fid = container.feature.fid || container.feature.id;\n- point = obj.points[i];\n- feature.geometry = point;\n- if (\"z\" in point) {\n- feature.attributes.altitude = point.z\n- }\n- if (this.internalProjection && this.externalProjection) {\n- feature.geometry.transform(this.externalProjection, this.internalProjection)\n- }\n- if (this.trackAttributes) {\n- for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) {\n- var name = this.trackAttributes[j];\n- feature.attributes[name] = obj.attributes[name][i]\n- }\n+ }\n+ point = null\n+ }\n+ return new OpenLayers.Geometry.LinearRing(ringPoints)\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML\"\n+});\n+OpenLayers.Format.ArcXML.Request = OpenLayers.Class({\n+ initialize: function(params) {\n+ var defaults = {\n+ get_image: {\n+ properties: {\n+ background: null,\n+ draw: true,\n+ envelope: {\n+ minx: 0,\n+ miny: 0,\n+ maxx: 0,\n+ maxy: 0\n+ },\n+ featurecoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ filtercoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ imagesize: {\n+ height: 0,\n+ width: 0,\n+ dpi: 96,\n+ printheight: 0,\n+ printwidth: 0,\n+ scalesymbols: false\n+ },\n+ layerlist: [],\n+ output: {\n+ baseurl: \"\",\n+ legendbaseurl: \"\",\n+ legendname: \"\",\n+ legendpath: \"\",\n+ legendurl: \"\",\n+ name: \"\",\n+ path: \"\",\n+ type: \"jpg\",\n+ url: \"\"\n }\n- feature.attributes.when = obj.whens[i];\n- feature.attributes.trackId = container.feature.id;\n- if (hasAngles) {\n- angles = obj.angles[i];\n- feature.attributes.heading = parseFloat(angles[0]);\n- feature.attributes.tilt = parseFloat(angles[1]);\n- feature.attributes.roll = parseFloat(angles[2])\n+ }\n+ },\n+ get_feature: {\n+ layer: \"\",\n+ query: {\n+ isspatial: false,\n+ featurecoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ filtercoordsys: {\n+ id: 0,\n+ string: \"\",\n+ datumtransformid: 0,\n+ datumtransformstring: \"\"\n+ },\n+ buffer: 0,\n+ where: \"\",\n+ spatialfilter: {\n+ relation: \"envelope_intersection\",\n+ envelope: null\n }\n- container.features.push(feature)\n }\n },\n- coord: function(node, container) {\n- var str = this.getChildValue(node);\n- var coords = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n- var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);\n- if (coords.length > 2) {\n- point.z = parseFloat(coords[2])\n+ environment: {\n+ separators: {\n+ cs: \" \",\n+ ts: \";\"\n }\n- container.points.push(point)\n },\n- angles: function(node, container) {\n- var str = this.getChildValue(node);\n- var parts = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n- container.angles.push(parts)\n+ layer: [],\n+ workspaces: []\n+ };\n+ return OpenLayers.Util.extend(this, defaults)\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML.Request\"\n+});\n+OpenLayers.Format.ArcXML.Response = OpenLayers.Class({\n+ initialize: function(params) {\n+ var defaults = {\n+ image: {\n+ envelope: null,\n+ output: \"\"\n+ },\n+ features: {\n+ featurecount: 0,\n+ envelope: null,\n+ feature: []\n+ },\n+ error: \"\"\n+ };\n+ return OpenLayers.Util.extend(this, defaults)\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.ArcXML.Response\"\n+});\n+OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {\n+ namespaces: {\n+ atom: \"http://www.w3.org/2005/Atom\",\n+ georss: \"http://www.georss.org/georss\"\n+ },\n+ feedTitle: \"untitled\",\n+ defaultEntryTitle: \"untitled\",\n+ gmlParser: null,\n+ xy: false,\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc])\n+ }\n+ return this.parseFeatures(doc)\n+ },\n+ write: function(features) {\n+ var doc;\n+ if (OpenLayers.Util.isArray(features)) {\n+ doc = this.createElementNSPlus(\"atom:feed\");\n+ doc.appendChild(this.createElementNSPlus(\"atom:title\", {\n+ value: this.feedTitle\n+ }));\n+ for (var i = 0, ii = features.length; i < ii; i++) {\n+ doc.appendChild(this.buildEntryNode(features[i]))\n }\n+ } else {\n+ doc = this.buildEntryNode(features)\n }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [doc])\n },\n- parseFeature: function(node) {\n- var order = [\"MultiGeometry\", \"Polygon\", \"LineString\", \"Point\"];\n- var type, nodeList, geometry, parser;\n- for (var i = 0, len = order.length; i < len; ++i) {\n- type = order[i];\n- this.internalns = node.namespaceURI ? node.namespaceURI : this.kmlns;\n- nodeList = this.getElementsByTagNameNS(node, this.internalns, type);\n- if (nodeList.length > 0) {\n- var parser = this.parseGeometry[type.toLowerCase()];\n- if (parser) {\n- geometry = parser.apply(this, [nodeList[0]]);\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection, this.internalProjection)\n+ buildContentNode: function(content) {\n+ var node = this.createElementNSPlus(\"atom:content\", {\n+ attributes: {\n+ type: content.type || null\n+ }\n+ });\n+ if (content.src) {\n+ node.setAttribute(\"src\", content.src)\n+ } else {\n+ if (content.type == \"text\" || content.type == null) {\n+ node.appendChild(this.createTextNode(content.value))\n+ } else if (content.type == \"html\") {\n+ if (typeof content.value != \"string\") {\n+ throw \"HTML content must be in form of an escaped string\"\n+ }\n+ node.appendChild(this.createTextNode(content.value))\n+ } else if (content.type == \"xhtml\") {\n+ node.appendChild(content.value)\n+ } else if (content.type == \"xhtml\" || content.type.match(/(\\+|\\/)xml$/)) {\n+ node.appendChild(content.value)\n+ } else {\n+ node.appendChild(this.createTextNode(content.value))\n+ }\n+ }\n+ return node\n+ },\n+ buildEntryNode: function(feature) {\n+ var attrib = feature.attributes;\n+ var atomAttrib = attrib.atom || {};\n+ var entryNode = this.createElementNSPlus(\"atom:entry\");\n+ if (atomAttrib.authors) {\n+ var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? atomAttrib.authors : [atomAttrib.authors];\n+ for (var i = 0, ii = authors.length; i < ii; i++) {\n+ entryNode.appendChild(this.buildPersonConstructNode(\"author\", authors[i]))\n+ }\n+ }\n+ if (atomAttrib.categories) {\n+ var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? atomAttrib.categories : [atomAttrib.categories];\n+ var category;\n+ for (var i = 0, ii = categories.length; i < ii; i++) {\n+ category = categories[i];\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:category\", {\n+ attributes: {\n+ term: category.term,\n+ scheme: category.scheme || null,\n+ label: category.label || null\n+ }\n+ }))\n+ }\n+ }\n+ if (atomAttrib.content) {\n+ entryNode.appendChild(this.buildContentNode(atomAttrib.content))\n+ }\n+ if (atomAttrib.contributors) {\n+ var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? atomAttrib.contributors : [atomAttrib.contributors];\n+ for (var i = 0, ii = contributors.length; i < ii; i++) {\n+ entryNode.appendChild(this.buildPersonConstructNode(\"contributor\", contributors[i]))\n+ }\n+ }\n+ if (feature.fid) {\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:id\", {\n+ value: feature.fid\n+ }))\n+ }\n+ if (atomAttrib.links) {\n+ var links = OpenLayers.Util.isArray(atomAttrib.links) ? atomAttrib.links : [atomAttrib.links];\n+ var link;\n+ for (var i = 0, ii = links.length; i < ii; i++) {\n+ link = links[i];\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:link\", {\n+ attributes: {\n+ href: link.href,\n+ rel: link.rel || null,\n+ type: link.type || null,\n+ hreflang: link.hreflang || null,\n+ title: link.title || null,\n+ length: link.length || null\n }\n+ }))\n+ }\n+ }\n+ if (atomAttrib.published) {\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:published\", {\n+ value: atomAttrib.published\n+ }))\n+ }\n+ if (atomAttrib.rights) {\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:rights\", {\n+ value: atomAttrib.rights\n+ }))\n+ }\n+ if (atomAttrib.summary || attrib.description) {\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:summary\", {\n+ value: atomAttrib.summary || attrib.description\n+ }))\n+ }\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:title\", {\n+ value: atomAttrib.title || attrib.title || this.defaultEntryTitle\n+ }));\n+ if (atomAttrib.updated) {\n+ entryNode.appendChild(this.createElementNSPlus(\"atom:updated\", {\n+ value: atomAttrib.updated\n+ }))\n+ }\n+ if (feature.geometry) {\n+ var whereNode = this.createElementNSPlus(\"georss:where\");\n+ whereNode.appendChild(this.buildGeometryNode(feature.geometry));\n+ entryNode.appendChild(whereNode)\n+ }\n+ return entryNode\n+ },\n+ initGmlParser: function() {\n+ this.gmlParser = new OpenLayers.Format.GML.v3({\n+ xy: this.xy,\n+ featureNS: \"http://example.com#feature\",\n+ internalProjection: this.internalProjection,\n+ externalProjection: this.externalProjection\n+ })\n+ },\n+ buildGeometryNode: function(geometry) {\n+ if (!this.gmlParser) {\n+ this.initGmlParser()\n+ }\n+ var node = this.gmlParser.writeNode(\"feature:_geometry\", geometry);\n+ return node.firstChild\n+ },\n+ buildPersonConstructNode: function(name, value) {\n+ var oNames = [\"uri\", \"email\"];\n+ var personNode = this.createElementNSPlus(\"atom:\" + name);\n+ personNode.appendChild(this.createElementNSPlus(\"atom:name\", {\n+ value: value.name\n+ }));\n+ for (var i = 0, ii = oNames.length; i < ii; i++) {\n+ if (value[oNames[i]]) {\n+ personNode.appendChild(this.createElementNSPlus(\"atom:\" + oNames[i], {\n+ value: value[oNames[i]]\n+ }))\n+ }\n+ }\n+ return personNode\n+ },\n+ getFirstChildValue: function(node, nsuri, name, def) {\n+ var value;\n+ var nodes = this.getElementsByTagNameNS(node, nsuri, name);\n+ if (nodes && nodes.length > 0) {\n+ value = this.getChildValue(nodes[0], def)\n+ } else {\n+ value = def\n+ }\n+ return value\n+ },\n+ parseFeature: function(node) {\n+ var atomAttrib = {};\n+ var value = null;\n+ var nodes = null;\n+ var attval = null;\n+ var atomns = this.namespaces.atom;\n+ this.parsePersonConstructs(node, \"author\", atomAttrib);\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"category\");\n+ if (nodes.length > 0) {\n+ atomAttrib.categories = []\n+ }\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ value = {};\n+ value.term = nodes[i].getAttribute(\"term\");\n+ attval = nodes[i].getAttribute(\"scheme\");\n+ if (attval) {\n+ value.scheme = attval\n+ }\n+ attval = nodes[i].getAttribute(\"label\");\n+ if (attval) {\n+ value.label = attval\n+ }\n+ atomAttrib.categories.push(value)\n+ }\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"content\");\n+ if (nodes.length > 0) {\n+ value = {};\n+ attval = nodes[0].getAttribute(\"type\");\n+ if (attval) {\n+ value.type = attval\n+ }\n+ attval = nodes[0].getAttribute(\"src\");\n+ if (attval) {\n+ value.src = attval\n+ } else {\n+ if (value.type == \"text\" || value.type == \"html\" || value.type == null) {\n+ value.value = this.getFirstChildValue(node, atomns, \"content\", null)\n+ } else if (value.type == \"xhtml\" || value.type.match(/(\\+|\\/)xml$/)) {\n+ value.value = this.getChildEl(nodes[0])\n } else {\n- throw new TypeError(\"Unsupported geometry type: \" + type)\n+ value.value = this.getFirstChildValue(node, atomns, \"content\", null)\n }\n- break\n+ atomAttrib.content = value\n+ }\n+ }\n+ this.parsePersonConstructs(node, \"contributor\", atomAttrib);\n+ atomAttrib.id = this.getFirstChildValue(node, atomns, \"id\", null);\n+ nodes = this.getElementsByTagNameNS(node, atomns, \"link\");\n+ if (nodes.length > 0) {\n+ atomAttrib.links = new Array(nodes.length)\n+ }\n+ var oAtts = [\"rel\", \"type\", \"hreflang\", \"title\", \"length\"];\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ value = {};\n+ value.href = nodes[i].getAttribute(\"href\");\n+ for (var j = 0, jj = oAtts.length; j < jj; j++) {\n+ attval = nodes[i].getAttribute(oAtts[j]);\n+ if (attval) {\n+ value[oAtts[j]] = attval\n+ }\n+ }\n+ atomAttrib.links[i] = value\n+ }\n+ value = this.getFirstChildValue(node, atomns, \"published\", null);\n+ if (value) {\n+ atomAttrib.published = value\n+ }\n+ value = this.getFirstChildValue(node, atomns, \"rights\", null);\n+ if (value) {\n+ atomAttrib.rights = value\n+ }\n+ value = this.getFirstChildValue(node, atomns, \"summary\", null);\n+ if (value) {\n+ atomAttrib.summary = value\n+ }\n+ atomAttrib.title = this.getFirstChildValue(node, atomns, \"title\", null);\n+ atomAttrib.updated = this.getFirstChildValue(node, atomns, \"updated\", null);\n+ var featureAttrib = {\n+ title: atomAttrib.title,\n+ description: atomAttrib.summary,\n+ atom: atomAttrib\n+ };\n+ var geometry = this.parseLocations(node)[0];\n+ var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);\n+ feature.fid = atomAttrib.id;\n+ return feature\n+ },\n+ parseFeatures: function(node) {\n+ var features = [];\n+ var entries = this.getElementsByTagNameNS(node, this.namespaces.atom, \"entry\");\n+ if (entries.length == 0) {\n+ entries = [node]\n+ }\n+ for (var i = 0, ii = entries.length; i < ii; i++) {\n+ features.push(this.parseFeature(entries[i]))\n+ }\n+ return features\n+ },\n+ parseLocations: function(node) {\n+ var georssns = this.namespaces.georss;\n+ var locations = {\n+ components: []\n+ };\n+ var where = this.getElementsByTagNameNS(node, georssns, \"where\");\n+ if (where && where.length > 0) {\n+ if (!this.gmlParser) {\n+ this.initGmlParser()\n+ }\n+ for (var i = 0, ii = where.length; i < ii; i++) {\n+ this.gmlParser.readChildNodes(where[i], locations)\n+ }\n+ }\n+ var components = locations.components;\n+ var point = this.getElementsByTagNameNS(node, georssns, \"point\");\n+ if (point && point.length > 0) {\n+ for (var i = 0, ii = point.length; i < ii; i++) {\n+ var xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\\s+/);\n+ if (xy.length != 2) {\n+ xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\\s*,\\s*/)\n+ }\n+ components.push(new OpenLayers.Geometry.Point(xy[1], xy[0]))\n+ }\n+ }\n+ var line = this.getElementsByTagNameNS(node, georssns, \"line\");\n+ if (line && line.length > 0) {\n+ var coords;\n+ var p;\n+ var points;\n+ for (var i = 0, ii = line.length; i < ii; i++) {\n+ coords = OpenLayers.String.trim(line[i].firstChild.nodeValue).split(/\\s+/);\n+ points = [];\n+ for (var j = 0, jj = coords.length; j < jj; j += 2) {\n+ p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n+ points.push(p)\n+ }\n+ components.push(new OpenLayers.Geometry.LineString(points))\n+ }\n+ }\n+ var polygon = this.getElementsByTagNameNS(node, georssns, \"polygon\");\n+ if (polygon && polygon.length > 0) {\n+ var coords;\n+ var p;\n+ var points;\n+ for (var i = 0, ii = polygon.length; i < ii; i++) {\n+ coords = OpenLayers.String.trim(polygon[i].firstChild.nodeValue).split(/\\s+/);\n+ points = [];\n+ for (var j = 0, jj = coords.length; j < jj; j += 2) {\n+ p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]);\n+ points.push(p)\n+ }\n+ components.push(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(points)]))\n+ }\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ for (var i = 0, ii = components.length; i < ii; i++) {\n+ if (components[i]) {\n+ components[i].transform(this.externalProjection, this.internalProjection)\n+ }\n+ }\n+ }\n+ return components\n+ },\n+ parsePersonConstructs: function(node, name, data) {\n+ var persons = [];\n+ var atomns = this.namespaces.atom;\n+ var nodes = this.getElementsByTagNameNS(node, atomns, name);\n+ var oAtts = [\"uri\", \"email\"];\n+ for (var i = 0, ii = nodes.length; i < ii; i++) {\n+ var value = {};\n+ value.name = this.getFirstChildValue(nodes[i], atomns, \"name\", null);\n+ for (var j = 0, jj = oAtts.length; j < jj; j++) {\n+ var attval = this.getFirstChildValue(nodes[i], atomns, oAtts[j], null);\n+ if (attval) {\n+ value[oAtts[j]] = attval\n+ }\n+ }\n+ persons.push(value)\n+ }\n+ if (persons.length > 0) {\n+ data[name + \"s\"] = persons\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.Atom\"\n+});\n+OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, {\n+ defaultVersion: \"1.1.0\",\n+ layerToContext: function(layer) {\n+ var parser = this.getParser();\n+ var layerContext = {\n+ queryable: layer.queryable,\n+ visibility: layer.visibility,\n+ name: layer.params[\"LAYERS\"],\n+ title: layer.name,\n+ abstract: layer.metadata[\"abstract\"],\n+ dataURL: layer.metadata.dataURL,\n+ metadataURL: layer.metadataURL,\n+ server: {\n+ version: layer.params[\"VERSION\"],\n+ url: layer.url\n+ },\n+ maxExtent: layer.maxExtent,\n+ transparent: layer.params[\"TRANSPARENT\"],\n+ numZoomLevels: layer.numZoomLevels,\n+ units: layer.units,\n+ isBaseLayer: layer.isBaseLayer,\n+ opacity: layer.opacity == 1 ? undefined : layer.opacity,\n+ displayInLayerSwitcher: layer.displayInLayerSwitcher,\n+ singleTile: layer.singleTile,\n+ tileSize: layer.singleTile || !layer.tileSize ? undefined : {\n+ width: layer.tileSize.w,\n+ height: layer.tileSize.h\n+ },\n+ minScale: layer.options.resolutions || layer.options.scales || layer.options.maxResolution || layer.options.minScale ? layer.minScale : undefined,\n+ maxScale: layer.options.resolutions || layer.options.scales || layer.options.minResolution || layer.options.maxScale ? layer.maxScale : undefined,\n+ formats: [],\n+ styles: [],\n+ srs: layer.srs,\n+ dimensions: layer.dimensions\n+ };\n+ if (layer.metadata.servertitle) {\n+ layerContext.server.title = layer.metadata.servertitle\n+ }\n+ if (layer.metadata.formats && layer.metadata.formats.length > 0) {\n+ for (var i = 0, len = layer.metadata.formats.length; i < len; i++) {\n+ var format = layer.metadata.formats[i];\n+ layerContext.formats.push({\n+ value: format.value,\n+ current: format.value == layer.params[\"FORMAT\"]\n+ })\n+ }\n+ } else {\n+ layerContext.formats.push({\n+ value: layer.params[\"FORMAT\"],\n+ current: true\n+ })\n+ }\n+ if (layer.metadata.styles && layer.metadata.styles.length > 0) {\n+ for (var i = 0, len = layer.metadata.styles.length; i < len; i++) {\n+ var style = layer.metadata.styles[i];\n+ if (style.href == layer.params[\"SLD\"] || style.body == layer.params[\"SLD_BODY\"] || style.name == layer.params[\"STYLES\"]) {\n+ style.current = true\n+ } else {\n+ style.current = false\n+ }\n+ layerContext.styles.push(style)\n+ }\n+ } else {\n+ layerContext.styles.push({\n+ href: layer.params[\"SLD\"],\n+ body: layer.params[\"SLD_BODY\"],\n+ name: layer.params[\"STYLES\"] || parser.defaultStyleName,\n+ title: parser.defaultStyleTitle,\n+ current: true\n+ })\n+ }\n+ return layerContext\n+ },\n+ toContext: function(obj) {\n+ var context = {};\n+ var layers = obj.layers;\n+ if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n+ var metadata = obj.metadata || {};\n+ context.size = obj.getSize();\n+ context.bounds = obj.getExtent();\n+ context.projection = obj.projection;\n+ context.title = obj.title;\n+ context.keywords = metadata.keywords;\n+ context[\"abstract\"] = metadata[\"abstract\"];\n+ context.logo = metadata.logo;\n+ context.descriptionURL = metadata.descriptionURL;\n+ context.contactInformation = metadata.contactInformation;\n+ context.maxExtent = obj.maxExtent\n+ } else {\n+ OpenLayers.Util.applyDefaults(context, obj);\n+ if (context.layers != undefined) {\n+ delete context.layers\n }\n }\n- var attributes;\n- if (this.extractAttributes) {\n- attributes = this.parseAttributes(node)\n+ if (context.layersContext == undefined) {\n+ context.layersContext = []\n }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n- var fid = node.getAttribute(\"id\") || node.getAttribute(\"name\");\n- if (fid != null) {\n- feature.fid = fid\n+ if (layers != undefined && OpenLayers.Util.isArray(layers)) {\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ if (layer instanceof OpenLayers.Layer.WMS) {\n+ context.layersContext.push(this.layerToContext(layer))\n+ }\n+ }\n }\n- return feature\n+ return context\n },\n- getStyle: function(styleUrl, options) {\n- var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);\n- var newOptions = OpenLayers.Util.extend({}, options);\n- newOptions.depth++;\n- newOptions.styleBaseUrl = styleBaseUrl;\n- if (!this.styles[styleUrl] && !OpenLayers.String.startsWith(styleUrl, \"#\") && newOptions.depth <= this.maxDepth && !this.fetched[styleBaseUrl]) {\n- var data = this.fetchLink(styleBaseUrl);\n- if (data) {\n- this.parseData(data, newOptions)\n- }\n+ CLASS_NAME: \"OpenLayers.Format.WMC\"\n+});\n+OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {\n+ checkTags: false,\n+ interestingTagsExclude: null,\n+ areaTags: null,\n+ initialize: function(options) {\n+ var layer_defaults = {\n+ interestingTagsExclude: [\"source\", \"source_ref\", \"source:ref\", \"history\", \"attribution\", \"created_by\"],\n+ areaTags: [\"area\", \"building\", \"leisure\", \"tourism\", \"ruins\", \"historic\", \"landuse\", \"military\", \"natural\", \"sport\"]\n+ };\n+ layer_defaults = OpenLayers.Util.extend(layer_defaults, options);\n+ var interesting = {};\n+ for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {\n+ interesting[layer_defaults.interestingTagsExclude[i]] = true\n }\n- var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);\n- return style\n+ layer_defaults.interestingTagsExclude = interesting;\n+ var area = {};\n+ for (var i = 0; i < layer_defaults.areaTags.length; i++) {\n+ area[layer_defaults.areaTags[i]] = true\n+ }\n+ layer_defaults.areaTags = area;\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults])\n },\n- parseGeometry: {\n- point: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"coordinates\");\n- var coords = [];\n- if (nodeList.length > 0) {\n- var coordString = nodeList[0].firstChild.nodeValue;\n- coordString = coordString.replace(this.regExes.removeSpace, \"\");\n- coords = coordString.split(\",\")\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc])\n+ }\n+ var nodes = this.getNodes(doc);\n+ var ways = this.getWays(doc);\n+ var feat_list = new Array(ways.length);\n+ for (var i = 0; i < ways.length; i++) {\n+ var point_list = new Array(ways[i].nodes.length);\n+ var poly = this.isWayArea(ways[i]) ? 1 : 0;\n+ for (var j = 0; j < ways[i].nodes.length; j++) {\n+ var node = nodes[ways[i].nodes[j]];\n+ var point = new OpenLayers.Geometry.Point(node.lon, node.lat);\n+ point.osm_id = parseInt(ways[i].nodes[j]);\n+ point_list[j] = point;\n+ node.used = true\n }\n- var point = null;\n- if (coords.length > 1) {\n- if (coords.length == 2) {\n- coords[2] = null\n- }\n- point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2])\n+ var geometry = null;\n+ if (poly) {\n+ geometry = new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(point_list))\n } else {\n- throw \"Bad coordinate string: \" + coordString\n+ geometry = new OpenLayers.Geometry.LineString(point_list)\n }\n- return point\n- },\n- linestring: function(node, ring) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"coordinates\");\n- var line = null;\n- if (nodeList.length > 0) {\n- var coordString = this.getChildValue(nodeList[0]);\n- coordString = coordString.replace(this.regExes.trimSpace, \"\");\n- coordString = coordString.replace(this.regExes.trimComma, \",\");\n- var pointList = coordString.split(this.regExes.splitSpace);\n- var numPoints = pointList.length;\n- var points = new Array(numPoints);\n- var coords, numCoords;\n- for (var i = 0; i < numPoints; ++i) {\n- coords = pointList[i].split(\",\");\n- numCoords = coords.length;\n- if (numCoords > 1) {\n- if (coords.length == 2) {\n- coords[2] = null\n- }\n- points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2])\n- } else {\n- throw \"Bad LineString point coordinates: \" + pointList[i]\n- }\n- }\n- if (numPoints) {\n- if (ring) {\n- line = new OpenLayers.Geometry.LinearRing(points)\n- } else {\n- line = new OpenLayers.Geometry.LineString(points)\n- }\n- } else {\n- throw \"Bad LineString coordinates: \" + coordString\n- }\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection, this.internalProjection)\n }\n- return line\n- },\n- polygon: function(node) {\n- var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"LinearRing\");\n- var numRings = nodeList.length;\n- var components = new Array(numRings);\n- if (numRings > 0) {\n- var ring;\n- for (var i = 0, len = nodeList.length; i < len; ++i) {\n- ring = this.parseGeometry.linestring.apply(this, [nodeList[i], true]);\n- if (ring) {\n- components[i] = ring\n- } else {\n- throw \"Bad LinearRing geometry: \" + i\n+ var feat = new OpenLayers.Feature.Vector(geometry, ways[i].tags);\n+ feat.osm_id = parseInt(ways[i].id);\n+ feat.fid = \"way.\" + feat.osm_id;\n+ feat_list[i] = feat\n+ }\n+ for (var node_id in nodes) {\n+ var node = nodes[node_id];\n+ if (!node.used || this.checkTags) {\n+ var tags = null;\n+ if (this.checkTags) {\n+ var result = this.getTags(node.node, true);\n+ if (node.used && !result[1]) {\n+ continue\n }\n+ tags = result[0]\n+ } else {\n+ tags = this.getTags(node.node)\n }\n- }\n- return new OpenLayers.Geometry.Polygon(components)\n- },\n- multigeometry: function(node) {\n- var child, parser;\n- var parts = [];\n- var children = node.childNodes;\n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- var type = child.prefix ? child.nodeName.split(\":\")[1] : child.nodeName;\n- var parser = this.parseGeometry[type.toLowerCase()];\n- if (parser) {\n- parts.push(parser.apply(this, [child]))\n- }\n+ var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(node[\"lon\"], node[\"lat\"]), tags);\n+ if (this.internalProjection && this.externalProjection) {\n+ feat.geometry.transform(this.externalProjection, this.internalProjection)\n }\n+ feat.osm_id = parseInt(node_id);\n+ feat.fid = \"node.\" + feat.osm_id;\n+ feat_list.push(feat)\n }\n- return new OpenLayers.Geometry.Collection(parts)\n+ node.node = null\n }\n+ return feat_list\n },\n- parseAttributes: function(node) {\n- var attributes = {};\n- var edNodes = node.getElementsByTagName(\"ExtendedData\");\n- if (edNodes.length) {\n- attributes = this.parseExtendedData(edNodes[0])\n- }\n- var child, grandchildren, grandchild;\n- var children = node.childNodes;\n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- grandchildren = child.childNodes;\n- if (grandchildren.length >= 1 && grandchildren.length <= 3) {\n- var grandchild;\n- switch (grandchildren.length) {\n- case 1:\n- grandchild = grandchildren[0];\n- break;\n- case 2:\n- var c1 = grandchildren[0];\n- var c2 = grandchildren[1];\n- grandchild = c1.nodeType == 3 || c1.nodeType == 4 ? c1 : c2;\n- break;\n- case 3:\n- default:\n- grandchild = grandchildren[1];\n- break\n- }\n- if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {\n- var name = child.prefix ? child.nodeName.split(\":\")[1] : child.nodeName;\n- var value = OpenLayers.Util.getXmlNodeValue(grandchild);\n- if (value) {\n- value = value.replace(this.regExes.trimSpace, \"\");\n- attributes[name] = value\n- }\n- }\n- }\n+ getNodes: function(doc) {\n+ var node_list = doc.getElementsByTagName(\"node\");\n+ var nodes = {};\n+ for (var i = 0; i < node_list.length; i++) {\n+ var node = node_list[i];\n+ var id = node.getAttribute(\"id\");\n+ nodes[id] = {\n+ lat: node.getAttribute(\"lat\"),\n+ lon: node.getAttribute(\"lon\"),\n+ node: node\n }\n }\n- return attributes\n+ return nodes\n },\n- parseExtendedData: function(node) {\n- var attributes = {};\n- var i, len, data, key;\n- var dataNodes = node.getElementsByTagName(\"Data\");\n- for (i = 0, len = dataNodes.length; i < len; i++) {\n- data = dataNodes[i];\n- key = data.getAttribute(\"name\");\n- var ed = {};\n- var valueNode = data.getElementsByTagName(\"value\");\n- if (valueNode.length) {\n- ed[\"value\"] = this.getChildValue(valueNode[0])\n- }\n- if (this.kvpAttributes) {\n- attributes[key] = ed[\"value\"]\n- } else {\n- var nameNode = data.getElementsByTagName(\"displayName\");\n- if (nameNode.length) {\n- ed[\"displayName\"] = this.getChildValue(nameNode[0])\n- }\n- attributes[key] = ed\n+ getWays: function(doc) {\n+ var way_list = doc.getElementsByTagName(\"way\");\n+ var return_ways = [];\n+ for (var i = 0; i < way_list.length; i++) {\n+ var way = way_list[i];\n+ var way_object = {\n+ id: way.getAttribute(\"id\")\n+ };\n+ way_object.tags = this.getTags(way);\n+ var node_list = way.getElementsByTagName(\"nd\");\n+ way_object.nodes = new Array(node_list.length);\n+ for (var j = 0; j < node_list.length; j++) {\n+ way_object.nodes[j] = node_list[j].getAttribute(\"ref\")\n }\n+ return_ways.push(way_object)\n }\n- var simpleDataNodes = node.getElementsByTagName(\"SimpleData\");\n- for (i = 0, len = simpleDataNodes.length; i < len; i++) {\n- var ed = {};\n- data = simpleDataNodes[i];\n- key = data.getAttribute(\"name\");\n- ed[\"value\"] = this.getChildValue(data);\n- if (this.kvpAttributes) {\n- attributes[key] = ed[\"value\"]\n- } else {\n- ed[\"displayName\"] = key;\n- attributes[key] = ed\n+ return return_ways\n+ },\n+ getTags: function(dom_node, interesting_tags) {\n+ var tag_list = dom_node.getElementsByTagName(\"tag\");\n+ var tags = {};\n+ var interesting = false;\n+ for (var j = 0; j < tag_list.length; j++) {\n+ var key = tag_list[j].getAttribute(\"k\");\n+ tags[key] = tag_list[j].getAttribute(\"v\");\n+ if (interesting_tags) {\n+ if (!this.interestingTagsExclude[key]) {\n+ interesting = true\n+ }\n }\n }\n- return attributes\n+ return interesting_tags ? [tags, interesting] : tags\n },\n- parseProperty: function(xmlNode, namespace, tagName) {\n- var value;\n- var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);\n- try {\n- value = OpenLayers.Util.getXmlNodeValue(nodeList[0])\n- } catch (e) {\n- value = null\n+ isWayArea: function(way) {\n+ var poly_shaped = false;\n+ var poly_tags = false;\n+ if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {\n+ poly_shaped = true\n }\n- return value\n+ if (this.checkTags) {\n+ for (var key in way.tags) {\n+ if (this.areaTags[key]) {\n+ poly_tags = true;\n+ break\n+ }\n+ }\n+ }\n+ return poly_shaped && (this.checkTags ? poly_tags : true)\n },\n write: function(features) {\n if (!OpenLayers.Util.isArray(features)) {\n features = [features]\n }\n- var kml = this.createElementNS(this.kmlns, \"kml\");\n- var folder = this.createFolderXML();\n- for (var i = 0, len = features.length; i < len; ++i) {\n- folder.appendChild(this.createPlacemarkXML(features[i]))\n- }\n- kml.appendChild(folder);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [kml])\n- },\n- createFolderXML: function() {\n- var folder = this.createElementNS(this.kmlns, \"Folder\");\n- if (this.foldersName) {\n- var folderName = this.createElementNS(this.kmlns, \"name\");\n- var folderNameText = this.createTextNode(this.foldersName);\n- folderName.appendChild(folderNameText);\n- folder.appendChild(folderName)\n- }\n- if (this.foldersDesc) {\n- var folderDesc = this.createElementNS(this.kmlns, \"description\");\n- var folderDescText = this.createTextNode(this.foldersDesc);\n- folderDesc.appendChild(folderDescText);\n- folder.appendChild(folderDesc)\n- }\n- return folder\n- },\n- createPlacemarkXML: function(feature) {\n- var placemarkName = this.createElementNS(this.kmlns, \"name\");\n- var label = feature.style && feature.style.label ? feature.style.label : feature.id;\n- var name = feature.attributes.name || label;\n- placemarkName.appendChild(this.createTextNode(name));\n- var placemarkDesc = this.createElementNS(this.kmlns, \"description\");\n- var desc = feature.attributes.description || this.placemarksDesc;\n- placemarkDesc.appendChild(this.createTextNode(desc));\n- var placemarkNode = this.createElementNS(this.kmlns, \"Placemark\");\n- if (feature.fid != null) {\n- placemarkNode.setAttribute(\"id\", feature.fid)\n- }\n- placemarkNode.appendChild(placemarkName);\n- placemarkNode.appendChild(placemarkDesc);\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n- placemarkNode.appendChild(geometryNode);\n- if (feature.attributes) {\n- var edNode = this.buildExtendedData(feature.attributes);\n- if (edNode) {\n- placemarkNode.appendChild(edNode)\n+ this.osm_id = 1;\n+ this.created_nodes = {};\n+ var root_node = this.createElementNS(null, \"osm\");\n+ root_node.setAttribute(\"version\", \"0.5\");\n+ root_node.setAttribute(\"generator\", \"OpenLayers \" + OpenLayers.VERSION_NUMBER);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ var nodes = this.createFeatureNodes(features[i]);\n+ for (var j = 0; j < nodes.length; j++) {\n+ root_node.appendChild(nodes[j])\n }\n }\n- return placemarkNode\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [root_node])\n },\n- buildGeometryNode: function(geometry) {\n- var className = geometry.CLASS_NAME;\n+ createFeatureNodes: function(feature) {\n+ var nodes = [];\n+ var className = feature.geometry.CLASS_NAME;\n var type = className.substring(className.lastIndexOf(\".\") + 1);\n- var builder = this.buildGeometry[type.toLowerCase()];\n- var node = null;\n+ type = type.toLowerCase();\n+ var builder = this.createXML[type];\n if (builder) {\n- node = builder.apply(this, [geometry])\n+ nodes = builder.apply(this, [feature])\n }\n- return node\n+ return nodes\n },\n- buildGeometry: {\n- point: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"Point\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml\n- },\n- multipoint: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry])\n- },\n- linestring: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"LineString\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml\n- },\n- multilinestring: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry])\n- },\n- linearring: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"LinearRing\");\n- kml.appendChild(this.buildCoordinatesNode(geometry));\n- return kml\n- },\n- polygon: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"Polygon\");\n- var rings = geometry.components;\n- var ringMember, ringGeom, type;\n- for (var i = 0, len = rings.length; i < len; ++i) {\n- type = i == 0 ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n- ringMember = this.createElementNS(this.kmlns, type);\n- ringGeom = this.buildGeometry.linearring.apply(this, [rings[i]]);\n- ringMember.appendChild(ringGeom);\n- kml.appendChild(ringMember)\n+ createXML: {\n+ point: function(point) {\n+ var id = null;\n+ var geometry = point.geometry ? point.geometry : point;\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection, this.externalProjection)\n }\n- return kml\n- },\n- multipolygon: function(geometry) {\n- return this.buildGeometry.collection.apply(this, [geometry])\n- },\n- collection: function(geometry) {\n- var kml = this.createElementNS(this.kmlns, \"MultiGeometry\");\n- var child;\n- for (var i = 0, len = geometry.components.length; i < len; ++i) {\n- child = this.buildGeometryNode.apply(this, [geometry.components[i]]);\n- if (child) {\n- kml.appendChild(child)\n+ var already_exists = false;\n+ if (point.osm_id) {\n+ id = point.osm_id;\n+ if (this.created_nodes[id]) {\n+ already_exists = true\n }\n+ } else {\n+ id = -this.osm_id;\n+ this.osm_id++\n }\n- return kml\n- }\n- },\n- buildCoordinatesNode: function(geometry) {\n- var coordinatesNode = this.createElementNS(this.kmlns, \"coordinates\");\n- var path;\n- var points = geometry.components;\n- if (points) {\n- var point;\n- var numPoints = points.length;\n- var parts = new Array(numPoints);\n- for (var i = 0; i < numPoints; ++i) {\n- point = points[i];\n- parts[i] = this.buildCoordinates(point)\n+ if (already_exists) {\n+ node = this.created_nodes[id]\n+ } else {\n+ var node = this.createElementNS(null, \"node\")\n }\n- path = parts.join(\" \")\n- } else {\n- path = this.buildCoordinates(geometry)\n+ this.created_nodes[id] = node;\n+ node.setAttribute(\"id\", id);\n+ node.setAttribute(\"lon\", geometry.x);\n+ node.setAttribute(\"lat\", geometry.y);\n+ if (point.attributes) {\n+ this.serializeTags(point, node)\n+ }\n+ this.setState(point, node);\n+ return already_exists ? [] : [node]\n+ },\n+ linestring: function(feature) {\n+ var id;\n+ var nodes = [];\n+ var geometry = feature.geometry;\n+ if (feature.osm_id) {\n+ id = feature.osm_id\n+ } else {\n+ id = -this.osm_id;\n+ this.osm_id++\n+ }\n+ var way = this.createElementNS(null, \"way\");\n+ way.setAttribute(\"id\", id);\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ var node = this.createXML[\"point\"].apply(this, [geometry.components[i]]);\n+ if (node.length) {\n+ node = node[0];\n+ var node_ref = node.getAttribute(\"id\");\n+ nodes.push(node)\n+ } else {\n+ node_ref = geometry.components[i].osm_id;\n+ node = this.created_nodes[node_ref]\n+ }\n+ this.setState(feature, node);\n+ var nd_dom = this.createElementNS(null, \"nd\");\n+ nd_dom.setAttribute(\"ref\", node_ref);\n+ way.appendChild(nd_dom)\n+ }\n+ this.serializeTags(feature, way);\n+ nodes.push(way);\n+ return nodes\n+ },\n+ polygon: function(feature) {\n+ var attrs = OpenLayers.Util.extend({\n+ area: \"yes\"\n+ }, feature.attributes);\n+ var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);\n+ feat.osm_id = feature.osm_id;\n+ return this.createXML[\"linestring\"].apply(this, [feat])\n }\n- var txtNode = this.createTextNode(path);\n- coordinatesNode.appendChild(txtNode);\n- return coordinatesNode\n },\n- buildCoordinates: function(point) {\n- if (this.internalProjection && this.externalProjection) {\n- point = point.clone();\n- point.transform(this.internalProjection, this.externalProjection)\n+ serializeTags: function(feature, node) {\n+ for (var key in feature.attributes) {\n+ var tag = this.createElementNS(null, \"tag\");\n+ tag.setAttribute(\"k\", key);\n+ tag.setAttribute(\"v\", feature.attributes[key]);\n+ node.appendChild(tag)\n }\n- return point.x + \",\" + point.y\n },\n- buildExtendedData: function(attributes) {\n- var extendedData = this.createElementNS(this.kmlns, \"ExtendedData\");\n- for (var attributeName in attributes) {\n- if (attributes[attributeName] && attributeName != \"name\" && attributeName != \"description\" && attributeName != \"styleUrl\") {\n- var data = this.createElementNS(this.kmlns, \"Data\");\n- data.setAttribute(\"name\", attributeName);\n- var value = this.createElementNS(this.kmlns, \"value\");\n- if (typeof attributes[attributeName] == \"object\") {\n- if (attributes[attributeName].value) {\n- value.appendChild(this.createTextNode(attributes[attributeName].value))\n- }\n- if (attributes[attributeName].displayName) {\n- var displayName = this.createElementNS(this.kmlns, \"displayName\");\n- displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName));\n- data.appendChild(displayName)\n- }\n- } else {\n- value.appendChild(this.createTextNode(attributes[attributeName]))\n- }\n- data.appendChild(value);\n- extendedData.appendChild(data)\n+ setState: function(feature, node) {\n+ if (feature.state) {\n+ var state = null;\n+ switch (feature.state) {\n+ case OpenLayers.State.UPDATE:\n+ state = \"modify\";\n+ case OpenLayers.State.DELETE:\n+ state = \"delete\"\n+ }\n+ if (state) {\n+ node.setAttribute(\"action\", state)\n }\n- }\n- if (this.isSimpleContent(extendedData)) {\n- return null\n- } else {\n- return extendedData\n }\n },\n- CLASS_NAME: \"OpenLayers.Format.KML\"\n-});\n-OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.1.0\",\n- CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n+ CLASS_NAME: \"OpenLayers.Format.OSM\"\n });\n-OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n- defaultDesc: \"No description available\",\n- extractWaypoints: true,\n- extractTracks: true,\n- extractRoutes: true,\n- extractAttributes: true,\n- namespaces: {\n- gpx: \"http://www.topografix.com/GPX/1/1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n- schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n- creator: \"OpenLayers\",\n- initialize: function(options) {\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n+OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.0.0\",\n+ yx: {\n+ \"urn:ogc:def:crs:EPSG::4326\": true\n },\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc])\n+ createLayer: function(capabilities, config) {\n+ var layer;\n+ if (!(\"layer\" in config)) {\n+ throw new Error(\"Missing property 'layer' in configuration.\")\n }\n- var features = [];\n- if (this.extractTracks) {\n- var tracks = doc.getElementsByTagName(\"trk\");\n- for (var i = 0, len = tracks.length; i < len; i++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(tracks[i])\n- }\n- var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n- for (var j = 0, seglen = segs.length; j < seglen; j++) {\n- var track = this.extractSegment(segs[j], \"trkpt\");\n- features.push(new OpenLayers.Feature.Vector(track, attrs))\n- }\n+ var contents = capabilities.contents;\n+ var layers = contents.layers;\n+ var layerDef;\n+ for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n+ if (contents.layers[i].identifier === config.layer) {\n+ layerDef = contents.layers[i];\n+ break\n }\n }\n- if (this.extractRoutes) {\n- var routes = doc.getElementsByTagName(\"rte\");\n- for (var k = 0, klen = routes.length; k < klen; k++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(routes[k])\n- }\n- var route = this.extractSegment(routes[k], \"rtept\");\n- features.push(new OpenLayers.Feature.Vector(route, attrs))\n- }\n+ if (!layerDef) {\n+ throw new Error(\"Layer not found\")\n }\n- if (this.extractWaypoints) {\n- var waypoints = doc.getElementsByTagName(\"wpt\");\n- for (var l = 0, len = waypoints.length; l < len; l++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(waypoints[l])\n- }\n- var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n- features.push(new OpenLayers.Feature.Vector(wpt, attrs))\n- }\n+ var format = config.format;\n+ if (!format && layerDef.formats && layerDef.formats.length) {\n+ format = layerDef.formats[0]\n }\n- if (this.internalProjection && this.externalProjection) {\n- for (var g = 0, featLength = features.length; g < featLength; g++) {\n- features[g].geometry.transform(this.externalProjection, this.internalProjection)\n- }\n+ var matrixSet;\n+ if (config.matrixSet) {\n+ matrixSet = contents.tileMatrixSets[config.matrixSet]\n+ } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n+ matrixSet = contents.tileMatrixSets[layerDef.tileMatrixSetLinks[0].tileMatrixSet]\n }\n- return features\n- },\n- extractSegment: function(segment, segmentType) {\n- var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n- var point_features = [];\n- for (var i = 0, len = points.length; i < len; i++) {\n- point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")))\n+ if (!matrixSet) {\n+ throw new Error(\"matrixSet not found\")\n }\n- return new OpenLayers.Geometry.LineString(point_features)\n- },\n- parseAttributes: function(node) {\n- var attributes = {};\n- var attrNode = node.firstChild,\n- value, name;\n- while (attrNode) {\n- if (attrNode.nodeType == 1 && attrNode.firstChild) {\n- value = attrNode.firstChild;\n- if (value.nodeType == 3 || value.nodeType == 4) {\n- name = attrNode.prefix ? attrNode.nodeName.split(\":\")[1] : attrNode.nodeName;\n- if (name != \"trkseg\" && name != \"rtept\") {\n- attributes[name] = value.nodeValue\n+ var style;\n+ for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n+ style = layerDef.styles[i];\n+ if (style.isDefault) {\n+ break\n+ }\n+ }\n+ var requestEncoding = config.requestEncoding;\n+ if (!requestEncoding) {\n+ requestEncoding = \"KVP\";\n+ if (capabilities.operationsMetadata.GetTile.dcp.http) {\n+ var http = capabilities.operationsMetadata.GetTile.dcp.http;\n+ if (http.get[0].constraints) {\n+ var constraints = http.get[0].constraints;\n+ var allowedValues = constraints.GetEncoding.allowedValues;\n+ if (!allowedValues.KVP && (allowedValues.REST || allowedValues.RESTful)) {\n+ requestEncoding = \"REST\"\n }\n }\n }\n- attrNode = attrNode.nextSibling\n- }\n- return attributes\n- },\n- write: function(features, metadata) {\n- features = OpenLayers.Util.isArray(features) ? features : [features];\n- var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n- gpx.setAttribute(\"version\", \"1.1\");\n- gpx.setAttribute(\"creator\", this.creator);\n- this.setAttributes(gpx, {\n- \"xsi:schemaLocation\": this.schemaLocation\n- });\n- if (metadata && typeof metadata == \"object\") {\n- gpx.appendChild(this.buildMetadataNode(metadata))\n }\n- for (var i = 0, len = features.length; i < len; i++) {\n- gpx.appendChild(this.buildFeatureNode(features[i]))\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [gpx])\n- },\n- buildMetadataNode: function(metadata) {\n- var types = [\"name\", \"desc\", \"author\"],\n- node = this.createElementNS(this.namespaces.gpx, \"metadata\");\n- for (var i = 0; i < types.length; i++) {\n- var type = types[i];\n- if (metadata[type]) {\n- var n = this.createElementNS(this.namespaces.gpx, type);\n- n.appendChild(this.createTextNode(metadata[type]));\n- node.appendChild(n)\n+ var dimensions = [];\n+ var params = config.params || {};\n+ delete config.params;\n+ for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n+ var dimension = layerDef.dimensions[id];\n+ dimensions.push(dimension.identifier);\n+ if (!params.hasOwnProperty(dimension.identifier)) {\n+ params[dimension.identifier] = dimension[\"default\"]\n }\n }\n- return node\n- },\n- buildFeatureNode: function(feature) {\n- var geometry = feature.geometry;\n- geometry = geometry.clone();\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.internalProjection, this.externalProjection)\n- }\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- var wpt = this.buildWptNode(geometry);\n- this.appendAttributesNode(wpt, feature);\n- return wpt\n- } else {\n- var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n- this.appendAttributesNode(trkNode, feature);\n- var trkSegNodes = this.buildTrkSegNode(geometry);\n- trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? trkSegNodes : [trkSegNodes];\n- for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n- trkNode.appendChild(trkSegNodes[i])\n+ var projection = config.projection || matrixSet.supportedCRS.replace(/urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n+ var units = config.units || (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n+ var resolutions = [];\n+ for (var mid in matrixSet.matrixIds) {\n+ if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n+ resolutions.push(matrixSet.matrixIds[mid].scaleDenominator * 28e-5 / OpenLayers.METERS_PER_INCH / OpenLayers.INCHES_PER_UNIT[units])\n }\n- return trkNode\n }\n- },\n- buildTrkSegNode: function(geometry) {\n- var node, i, len, point, nodes;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- point = geometry.components[i];\n- node.appendChild(this.buildTrkPtNode(point))\n+ var url;\n+ if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n+ url = [];\n+ var resourceUrls = layerDef.resourceUrls,\n+ resourceUrl;\n+ for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n+ resourceUrl = layerDef.resourceUrls[t];\n+ if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n+ url.push(resourceUrl.template)\n+ }\n }\n- return node\n } else {\n- nodes = [];\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- nodes.push(this.buildTrkSegNode(geometry.components[i]))\n+ var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n+ url = [];\n+ var constraint;\n+ for (var i = 0, ii = httpGet.length; i < ii; i++) {\n+ constraint = httpGet[i].constraints;\n+ if (!constraint || constraint && constraint.GetEncoding.allowedValues[requestEncoding]) {\n+ url.push(httpGet[i].url)\n+ }\n }\n- return nodes\n }\n+ return new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults(config, {\n+ url: url,\n+ requestEncoding: requestEncoding,\n+ name: layerDef.title,\n+ style: style.identifier,\n+ format: format,\n+ matrixIds: matrixSet.matrixIds,\n+ matrixSet: matrixSet.identifier,\n+ projection: projection,\n+ units: units,\n+ resolutions: config.isBaseLayer === false ? undefined : resolutions,\n+ serverResolutions: resolutions,\n+ tileFullExtent: matrixSet.bounds,\n+ dimensions: dimensions,\n+ params: params\n+ }))\n },\n- buildTrkPtNode: function(point) {\n- var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n- node.setAttribute(\"lon\", point.x);\n- node.setAttribute(\"lat\", point.y);\n- return node\n- },\n- buildWptNode: function(geometry) {\n- var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n- node.setAttribute(\"lon\", geometry.x);\n- node.setAttribute(\"lat\", geometry.y);\n- return node\n- },\n- appendAttributesNode: function(node, feature) {\n- var name = this.createElementNS(this.namespaces.gpx, \"name\");\n- name.appendChild(this.createTextNode(feature.attributes.name || feature.id));\n- node.appendChild(name);\n- var desc = this.createElementNS(this.namespaces.gpx, \"desc\");\n- desc.appendChild(this.createTextNode(feature.attributes.description || this.defaultDesc));\n- node.appendChild(desc)\n- },\n- CLASS_NAME: \"OpenLayers.Format.GPX\"\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n });\n OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {\n rssns: \"http://backend.userland.com/rss2\",\n featureNS: \"http://mapserver.gis.umn.edu/mapserver\",\n georssns: \"http://www.georss.org/georss\",\n geons: \"http://www.w3.org/2003/01/geo/wgs84_pos#\",\n featureTitle: \"Untitled\",\n@@ -21190,145 +16843,14 @@\n } else {\n path = geometry.y + \" \" + geometry.x\n }\n return this.createTextNode(path)\n },\n CLASS_NAME: \"OpenLayers.Format.GeoRSS\"\n });\n-OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.1.1\",\n- profile: null,\n- CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n-});\n-OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.0.0\",\n- yx: {\n- \"urn:ogc:def:crs:EPSG::4326\": true\n- },\n- createLayer: function(capabilities, config) {\n- var layer;\n- if (!(\"layer\" in config)) {\n- throw new Error(\"Missing property 'layer' in configuration.\")\n- }\n- var contents = capabilities.contents;\n- var layers = contents.layers;\n- var layerDef;\n- for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n- if (contents.layers[i].identifier === config.layer) {\n- layerDef = contents.layers[i];\n- break\n- }\n- }\n- if (!layerDef) {\n- throw new Error(\"Layer not found\")\n- }\n- var format = config.format;\n- if (!format && layerDef.formats && layerDef.formats.length) {\n- format = layerDef.formats[0]\n- }\n- var matrixSet;\n- if (config.matrixSet) {\n- matrixSet = contents.tileMatrixSets[config.matrixSet]\n- } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n- matrixSet = contents.tileMatrixSets[layerDef.tileMatrixSetLinks[0].tileMatrixSet]\n- }\n- if (!matrixSet) {\n- throw new Error(\"matrixSet not found\")\n- }\n- var style;\n- for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n- style = layerDef.styles[i];\n- if (style.isDefault) {\n- break\n- }\n- }\n- var requestEncoding = config.requestEncoding;\n- if (!requestEncoding) {\n- requestEncoding = \"KVP\";\n- if (capabilities.operationsMetadata.GetTile.dcp.http) {\n- var http = capabilities.operationsMetadata.GetTile.dcp.http;\n- if (http.get[0].constraints) {\n- var constraints = http.get[0].constraints;\n- var allowedValues = constraints.GetEncoding.allowedValues;\n- if (!allowedValues.KVP && (allowedValues.REST || allowedValues.RESTful)) {\n- requestEncoding = \"REST\"\n- }\n- }\n- }\n- }\n- var dimensions = [];\n- var params = config.params || {};\n- delete config.params;\n- for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n- var dimension = layerDef.dimensions[id];\n- dimensions.push(dimension.identifier);\n- if (!params.hasOwnProperty(dimension.identifier)) {\n- params[dimension.identifier] = dimension[\"default\"]\n- }\n- }\n- var projection = config.projection || matrixSet.supportedCRS.replace(/urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n- var units = config.units || (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n- var resolutions = [];\n- for (var mid in matrixSet.matrixIds) {\n- if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n- resolutions.push(matrixSet.matrixIds[mid].scaleDenominator * 28e-5 / OpenLayers.METERS_PER_INCH / OpenLayers.INCHES_PER_UNIT[units])\n- }\n- }\n- var url;\n- if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n- url = [];\n- var resourceUrls = layerDef.resourceUrls,\n- resourceUrl;\n- for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n- resourceUrl = layerDef.resourceUrls[t];\n- if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n- url.push(resourceUrl.template)\n- }\n- }\n- } else {\n- var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n- url = [];\n- var constraint;\n- for (var i = 0, ii = httpGet.length; i < ii; i++) {\n- constraint = httpGet[i].constraints;\n- if (!constraint || constraint && constraint.GetEncoding.allowedValues[requestEncoding]) {\n- url.push(httpGet[i].url)\n- }\n- }\n- }\n- return new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults(config, {\n- url: url,\n- requestEncoding: requestEncoding,\n- name: layerDef.title,\n- style: style.identifier,\n- format: format,\n- matrixIds: matrixSet.matrixIds,\n- matrixSet: matrixSet.identifier,\n- projection: projection,\n- units: units,\n- resolutions: config.isBaseLayer === false ? undefined : resolutions,\n- serverResolutions: resolutions,\n- tileFullExtent: matrixSet.bounds,\n- dimensions: dimensions,\n- params: params\n- }))\n- },\n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n-});\n-OpenLayers.Format.CSWGetRecords = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetRecords.DEFAULTS);\n- var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetRecords version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n- version: \"2.0.2\"\n-};\n OpenLayers.Format.CQL = function() {\n var tokens = [\"PROPERTY\", \"COMPARISON\", \"VALUE\", \"LOGICAL\"],\n patterns = {\n PROPERTY: /^[_a-zA-Z]\\w*/,\n COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i,\n IS_NULL: /^IS NULL/i,\n COMMA: /^,/,\n@@ -21833,21 +17355,14 @@\n } else {\n this.readNode(data, schema)\n }\n return schema\n },\n CLASS_NAME: \"OpenLayers.Format.WFSDescribeFeatureType\"\n });\n-OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- profile: null,\n- defaultVersion: \"1.0.0\",\n- stringifyOutput: true,\n- namedLayersAsArray: false,\n- CLASS_NAME: \"OpenLayers.Format.SLD\"\n-});\n OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {\n layer: null,\n wfsns: \"http://www.opengis.net/wfs\",\n ogcns: \"http://www.opengis.net/ogc\",\n initialize: function(options, layer) {\n OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);\n this.layer = layer;\n@@ -21956,18 +17471,826 @@\n return deleteNode\n },\n destroy: function() {\n this.layer = null\n },\n CLASS_NAME: \"OpenLayers.Format.WFS\"\n });\n-OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n- defaultVersion: \"1.1.0\",\n+OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {\n+ namespaces: {\n+ kml: \"http://www.opengis.net/kml/2.2\",\n+ gx: \"http://www.google.com/kml/ext/2.2\"\n+ },\n+ kmlns: \"http://earth.google.com/kml/2.0\",\n+ placemarksDesc: \"No description available\",\n+ foldersName: \"OpenLayers export\",\n+ foldersDesc: \"Exported on \" + new Date,\n+ extractAttributes: true,\n+ kvpAttributes: false,\n+ extractStyles: false,\n+ extractTracks: false,\n+ trackAttributes: null,\n+ internalns: null,\n+ features: null,\n+ styles: null,\n+ styleBaseUrl: \"\",\n+ fetched: null,\n+ maxDepth: 0,\n+ initialize: function(options) {\n+ this.regExes = {\n+ trimSpace: /^\\s*|\\s*$/g,\n+ removeSpace: /\\s*/g,\n+ splitSpace: /\\s+/,\n+ trimComma: /\\s*,\\s*/g,\n+ kmlColor: /(\\w{2})(\\w{2})(\\w{2})(\\w{2})/,\n+ kmlIconPalette: /root:\\/\\/icons\\/palette-(\\d+)(\\.\\w+)/,\n+ straightBracket: /\\$\\[(.*?)\\]/g\n+ };\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n+ },\n+ read: function(data) {\n+ this.features = [];\n+ this.styles = {};\n+ this.fetched = {};\n+ var options = {\n+ depth: 0,\n+ styleBaseUrl: this.styleBaseUrl\n+ };\n+ return this.parseData(data, options)\n+ },\n+ parseData: function(data, options) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n+ }\n+ var types = [\"Link\", \"NetworkLink\", \"Style\", \"StyleMap\", \"Placemark\"];\n+ for (var i = 0, len = types.length; i < len; ++i) {\n+ var type = types[i];\n+ var nodes = this.getElementsByTagNameNS(data, \"*\", type);\n+ if (nodes.length == 0) {\n+ continue\n+ }\n+ switch (type.toLowerCase()) {\n+ case \"link\":\n+ case \"networklink\":\n+ this.parseLinks(nodes, options);\n+ break;\n+ case \"style\":\n+ if (this.extractStyles) {\n+ this.parseStyles(nodes, options)\n+ }\n+ break;\n+ case \"stylemap\":\n+ if (this.extractStyles) {\n+ this.parseStyleMaps(nodes, options)\n+ }\n+ break;\n+ case \"placemark\":\n+ this.parseFeatures(nodes, options);\n+ break\n+ }\n+ }\n+ return this.features\n+ },\n+ parseLinks: function(nodes, options) {\n+ if (options.depth >= this.maxDepth) {\n+ return false\n+ }\n+ var newOptions = OpenLayers.Util.extend({}, options);\n+ newOptions.depth++;\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var href = this.parseProperty(nodes[i], \"*\", \"href\");\n+ if (href && !this.fetched[href]) {\n+ this.fetched[href] = true;\n+ var data = this.fetchLink(href);\n+ if (data) {\n+ this.parseData(data, newOptions)\n+ }\n+ }\n+ }\n+ },\n+ fetchLink: function(href) {\n+ var request = OpenLayers.Request.GET({\n+ url: href,\n+ async: false\n+ });\n+ if (request) {\n+ return request.responseText\n+ }\n+ },\n+ parseStyles: function(nodes, options) {\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var style = this.parseStyle(nodes[i]);\n+ if (style) {\n+ var styleName = (options.styleBaseUrl || \"\") + \"#\" + style.id;\n+ this.styles[styleName] = style\n+ }\n+ }\n+ },\n+ parseKmlColor: function(kmlColor) {\n+ var color = null;\n+ if (kmlColor) {\n+ var matches = kmlColor.match(this.regExes.kmlColor);\n+ if (matches) {\n+ color = {\n+ color: \"#\" + matches[4] + matches[3] + matches[2],\n+ opacity: parseInt(matches[1], 16) / 255\n+ }\n+ }\n+ }\n+ return color\n+ },\n+ parseStyle: function(node) {\n+ var style = {};\n+ var types = [\"LineStyle\", \"PolyStyle\", \"IconStyle\", \"BalloonStyle\", \"LabelStyle\"];\n+ var type, styleTypeNode, nodeList, geometry, parser;\n+ for (var i = 0, len = types.length; i < len; ++i) {\n+ type = types[i];\n+ styleTypeNode = this.getElementsByTagNameNS(node, \"*\", type)[0];\n+ if (!styleTypeNode) {\n+ continue\n+ }\n+ switch (type.toLowerCase()) {\n+ case \"linestyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"strokeColor\"] = color.color;\n+ style[\"strokeOpacity\"] = color.opacity\n+ }\n+ var width = this.parseProperty(styleTypeNode, \"*\", \"width\");\n+ if (width) {\n+ style[\"strokeWidth\"] = width\n+ }\n+ break;\n+ case \"polystyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"fillOpacity\"] = color.opacity;\n+ style[\"fillColor\"] = color.color\n+ }\n+ var fill = this.parseProperty(styleTypeNode, \"*\", \"fill\");\n+ if (fill == \"0\") {\n+ style[\"fillColor\"] = \"none\"\n+ }\n+ var outline = this.parseProperty(styleTypeNode, \"*\", \"outline\");\n+ if (outline == \"0\") {\n+ style[\"strokeWidth\"] = \"0\"\n+ }\n+ break;\n+ case \"iconstyle\":\n+ var scale = parseFloat(this.parseProperty(styleTypeNode, \"*\", \"scale\") || 1);\n+ var width = 32 * scale;\n+ var height = 32 * scale;\n+ var iconNode = this.getElementsByTagNameNS(styleTypeNode, \"*\", \"Icon\")[0];\n+ if (iconNode) {\n+ var href = this.parseProperty(iconNode, \"*\", \"href\");\n+ if (href) {\n+ var w = this.parseProperty(iconNode, \"*\", \"w\");\n+ var h = this.parseProperty(iconNode, \"*\", \"h\");\n+ var google = \"http://maps.google.com/mapfiles/kml\";\n+ if (OpenLayers.String.startsWith(href, google) && !w && !h) {\n+ w = 64;\n+ h = 64;\n+ scale = scale / 2\n+ }\n+ w = w || h;\n+ h = h || w;\n+ if (w) {\n+ width = parseInt(w) * scale\n+ }\n+ if (h) {\n+ height = parseInt(h) * scale\n+ }\n+ var matches = href.match(this.regExes.kmlIconPalette);\n+ if (matches) {\n+ var palette = matches[1];\n+ var file_extension = matches[2];\n+ var x = this.parseProperty(iconNode, \"*\", \"x\");\n+ var y = this.parseProperty(iconNode, \"*\", \"y\");\n+ var posX = x ? x / 32 : 0;\n+ var posY = y ? 7 - y / 32 : 7;\n+ var pos = posY * 8 + posX;\n+ href = \"http://maps.google.com/mapfiles/kml/pal\" + palette + \"/icon\" + pos + file_extension\n+ }\n+ style[\"graphicOpacity\"] = 1;\n+ style[\"externalGraphic\"] = href\n+ }\n+ }\n+ var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, \"*\", \"hotSpot\")[0];\n+ if (hotSpotNode) {\n+ var x = parseFloat(hotSpotNode.getAttribute(\"x\"));\n+ var y = parseFloat(hotSpotNode.getAttribute(\"y\"));\n+ var xUnits = hotSpotNode.getAttribute(\"xunits\");\n+ if (xUnits == \"pixels\") {\n+ style[\"graphicXOffset\"] = -x * scale\n+ } else if (xUnits == \"insetPixels\") {\n+ style[\"graphicXOffset\"] = -width + x * scale\n+ } else if (xUnits == \"fraction\") {\n+ style[\"graphicXOffset\"] = -width * x\n+ }\n+ var yUnits = hotSpotNode.getAttribute(\"yunits\");\n+ if (yUnits == \"pixels\") {\n+ style[\"graphicYOffset\"] = -height + y * scale + 1\n+ } else if (yUnits == \"insetPixels\") {\n+ style[\"graphicYOffset\"] = -(y * scale) + 1\n+ } else if (yUnits == \"fraction\") {\n+ style[\"graphicYOffset\"] = -height * (1 - y) + 1\n+ }\n+ }\n+ style[\"graphicWidth\"] = width;\n+ style[\"graphicHeight\"] = height;\n+ break;\n+ case \"balloonstyle\":\n+ var balloonStyle = OpenLayers.Util.getXmlNodeValue(styleTypeNode);\n+ if (balloonStyle) {\n+ style[\"balloonStyle\"] = balloonStyle.replace(this.regExes.straightBracket, \"${$1}\")\n+ }\n+ break;\n+ case \"labelstyle\":\n+ var kmlColor = this.parseProperty(styleTypeNode, \"*\", \"color\");\n+ var color = this.parseKmlColor(kmlColor);\n+ if (color) {\n+ style[\"fontColor\"] = color.color;\n+ style[\"fontOpacity\"] = color.opacity\n+ }\n+ break;\n+ default:\n+ }\n+ }\n+ if (!style[\"strokeColor\"] && style[\"fillColor\"]) {\n+ style[\"strokeColor\"] = style[\"fillColor\"]\n+ }\n+ var id = node.getAttribute(\"id\");\n+ if (id && style) {\n+ style.id = id\n+ }\n+ return style\n+ },\n+ parseStyleMaps: function(nodes, options) {\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var node = nodes[i];\n+ var pairs = this.getElementsByTagNameNS(node, \"*\", \"Pair\");\n+ var id = node.getAttribute(\"id\");\n+ for (var j = 0, jlen = pairs.length; j < jlen; j++) {\n+ var pair = pairs[j];\n+ var key = this.parseProperty(pair, \"*\", \"key\");\n+ var styleUrl = this.parseProperty(pair, \"*\", \"styleUrl\");\n+ if (styleUrl && key == \"normal\") {\n+ this.styles[(options.styleBaseUrl || \"\") + \"#\" + id] = this.styles[(options.styleBaseUrl || \"\") + styleUrl]\n+ }\n+ }\n+ }\n+ },\n+ parseFeatures: function(nodes, options) {\n+ var features = [];\n+ for (var i = 0, len = nodes.length; i < len; i++) {\n+ var featureNode = nodes[i];\n+ var feature = this.parseFeature.apply(this, [featureNode]);\n+ if (feature) {\n+ if (this.extractStyles && feature.attributes && feature.attributes.styleUrl) {\n+ feature.style = this.getStyle(feature.attributes.styleUrl, options)\n+ }\n+ if (this.extractStyles) {\n+ var inlineStyleNode = this.getElementsByTagNameNS(featureNode, \"*\", \"Style\")[0];\n+ if (inlineStyleNode) {\n+ var inlineStyle = this.parseStyle(inlineStyleNode);\n+ if (inlineStyle) {\n+ feature.style = OpenLayers.Util.extend(feature.style, inlineStyle)\n+ }\n+ }\n+ }\n+ if (this.extractTracks) {\n+ var tracks = this.getElementsByTagNameNS(featureNode, this.namespaces.gx, \"Track\");\n+ if (tracks && tracks.length > 0) {\n+ var track = tracks[0];\n+ var container = {\n+ features: [],\n+ feature: feature\n+ };\n+ this.readNode(track, container);\n+ if (container.features.length > 0) {\n+ features.push.apply(features, container.features)\n+ }\n+ }\n+ } else {\n+ features.push(feature)\n+ }\n+ } else {\n+ throw \"Bad Placemark: \" + i\n+ }\n+ }\n+ this.features = this.features.concat(features)\n+ },\n+ readers: {\n+ kml: {\n+ when: function(node, container) {\n+ container.whens.push(OpenLayers.Date.parse(this.getChildValue(node)))\n+ },\n+ _trackPointAttribute: function(node, container) {\n+ var name = node.nodeName.split(\":\").pop();\n+ container.attributes[name].push(this.getChildValue(node))\n+ }\n+ },\n+ gx: {\n+ Track: function(node, container) {\n+ var obj = {\n+ whens: [],\n+ points: [],\n+ angles: []\n+ };\n+ if (this.trackAttributes) {\n+ var name;\n+ obj.attributes = {};\n+ for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) {\n+ name = this.trackAttributes[i];\n+ obj.attributes[name] = [];\n+ if (!(name in this.readers.kml)) {\n+ this.readers.kml[name] = this.readers.kml._trackPointAttribute\n+ }\n+ }\n+ }\n+ this.readChildNodes(node, obj);\n+ if (obj.whens.length !== obj.points.length) {\n+ throw new Error(\"gx:Track with unequal number of when (\" + obj.whens.length + \") and gx:coord (\" + obj.points.length + \") elements.\")\n+ }\n+ var hasAngles = obj.angles.length > 0;\n+ if (hasAngles && obj.whens.length !== obj.angles.length) {\n+ throw new Error(\"gx:Track with unequal number of when (\" + obj.whens.length + \") and gx:angles (\" + obj.angles.length + \") elements.\")\n+ }\n+ var feature, point, angles;\n+ for (var i = 0, ii = obj.whens.length; i < ii; ++i) {\n+ feature = container.feature.clone();\n+ feature.fid = container.feature.fid || container.feature.id;\n+ point = obj.points[i];\n+ feature.geometry = point;\n+ if (\"z\" in point) {\n+ feature.attributes.altitude = point.z\n+ }\n+ if (this.internalProjection && this.externalProjection) {\n+ feature.geometry.transform(this.externalProjection, this.internalProjection)\n+ }\n+ if (this.trackAttributes) {\n+ for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) {\n+ var name = this.trackAttributes[j];\n+ feature.attributes[name] = obj.attributes[name][i]\n+ }\n+ }\n+ feature.attributes.when = obj.whens[i];\n+ feature.attributes.trackId = container.feature.id;\n+ if (hasAngles) {\n+ angles = obj.angles[i];\n+ feature.attributes.heading = parseFloat(angles[0]);\n+ feature.attributes.tilt = parseFloat(angles[1]);\n+ feature.attributes.roll = parseFloat(angles[2])\n+ }\n+ container.features.push(feature)\n+ }\n+ },\n+ coord: function(node, container) {\n+ var str = this.getChildValue(node);\n+ var coords = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n+ var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);\n+ if (coords.length > 2) {\n+ point.z = parseFloat(coords[2])\n+ }\n+ container.points.push(point)\n+ },\n+ angles: function(node, container) {\n+ var str = this.getChildValue(node);\n+ var parts = str.replace(this.regExes.trimSpace, \"\").split(/\\s+/);\n+ container.angles.push(parts)\n+ }\n+ }\n+ },\n+ parseFeature: function(node) {\n+ var order = [\"MultiGeometry\", \"Polygon\", \"LineString\", \"Point\"];\n+ var type, nodeList, geometry, parser;\n+ for (var i = 0, len = order.length; i < len; ++i) {\n+ type = order[i];\n+ this.internalns = node.namespaceURI ? node.namespaceURI : this.kmlns;\n+ nodeList = this.getElementsByTagNameNS(node, this.internalns, type);\n+ if (nodeList.length > 0) {\n+ var parser = this.parseGeometry[type.toLowerCase()];\n+ if (parser) {\n+ geometry = parser.apply(this, [nodeList[0]]);\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection, this.internalProjection)\n+ }\n+ } else {\n+ throw new TypeError(\"Unsupported geometry type: \" + type)\n+ }\n+ break\n+ }\n+ }\n+ var attributes;\n+ if (this.extractAttributes) {\n+ attributes = this.parseAttributes(node)\n+ }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes);\n+ var fid = node.getAttribute(\"id\") || node.getAttribute(\"name\");\n+ if (fid != null) {\n+ feature.fid = fid\n+ }\n+ return feature\n+ },\n+ getStyle: function(styleUrl, options) {\n+ var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);\n+ var newOptions = OpenLayers.Util.extend({}, options);\n+ newOptions.depth++;\n+ newOptions.styleBaseUrl = styleBaseUrl;\n+ if (!this.styles[styleUrl] && !OpenLayers.String.startsWith(styleUrl, \"#\") && newOptions.depth <= this.maxDepth && !this.fetched[styleBaseUrl]) {\n+ var data = this.fetchLink(styleBaseUrl);\n+ if (data) {\n+ this.parseData(data, newOptions)\n+ }\n+ }\n+ var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);\n+ return style\n+ },\n+ parseGeometry: {\n+ point: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"coordinates\");\n+ var coords = [];\n+ if (nodeList.length > 0) {\n+ var coordString = nodeList[0].firstChild.nodeValue;\n+ coordString = coordString.replace(this.regExes.removeSpace, \"\");\n+ coords = coordString.split(\",\")\n+ }\n+ var point = null;\n+ if (coords.length > 1) {\n+ if (coords.length == 2) {\n+ coords[2] = null\n+ }\n+ point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2])\n+ } else {\n+ throw \"Bad coordinate string: \" + coordString\n+ }\n+ return point\n+ },\n+ linestring: function(node, ring) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"coordinates\");\n+ var line = null;\n+ if (nodeList.length > 0) {\n+ var coordString = this.getChildValue(nodeList[0]);\n+ coordString = coordString.replace(this.regExes.trimSpace, \"\");\n+ coordString = coordString.replace(this.regExes.trimComma, \",\");\n+ var pointList = coordString.split(this.regExes.splitSpace);\n+ var numPoints = pointList.length;\n+ var points = new Array(numPoints);\n+ var coords, numCoords;\n+ for (var i = 0; i < numPoints; ++i) {\n+ coords = pointList[i].split(\",\");\n+ numCoords = coords.length;\n+ if (numCoords > 1) {\n+ if (coords.length == 2) {\n+ coords[2] = null\n+ }\n+ points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2])\n+ } else {\n+ throw \"Bad LineString point coordinates: \" + pointList[i]\n+ }\n+ }\n+ if (numPoints) {\n+ if (ring) {\n+ line = new OpenLayers.Geometry.LinearRing(points)\n+ } else {\n+ line = new OpenLayers.Geometry.LineString(points)\n+ }\n+ } else {\n+ throw \"Bad LineString coordinates: \" + coordString\n+ }\n+ }\n+ return line\n+ },\n+ polygon: function(node) {\n+ var nodeList = this.getElementsByTagNameNS(node, this.internalns, \"LinearRing\");\n+ var numRings = nodeList.length;\n+ var components = new Array(numRings);\n+ if (numRings > 0) {\n+ var ring;\n+ for (var i = 0, len = nodeList.length; i < len; ++i) {\n+ ring = this.parseGeometry.linestring.apply(this, [nodeList[i], true]);\n+ if (ring) {\n+ components[i] = ring\n+ } else {\n+ throw \"Bad LinearRing geometry: \" + i\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.Polygon(components)\n+ },\n+ multigeometry: function(node) {\n+ var child, parser;\n+ var parts = [];\n+ var children = node.childNodes;\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ var type = child.prefix ? child.nodeName.split(\":\")[1] : child.nodeName;\n+ var parser = this.parseGeometry[type.toLowerCase()];\n+ if (parser) {\n+ parts.push(parser.apply(this, [child]))\n+ }\n+ }\n+ }\n+ return new OpenLayers.Geometry.Collection(parts)\n+ }\n+ },\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+ var edNodes = node.getElementsByTagName(\"ExtendedData\");\n+ if (edNodes.length) {\n+ attributes = this.parseExtendedData(edNodes[0])\n+ }\n+ var child, grandchildren, grandchild;\n+ var children = node.childNodes;\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ grandchildren = child.childNodes;\n+ if (grandchildren.length >= 1 && grandchildren.length <= 3) {\n+ var grandchild;\n+ switch (grandchildren.length) {\n+ case 1:\n+ grandchild = grandchildren[0];\n+ break;\n+ case 2:\n+ var c1 = grandchildren[0];\n+ var c2 = grandchildren[1];\n+ grandchild = c1.nodeType == 3 || c1.nodeType == 4 ? c1 : c2;\n+ break;\n+ case 3:\n+ default:\n+ grandchild = grandchildren[1];\n+ break\n+ }\n+ if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {\n+ var name = child.prefix ? child.nodeName.split(\":\")[1] : child.nodeName;\n+ var value = OpenLayers.Util.getXmlNodeValue(grandchild);\n+ if (value) {\n+ value = value.replace(this.regExes.trimSpace, \"\");\n+ attributes[name] = value\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return attributes\n+ },\n+ parseExtendedData: function(node) {\n+ var attributes = {};\n+ var i, len, data, key;\n+ var dataNodes = node.getElementsByTagName(\"Data\");\n+ for (i = 0, len = dataNodes.length; i < len; i++) {\n+ data = dataNodes[i];\n+ key = data.getAttribute(\"name\");\n+ var ed = {};\n+ var valueNode = data.getElementsByTagName(\"value\");\n+ if (valueNode.length) {\n+ ed[\"value\"] = this.getChildValue(valueNode[0])\n+ }\n+ if (this.kvpAttributes) {\n+ attributes[key] = ed[\"value\"]\n+ } else {\n+ var nameNode = data.getElementsByTagName(\"displayName\");\n+ if (nameNode.length) {\n+ ed[\"displayName\"] = this.getChildValue(nameNode[0])\n+ }\n+ attributes[key] = ed\n+ }\n+ }\n+ var simpleDataNodes = node.getElementsByTagName(\"SimpleData\");\n+ for (i = 0, len = simpleDataNodes.length; i < len; i++) {\n+ var ed = {};\n+ data = simpleDataNodes[i];\n+ key = data.getAttribute(\"name\");\n+ ed[\"value\"] = this.getChildValue(data);\n+ if (this.kvpAttributes) {\n+ attributes[key] = ed[\"value\"]\n+ } else {\n+ ed[\"displayName\"] = key;\n+ attributes[key] = ed\n+ }\n+ }\n+ return attributes\n+ },\n+ parseProperty: function(xmlNode, namespace, tagName) {\n+ var value;\n+ var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);\n+ try {\n+ value = OpenLayers.Util.getXmlNodeValue(nodeList[0])\n+ } catch (e) {\n+ value = null\n+ }\n+ return value\n+ },\n+ write: function(features) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ var kml = this.createElementNS(this.kmlns, \"kml\");\n+ var folder = this.createFolderXML();\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ folder.appendChild(this.createPlacemarkXML(features[i]))\n+ }\n+ kml.appendChild(folder);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [kml])\n+ },\n+ createFolderXML: function() {\n+ var folder = this.createElementNS(this.kmlns, \"Folder\");\n+ if (this.foldersName) {\n+ var folderName = this.createElementNS(this.kmlns, \"name\");\n+ var folderNameText = this.createTextNode(this.foldersName);\n+ folderName.appendChild(folderNameText);\n+ folder.appendChild(folderName)\n+ }\n+ if (this.foldersDesc) {\n+ var folderDesc = this.createElementNS(this.kmlns, \"description\");\n+ var folderDescText = this.createTextNode(this.foldersDesc);\n+ folderDesc.appendChild(folderDescText);\n+ folder.appendChild(folderDesc)\n+ }\n+ return folder\n+ },\n+ createPlacemarkXML: function(feature) {\n+ var placemarkName = this.createElementNS(this.kmlns, \"name\");\n+ var label = feature.style && feature.style.label ? feature.style.label : feature.id;\n+ var name = feature.attributes.name || label;\n+ placemarkName.appendChild(this.createTextNode(name));\n+ var placemarkDesc = this.createElementNS(this.kmlns, \"description\");\n+ var desc = feature.attributes.description || this.placemarksDesc;\n+ placemarkDesc.appendChild(this.createTextNode(desc));\n+ var placemarkNode = this.createElementNS(this.kmlns, \"Placemark\");\n+ if (feature.fid != null) {\n+ placemarkNode.setAttribute(\"id\", feature.fid)\n+ }\n+ placemarkNode.appendChild(placemarkName);\n+ placemarkNode.appendChild(placemarkDesc);\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n+ placemarkNode.appendChild(geometryNode);\n+ if (feature.attributes) {\n+ var edNode = this.buildExtendedData(feature.attributes);\n+ if (edNode) {\n+ placemarkNode.appendChild(edNode)\n+ }\n+ }\n+ return placemarkNode\n+ },\n+ buildGeometryNode: function(geometry) {\n+ var className = geometry.CLASS_NAME;\n+ var type = className.substring(className.lastIndexOf(\".\") + 1);\n+ var builder = this.buildGeometry[type.toLowerCase()];\n+ var node = null;\n+ if (builder) {\n+ node = builder.apply(this, [geometry])\n+ }\n+ return node\n+ },\n+ buildGeometry: {\n+ point: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"Point\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml\n+ },\n+ multipoint: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry])\n+ },\n+ linestring: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"LineString\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml\n+ },\n+ multilinestring: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry])\n+ },\n+ linearring: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"LinearRing\");\n+ kml.appendChild(this.buildCoordinatesNode(geometry));\n+ return kml\n+ },\n+ polygon: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"Polygon\");\n+ var rings = geometry.components;\n+ var ringMember, ringGeom, type;\n+ for (var i = 0, len = rings.length; i < len; ++i) {\n+ type = i == 0 ? \"outerBoundaryIs\" : \"innerBoundaryIs\";\n+ ringMember = this.createElementNS(this.kmlns, type);\n+ ringGeom = this.buildGeometry.linearring.apply(this, [rings[i]]);\n+ ringMember.appendChild(ringGeom);\n+ kml.appendChild(ringMember)\n+ }\n+ return kml\n+ },\n+ multipolygon: function(geometry) {\n+ return this.buildGeometry.collection.apply(this, [geometry])\n+ },\n+ collection: function(geometry) {\n+ var kml = this.createElementNS(this.kmlns, \"MultiGeometry\");\n+ var child;\n+ for (var i = 0, len = geometry.components.length; i < len; ++i) {\n+ child = this.buildGeometryNode.apply(this, [geometry.components[i]]);\n+ if (child) {\n+ kml.appendChild(child)\n+ }\n+ }\n+ return kml\n+ }\n+ },\n+ buildCoordinatesNode: function(geometry) {\n+ var coordinatesNode = this.createElementNS(this.kmlns, \"coordinates\");\n+ var path;\n+ var points = geometry.components;\n+ if (points) {\n+ var point;\n+ var numPoints = points.length;\n+ var parts = new Array(numPoints);\n+ for (var i = 0; i < numPoints; ++i) {\n+ point = points[i];\n+ parts[i] = this.buildCoordinates(point)\n+ }\n+ path = parts.join(\" \")\n+ } else {\n+ path = this.buildCoordinates(geometry)\n+ }\n+ var txtNode = this.createTextNode(path);\n+ coordinatesNode.appendChild(txtNode);\n+ return coordinatesNode\n+ },\n+ buildCoordinates: function(point) {\n+ if (this.internalProjection && this.externalProjection) {\n+ point = point.clone();\n+ point.transform(this.internalProjection, this.externalProjection)\n+ }\n+ return point.x + \",\" + point.y\n+ },\n+ buildExtendedData: function(attributes) {\n+ var extendedData = this.createElementNS(this.kmlns, \"ExtendedData\");\n+ for (var attributeName in attributes) {\n+ if (attributes[attributeName] && attributeName != \"name\" && attributeName != \"description\" && attributeName != \"styleUrl\") {\n+ var data = this.createElementNS(this.kmlns, \"Data\");\n+ data.setAttribute(\"name\", attributeName);\n+ var value = this.createElementNS(this.kmlns, \"value\");\n+ if (typeof attributes[attributeName] == \"object\") {\n+ if (attributes[attributeName].value) {\n+ value.appendChild(this.createTextNode(attributes[attributeName].value))\n+ }\n+ if (attributes[attributeName].displayName) {\n+ var displayName = this.createElementNS(this.kmlns, \"displayName\");\n+ displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName));\n+ data.appendChild(displayName)\n+ }\n+ } else {\n+ value.appendChild(this.createTextNode(attributes[attributeName]))\n+ }\n+ data.appendChild(value);\n+ extendedData.appendChild(data)\n+ }\n+ }\n+ if (this.isSimpleContent(extendedData)) {\n+ return null\n+ } else {\n+ return extendedData\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.KML\"\n+});\n+OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ profile: null,\n+ defaultVersion: \"1.0.0\",\n stringifyOutput: true,\n- CLASS_NAME: \"OpenLayers.Format.XLS\"\n+ namedLayersAsArray: false,\n+ CLASS_NAME: \"OpenLayers.Format.SLD\"\n+});\n+OpenLayers.Format.CSWGetDomain = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetDomain.DEFAULTS);\n+ var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetDomain version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n+ version: \"2.0.2\"\n+};\n+OpenLayers.Format.CSWGetRecords = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetRecords.DEFAULTS);\n+ var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetRecords version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n+ version: \"2.0.2\"\n+};\n+OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+ defaultVersion: \"1.1.1\",\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n });\n OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {\n schemaLocation: \"http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd\",\n initialize: function(options) {\n OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options])\n },\n readers: {\n@@ -22179,285 +18502,170 @@\n }\n node.appendChild(child)\n }\n return node\n },\n CLASS_NAME: \"OpenLayers.Format.Filter.v1_0_0\"\n });\n-OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(OpenLayers.Format.WMSDescribeLayer, {\n+ initialize: function(options) {\n+ OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, [options])\n+ },\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n+ }\n+ var root = data.documentElement;\n+ var children = root.childNodes;\n+ var describelayer = {\n+ layerDescriptions: []\n+ };\n+ var childNode, nodeName;\n+ for (var i = 0; i < children.length; ++i) {\n+ childNode = children[i];\n+ nodeName = childNode.nodeName;\n+ if (nodeName == \"LayerDescription\") {\n+ var layerName = childNode.getAttribute(\"name\");\n+ var owsType = \"\";\n+ var owsURL = \"\";\n+ var typeName = \"\";\n+ if (childNode.getAttribute(\"owsType\")) {\n+ owsType = childNode.getAttribute(\"owsType\");\n+ owsURL = childNode.getAttribute(\"owsURL\")\n+ } else {\n+ if (childNode.getAttribute(\"wfs\") != \"\") {\n+ owsType = \"WFS\";\n+ owsURL = childNode.getAttribute(\"wfs\")\n+ } else if (childNode.getAttribute(\"wcs\") != \"\") {\n+ owsType = \"WCS\";\n+ owsURL = childNode.getAttribute(\"wcs\")\n+ }\n+ }\n+ var query = childNode.getElementsByTagName(\"Query\");\n+ if (query.length > 0) {\n+ typeName = query[0].getAttribute(\"typeName\");\n+ if (!typeName) {\n+ typeName = query[0].getAttribute(\"typename\")\n+ }\n+ }\n+ var layerDescription = {\n+ layerName: layerName,\n+ owsType: owsType,\n+ owsURL: owsURL,\n+ typeName: typeName\n+ };\n+ describelayer.layerDescriptions.push(layerDescription);\n+ describelayer.length = describelayer.layerDescriptions.length;\n+ describelayer[describelayer.length - 1] = layerDescription\n+ } else if (nodeName == \"ServiceException\") {\n+ var parser = new OpenLayers.Format.OGCExceptionReport;\n+ return {\n+ error: parser.read(data)\n+ }\n+ }\n+ }\n+ return describelayer\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n+});\n+OpenLayers.Format.WMSDescribeLayer.v1_1_0 = OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n+OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.SOSCapabilities, {\n namespaces: {\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\",\n- dc: \"http://purl.org/dc/elements/1.1/\",\n- dct: \"http://purl.org/dc/terms/\",\n- gmd: \"http://www.isotc211.org/2005/gmd\",\n- geonet: \"http://www.fao.org/geonetwork\",\n- ogc: \"http://www.opengis.net/ogc\",\n- ows: \"http://www.opengis.net/ows\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ sos: \"http://www.opengis.net/sos/1.0\",\n+ gml: \"http://www.opengis.net/gml\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n },\n- defaultPrefix: \"csw\",\n- version: \"2.0.2\",\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n- requestId: null,\n- resultType: null,\n- outputFormat: null,\n- outputSchema: null,\n- startPosition: null,\n- maxRecords: null,\n- DistributedSearch: null,\n- ResponseHandler: null,\n- Query: null,\n regExes: {\n trimSpace: /^\\s*|\\s*$/g,\n removeSpace: /\\s*/g,\n splitSpace: /\\s+/,\n trimComma: /\\s*,\\s*/g\n },\n initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options\n },\n read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n }\n if (data && data.nodeType == 9) {\n data = data.documentElement\n }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities\n },\n readers: {\n- csw: {\n- GetRecordsResponse: function(node, obj) {\n- obj.records = [];\n- this.readChildNodes(node, obj);\n- var version = this.getAttributeNS(node, \"\", \"version\");\n- if (version != \"\") {\n- obj.version = version\n- }\n- },\n- RequestId: function(node, obj) {\n- obj.RequestId = this.getChildValue(node)\n+ gml: OpenLayers.Util.applyDefaults({\n+ name: function(node, obj) {\n+ obj.name = this.getChildValue(node)\n },\n- SearchStatus: function(node, obj) {\n- obj.SearchStatus = {};\n- var timestamp = this.getAttributeNS(node, \"\", \"timestamp\");\n- if (timestamp != \"\") {\n- obj.SearchStatus.timestamp = timestamp\n- }\n+ TimePeriod: function(node, obj) {\n+ obj.timePeriod = {};\n+ this.readChildNodes(node, obj.timePeriod)\n },\n- SearchResults: function(node, obj) {\n- this.readChildNodes(node, obj);\n- var attrs = node.attributes;\n- var SearchResults = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- if (attrs[i].name == \"numberOfRecordsMatched\" || attrs[i].name == \"numberOfRecordsReturned\" || attrs[i].name == \"nextRecord\") {\n- SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue)\n- } else {\n- SearchResults[attrs[i].name] = attrs[i].nodeValue\n- }\n- }\n- obj.SearchResults = SearchResults\n+ beginPosition: function(node, timePeriod) {\n+ timePeriod.beginPosition = this.getChildValue(node)\n },\n- SummaryRecord: function(node, obj) {\n- var record = {\n- type: \"SummaryRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record)\n+ endPosition: function(node, timePeriod) {\n+ timePeriod.endPosition = this.getChildValue(node)\n+ }\n+ }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n+ sos: {\n+ Capabilities: function(node, obj) {\n+ this.readChildNodes(node, obj)\n },\n- BriefRecord: function(node, obj) {\n- var record = {\n- type: \"BriefRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record)\n+ Contents: function(node, obj) {\n+ obj.contents = {};\n+ this.readChildNodes(node, obj.contents)\n },\n- DCMIRecord: function(node, obj) {\n- var record = {\n- type: \"DCMIRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record)\n+ ObservationOfferingList: function(node, contents) {\n+ contents.offeringList = {};\n+ this.readChildNodes(node, contents.offeringList)\n },\n- Record: function(node, obj) {\n- var record = {\n- type: \"Record\"\n+ ObservationOffering: function(node, offeringList) {\n+ var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n+ offeringList[id] = {\n+ procedures: [],\n+ observedProperties: [],\n+ featureOfInterestIds: [],\n+ responseFormats: [],\n+ resultModels: [],\n+ responseModes: []\n };\n- this.readChildNodes(node, record);\n- obj.records.push(record)\n+ this.readChildNodes(node, offeringList[id])\n },\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- obj[name] = this.getChildValue(node)\n- }\n- },\n- geonet: {\n- info: function(node, obj) {\n- var gninfo = {};\n- this.readChildNodes(node, gninfo);\n- obj.gninfo = gninfo\n- }\n- },\n- dc: {\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!OpenLayers.Util.isArray(obj[name])) {\n- obj[name] = []\n- }\n- var dc_element = {};\n- var attrs = node.attributes;\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- dc_element[attrs[i].name] = attrs[i].nodeValue\n- }\n- dc_element.value = this.getChildValue(node);\n- if (dc_element.value != \"\") {\n- obj[name].push(dc_element)\n- }\n- }\n- },\n- dct: {\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!OpenLayers.Util.isArray(obj[name])) {\n- obj[name] = []\n- }\n- obj[name].push(this.getChildValue(node))\n- }\n- },\n- ows: OpenLayers.Util.applyDefaults({\n- BoundingBox: function(node, obj) {\n- if (obj.bounds) {\n- obj.BoundingBox = [{\n- crs: obj.projection,\n- value: [obj.bounds.left, obj.bounds.bottom, obj.bounds.right, obj.bounds.top]\n- }];\n- delete obj.projection;\n- delete obj.bounds\n- }\n- OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"][\"BoundingBox\"].apply(this, arguments)\n- }\n- }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"])\n- },\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetRecords\", options);\n- node.setAttribute(\"xmlns:gmd\", this.namespaces.gmd);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node])\n- },\n- writers: {\n- csw: {\n- GetRecords: function(options) {\n- if (!options) {\n- options = {}\n- }\n- var node = this.createElementNSPlus(\"csw:GetRecords\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version,\n- requestId: options.requestId || this.requestId,\n- resultType: options.resultType || this.resultType,\n- outputFormat: options.outputFormat || this.outputFormat,\n- outputSchema: options.outputSchema || this.outputSchema,\n- startPosition: options.startPosition || this.startPosition,\n- maxRecords: options.maxRecords || this.maxRecords\n- }\n- });\n- if (options.DistributedSearch || this.DistributedSearch) {\n- this.writeNode(\"csw:DistributedSearch\", options.DistributedSearch || this.DistributedSearch, node)\n- }\n- var ResponseHandler = options.ResponseHandler || this.ResponseHandler;\n- if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {\n- for (var i = 0, len = ResponseHandler.length; i < len; i++) {\n- this.writeNode(\"csw:ResponseHandler\", ResponseHandler[i], node)\n- }\n- }\n- this.writeNode(\"Query\", options.Query || this.Query, node);\n- return node\n+ time: function(node, offering) {\n+ offering.time = {};\n+ this.readChildNodes(node, offering.time)\n },\n- DistributedSearch: function(options) {\n- var node = this.createElementNSPlus(\"csw:DistributedSearch\", {\n- attributes: {\n- hopCount: options.hopCount\n- }\n- });\n- return node\n+ procedure: function(node, offering) {\n+ offering.procedures.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n },\n- ResponseHandler: function(options) {\n- var node = this.createElementNSPlus(\"csw:ResponseHandler\", {\n- value: options.value\n- });\n- return node\n+ observedProperty: function(node, offering) {\n+ offering.observedProperties.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n },\n- Query: function(options) {\n- if (!options) {\n- options = {}\n- }\n- var node = this.createElementNSPlus(\"csw:Query\", {\n- attributes: {\n- typeNames: options.typeNames || \"csw:Record\"\n- }\n- });\n- var ElementName = options.ElementName;\n- if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {\n- for (var i = 0, len = ElementName.length; i < len; i++) {\n- this.writeNode(\"csw:ElementName\", ElementName[i], node)\n- }\n- } else {\n- this.writeNode(\"csw:ElementSetName\", options.ElementSetName || {\n- value: \"summary\"\n- }, node)\n- }\n- if (options.Constraint) {\n- this.writeNode(\"csw:Constraint\", options.Constraint, node)\n- }\n- if (options.SortBy) {\n- this.writeNode(\"ogc:SortBy\", options.SortBy, node)\n- }\n- return node\n+ featureOfInterest: function(node, offering) {\n+ offering.featureOfInterestIds.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n },\n- ElementName: function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementName\", {\n- value: options.value\n- });\n- return node\n+ responseFormat: function(node, offering) {\n+ offering.responseFormats.push(this.getChildValue(node))\n },\n- ElementSetName: function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementSetName\", {\n- attributes: {\n- typeNames: options.typeNames\n- },\n- value: options.value\n- });\n- return node\n+ resultModel: function(node, offering) {\n+ offering.resultModels.push(this.getChildValue(node))\n },\n- Constraint: function(options) {\n- var node = this.createElementNSPlus(\"csw:Constraint\", {\n- attributes: {\n- version: options.version\n- }\n- });\n- if (options.Filter) {\n- var format = new OpenLayers.Format.Filter({\n- version: options.version\n- });\n- node.appendChild(format.write(options.Filter))\n- } else if (options.CqlText) {\n- var child = this.createElementNSPlus(\"CqlText\", {\n- value: options.CqlText.value\n- });\n- node.appendChild(child)\n- }\n- return node\n+ responseMode: function(node, offering) {\n+ offering.responseModes.push(this.getChildValue(node))\n }\n },\n- ogc: OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n+ ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n- CLASS_NAME: \"OpenLayers.Format.CSWGetRecords.v2_0_2\"\n+ CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n });\n OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {\n namespaces: {\n sld: \"http://www.opengis.net/sld\",\n ogc: \"http://www.opengis.net/ogc\",\n gml: \"http://www.opengis.net/gml\",\n xlink: \"http://www.w3.org/1999/xlink\",\n@@ -23849,14 +20057,451 @@\n }, OpenLayers.Format.GML.v2.prototype.writers.gml),\n ows: OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows,\n sld: OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld,\n feature: OpenLayers.Format.GML.v2.prototype.writers.feature\n },\n CLASS_NAME: \"OpenLayers.Format.OWSContext.v0_3_1\"\n });\n+OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.XML, {\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+ regExes: {\n+ trimSpace: /^\\s*|\\s*$/g,\n+ removeSpace: /\\s*/g,\n+ splitSpace: /\\s+/,\n+ trimComma: /\\s*,\\s*/g\n+ },\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n+ },\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities\n+ },\n+ readers: {\n+ wps: {\n+ Capabilities: function(node, obj) {\n+ this.readChildNodes(node, obj)\n+ },\n+ ProcessOfferings: function(node, obj) {\n+ obj.processOfferings = {};\n+ this.readChildNodes(node, obj.processOfferings)\n+ },\n+ Process: function(node, processOfferings) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var process = {\n+ processVersion: processVersion\n+ };\n+ this.readChildNodes(node, process);\n+ processOfferings[process.identifier] = process\n+ },\n+ Languages: function(node, obj) {\n+ obj.languages = [];\n+ this.readChildNodes(node, obj.languages)\n+ },\n+ Default: function(node, languages) {\n+ var language = {\n+ isDefault: true\n+ };\n+ this.readChildNodes(node, language);\n+ languages.push(language)\n+ },\n+ Supported: function(node, languages) {\n+ var language = {};\n+ this.readChildNodes(node, language);\n+ languages.push(language)\n+ }\n+ },\n+ ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n+});\n+OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n+ version: \"1.0.0\",\n+ srsNameInQuery: false,\n+ schemaLocations: {\n+ wfs: \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n+ },\n+ initialize: function(options) {\n+ OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n+ OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options])\n+ },\n+ readNode: function(node, obj, first) {\n+ return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments)\n+ },\n+ readers: {\n+ wfs: OpenLayers.Util.applyDefaults({\n+ WFS_TransactionResponse: function(node, obj) {\n+ obj.insertIds = [];\n+ obj.success = false;\n+ this.readChildNodes(node, obj)\n+ },\n+ InsertResult: function(node, container) {\n+ var obj = {\n+ fids: []\n+ };\n+ this.readChildNodes(node, obj);\n+ container.insertIds = container.insertIds.concat(obj.fids)\n+ },\n+ TransactionResult: function(node, obj) {\n+ this.readChildNodes(node, obj)\n+ },\n+ Status: function(node, obj) {\n+ this.readChildNodes(node, obj)\n+ },\n+ SUCCESS: function(node, obj) {\n+ obj.success = true\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n+ gml: OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n+ feature: OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n+ ogc: OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n+ },\n+ writers: {\n+ wfs: OpenLayers.Util.applyDefaults({\n+ Query: function(options) {\n+ options = OpenLayers.Util.extend({\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ featureType: this.featureType,\n+ srsName: this.srsName,\n+ srsNameInQuery: this.srsNameInQuery\n+ }, options);\n+ var prefix = options.featurePrefix;\n+ var node = this.createElementNSPlus(\"wfs:Query\", {\n+ attributes: {\n+ typeName: (prefix ? prefix + \":\" : \"\") + options.featureType\n+ }\n+ });\n+ if (options.srsNameInQuery && options.srsName) {\n+ node.setAttribute(\"srsName\", options.srsName)\n+ }\n+ if (options.featureNS) {\n+ node.setAttribute(\"xmlns:\" + prefix, options.featureNS)\n+ }\n+ if (options.propertyNames) {\n+ for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n+ this.writeNode(\"ogc:PropertyName\", {\n+ property: options.propertyNames[i]\n+ }, node)\n+ }\n+ }\n+ if (options.filter) {\n+ this.setFilterProperty(options.filter);\n+ this.writeNode(\"ogc:Filter\", options.filter, node)\n+ }\n+ return node\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n+ gml: OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n+ feature: OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n+ ogc: OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n+});\n+OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1_1_0, {\n+ version: \"1.0.0\",\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wmts: \"http://www.opengis.net/wmts/1.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+ yx: null,\n+ defaultPrefix: \"wmts\",\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options;\n+ var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n+ this.yx = OpenLayers.Util.extend(yx, this.yx)\n+ },\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ capabilities.version = this.version;\n+ return capabilities\n+ },\n+ readers: {\n+ wmts: {\n+ Capabilities: function(node, obj) {\n+ this.readChildNodes(node, obj)\n+ },\n+ Contents: function(node, obj) {\n+ obj.contents = {};\n+ obj.contents.layers = [];\n+ obj.contents.tileMatrixSets = {};\n+ this.readChildNodes(node, obj.contents)\n+ },\n+ Layer: function(node, obj) {\n+ var layer = {\n+ styles: [],\n+ formats: [],\n+ dimensions: [],\n+ tileMatrixSetLinks: []\n+ };\n+ layer.layers = [];\n+ this.readChildNodes(node, layer);\n+ obj.layers.push(layer)\n+ },\n+ Style: function(node, obj) {\n+ var style = {};\n+ style.isDefault = node.getAttribute(\"isDefault\") === \"true\";\n+ this.readChildNodes(node, style);\n+ obj.styles.push(style)\n+ },\n+ Format: function(node, obj) {\n+ obj.formats.push(this.getChildValue(node))\n+ },\n+ TileMatrixSetLink: function(node, obj) {\n+ var tileMatrixSetLink = {};\n+ this.readChildNodes(node, tileMatrixSetLink);\n+ obj.tileMatrixSetLinks.push(tileMatrixSetLink)\n+ },\n+ TileMatrixSet: function(node, obj) {\n+ if (obj.layers) {\n+ var tileMatrixSet = {\n+ matrixIds: []\n+ };\n+ this.readChildNodes(node, tileMatrixSet);\n+ obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet\n+ } else {\n+ obj.tileMatrixSet = this.getChildValue(node)\n+ }\n+ },\n+ TileMatrix: function(node, obj) {\n+ var tileMatrix = {\n+ supportedCRS: obj.supportedCRS\n+ };\n+ this.readChildNodes(node, tileMatrix);\n+ obj.matrixIds.push(tileMatrix)\n+ },\n+ ScaleDenominator: function(node, obj) {\n+ obj.scaleDenominator = parseFloat(this.getChildValue(node))\n+ },\n+ TopLeftCorner: function(node, obj) {\n+ var topLeftCorner = this.getChildValue(node);\n+ var coords = topLeftCorner.split(\" \");\n+ var yx;\n+ if (obj.supportedCRS) {\n+ var crs = obj.supportedCRS.replace(/urn:ogc:def:crs:(\\w+):.+:(\\w+)$/, \"urn:ogc:def:crs:$1::$2\");\n+ yx = !!this.yx[crs]\n+ }\n+ if (yx) {\n+ obj.topLeftCorner = new OpenLayers.LonLat(coords[1], coords[0])\n+ } else {\n+ obj.topLeftCorner = new OpenLayers.LonLat(coords[0], coords[1])\n+ }\n+ },\n+ TileWidth: function(node, obj) {\n+ obj.tileWidth = parseInt(this.getChildValue(node))\n+ },\n+ TileHeight: function(node, obj) {\n+ obj.tileHeight = parseInt(this.getChildValue(node))\n+ },\n+ MatrixWidth: function(node, obj) {\n+ obj.matrixWidth = parseInt(this.getChildValue(node))\n+ },\n+ MatrixHeight: function(node, obj) {\n+ obj.matrixHeight = parseInt(this.getChildValue(node))\n+ },\n+ ResourceURL: function(node, obj) {\n+ obj.resourceUrl = obj.resourceUrl || {};\n+ var resourceType = node.getAttribute(\"resourceType\");\n+ if (!obj.resourceUrls) {\n+ obj.resourceUrls = []\n+ }\n+ var resourceUrl = obj.resourceUrl[resourceType] = {\n+ format: node.getAttribute(\"format\"),\n+ template: node.getAttribute(\"template\"),\n+ resourceType: resourceType\n+ };\n+ obj.resourceUrls.push(resourceUrl)\n+ },\n+ WSDL: function(node, obj) {\n+ obj.wsdl = {};\n+ obj.wsdl.href = node.getAttribute(\"xlink:href\")\n+ },\n+ ServiceMetadataURL: function(node, obj) {\n+ obj.serviceMetadataUrl = {};\n+ obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\")\n+ },\n+ LegendURL: function(node, obj) {\n+ obj.legend = {};\n+ obj.legend.href = node.getAttribute(\"xlink:href\");\n+ obj.legend.format = node.getAttribute(\"format\")\n+ },\n+ Dimension: function(node, obj) {\n+ var dimension = {\n+ values: []\n+ };\n+ this.readChildNodes(node, dimension);\n+ obj.dimensions.push(dimension)\n+ },\n+ Default: function(node, obj) {\n+ obj[\"default\"] = this.getChildValue(node)\n+ },\n+ Value: function(node, obj) {\n+ obj.values.push(this.getChildValue(node))\n+ }\n+ },\n+ ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+});\n+OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n+ namespaces: {\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n+ },\n+ defaultPrefix: \"csw\",\n+ version: \"2.0.2\",\n+ schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n+ PropertyName: null,\n+ ParameterName: null,\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement\n+ }\n+ var obj = {};\n+ this.readNode(data, obj);\n+ return obj\n+ },\n+ readers: {\n+ csw: {\n+ GetDomainResponse: function(node, obj) {\n+ this.readChildNodes(node, obj)\n+ },\n+ DomainValues: function(node, obj) {\n+ if (!OpenLayers.Util.isArray(obj.DomainValues)) {\n+ obj.DomainValues = []\n+ }\n+ var attrs = node.attributes;\n+ var domainValue = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ domainValue[attrs[i].name] = attrs[i].nodeValue\n+ }\n+ this.readChildNodes(node, domainValue);\n+ obj.DomainValues.push(domainValue)\n+ },\n+ PropertyName: function(node, obj) {\n+ obj.PropertyName = this.getChildValue(node)\n+ },\n+ ParameterName: function(node, obj) {\n+ obj.ParameterName = this.getChildValue(node)\n+ },\n+ ListOfValues: function(node, obj) {\n+ if (!OpenLayers.Util.isArray(obj.ListOfValues)) {\n+ obj.ListOfValues = []\n+ }\n+ this.readChildNodes(node, obj.ListOfValues)\n+ },\n+ Value: function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.push({\n+ Value: value\n+ })\n+ },\n+ ConceptualScheme: function(node, obj) {\n+ obj.ConceptualScheme = {};\n+ this.readChildNodes(node, obj.ConceptualScheme)\n+ },\n+ Name: function(node, obj) {\n+ obj.Name = this.getChildValue(node)\n+ },\n+ Document: function(node, obj) {\n+ obj.Document = this.getChildValue(node)\n+ },\n+ Authority: function(node, obj) {\n+ obj.Authority = this.getChildValue(node)\n+ },\n+ RangeOfValues: function(node, obj) {\n+ obj.RangeOfValues = {};\n+ this.readChildNodes(node, obj.RangeOfValues)\n+ },\n+ MinValue: function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MinValue = value\n+ },\n+ MaxValue: function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MaxValue = value\n+ }\n+ }\n+ },\n+ write: function(options) {\n+ var node = this.writeNode(\"csw:GetDomain\", options);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node])\n+ },\n+ writers: {\n+ csw: {\n+ GetDomain: function(options) {\n+ var node = this.createElementNSPlus(\"csw:GetDomain\", {\n+ attributes: {\n+ service: \"CSW\",\n+ version: this.version\n+ }\n+ });\n+ if (options.PropertyName || this.PropertyName) {\n+ this.writeNode(\"csw:PropertyName\", options.PropertyName || this.PropertyName, node)\n+ } else if (options.ParameterName || this.ParameterName) {\n+ this.writeNode(\"csw:ParameterName\", options.ParameterName || this.ParameterName, node)\n+ }\n+ this.readChildNodes(node, options);\n+ return node\n+ },\n+ PropertyName: function(value) {\n+ var node = this.createElementNSPlus(\"csw:PropertyName\", {\n+ value: value\n+ });\n+ return node\n+ },\n+ ParameterName: function(value) {\n+ var node = this.createElementNSPlus(\"csw:ParameterName\", {\n+ value: value\n+ });\n+ return node\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n+});\n OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n namespaces: {\n xls: \"http://www.opengis.net/xls\",\n gml: \"http://www.opengis.net/gml\",\n xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n },\n regExes: {\n@@ -24251,696 +20896,182 @@\n }\n }\n }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers[\"wcs\"]),\n ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_1_0\"\n });\n-OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(OpenLayers.Format.SLD.v1_0_0, {\n- version: \"1.0.0\",\n- profile: \"GeoServer\",\n- readers: OpenLayers.Util.applyDefaults({\n- sld: OpenLayers.Util.applyDefaults({\n- Priority: function(node, obj) {\n- var value = this.readers.ogc._expression.call(this, node);\n- if (value) {\n- obj.priority = value\n- }\n- },\n- VendorOption: function(node, obj) {\n- if (!obj.vendorOptions) {\n- obj.vendorOptions = {}\n- }\n- obj.vendorOptions[node.getAttribute(\"name\")] = this.getChildValue(node)\n- },\n- TextSymbolizer: function(node, rule) {\n- OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);\n- var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer[\"Text\"];\n- if (symbolizer.graphic === undefined) {\n- symbolizer.graphic = false\n- }\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers),\n- writers: OpenLayers.Util.applyDefaults({\n- sld: OpenLayers.Util.applyDefaults({\n- Priority: function(priority) {\n- return this.writers.sld._OGCExpression.call(this, \"sld:Priority\", priority)\n- },\n- VendorOption: function(option) {\n- return this.createElementNSPlus(\"sld:VendorOption\", {\n- attributes: {\n- name: option.name\n- },\n- value: option.value\n- })\n- },\n- TextSymbolizer: function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"TextSymbolizer\"].apply(this, arguments);\n- if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {\n- this.writeNode(\"Graphic\", symbolizer, node)\n- }\n- if (\"priority\" in symbolizer) {\n- this.writeNode(\"Priority\", symbolizer.priority, node)\n- }\n- return this.addVendorOptions(node, symbolizer)\n- },\n- PointSymbolizer: function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PointSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer)\n- },\n- LineSymbolizer: function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"LineSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer)\n- },\n- PolygonSymbolizer: function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PolygonSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer)\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers),\n- addVendorOptions: function(node, symbolizer) {\n- var options = symbolizer.vendorOptions;\n- if (options) {\n- for (var key in symbolizer.vendorOptions) {\n- this.writeNode(\"VendorOption\", {\n- name: key,\n- value: symbolizer.vendorOptions[key]\n- }, node)\n- }\n- }\n- return node\n- },\n- CLASS_NAME: \"OpenLayers.Format.SLD.v1_0_0_GeoServer\"\n-});\n-OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n namespaces: {\n+ wfs: \"http://www.opengis.net/wfs\",\n xlink: \"http://www.w3.org/1999/xlink\",\n xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n+ ows: \"http://www.opengis.net/ows\"\n },\n- defaultPrefix: \"csw\",\n- version: \"2.0.2\",\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n- PropertyName: null,\n- ParameterName: null,\n+ errorProperty: \"featureTypeList\",\n+ defaultPrefix: \"wfs\",\n read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n }\n+ var raw = data;\n if (data && data.nodeType == 9) {\n data = data.documentElement\n }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities\n },\n readers: {\n- csw: {\n- GetDomainResponse: function(node, obj) {\n+ wfs: {\n+ WFS_Capabilities: function(node, obj) {\n this.readChildNodes(node, obj)\n },\n- DomainValues: function(node, obj) {\n- if (!OpenLayers.Util.isArray(obj.DomainValues)) {\n- obj.DomainValues = []\n- }\n- var attrs = node.attributes;\n- var domainValue = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- domainValue[attrs[i].name] = attrs[i].nodeValue\n- }\n- this.readChildNodes(node, domainValue);\n- obj.DomainValues.push(domainValue)\n- },\n- PropertyName: function(node, obj) {\n- obj.PropertyName = this.getChildValue(node)\n- },\n- ParameterName: function(node, obj) {\n- obj.ParameterName = this.getChildValue(node)\n- },\n- ListOfValues: function(node, obj) {\n- if (!OpenLayers.Util.isArray(obj.ListOfValues)) {\n- obj.ListOfValues = []\n- }\n- this.readChildNodes(node, obj.ListOfValues)\n- },\n- Value: function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue\n- }\n- value.value = this.getChildValue(node);\n- obj.push({\n- Value: value\n- })\n+ FeatureTypeList: function(node, request) {\n+ request.featureTypeList = {\n+ featureTypes: []\n+ };\n+ this.readChildNodes(node, request.featureTypeList)\n },\n- ConceptualScheme: function(node, obj) {\n- obj.ConceptualScheme = {};\n- this.readChildNodes(node, obj.ConceptualScheme)\n+ FeatureType: function(node, featureTypeList) {\n+ var featureType = {};\n+ this.readChildNodes(node, featureType);\n+ featureTypeList.featureTypes.push(featureType)\n },\n Name: function(node, obj) {\n- obj.Name = this.getChildValue(node)\n- },\n- Document: function(node, obj) {\n- obj.Document = this.getChildValue(node)\n- },\n- Authority: function(node, obj) {\n- obj.Authority = this.getChildValue(node)\n- },\n- RangeOfValues: function(node, obj) {\n- obj.RangeOfValues = {};\n- this.readChildNodes(node, obj.RangeOfValues)\n- },\n- MinValue: function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue\n- }\n- value.value = this.getChildValue(node);\n- obj.MinValue = value\n- },\n- MaxValue: function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue\n- }\n- value.value = this.getChildValue(node);\n- obj.MaxValue = value\n- }\n- }\n- },\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetDomain\", options);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node])\n- },\n- writers: {\n- csw: {\n- GetDomain: function(options) {\n- var node = this.createElementNSPlus(\"csw:GetDomain\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version\n+ var name = this.getChildValue(node);\n+ if (name) {\n+ var parts = name.split(\":\");\n+ obj.name = parts.pop();\n+ if (parts.length > 0) {\n+ obj.featureNS = this.lookupNamespaceURI(node, parts[0])\n }\n- });\n- if (options.PropertyName || this.PropertyName) {\n- this.writeNode(\"csw:PropertyName\", options.PropertyName || this.PropertyName, node)\n- } else if (options.ParameterName || this.ParameterName) {\n- this.writeNode(\"csw:ParameterName\", options.ParameterName || this.ParameterName, node)\n }\n- this.readChildNodes(node, options);\n- return node\n },\n- PropertyName: function(value) {\n- var node = this.createElementNSPlus(\"csw:PropertyName\", {\n- value: value\n- });\n- return node\n+ Title: function(node, obj) {\n+ var title = this.getChildValue(node);\n+ if (title) {\n+ obj.title = title\n+ }\n },\n- ParameterName: function(value) {\n- var node = this.createElementNSPlus(\"csw:ParameterName\", {\n- value: value\n- });\n- return node\n+ Abstract: function(node, obj) {\n+ var abst = this.getChildValue(node);\n+ if (abst) {\n+ obj[\"abstract\"] = abst\n+ }\n }\n }\n },\n- CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1\"\n });\n-OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1_1_0, {\n- version: \"1.0.0\",\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wmts: \"http://www.opengis.net/wmts/1.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n- yx: null,\n- defaultPrefix: \"wmts\",\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options;\n- var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n- this.yx = OpenLayers.Util.extend(yx, this.yx)\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- capabilities.version = this.version;\n- return capabilities\n- },\n+OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {\n readers: {\n- wmts: {\n- Capabilities: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- Contents: function(node, obj) {\n- obj.contents = {};\n- obj.contents.layers = [];\n- obj.contents.tileMatrixSets = {};\n- this.readChildNodes(node, obj.contents)\n- },\n- Layer: function(node, obj) {\n- var layer = {\n- styles: [],\n- formats: [],\n- dimensions: [],\n- tileMatrixSetLinks: []\n- };\n- layer.layers = [];\n- this.readChildNodes(node, layer);\n- obj.layers.push(layer)\n- },\n- Style: function(node, obj) {\n- var style = {};\n- style.isDefault = node.getAttribute(\"isDefault\") === \"true\";\n- this.readChildNodes(node, style);\n- obj.styles.push(style)\n- },\n- Format: function(node, obj) {\n- obj.formats.push(this.getChildValue(node))\n- },\n- TileMatrixSetLink: function(node, obj) {\n- var tileMatrixSetLink = {};\n- this.readChildNodes(node, tileMatrixSetLink);\n- obj.tileMatrixSetLinks.push(tileMatrixSetLink)\n+ wfs: OpenLayers.Util.applyDefaults({\n+ Service: function(node, capabilities) {\n+ capabilities.service = {};\n+ this.readChildNodes(node, capabilities.service)\n },\n- TileMatrixSet: function(node, obj) {\n- if (obj.layers) {\n- var tileMatrixSet = {\n- matrixIds: []\n- };\n- this.readChildNodes(node, tileMatrixSet);\n- obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet\n- } else {\n- obj.tileMatrixSet = this.getChildValue(node)\n+ Fees: function(node, service) {\n+ var fees = this.getChildValue(node);\n+ if (fees && fees.toLowerCase() != \"none\") {\n+ service.fees = fees\n }\n },\n- TileMatrix: function(node, obj) {\n- var tileMatrix = {\n- supportedCRS: obj.supportedCRS\n- };\n- this.readChildNodes(node, tileMatrix);\n- obj.matrixIds.push(tileMatrix)\n- },\n- ScaleDenominator: function(node, obj) {\n- obj.scaleDenominator = parseFloat(this.getChildValue(node))\n- },\n- TopLeftCorner: function(node, obj) {\n- var topLeftCorner = this.getChildValue(node);\n- var coords = topLeftCorner.split(\" \");\n- var yx;\n- if (obj.supportedCRS) {\n- var crs = obj.supportedCRS.replace(/urn:ogc:def:crs:(\\w+):.+:(\\w+)$/, \"urn:ogc:def:crs:$1::$2\");\n- yx = !!this.yx[crs]\n- }\n- if (yx) {\n- obj.topLeftCorner = new OpenLayers.LonLat(coords[1], coords[0])\n- } else {\n- obj.topLeftCorner = new OpenLayers.LonLat(coords[0], coords[1])\n+ AccessConstraints: function(node, service) {\n+ var constraints = this.getChildValue(node);\n+ if (constraints && constraints.toLowerCase() != \"none\") {\n+ service.accessConstraints = constraints\n }\n },\n- TileWidth: function(node, obj) {\n- obj.tileWidth = parseInt(this.getChildValue(node))\n+ OnlineResource: function(node, service) {\n+ var onlineResource = this.getChildValue(node);\n+ if (onlineResource && onlineResource.toLowerCase() != \"none\") {\n+ service.onlineResource = onlineResource\n+ }\n },\n- TileHeight: function(node, obj) {\n- obj.tileHeight = parseInt(this.getChildValue(node))\n+ Keywords: function(node, service) {\n+ var keywords = this.getChildValue(node);\n+ if (keywords && keywords.toLowerCase() != \"none\") {\n+ service.keywords = keywords.split(\", \")\n+ }\n },\n- MatrixWidth: function(node, obj) {\n- obj.matrixWidth = parseInt(this.getChildValue(node))\n+ Capability: function(node, capabilities) {\n+ capabilities.capability = {};\n+ this.readChildNodes(node, capabilities.capability)\n },\n- MatrixHeight: function(node, obj) {\n- obj.matrixHeight = parseInt(this.getChildValue(node))\n+ Request: function(node, obj) {\n+ obj.request = {};\n+ this.readChildNodes(node, obj.request)\n },\n- ResourceURL: function(node, obj) {\n- obj.resourceUrl = obj.resourceUrl || {};\n- var resourceType = node.getAttribute(\"resourceType\");\n- if (!obj.resourceUrls) {\n- obj.resourceUrls = []\n- }\n- var resourceUrl = obj.resourceUrl[resourceType] = {\n- format: node.getAttribute(\"format\"),\n- template: node.getAttribute(\"template\"),\n- resourceType: resourceType\n+ GetFeature: function(node, request) {\n+ request.getfeature = {\n+ href: {},\n+ formats: []\n };\n- obj.resourceUrls.push(resourceUrl)\n+ this.readChildNodes(node, request.getfeature)\n },\n- WSDL: function(node, obj) {\n- obj.wsdl = {};\n- obj.wsdl.href = node.getAttribute(\"xlink:href\")\n+ ResultFormat: function(node, obj) {\n+ var children = node.childNodes;\n+ var childNode;\n+ for (var i = 0; i < children.length; i++) {\n+ childNode = children[i];\n+ if (childNode.nodeType == 1) {\n+ obj.formats.push(childNode.nodeName)\n+ }\n+ }\n },\n- ServiceMetadataURL: function(node, obj) {\n- obj.serviceMetadataUrl = {};\n- obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\")\n+ DCPType: function(node, obj) {\n+ this.readChildNodes(node, obj)\n },\n- LegendURL: function(node, obj) {\n- obj.legend = {};\n- obj.legend.href = node.getAttribute(\"xlink:href\");\n- obj.legend.format = node.getAttribute(\"format\")\n+ HTTP: function(node, obj) {\n+ this.readChildNodes(node, obj.href)\n },\n- Dimension: function(node, obj) {\n- var dimension = {\n- values: []\n- };\n- this.readChildNodes(node, dimension);\n- obj.dimensions.push(dimension)\n+ Get: function(node, obj) {\n+ obj.get = node.getAttribute(\"onlineResource\")\n },\n- Default: function(node, obj) {\n- obj[\"default\"] = this.getChildValue(node)\n+ Post: function(node, obj) {\n+ obj.post = node.getAttribute(\"onlineResource\")\n },\n- Value: function(node, obj) {\n- obj.values.push(this.getChildValue(node))\n+ SRS: function(node, obj) {\n+ var srs = this.getChildValue(node);\n+ if (srs) {\n+ obj.srs = srs\n+ }\n }\n- },\n- ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"])\n },\n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_0_0\"\n });\n-OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.XML, {\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n+OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {\n regExes: {\n trimSpace: /^\\s*|\\s*$/g,\n removeSpace: /\\s*/g,\n splitSpace: /\\s+/,\n trimComma: /\\s*,\\s*/g\n },\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities\n- },\n readers: {\n- wps: {\n- Capabilities: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- ProcessOfferings: function(node, obj) {\n- obj.processOfferings = {};\n- this.readChildNodes(node, obj.processOfferings)\n- },\n- Process: function(node, processOfferings) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var process = {\n- processVersion: processVersion\n- };\n- this.readChildNodes(node, process);\n- processOfferings[process.identifier] = process\n- },\n- Languages: function(node, obj) {\n- obj.languages = [];\n- this.readChildNodes(node, obj.languages)\n- },\n- Default: function(node, languages) {\n- var language = {\n- isDefault: true\n- };\n- this.readChildNodes(node, language);\n- languages.push(language)\n- },\n- Supported: function(node, languages) {\n- var language = {};\n- this.readChildNodes(node, language);\n- languages.push(language)\n- }\n- },\n- ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n-});\n-OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(OpenLayers.Format.WMSDescribeLayer, {\n- initialize: function(options) {\n- OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, [options])\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- var root = data.documentElement;\n- var children = root.childNodes;\n- var describelayer = {\n- layerDescriptions: []\n- };\n- var childNode, nodeName;\n- for (var i = 0; i < children.length; ++i) {\n- childNode = children[i];\n- nodeName = childNode.nodeName;\n- if (nodeName == \"LayerDescription\") {\n- var layerName = childNode.getAttribute(\"name\");\n- var owsType = \"\";\n- var owsURL = \"\";\n- var typeName = \"\";\n- if (childNode.getAttribute(\"owsType\")) {\n- owsType = childNode.getAttribute(\"owsType\");\n- owsURL = childNode.getAttribute(\"owsURL\")\n- } else {\n- if (childNode.getAttribute(\"wfs\") != \"\") {\n- owsType = \"WFS\";\n- owsURL = childNode.getAttribute(\"wfs\")\n- } else if (childNode.getAttribute(\"wcs\") != \"\") {\n- owsType = \"WCS\";\n- owsURL = childNode.getAttribute(\"wcs\")\n- }\n- }\n- var query = childNode.getElementsByTagName(\"Query\");\n- if (query.length > 0) {\n- typeName = query[0].getAttribute(\"typeName\");\n- if (!typeName) {\n- typeName = query[0].getAttribute(\"typename\")\n- }\n- }\n- var layerDescription = {\n- layerName: layerName,\n- owsType: owsType,\n- owsURL: owsURL,\n- typeName: typeName\n- };\n- describelayer.layerDescriptions.push(layerDescription);\n- describelayer.length = describelayer.layerDescriptions.length;\n- describelayer[describelayer.length - 1] = layerDescription\n- } else if (nodeName == \"ServiceException\") {\n- var parser = new OpenLayers.Format.OGCExceptionReport;\n- return {\n- error: parser.read(data)\n+ wfs: OpenLayers.Util.applyDefaults({\n+ DefaultSRS: function(node, obj) {\n+ var defaultSRS = this.getChildValue(node);\n+ if (defaultSRS) {\n+ obj.srs = defaultSRS\n }\n }\n- }\n- return describelayer\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"]),\n+ ows: OpenLayers.Format.OWSCommon.v1.prototype.readers.ows\n },\n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_1_0\"\n });\n-OpenLayers.Format.WMSDescribeLayer.v1_1_0 = OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, {\n read: function(data) {\n var axl = new OpenLayers.Format.ArcXML;\n var parsed = axl.read(data);\n return parsed.features.feature\n }\n });\n-OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n- version: \"1.0.0\",\n- srsNameInQuery: false,\n- schemaLocations: {\n- wfs: \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n- },\n- initialize: function(options) {\n- OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n- OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options])\n- },\n- readNode: function(node, obj, first) {\n- return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments)\n- },\n- readers: {\n- wfs: OpenLayers.Util.applyDefaults({\n- WFS_TransactionResponse: function(node, obj) {\n- obj.insertIds = [];\n- obj.success = false;\n- this.readChildNodes(node, obj)\n- },\n- InsertResult: function(node, container) {\n- var obj = {\n- fids: []\n- };\n- this.readChildNodes(node, obj);\n- container.insertIds = container.insertIds.concat(obj.fids)\n- },\n- TransactionResult: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- Status: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- SUCCESS: function(node, obj) {\n- obj.success = true\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n- gml: OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n- feature: OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n- ogc: OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n- },\n- writers: {\n- wfs: OpenLayers.Util.applyDefaults({\n- Query: function(options) {\n- options = OpenLayers.Util.extend({\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- featureType: this.featureType,\n- srsName: this.srsName,\n- srsNameInQuery: this.srsNameInQuery\n- }, options);\n- var prefix = options.featurePrefix;\n- var node = this.createElementNSPlus(\"wfs:Query\", {\n- attributes: {\n- typeName: (prefix ? prefix + \":\" : \"\") + options.featureType\n- }\n- });\n- if (options.srsNameInQuery && options.srsName) {\n- node.setAttribute(\"srsName\", options.srsName)\n- }\n- if (options.featureNS) {\n- node.setAttribute(\"xmlns:\" + prefix, options.featureNS)\n- }\n- if (options.propertyNames) {\n- for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n- this.writeNode(\"ogc:PropertyName\", {\n- property: options.propertyNames[i]\n- }, node)\n- }\n- }\n- if (options.filter) {\n- this.setFilterProperty(options.filter);\n- this.writeNode(\"ogc:Filter\", options.filter, node)\n- }\n- return node\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n- gml: OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n- feature: OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n- ogc: OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n- },\n- CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n-});\n-OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.SOSCapabilities, {\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- sos: \"http://www.opengis.net/sos/1.0\",\n- gml: \"http://www.opengis.net/gml\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n- regExes: {\n- trimSpace: /^\\s*|\\s*$/g,\n- removeSpace: /\\s*/g,\n- splitSpace: /\\s+/,\n- trimComma: /\\s*,\\s*/g\n- },\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options\n- },\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities\n- },\n- readers: {\n- gml: OpenLayers.Util.applyDefaults({\n- name: function(node, obj) {\n- obj.name = this.getChildValue(node)\n- },\n- TimePeriod: function(node, obj) {\n- obj.timePeriod = {};\n- this.readChildNodes(node, obj.timePeriod)\n- },\n- beginPosition: function(node, timePeriod) {\n- timePeriod.beginPosition = this.getChildValue(node)\n- },\n- endPosition: function(node, timePeriod) {\n- timePeriod.endPosition = this.getChildValue(node)\n- }\n- }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n- sos: {\n- Capabilities: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- Contents: function(node, obj) {\n- obj.contents = {};\n- this.readChildNodes(node, obj.contents)\n- },\n- ObservationOfferingList: function(node, contents) {\n- contents.offeringList = {};\n- this.readChildNodes(node, contents.offeringList)\n- },\n- ObservationOffering: function(node, offeringList) {\n- var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n- offeringList[id] = {\n- procedures: [],\n- observedProperties: [],\n- featureOfInterestIds: [],\n- responseFormats: [],\n- resultModels: [],\n- responseModes: []\n- };\n- this.readChildNodes(node, offeringList[id])\n- },\n- time: function(node, offering) {\n- offering.time = {};\n- this.readChildNodes(node, offering.time)\n- },\n- procedure: function(node, offering) {\n- offering.procedures.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n- },\n- observedProperty: function(node, offering) {\n- offering.observedProperties.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n- },\n- featureOfInterest: function(node, offering) {\n- offering.featureOfInterestIds.push(this.getAttributeNS(node, this.namespaces.xlink, \"href\"))\n- },\n- responseFormat: function(node, offering) {\n- offering.responseFormats.push(this.getChildValue(node))\n- },\n- resultModel: function(node, offering) {\n- offering.resultModels.push(this.getChildValue(node))\n- },\n- responseMode: function(node, offering) {\n- offering.responseModes.push(this.getChildValue(node))\n- }\n- },\n- ows: OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n- CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n-});\n OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n namespaces: {\n ol: \"http://openlayers.org/context\",\n wmc: \"http://www.opengis.net/context\",\n sld: \"http://www.opengis.net/sld\",\n xlink: \"http://www.w3.org/1999/xlink\",\n xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n@@ -26062,14 +22193,58 @@\n SRS: function(node, obj) {\n obj.srs[this.getChildValue(node)] = true\n }\n }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers[\"wms\"])\n },\n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1\"\n });\n+OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1_1, {\n+ version: \"1.1.1\",\n+ profile: \"WMSC\",\n+ readers: {\n+ wms: OpenLayers.Util.applyDefaults({\n+ VendorSpecificCapabilities: function(node, obj) {\n+ obj.vendorSpecific = {\n+ tileSets: []\n+ };\n+ this.readChildNodes(node, obj.vendorSpecific)\n+ },\n+ TileSet: function(node, vendorSpecific) {\n+ var tileset = {\n+ srs: {},\n+ bbox: {},\n+ resolutions: []\n+ };\n+ this.readChildNodes(node, tileset);\n+ vendorSpecific.tileSets.push(tileset)\n+ },\n+ Resolutions: function(node, tileset) {\n+ var res = this.getChildValue(node).split(\" \");\n+ for (var i = 0, len = res.length; i < len; i++) {\n+ if (res[i] != \"\") {\n+ tileset.resolutions.push(parseFloat(res[i]))\n+ }\n+ }\n+ },\n+ Width: function(node, tileset) {\n+ tileset.width = parseInt(this.getChildValue(node))\n+ },\n+ Height: function(node, tileset) {\n+ tileset.height = parseInt(this.getChildValue(node))\n+ },\n+ Layers: function(node, tileset) {\n+ tileset.layers = this.getChildValue(node)\n+ },\n+ Styles: function(node, tileset) {\n+ tileset.styles = this.getChildValue(node)\n+ }\n+ }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers[\"wms\"])\n+ },\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC\"\n+});\n OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1, {\n readers: {\n wms: OpenLayers.Util.applyDefaults({\n WMS_Capabilities: function(node, obj) {\n this.readChildNodes(node, obj)\n },\n LayerLimit: function(node, obj) {\n@@ -26151,58 +22326,14 @@\n },\n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_3\"\n });\n OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_3, {\n version: \"1.3.0\",\n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_3_0\"\n });\n-OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1_1, {\n- version: \"1.1.1\",\n- profile: \"WMSC\",\n- readers: {\n- wms: OpenLayers.Util.applyDefaults({\n- VendorSpecificCapabilities: function(node, obj) {\n- obj.vendorSpecific = {\n- tileSets: []\n- };\n- this.readChildNodes(node, obj.vendorSpecific)\n- },\n- TileSet: function(node, vendorSpecific) {\n- var tileset = {\n- srs: {},\n- bbox: {},\n- resolutions: []\n- };\n- this.readChildNodes(node, tileset);\n- vendorSpecific.tileSets.push(tileset)\n- },\n- Resolutions: function(node, tileset) {\n- var res = this.getChildValue(node).split(\" \");\n- for (var i = 0, len = res.length; i < len; i++) {\n- if (res[i] != \"\") {\n- tileset.resolutions.push(parseFloat(res[i]))\n- }\n- }\n- },\n- Width: function(node, tileset) {\n- tileset.width = parseInt(this.getChildValue(node))\n- },\n- Height: function(node, tileset) {\n- tileset.height = parseInt(this.getChildValue(node))\n- },\n- Layers: function(node, tileset) {\n- tileset.layers = this.getChildValue(node)\n- },\n- Styles: function(node, tileset) {\n- tileset.styles = this.getChildValue(node)\n- }\n- }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers[\"wms\"])\n- },\n- CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC\"\n-});\n OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1, {\n version: \"1.1.0\",\n readers: {\n wms: OpenLayers.Util.applyDefaults({\n SRS: function(node, obj) {\n var srs = this.getChildValue(node);\n var values = srs.split(/ +/);\n@@ -26210,466 +22341,656 @@\n obj.srs[values[i]] = true\n }\n }\n }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers[\"wms\"])\n },\n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_0\"\n });\n-OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n namespaces: {\n- wfs: \"http://www.opengis.net/wfs\",\n+ csw: \"http://www.opengis.net/cat/csw/2.0.2\",\n+ dc: \"http://purl.org/dc/elements/1.1/\",\n+ dct: \"http://purl.org/dc/terms/\",\n+ gmd: \"http://www.isotc211.org/2005/gmd\",\n+ geonet: \"http://www.fao.org/geonetwork\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ ows: \"http://www.opengis.net/ows\",\n xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- ows: \"http://www.opengis.net/ows\"\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+ defaultPrefix: \"csw\",\n+ version: \"2.0.2\",\n+ schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n+ requestId: null,\n+ resultType: null,\n+ outputFormat: null,\n+ outputSchema: null,\n+ startPosition: null,\n+ maxRecords: null,\n+ DistributedSearch: null,\n+ ResponseHandler: null,\n+ Query: null,\n+ regExes: {\n+ trimSpace: /^\\s*|\\s*$/g,\n+ removeSpace: /\\s*/g,\n+ splitSpace: /\\s+/,\n+ trimComma: /\\s*,\\s*/g\n+ },\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options])\n },\n- errorProperty: \"featureTypeList\",\n- defaultPrefix: \"wfs\",\n read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data])\n }\n- var raw = data;\n if (data && data.nodeType == 9) {\n data = data.documentElement\n }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities\n+ var obj = {};\n+ this.readNode(data, obj);\n+ return obj\n },\n readers: {\n- wfs: {\n- WFS_Capabilities: function(node, obj) {\n- this.readChildNodes(node, obj)\n+ csw: {\n+ GetRecordsResponse: function(node, obj) {\n+ obj.records = [];\n+ this.readChildNodes(node, obj);\n+ var version = this.getAttributeNS(node, \"\", \"version\");\n+ if (version != \"\") {\n+ obj.version = version\n+ }\n },\n- FeatureTypeList: function(node, request) {\n- request.featureTypeList = {\n- featureTypes: []\n- };\n- this.readChildNodes(node, request.featureTypeList)\n+ RequestId: function(node, obj) {\n+ obj.RequestId = this.getChildValue(node)\n },\n- FeatureType: function(node, featureTypeList) {\n- var featureType = {};\n- this.readChildNodes(node, featureType);\n- featureTypeList.featureTypes.push(featureType)\n+ SearchStatus: function(node, obj) {\n+ obj.SearchStatus = {};\n+ var timestamp = this.getAttributeNS(node, \"\", \"timestamp\");\n+ if (timestamp != \"\") {\n+ obj.SearchStatus.timestamp = timestamp\n+ }\n },\n- Name: function(node, obj) {\n- var name = this.getChildValue(node);\n- if (name) {\n- var parts = name.split(\":\");\n- obj.name = parts.pop();\n- if (parts.length > 0) {\n- obj.featureNS = this.lookupNamespaceURI(node, parts[0])\n+ SearchResults: function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ var attrs = node.attributes;\n+ var SearchResults = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ if (attrs[i].name == \"numberOfRecordsMatched\" || attrs[i].name == \"numberOfRecordsReturned\" || attrs[i].name == \"nextRecord\") {\n+ SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue)\n+ } else {\n+ SearchResults[attrs[i].name] = attrs[i].nodeValue\n }\n }\n+ obj.SearchResults = SearchResults\n },\n- Title: function(node, obj) {\n- var title = this.getChildValue(node);\n- if (title) {\n- obj.title = title\n- }\n+ SummaryRecord: function(node, obj) {\n+ var record = {\n+ type: \"SummaryRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record)\n },\n- Abstract: function(node, obj) {\n- var abst = this.getChildValue(node);\n- if (abst) {\n- obj[\"abstract\"] = abst\n+ BriefRecord: function(node, obj) {\n+ var record = {\n+ type: \"BriefRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record)\n+ },\n+ DCMIRecord: function(node, obj) {\n+ var record = {\n+ type: \"DCMIRecord\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record)\n+ },\n+ Record: function(node, obj) {\n+ var record = {\n+ type: \"Record\"\n+ };\n+ this.readChildNodes(node, record);\n+ obj.records.push(record)\n+ },\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ obj[name] = this.getChildValue(node)\n+ }\n+ },\n+ geonet: {\n+ info: function(node, obj) {\n+ var gninfo = {};\n+ this.readChildNodes(node, gninfo);\n+ obj.gninfo = gninfo\n+ }\n+ },\n+ dc: {\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ if (!OpenLayers.Util.isArray(obj[name])) {\n+ obj[name] = []\n+ }\n+ var dc_element = {};\n+ var attrs = node.attributes;\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ dc_element[attrs[i].name] = attrs[i].nodeValue\n+ }\n+ dc_element.value = this.getChildValue(node);\n+ if (dc_element.value != \"\") {\n+ obj[name].push(dc_element)\n }\n }\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1\"\n-});\n-OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {\n- readers: {\n- wfs: OpenLayers.Util.applyDefaults({\n- Service: function(node, capabilities) {\n- capabilities.service = {};\n- this.readChildNodes(node, capabilities.service)\n- },\n- Fees: function(node, service) {\n- var fees = this.getChildValue(node);\n- if (fees && fees.toLowerCase() != \"none\") {\n- service.fees = fees\n+ },\n+ dct: {\n+ \"*\": function(node, obj) {\n+ var name = node.localName || node.nodeName.split(\":\").pop();\n+ if (!OpenLayers.Util.isArray(obj[name])) {\n+ obj[name] = []\n }\n- },\n- AccessConstraints: function(node, service) {\n- var constraints = this.getChildValue(node);\n- if (constraints && constraints.toLowerCase() != \"none\") {\n- service.accessConstraints = constraints\n+ obj[name].push(this.getChildValue(node))\n+ }\n+ },\n+ ows: OpenLayers.Util.applyDefaults({\n+ BoundingBox: function(node, obj) {\n+ if (obj.bounds) {\n+ obj.BoundingBox = [{\n+ crs: obj.projection,\n+ value: [obj.bounds.left, obj.bounds.bottom, obj.bounds.right, obj.bounds.top]\n+ }];\n+ delete obj.projection;\n+ delete obj.bounds\n }\n- },\n- OnlineResource: function(node, service) {\n- var onlineResource = this.getChildValue(node);\n- if (onlineResource && onlineResource.toLowerCase() != \"none\") {\n- service.onlineResource = onlineResource\n+ OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"][\"BoundingBox\"].apply(this, arguments)\n+ }\n+ }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"])\n+ },\n+ write: function(options) {\n+ var node = this.writeNode(\"csw:GetRecords\", options);\n+ node.setAttribute(\"xmlns:gmd\", this.namespaces.gmd);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node])\n+ },\n+ writers: {\n+ csw: {\n+ GetRecords: function(options) {\n+ if (!options) {\n+ options = {}\n }\n- },\n- Keywords: function(node, service) {\n- var keywords = this.getChildValue(node);\n- if (keywords && keywords.toLowerCase() != \"none\") {\n- service.keywords = keywords.split(\", \")\n+ var node = this.createElementNSPlus(\"csw:GetRecords\", {\n+ attributes: {\n+ service: \"CSW\",\n+ version: this.version,\n+ requestId: options.requestId || this.requestId,\n+ resultType: options.resultType || this.resultType,\n+ outputFormat: options.outputFormat || this.outputFormat,\n+ outputSchema: options.outputSchema || this.outputSchema,\n+ startPosition: options.startPosition || this.startPosition,\n+ maxRecords: options.maxRecords || this.maxRecords\n+ }\n+ });\n+ if (options.DistributedSearch || this.DistributedSearch) {\n+ this.writeNode(\"csw:DistributedSearch\", options.DistributedSearch || this.DistributedSearch, node)\n }\n+ var ResponseHandler = options.ResponseHandler || this.ResponseHandler;\n+ if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {\n+ for (var i = 0, len = ResponseHandler.length; i < len; i++) {\n+ this.writeNode(\"csw:ResponseHandler\", ResponseHandler[i], node)\n+ }\n+ }\n+ this.writeNode(\"Query\", options.Query || this.Query, node);\n+ return node\n },\n- Capability: function(node, capabilities) {\n- capabilities.capability = {};\n- this.readChildNodes(node, capabilities.capability)\n- },\n- Request: function(node, obj) {\n- obj.request = {};\n- this.readChildNodes(node, obj.request)\n+ DistributedSearch: function(options) {\n+ var node = this.createElementNSPlus(\"csw:DistributedSearch\", {\n+ attributes: {\n+ hopCount: options.hopCount\n+ }\n+ });\n+ return node\n },\n- GetFeature: function(node, request) {\n- request.getfeature = {\n- href: {},\n- formats: []\n- };\n- this.readChildNodes(node, request.getfeature)\n+ ResponseHandler: function(options) {\n+ var node = this.createElementNSPlus(\"csw:ResponseHandler\", {\n+ value: options.value\n+ });\n+ return node\n },\n- ResultFormat: function(node, obj) {\n- var children = node.childNodes;\n- var childNode;\n- for (var i = 0; i < children.length; i++) {\n- childNode = children[i];\n- if (childNode.nodeType == 1) {\n- obj.formats.push(childNode.nodeName)\n+ Query: function(options) {\n+ if (!options) {\n+ options = {}\n+ }\n+ var node = this.createElementNSPlus(\"csw:Query\", {\n+ attributes: {\n+ typeNames: options.typeNames || \"csw:Record\"\n+ }\n+ });\n+ var ElementName = options.ElementName;\n+ if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {\n+ for (var i = 0, len = ElementName.length; i < len; i++) {\n+ this.writeNode(\"csw:ElementName\", ElementName[i], node)\n }\n+ } else {\n+ this.writeNode(\"csw:ElementSetName\", options.ElementSetName || {\n+ value: \"summary\"\n+ }, node)\n }\n+ if (options.Constraint) {\n+ this.writeNode(\"csw:Constraint\", options.Constraint, node)\n+ }\n+ if (options.SortBy) {\n+ this.writeNode(\"ogc:SortBy\", options.SortBy, node)\n+ }\n+ return node\n },\n- DCPType: function(node, obj) {\n- this.readChildNodes(node, obj)\n- },\n- HTTP: function(node, obj) {\n- this.readChildNodes(node, obj.href)\n- },\n- Get: function(node, obj) {\n- obj.get = node.getAttribute(\"onlineResource\")\n+ ElementName: function(options) {\n+ var node = this.createElementNSPlus(\"csw:ElementName\", {\n+ value: options.value\n+ });\n+ return node\n },\n- Post: function(node, obj) {\n- obj.post = node.getAttribute(\"onlineResource\")\n+ ElementSetName: function(options) {\n+ var node = this.createElementNSPlus(\"csw:ElementSetName\", {\n+ attributes: {\n+ typeNames: options.typeNames\n+ },\n+ value: options.value\n+ });\n+ return node\n },\n- SRS: function(node, obj) {\n- var srs = this.getChildValue(node);\n- if (srs) {\n- obj.srs = srs\n+ Constraint: function(options) {\n+ var node = this.createElementNSPlus(\"csw:Constraint\", {\n+ attributes: {\n+ version: options.version\n+ }\n+ });\n+ if (options.Filter) {\n+ var format = new OpenLayers.Format.Filter({\n+ version: options.version\n+ });\n+ node.appendChild(format.write(options.Filter))\n+ } else if (options.CqlText) {\n+ var child = this.createElementNSPlus(\"CqlText\", {\n+ value: options.CqlText.value\n+ });\n+ node.appendChild(child)\n }\n+ return node\n }\n- }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"])\n+ },\n+ ogc: OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n },\n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.CSWGetRecords.v2_0_2\"\n });\n-OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {\n- regExes: {\n- trimSpace: /^\\s*|\\s*$/g,\n- removeSpace: /\\s*/g,\n- splitSpace: /\\s+/,\n- trimComma: /\\s*,\\s*/g\n- },\n- readers: {\n- wfs: OpenLayers.Util.applyDefaults({\n- DefaultSRS: function(node, obj) {\n- var defaultSRS = this.getChildValue(node);\n- if (defaultSRS) {\n- obj.srs = defaultSRS\n+OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(OpenLayers.Format.SLD.v1_0_0, {\n+ version: \"1.0.0\",\n+ profile: \"GeoServer\",\n+ readers: OpenLayers.Util.applyDefaults({\n+ sld: OpenLayers.Util.applyDefaults({\n+ Priority: function(node, obj) {\n+ var value = this.readers.ogc._expression.call(this, node);\n+ if (value) {\n+ obj.priority = value\n+ }\n+ },\n+ VendorOption: function(node, obj) {\n+ if (!obj.vendorOptions) {\n+ obj.vendorOptions = {}\n+ }\n+ obj.vendorOptions[node.getAttribute(\"name\")] = this.getChildValue(node)\n+ },\n+ TextSymbolizer: function(node, rule) {\n+ OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);\n+ var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer[\"Text\"];\n+ if (symbolizer.graphic === undefined) {\n+ symbolizer.graphic = false\n }\n }\n- }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"]),\n- ows: OpenLayers.Format.OWSCommon.v1.prototype.readers.ows\n- },\n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_1_0\"\n-});\n-OpenLayers.Events.featureclick = OpenLayers.Class({\n- cache: null,\n- map: null,\n- provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n- initialize: function(target) {\n- this.target = target;\n- if (target.object instanceof OpenLayers.Map) {\n- this.setMap(target.object)\n- } else if (target.object instanceof OpenLayers.Layer.Vector) {\n- if (target.object.map) {\n- this.setMap(target.object.map)\n- } else {\n- target.object.events.register(\"added\", this, function(evt) {\n- this.setMap(target.object.map)\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.readers[\"sld\"])\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.readers),\n+ writers: OpenLayers.Util.applyDefaults({\n+ sld: OpenLayers.Util.applyDefaults({\n+ Priority: function(priority) {\n+ return this.writers.sld._OGCExpression.call(this, \"sld:Priority\", priority)\n+ },\n+ VendorOption: function(option) {\n+ return this.createElementNSPlus(\"sld:VendorOption\", {\n+ attributes: {\n+ name: option.name\n+ },\n+ value: option.value\n })\n+ },\n+ TextSymbolizer: function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"TextSymbolizer\"].apply(this, arguments);\n+ if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {\n+ this.writeNode(\"Graphic\", symbolizer, node)\n+ }\n+ if (\"priority\" in symbolizer) {\n+ this.writeNode(\"Priority\", symbolizer.priority, node)\n+ }\n+ return this.addVendorOptions(node, symbolizer)\n+ },\n+ PointSymbolizer: function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"PointSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer)\n+ },\n+ LineSymbolizer: function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"LineSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer)\n+ },\n+ PolygonSymbolizer: function(symbolizer) {\n+ var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n+ var node = writers[\"sld\"][\"PolygonSymbolizer\"].apply(this, arguments);\n+ return this.addVendorOptions(node, symbolizer)\n+ }\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.writers[\"sld\"])\n+ }, OpenLayers.Format.SLD.v1_0_0.prototype.writers),\n+ addVendorOptions: function(node, symbolizer) {\n+ var options = symbolizer.vendorOptions;\n+ if (options) {\n+ for (var key in symbolizer.vendorOptions) {\n+ this.writeNode(\"VendorOption\", {\n+ name: key,\n+ value: symbolizer.vendorOptions[key]\n+ }, node)\n }\n- } else {\n- throw \"Listeners for '\" + this.provides.join(\"', '\") + \"' events can only be registered for OpenLayers.Layer.Vector \" + \"or OpenLayers.Map instances\"\n- }\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- target.extensions[this.provides[i]] = true\n }\n+ return node\n },\n- setMap: function(map) {\n- this.map = map;\n- this.cache = {};\n- map.events.register(\"mousedown\", this, this.start, {\n- extension: true\n- });\n- map.events.register(\"mouseup\", this, this.onClick, {\n- extension: true\n- });\n- map.events.register(\"touchstart\", this, this.start, {\n- extension: true\n- });\n- map.events.register(\"touchmove\", this, this.cancel, {\n- extension: true\n- });\n- map.events.register(\"touchend\", this, this.onClick, {\n- extension: true\n- });\n- map.events.register(\"mousemove\", this, this.onMousemove, {\n- extension: true\n- })\n+ CLASS_NAME: \"OpenLayers.Format.SLD.v1_0_0_GeoServer\"\n+});\n+OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n+ delay: 300,\n+ single: true,\n+ double: false,\n+ pixelTolerance: 0,\n+ dblclickTolerance: 13,\n+ stopSingle: false,\n+ stopDouble: false,\n+ timerId: null,\n+ down: null,\n+ last: null,\n+ first: null,\n+ rightclickTimerId: null,\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true\n },\n- start: function(evt) {\n- this.startEvt = evt\n+ touchmove: function(evt) {\n+ this.last = this.getEventInfo(evt);\n+ return true\n },\n- cancel: function(evt) {\n- delete this.startEvt\n+ touchend: function(evt) {\n+ if (this.down) {\n+ evt.xy = this.last.xy;\n+ evt.lastTouches = this.last.touches;\n+ this.handleSingle(evt);\n+ this.down = null\n+ }\n+ return true\n },\n- onClick: function(evt) {\n- if (!this.startEvt || evt.type !== \"touchend\" && !OpenLayers.Event.isLeftClick(evt)) {\n- return\n+ mousedown: function(evt) {\n+ this.down = this.getEventInfo(evt);\n+ this.last = this.getEventInfo(evt);\n+ return true\n+ },\n+ mouseup: function(evt) {\n+ var propagate = true;\n+ if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) {\n+ propagate = this.rightclick(evt)\n }\n- var features = this.getFeatures(this.startEvt);\n- delete this.startEvt;\n- var feature, layer, more, clicked = {};\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- layer = feature.layer;\n- clicked[layer.id] = true;\n- more = this.triggerEvent(\"featureclick\", {\n- feature: feature\n- });\n- if (more === false) {\n- break\n+ return propagate\n+ },\n+ rightclick: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.rightclickTimerId != null) {\n+ this.clearTimer();\n+ this.callback(\"dblrightclick\", [evt]);\n+ return !this.stopDouble\n+ } else {\n+ var clickEvent = this[\"double\"] ? OpenLayers.Util.extend({}, evt) : this.callback(\"rightclick\", [evt]);\n+ var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent);\n+ this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay)\n }\n }\n- for (i = 0, len = this.map.layers.length; i < len; ++i) {\n- layer = this.map.layers[i];\n- if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {\n- this.triggerEvent(\"nofeatureclick\", {\n- layer: layer\n- })\n- }\n+ return !this.stopSingle\n+ },\n+ delayedRightCall: function(evt) {\n+ this.rightclickTimerId = null;\n+ if (evt) {\n+ this.callback(\"rightclick\", [evt])\n }\n },\n- onMousemove: function(evt) {\n- delete this.startEvt;\n- var features = this.getFeatures(evt);\n- var over = {},\n- newly = [],\n- feature;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- over[feature.id] = feature;\n- if (!this.cache[feature.id]) {\n- newly.push(feature)\n+ click: function(evt) {\n+ if (!this.last) {\n+ this.last = this.getEventInfo(evt)\n+ }\n+ this.handleSingle(evt);\n+ return !this.stopSingle\n+ },\n+ dblclick: function(evt) {\n+ this.handleDouble(evt);\n+ return !this.stopDouble\n+ },\n+ handleDouble: function(evt) {\n+ if (this.passesDblclickTolerance(evt)) {\n+ if (this[\"double\"]) {\n+ this.callback(\"dblclick\", [evt])\n }\n+ this.clearTimer()\n }\n- var out = [];\n- for (var id in this.cache) {\n- feature = this.cache[id];\n- if (feature.layer && feature.layer.map) {\n- if (!over[feature.id]) {\n- out.push(feature)\n+ },\n+ handleSingle: function(evt) {\n+ if (this.passesTolerance(evt)) {\n+ if (this.timerId != null) {\n+ if (this.last.touches && this.last.touches.length === 1) {\n+ if (this[\"double\"]) {\n+ OpenLayers.Event.preventDefault(evt)\n+ }\n+ this.handleDouble(evt)\n+ }\n+ if (!this.last.touches || this.last.touches.length !== 2) {\n+ this.clearTimer()\n }\n } else {\n- delete this.cache[id]\n+ this.first = this.getEventInfo(evt);\n+ var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null;\n+ this.queuePotentialClick(clickEvent)\n }\n }\n- var more;\n- for (i = 0, len = newly.length; i < len; ++i) {\n- feature = newly[i];\n- this.cache[feature.id] = feature;\n- more = this.triggerEvent(\"featureover\", {\n- feature: feature\n- });\n- if (more === false) {\n- break\n+ },\n+ queuePotentialClick: function(evt) {\n+ this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n+ },\n+ passesTolerance: function(evt) {\n+ var passes = true;\n+ if (this.pixelTolerance != null && this.down && this.down.xy) {\n+ passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n+ if (passes && this.touch && this.down.touches.length === this.last.touches.length) {\n+ for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n+ if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) {\n+ passes = false;\n+ break\n+ }\n+ }\n }\n }\n- for (i = 0, len = out.length; i < len; ++i) {\n- feature = out[i];\n- delete this.cache[feature.id];\n- more = this.triggerEvent(\"featureout\", {\n- feature: feature\n- });\n- if (more === false) {\n- break\n- }\n+ return passes\n+ },\n+ getTouchDistance: function(from, to) {\n+ return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2))\n+ },\n+ passesDblclickTolerance: function(evt) {\n+ var passes = true;\n+ if (this.down && this.first) {\n+ passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance\n }\n+ return passes\n },\n- triggerEvent: function(type, evt) {\n- var layer = evt.feature ? evt.feature.layer : evt.layer,\n- object = this.target.object;\n- if (object instanceof OpenLayers.Map || object === layer) {\n- return this.target.triggerEvent(type, evt)\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null\n+ }\n+ if (this.rightclickTimerId != null) {\n+ window.clearTimeout(this.rightclickTimerId);\n+ this.rightclickTimerId = null\n }\n },\n- getFeatures: function(evt) {\n- var x = evt.clientX,\n- y = evt.clientY,\n- features = [],\n- targets = [],\n- layers = [],\n- layer, target, feature, i, len;\n- for (i = this.map.layers.length - 1; i >= 0; --i) {\n- layer = this.map.layers[i];\n- if (layer.div.style.display !== \"none\") {\n- if (layer.renderer instanceof OpenLayers.Renderer.Elements) {\n- if (layer instanceof OpenLayers.Layer.Vector) {\n- target = document.elementFromPoint(x, y);\n- while (target && target._featureId) {\n- feature = layer.getFeatureById(target._featureId);\n- if (feature) {\n- features.push(feature);\n- target.style.display = \"none\";\n- targets.push(target);\n- target = document.elementFromPoint(x, y)\n- } else {\n- target = false\n- }\n- }\n- }\n- layers.push(layer);\n- layer.div.style.display = \"none\"\n- } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {\n- feature = layer.renderer.getFeatureIdFromEvent(evt);\n- if (feature) {\n- features.push(feature);\n- layers.push(layer)\n- }\n+ delayedCall: function(evt) {\n+ this.timerId = null;\n+ if (evt) {\n+ this.callback(\"click\", [evt])\n+ }\n+ },\n+ getEventInfo: function(evt) {\n+ var touches;\n+ if (evt.touches) {\n+ var len = evt.touches.length;\n+ touches = new Array(len);\n+ var touch;\n+ for (var i = 0; i < len; i++) {\n+ touch = evt.touches[i];\n+ touches[i] = {\n+ clientX: touch.olClientX,\n+ clientY: touch.olClientY\n }\n }\n }\n- for (i = 0, len = targets.length; i < len; ++i) {\n- targets[i].style.display = \"\"\n- }\n- for (i = layers.length - 1; i >= 0; --i) {\n- layers[i].div.style.display = \"block\"\n+ return {\n+ xy: evt.xy,\n+ touches: touches\n }\n- return features\n },\n- destroy: function() {\n- for (var i = this.provides.length - 1; i >= 0; --i) {\n- delete this.target.extensions[this.provides[i]]\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.clearTimer();\n+ this.down = null;\n+ this.first = null;\n+ this.last = null;\n+ deactivated = true\n }\n- this.map.events.un({\n- mousemove: this.onMousemove,\n- mousedown: this.start,\n- mouseup: this.onClick,\n- touchstart: this.start,\n- touchmove: this.cancel,\n- touchend: this.onClick,\n- scope: this\n- });\n- delete this.cache;\n- delete this.map;\n- delete this.target\n- }\n+ return deactivated\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Click\"\n });\n-OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;\n-OpenLayers.Events.featureover = OpenLayers.Events.featureclick;\n-OpenLayers.Events.featureout = OpenLayers.Events.featureclick;\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n- target: null,\n- events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n- startRegEx: /^mousedown|touchstart$/,\n- cancelRegEx: /^touchmove$/,\n- completeRegEx: /^mouseup|touchend$/,\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- })\n- }\n+OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n+ wheelListener: null,\n+ interval: 0,\n+ maxDelta: Number.POSITIVE_INFINITY,\n+ delta: 0,\n+ cumulative: true,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this)\n },\n destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick)\n- }\n- delete this.target\n- },\n- getPressedButton: function(element) {\n- var depth = 3,\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- button = element;\n- break\n- }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return button\n+ OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n+ this.wheelListener = null\n },\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === \"a\") {\n- ignore = true;\n- break\n+ onWheelEvent: function(e) {\n+ if (!this.map || !this.checkModifiers(e)) {\n+ return\n+ }\n+ var overScrollableDiv = false;\n+ var allowScroll = false;\n+ var overMapDiv = false;\n+ var elem = OpenLayers.Event.element(e);\n+ while (elem != null && !overMapDiv && !overScrollableDiv) {\n+ if (!overScrollableDiv) {\n+ try {\n+ var overflow;\n+ if (elem.currentStyle) {\n+ overflow = elem.currentStyle[\"overflow\"]\n+ } else {\n+ var style = document.defaultView.getComputedStyle(elem, null);\n+ overflow = style.getPropertyValue(\"overflow\")\n+ }\n+ overScrollableDiv = overflow && overflow == \"auto\" || overflow == \"scroll\"\n+ } catch (err) {}\n }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return ignore\n- },\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n+ if (!allowScroll) {\n+ allowScroll = OpenLayers.Element.hasClass(elem, \"olScrollable\");\n+ if (!allowScroll) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (elem == layer.div || elem == layer.pane) {\n+ allowScroll = true;\n break\n+ }\n }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- })\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt\n+ }\n+ }\n+ overMapDiv = elem == this.map.div;\n+ elem = elem.parentNode\n+ }\n+ if (!overScrollableDiv && overMapDiv) {\n+ if (allowScroll) {\n+ var delta = 0;\n+ if (e.wheelDelta) {\n+ delta = e.wheelDelta;\n+ if (delta % 160 === 0) {\n+ delta = delta * .75\n }\n- OpenLayers.Event.stop(evt);\n- propagate = false\n+ delta = delta / 120\n+ } else if (e.detail) {\n+ delta = -(e.detail / Math.abs(e.detail))\n }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false\n+ this.delta += delta;\n+ window.clearTimeout(this._timeoutId);\n+ if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n+ var evt = OpenLayers.Util.extend({}, e);\n+ this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() {\n+ this.wheelZoom(evt)\n+ }, this), this.interval)\n+ } else {\n+ this.wheelZoom(e)\n }\n+ }\n+ OpenLayers.Event.stop(e)\n+ }\n+ },\n+ wheelZoom: function(e) {\n+ var delta = this.delta;\n+ this.delta = 0;\n+ if (delta) {\n+ e.xy = this.map.events.getMousePosition(e);\n+ if (delta < 0) {\n+ this.callback(\"down\", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1])\n } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt\n+ this.callback(\"up\", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1])\n }\n }\n- return propagate\n- }\n+ },\n+ activate: function(evt) {\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ deactivate: function(evt) {\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ var wheelListener = this.wheelListener;\n+ OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n+ OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n+ OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n });\n OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {\n point: null,\n layer: null,\n multi: false,\n citeCompliant: false,\n mouseDown: false,\n@@ -26854,731 +23175,14 @@\n passes = false\n }\n }\n return passes\n },\n CLASS_NAME: \"OpenLayers.Handler.Point\"\n });\n-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n- EVENTMAP: {\n- click: {\n- in: \"click\",\n- out: \"clickout\"\n- },\n- mousemove: {\n- in: \"over\",\n- out: \"out\"\n- },\n- dblclick: {\n- in: \"dblclick\",\n- out: null\n- },\n- mousedown: {\n- in: null,\n- out: null\n- },\n- mouseup: {\n- in: null,\n- out: null\n- },\n- touchstart: {\n- in: \"click\",\n- out: \"clickout\"\n- }\n- },\n- feature: null,\n- lastFeature: null,\n- down: null,\n- up: null,\n- clickTolerance: 4,\n- geometryTypes: null,\n- stopClick: true,\n- stopDown: true,\n- stopUp: false,\n- initialize: function(control, layer, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.layer = layer\n- },\n- touchstart: function(evt) {\n- this.startTouch();\n- return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n- },\n- touchmove: function(evt) {\n- OpenLayers.Event.preventDefault(evt)\n- },\n- mousedown: function(evt) {\n- if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n- this.down = evt.xy\n- }\n- return this.handle(evt) ? !this.stopDown : true\n- },\n- mouseup: function(evt) {\n- this.up = evt.xy;\n- return this.handle(evt) ? !this.stopUp : true\n- },\n- click: function(evt) {\n- return this.handle(evt) ? !this.stopClick : true\n- },\n- mousemove: function(evt) {\n- if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n- return true\n- }\n- this.handle(evt);\n- return true\n- },\n- dblclick: function(evt) {\n- return !this.handle(evt)\n- },\n- geometryTypeMatches: function(feature) {\n- return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n- },\n- handle: function(evt) {\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n- }\n- var type = evt.type;\n- var handled = false;\n- var previouslyIn = !!this.feature;\n- var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n- this.feature = this.layer.getFeatureFromEvent(evt);\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n- }\n- if (this.lastFeature && !this.lastFeature.layer) {\n- this.lastFeature = null\n- }\n- if (this.feature) {\n- if (type === \"touchstart\") {\n- OpenLayers.Event.preventDefault(evt)\n- }\n- var inNew = this.feature != this.lastFeature;\n- if (this.geometryTypeMatches(this.feature)) {\n- if (previouslyIn && inNew) {\n- if (this.lastFeature) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- this.triggerCallback(type, \"in\", [this.feature])\n- } else if (!previouslyIn || click) {\n- this.triggerCallback(type, \"in\", [this.feature])\n- }\n- this.lastFeature = this.feature;\n- handled = true\n- } else {\n- if (this.lastFeature && (previouslyIn && inNew || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- this.feature = null\n- }\n- } else if (this.lastFeature && (previouslyIn || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- return handled\n- },\n- triggerCallback: function(type, mode, args) {\n- var key = this.EVENTMAP[type][mode];\n- if (key) {\n- if (type == \"click\" && this.up && this.down) {\n- var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n- if (dpx <= this.clickTolerance) {\n- this.callback(key, args)\n- }\n- this.up = this.down = null\n- } else {\n- this.callback(key, args)\n- }\n- }\n- },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.moveLayerToTop();\n- this.map.events.on({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n- });\n- activated = true\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.feature = null;\n- this.lastFeature = null;\n- this.down = null;\n- this.up = null;\n- this.map.events.un({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n- });\n- deactivated = true\n- }\n- return deactivated\n- },\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop()\n- }\n- },\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index)\n- },\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n- this.layer.setZIndex(index)\n- } else {\n- this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Feature\"\n-});\n-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n- started: false,\n- stopDown: true,\n- dragging: false,\n- last: null,\n- start: null,\n- lastMoveEvt: null,\n- oldOnselectstart: null,\n- interval: 0,\n- timeoutId: null,\n- documentDrag: false,\n- documentEvents: null,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- if (this.documentDrag === true) {\n- var me = this;\n- this._docMove = function(evt) {\n- me.mousemove({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- },\n- element: document\n- })\n- };\n- this._docUp = function(evt) {\n- me.mouseup({\n- xy: {\n- x: evt.clientX,\n- y: evt.clientY\n- }\n- })\n- }\n- }\n- },\n- dragstart: function(evt) {\n- var propagate = true;\n- this.dragging = false;\n- if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) {\n- this.started = true;\n- this.start = evt.xy;\n- this.last = evt.xy;\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDragDown\");\n- this.down(evt);\n- this.callback(\"down\", [evt.xy]);\n- OpenLayers.Event.preventDefault(evt);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True\n- }\n- document.onselectstart = OpenLayers.Function.False;\n- propagate = !this.stopDown\n- } else {\n- this.started = false;\n- this.start = null;\n- this.last = null\n- }\n- return propagate\n- },\n- dragmove: function(evt) {\n- this.lastMoveEvt = evt;\n- if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {\n- if (this.documentDrag === true && this.documentEvents) {\n- if (evt.element === document) {\n- this.adjustXY(evt);\n- this.setEvent(evt)\n- } else {\n- this.removeDocumentEvents()\n- }\n- }\n- if (this.interval > 0) {\n- this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval)\n- }\n- this.dragging = true;\n- this.move(evt);\n- this.callback(\"move\", [evt.xy]);\n- if (!this.oldOnselectstart) {\n- this.oldOnselectstart = document.onselectstart;\n- document.onselectstart = OpenLayers.Function.False\n- }\n- this.last = evt.xy\n- }\n- return true\n- },\n- dragend: function(evt) {\n- if (this.started) {\n- if (this.documentDrag === true && this.documentEvents) {\n- this.adjustXY(evt);\n- this.removeDocumentEvents()\n- }\n- var dragged = this.start != this.last;\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n- this.up(evt);\n- this.callback(\"up\", [evt.xy]);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy])\n- }\n- document.onselectstart = this.oldOnselectstart\n- }\n- return true\n- },\n- down: function(evt) {},\n- move: function(evt) {},\n- up: function(evt) {},\n- out: function(evt) {},\n- mousedown: function(evt) {\n- return this.dragstart(evt)\n- },\n- touchstart: function(evt) {\n- this.startTouch();\n- return this.dragstart(evt)\n- },\n- mousemove: function(evt) {\n- return this.dragmove(evt)\n- },\n- touchmove: function(evt) {\n- return this.dragmove(evt)\n- },\n- removeTimeout: function() {\n- this.timeoutId = null;\n- if (this.dragging) {\n- this.mousemove(this.lastMoveEvt)\n- }\n- },\n- mouseup: function(evt) {\n- return this.dragend(evt)\n- },\n- touchend: function(evt) {\n- evt.xy = this.last;\n- return this.dragend(evt)\n- },\n- mouseout: function(evt) {\n- if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- if (this.documentDrag === true) {\n- this.addDocumentEvents()\n- } else {\n- var dragged = this.start != this.last;\n- this.started = false;\n- this.dragging = false;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n- this.out(evt);\n- this.callback(\"out\", []);\n- if (dragged) {\n- this.callback(\"done\", [evt.xy])\n- }\n- if (document.onselectstart) {\n- document.onselectstart = this.oldOnselectstart\n- }\n- }\n- }\n- return true\n- },\n- click: function(evt) {\n- return this.start == this.last\n- },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.dragging = false;\n- activated = true\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.started = false;\n- this.dragging = false;\n- this.start = null;\n- this.last = null;\n- deactivated = true;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\")\n- }\n- return deactivated\n- },\n- adjustXY: function(evt) {\n- var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n- evt.xy.x -= pos[0];\n- evt.xy.y -= pos[1]\n- },\n- addDocumentEvents: function() {\n- OpenLayers.Element.addClass(document.body, \"olDragDown\");\n- this.documentEvents = true;\n- OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.observe(document, \"mouseup\", this._docUp)\n- },\n- removeDocumentEvents: function() {\n- OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n- this.documentEvents = false;\n- OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n- OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp)\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Drag\"\n-});\n-OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {\n- sides: 4,\n- radius: null,\n- snapAngle: null,\n- snapToggle: \"shiftKey\",\n- layerOptions: null,\n- persist: false,\n- irregular: false,\n- citeCompliant: false,\n- angle: null,\n- fixedRadius: false,\n- feature: null,\n- layer: null,\n- origin: null,\n- initialize: function(control, callbacks, options) {\n- if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n- this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style[\"default\"], {})\n- }\n- OpenLayers.Handler.Drag.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.options = options ? options : {}\n- },\n- setOptions: function(newOptions) {\n- OpenLayers.Util.extend(this.options, newOptions);\n- OpenLayers.Util.extend(this, newOptions)\n- },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {\n- var options = OpenLayers.Util.extend({\n- displayInLayerSwitcher: false,\n- calculateInRange: OpenLayers.Function.True,\n- wrapDateLine: this.citeCompliant\n- }, this.layerOptions);\n- this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n- this.map.addLayer(this.layer);\n- activated = true\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {\n- if (this.dragging) {\n- this.cancel()\n- }\n- if (this.layer.map != null) {\n- this.layer.destroy(false);\n- if (this.feature) {\n- this.feature.destroy()\n- }\n- }\n- this.layer = null;\n- this.feature = null;\n- deactivated = true\n- }\n- return deactivated\n- },\n- down: function(evt) {\n- this.fixedRadius = !!this.radius;\n- var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n- this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n- if (!this.fixedRadius || this.irregular) {\n- this.radius = this.map.getResolution()\n- }\n- if (this.persist) {\n- this.clear()\n- }\n- this.feature = new OpenLayers.Feature.Vector;\n- this.createGeometry();\n- this.callback(\"create\", [this.origin, this.feature]);\n- this.layer.addFeatures([this.feature], {\n- silent: true\n- });\n- this.layer.drawFeature(this.feature, this.style)\n- },\n- move: function(evt) {\n- var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n- var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n- if (this.irregular) {\n- var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;\n- this.radius = Math.max(this.map.getResolution() / 2, ry)\n- } else if (this.fixedRadius) {\n- this.origin = point\n- } else {\n- this.calculateAngle(point, evt);\n- this.radius = Math.max(this.map.getResolution() / 2, point.distanceTo(this.origin))\n- }\n- this.modifyGeometry();\n- if (this.irregular) {\n- var dx = point.x - this.origin.x;\n- var dy = point.y - this.origin.y;\n- var ratio;\n- if (dy == 0) {\n- ratio = dx / (this.radius * Math.sqrt(2))\n- } else {\n- ratio = dx / dy\n- }\n- this.feature.geometry.resize(1, this.origin, ratio);\n- this.feature.geometry.move(dx / 2, dy / 2)\n- }\n- this.layer.drawFeature(this.feature, this.style)\n- },\n- up: function(evt) {\n- this.finalize();\n- if (this.start == this.last) {\n- this.callback(\"done\", [evt.xy])\n- }\n- },\n- out: function(evt) {\n- this.finalize()\n- },\n- createGeometry: function() {\n- this.angle = Math.PI * (1 / this.sides - 1 / 2);\n- if (this.snapAngle) {\n- this.angle += this.snapAngle * (Math.PI / 180)\n- }\n- this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin, this.radius, this.sides, this.snapAngle)\n- },\n- modifyGeometry: function() {\n- var angle, point;\n- var ring = this.feature.geometry.components[0];\n- if (ring.components.length != this.sides + 1) {\n- this.createGeometry();\n- ring = this.feature.geometry.components[0]\n- }\n- for (var i = 0; i < this.sides; ++i) {\n- point = ring.components[i];\n- angle = this.angle + i * 2 * Math.PI / this.sides;\n- point.x = this.origin.x + this.radius * Math.cos(angle);\n- point.y = this.origin.y + this.radius * Math.sin(angle);\n- point.clearBounds()\n- }\n- },\n- calculateAngle: function(point, evt) {\n- var alpha = Math.atan2(point.y - this.origin.y, point.x - this.origin.x);\n- if (this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {\n- var snapAngleRad = Math.PI / 180 * this.snapAngle;\n- this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad\n- } else {\n- this.angle = alpha\n- }\n- },\n- cancel: function() {\n- this.callback(\"cancel\", null);\n- this.finalize()\n- },\n- finalize: function() {\n- this.origin = null;\n- this.radius = this.options.radius\n- },\n- clear: function() {\n- if (this.layer) {\n- this.layer.renderer.clear();\n- this.layer.destroyFeatures()\n- }\n- },\n- callback: function(name, args) {\n- if (this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, [this.feature.geometry.clone()])\n- }\n- if (!this.persist && (name == \"done\" || name == \"cancel\")) {\n- this.clear()\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Handler.RegularPolygon\"\n-});\n-OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {\n- delay: 300,\n- single: true,\n- double: false,\n- pixelTolerance: 0,\n- dblclickTolerance: 13,\n- stopSingle: false,\n- stopDouble: false,\n- timerId: null,\n- down: null,\n- last: null,\n- first: null,\n- rightclickTimerId: null,\n- touchstart: function(evt) {\n- this.startTouch();\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true\n- },\n- touchmove: function(evt) {\n- this.last = this.getEventInfo(evt);\n- return true\n- },\n- touchend: function(evt) {\n- if (this.down) {\n- evt.xy = this.last.xy;\n- evt.lastTouches = this.last.touches;\n- this.handleSingle(evt);\n- this.down = null\n- }\n- return true\n- },\n- mousedown: function(evt) {\n- this.down = this.getEventInfo(evt);\n- this.last = this.getEventInfo(evt);\n- return true\n- },\n- mouseup: function(evt) {\n- var propagate = true;\n- if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) {\n- propagate = this.rightclick(evt)\n- }\n- return propagate\n- },\n- rightclick: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.rightclickTimerId != null) {\n- this.clearTimer();\n- this.callback(\"dblrightclick\", [evt]);\n- return !this.stopDouble\n- } else {\n- var clickEvent = this[\"double\"] ? OpenLayers.Util.extend({}, evt) : this.callback(\"rightclick\", [evt]);\n- var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent);\n- this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay)\n- }\n- }\n- return !this.stopSingle\n- },\n- delayedRightCall: function(evt) {\n- this.rightclickTimerId = null;\n- if (evt) {\n- this.callback(\"rightclick\", [evt])\n- }\n- },\n- click: function(evt) {\n- if (!this.last) {\n- this.last = this.getEventInfo(evt)\n- }\n- this.handleSingle(evt);\n- return !this.stopSingle\n- },\n- dblclick: function(evt) {\n- this.handleDouble(evt);\n- return !this.stopDouble\n- },\n- handleDouble: function(evt) {\n- if (this.passesDblclickTolerance(evt)) {\n- if (this[\"double\"]) {\n- this.callback(\"dblclick\", [evt])\n- }\n- this.clearTimer()\n- }\n- },\n- handleSingle: function(evt) {\n- if (this.passesTolerance(evt)) {\n- if (this.timerId != null) {\n- if (this.last.touches && this.last.touches.length === 1) {\n- if (this[\"double\"]) {\n- OpenLayers.Event.preventDefault(evt)\n- }\n- this.handleDouble(evt)\n- }\n- if (!this.last.touches || this.last.touches.length !== 2) {\n- this.clearTimer()\n- }\n- } else {\n- this.first = this.getEventInfo(evt);\n- var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null;\n- this.queuePotentialClick(clickEvent)\n- }\n- }\n- },\n- queuePotentialClick: function(evt) {\n- this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n- },\n- passesTolerance: function(evt) {\n- var passes = true;\n- if (this.pixelTolerance != null && this.down && this.down.xy) {\n- passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);\n- if (passes && this.touch && this.down.touches.length === this.last.touches.length) {\n- for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {\n- if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) {\n- passes = false;\n- break\n- }\n- }\n- }\n- }\n- return passes\n- },\n- getTouchDistance: function(from, to) {\n- return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2))\n- },\n- passesDblclickTolerance: function(evt) {\n- var passes = true;\n- if (this.down && this.first) {\n- passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance\n- }\n- return passes\n- },\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null\n- }\n- if (this.rightclickTimerId != null) {\n- window.clearTimeout(this.rightclickTimerId);\n- this.rightclickTimerId = null\n- }\n- },\n- delayedCall: function(evt) {\n- this.timerId = null;\n- if (evt) {\n- this.callback(\"click\", [evt])\n- }\n- },\n- getEventInfo: function(evt) {\n- var touches;\n- if (evt.touches) {\n- var len = evt.touches.length;\n- touches = new Array(len);\n- var touch;\n- for (var i = 0; i < len; i++) {\n- touch = evt.touches[i];\n- touches[i] = {\n- clientX: touch.olClientX,\n- clientY: touch.olClientY\n- }\n- }\n- }\n- return {\n- xy: evt.xy,\n- touches: touches\n- }\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- this.down = null;\n- this.first = null;\n- this.last = null;\n- deactivated = true\n- }\n- return deactivated\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Click\"\n-});\n OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {\n line: null,\n maxVertices: null,\n doubleTouchTolerance: 20,\n freehand: false,\n freehandToggle: \"shiftKey\",\n timerId: null,\n@@ -27799,259 +23403,65 @@\n if (!this.freehandMode(evt)) {\n this.finishGeometry()\n }\n return false\n },\n CLASS_NAME: \"OpenLayers.Handler.Path\"\n });\n-OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {\n- holeModifier: null,\n- drawingHole: false,\n- polygon: null,\n- createFeature: function(pixel) {\n- var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n- var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);\n- this.point = new OpenLayers.Feature.Vector(geometry);\n- this.line = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LinearRing([this.point.geometry]));\n- this.polygon = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([this.line.geometry]));\n- this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n- this.point.geometry.clearBounds();\n- this.layer.addFeatures([this.polygon, this.point], {\n- silent: true\n- })\n- },\n- addPoint: function(pixel) {\n- if (!this.drawingHole && this.holeModifier && this.evt && this.evt[this.holeModifier]) {\n- var geometry = this.point.geometry;\n- var features = this.control.layer.features;\n- var candidate, polygon;\n- for (var i = features.length - 1; i >= 0; --i) {\n- candidate = features[i].geometry;\n- if ((candidate instanceof OpenLayers.Geometry.Polygon || candidate instanceof OpenLayers.Geometry.MultiPolygon) && candidate.intersects(geometry)) {\n- polygon = features[i];\n- this.control.layer.removeFeatures([polygon], {\n- silent: true\n- });\n- this.control.layer.events.registerPriority(\"sketchcomplete\", this, this.finalizeInteriorRing);\n- this.control.layer.events.registerPriority(\"sketchmodified\", this, this.enforceTopology);\n- polygon.geometry.addComponent(this.line.geometry);\n- this.polygon = polygon;\n- this.drawingHole = true;\n- break\n- }\n- }\n- }\n- OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments)\n- },\n- getCurrentPointIndex: function() {\n- return this.line.geometry.components.length - 2\n- },\n- enforceTopology: function(event) {\n- var point = event.vertex;\n- var components = this.line.geometry.components;\n- if (!this.polygon.geometry.intersects(point)) {\n- var last = components[components.length - 3];\n- point.x = last.x;\n- point.y = last.y\n- }\n- },\n- finishGeometry: function() {\n- var index = this.line.geometry.components.length - 2;\n- this.line.geometry.removeComponent(this.line.geometry.components[index]);\n- this.removePoint();\n- this.finalize()\n- },\n- finalizeInteriorRing: function() {\n- var ring = this.line.geometry;\n- var modified = ring.getArea() !== 0;\n- if (modified) {\n- var rings = this.polygon.geometry.components;\n- for (var i = rings.length - 2; i >= 0; --i) {\n- if (ring.intersects(rings[i])) {\n- modified = false;\n- break\n- }\n- }\n- if (modified) {\n- var target;\n- outer: for (var i = rings.length - 2; i > 0; --i) {\n- var points = rings[i].components;\n- for (var j = 0, jj = points.length; j < jj; ++j) {\n- if (ring.containsPoint(points[j])) {\n- modified = false;\n- break outer\n- }\n- }\n- }\n- }\n- }\n- if (modified) {\n- if (this.polygon.state !== OpenLayers.State.INSERT) {\n- this.polygon.state = OpenLayers.State.UPDATE\n- }\n- } else {\n- this.polygon.geometry.removeComponent(ring)\n- }\n- this.restoreFeature();\n- return false\n- },\n- cancel: function() {\n- if (this.drawingHole) {\n- this.polygon.geometry.removeComponent(this.line.geometry);\n- this.restoreFeature(true)\n- }\n- return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments)\n- },\n- restoreFeature: function(cancel) {\n- this.control.layer.events.unregister(\"sketchcomplete\", this, this.finalizeInteriorRing);\n- this.control.layer.events.unregister(\"sketchmodified\", this, this.enforceTopology);\n- this.layer.removeFeatures([this.polygon], {\n- silent: true\n- });\n- this.control.layer.addFeatures([this.polygon], {\n- silent: true\n- });\n- this.drawingHole = false;\n- if (!cancel) {\n- this.control.layer.events.triggerEvent(\"sketchcomplete\", {\n- feature: this.polygon\n- })\n+OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {\n+ delay: 500,\n+ pixelTolerance: null,\n+ stopMove: false,\n+ px: null,\n+ timerId: null,\n+ mousemove: function(evt) {\n+ if (this.passesTolerance(evt.xy)) {\n+ this.clearTimer();\n+ this.callback(\"move\", [evt]);\n+ this.px = evt.xy;\n+ evt = OpenLayers.Util.extend({}, evt);\n+ this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n }\n+ return !this.stopMove\n },\n- destroyFeature: function(force) {\n- OpenLayers.Handler.Path.prototype.destroyFeature.call(this, force);\n- this.polygon = null\n- },\n- drawFeature: function() {\n- this.layer.drawFeature(this.polygon, this.style);\n- this.layer.drawFeature(this.point, this.style)\n- },\n- getSketch: function() {\n- return this.polygon\n- },\n- getGeometry: function() {\n- var geometry = this.polygon && this.polygon.geometry;\n- if (geometry && this.multi) {\n- geometry = new OpenLayers.Geometry.MultiPolygon([geometry])\n+ mouseout: function(evt) {\n+ if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ this.clearTimer();\n+ this.callback(\"move\", [evt])\n }\n- return geometry\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n-});\n-OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {\n- wheelListener: null,\n- interval: 0,\n- maxDelta: Number.POSITIVE_INFINITY,\n- delta: 0,\n- cumulative: true,\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n- this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this)\n- },\n- destroy: function() {\n- OpenLayers.Handler.prototype.destroy.apply(this, arguments);\n- this.wheelListener = null\n+ return true\n },\n- onWheelEvent: function(e) {\n- if (!this.map || !this.checkModifiers(e)) {\n- return\n- }\n- var overScrollableDiv = false;\n- var allowScroll = false;\n- var overMapDiv = false;\n- var elem = OpenLayers.Event.element(e);\n- while (elem != null && !overMapDiv && !overScrollableDiv) {\n- if (!overScrollableDiv) {\n- try {\n- var overflow;\n- if (elem.currentStyle) {\n- overflow = elem.currentStyle[\"overflow\"]\n- } else {\n- var style = document.defaultView.getComputedStyle(elem, null);\n- overflow = style.getPropertyValue(\"overflow\")\n- }\n- overScrollableDiv = overflow && overflow == \"auto\" || overflow == \"scroll\"\n- } catch (err) {}\n- }\n- if (!allowScroll) {\n- allowScroll = OpenLayers.Element.hasClass(elem, \"olScrollable\");\n- if (!allowScroll) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (elem == layer.div || elem == layer.pane) {\n- allowScroll = true;\n- break\n- }\n- }\n- }\n- }\n- overMapDiv = elem == this.map.div;\n- elem = elem.parentNode\n- }\n- if (!overScrollableDiv && overMapDiv) {\n- if (allowScroll) {\n- var delta = 0;\n- if (e.wheelDelta) {\n- delta = e.wheelDelta;\n- if (delta % 160 === 0) {\n- delta = delta * .75\n- }\n- delta = delta / 120\n- } else if (e.detail) {\n- delta = -(e.detail / Math.abs(e.detail))\n- }\n- this.delta += delta;\n- window.clearTimeout(this._timeoutId);\n- if (this.interval && Math.abs(this.delta) < this.maxDelta) {\n- var evt = OpenLayers.Util.extend({}, e);\n- this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() {\n- this.wheelZoom(evt)\n- }, this), this.interval)\n- } else {\n- this.wheelZoom(e)\n- }\n+ passesTolerance: function(px) {\n+ var passes = true;\n+ if (this.pixelTolerance && this.px) {\n+ var dpx = Math.sqrt(Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2));\n+ if (dpx < this.pixelTolerance) {\n+ passes = false\n }\n- OpenLayers.Event.stop(e)\n }\n+ return passes\n },\n- wheelZoom: function(e) {\n- var delta = this.delta;\n- this.delta = 0;\n- if (delta) {\n- e.xy = this.map.events.getMousePosition(e);\n- if (delta < 0) {\n- this.callback(\"down\", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1])\n- } else {\n- this.callback(\"up\", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1])\n- }\n+ clearTimer: function() {\n+ if (this.timerId != null) {\n+ window.clearTimeout(this.timerId);\n+ this.timerId = null\n }\n },\n- activate: function(evt) {\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.observe(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.observe(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.observe(document, \"mousewheel\", wheelListener);\n- return true\n- } else {\n- return false\n- }\n+ delayedCall: function(evt) {\n+ this.callback(\"pause\", [evt])\n },\n- deactivate: function(evt) {\n+ deactivate: function() {\n+ var deactivated = false;\n if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- var wheelListener = this.wheelListener;\n- OpenLayers.Event.stopObserving(window, \"DOMMouseScroll\", wheelListener);\n- OpenLayers.Event.stopObserving(window, \"mousewheel\", wheelListener);\n- OpenLayers.Event.stopObserving(document, \"mousewheel\", wheelListener);\n- return true\n- } else {\n- return false\n+ this.clearTimer();\n+ deactivated = true\n }\n+ return deactivated\n },\n- CLASS_NAME: \"OpenLayers.Handler.MouseWheel\"\n+ CLASS_NAME: \"OpenLayers.Handler.Hover\"\n });\n OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {\n KEY_EVENTS: [\"keydown\", \"keyup\"],\n eventListener: null,\n observeElement: null,\n initialize: function(control, callbacks, options) {\n OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n@@ -28086,66 +23496,14 @@\n handleKeyEvent: function(evt) {\n if (this.checkModifiers(evt)) {\n this.callback(evt.type, [evt])\n }\n },\n CLASS_NAME: \"OpenLayers.Handler.Keyboard\"\n });\n-OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {\n- delay: 500,\n- pixelTolerance: null,\n- stopMove: false,\n- px: null,\n- timerId: null,\n- mousemove: function(evt) {\n- if (this.passesTolerance(evt.xy)) {\n- this.clearTimer();\n- this.callback(\"move\", [evt]);\n- this.px = evt.xy;\n- evt = OpenLayers.Util.extend({}, evt);\n- this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay)\n- }\n- return !this.stopMove\n- },\n- mouseout: function(evt) {\n- if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n- this.clearTimer();\n- this.callback(\"move\", [evt])\n- }\n- return true\n- },\n- passesTolerance: function(px) {\n- var passes = true;\n- if (this.pixelTolerance && this.px) {\n- var dpx = Math.sqrt(Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2));\n- if (dpx < this.pixelTolerance) {\n- passes = false\n- }\n- }\n- return passes\n- },\n- clearTimer: function() {\n- if (this.timerId != null) {\n- window.clearTimeout(this.timerId);\n- this.timerId = null\n- }\n- },\n- delayedCall: function(evt) {\n- this.callback(\"pause\", [evt])\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.clearTimer();\n- deactivated = true\n- }\n- return deactivated\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Hover\"\n-});\n OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {\n started: false,\n stopDown: false,\n pinching: false,\n last: null,\n start: null,\n touchstart: function(evt) {\n@@ -28224,14 +23582,209 @@\n distance: distance,\n delta: this.last.distance - distance,\n scale: scale\n }\n },\n CLASS_NAME: \"OpenLayers.Handler.Pinch\"\n });\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+ started: false,\n+ stopDown: true,\n+ dragging: false,\n+ last: null,\n+ start: null,\n+ lastMoveEvt: null,\n+ oldOnselectstart: null,\n+ interval: 0,\n+ timeoutId: null,\n+ documentDrag: false,\n+ documentEvents: null,\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n+ if (this.documentDrag === true) {\n+ var me = this;\n+ this._docMove = function(evt) {\n+ me.mousemove({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ },\n+ element: document\n+ })\n+ };\n+ this._docUp = function(evt) {\n+ me.mouseup({\n+ xy: {\n+ x: evt.clientX,\n+ y: evt.clientY\n+ }\n+ })\n+ }\n+ }\n+ },\n+ dragstart: function(evt) {\n+ var propagate = true;\n+ this.dragging = false;\n+ if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) {\n+ this.started = true;\n+ this.start = evt.xy;\n+ this.last = evt.xy;\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.down(evt);\n+ this.callback(\"down\", [evt.xy]);\n+ OpenLayers.Event.preventDefault(evt);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True\n+ }\n+ document.onselectstart = OpenLayers.Function.False;\n+ propagate = !this.stopDown\n+ } else {\n+ this.started = false;\n+ this.start = null;\n+ this.last = null\n+ }\n+ return propagate\n+ },\n+ dragmove: function(evt) {\n+ this.lastMoveEvt = evt;\n+ if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ if (evt.element === document) {\n+ this.adjustXY(evt);\n+ this.setEvent(evt)\n+ } else {\n+ this.removeDocumentEvents()\n+ }\n+ }\n+ if (this.interval > 0) {\n+ this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval)\n+ }\n+ this.dragging = true;\n+ this.move(evt);\n+ this.callback(\"move\", [evt.xy]);\n+ if (!this.oldOnselectstart) {\n+ this.oldOnselectstart = document.onselectstart;\n+ document.onselectstart = OpenLayers.Function.False\n+ }\n+ this.last = evt.xy\n+ }\n+ return true\n+ },\n+ dragend: function(evt) {\n+ if (this.started) {\n+ if (this.documentDrag === true && this.documentEvents) {\n+ this.adjustXY(evt);\n+ this.removeDocumentEvents()\n+ }\n+ var dragged = this.start != this.last;\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.up(evt);\n+ this.callback(\"up\", [evt.xy]);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy])\n+ }\n+ document.onselectstart = this.oldOnselectstart\n+ }\n+ return true\n+ },\n+ down: function(evt) {},\n+ move: function(evt) {},\n+ up: function(evt) {},\n+ out: function(evt) {},\n+ mousedown: function(evt) {\n+ return this.dragstart(evt)\n+ },\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return this.dragstart(evt)\n+ },\n+ mousemove: function(evt) {\n+ return this.dragmove(evt)\n+ },\n+ touchmove: function(evt) {\n+ return this.dragmove(evt)\n+ },\n+ removeTimeout: function() {\n+ this.timeoutId = null;\n+ if (this.dragging) {\n+ this.mousemove(this.lastMoveEvt)\n+ }\n+ },\n+ mouseup: function(evt) {\n+ return this.dragend(evt)\n+ },\n+ touchend: function(evt) {\n+ evt.xy = this.last;\n+ return this.dragend(evt)\n+ },\n+ mouseout: function(evt) {\n+ if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {\n+ if (this.documentDrag === true) {\n+ this.addDocumentEvents()\n+ } else {\n+ var dragged = this.start != this.last;\n+ this.started = false;\n+ this.dragging = false;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\");\n+ this.out(evt);\n+ this.callback(\"out\", []);\n+ if (dragged) {\n+ this.callback(\"done\", [evt.xy])\n+ }\n+ if (document.onselectstart) {\n+ document.onselectstart = this.oldOnselectstart\n+ }\n+ }\n+ }\n+ return true\n+ },\n+ click: function(evt) {\n+ return this.start == this.last\n+ },\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.dragging = false;\n+ activated = true\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.started = false;\n+ this.dragging = false;\n+ this.start = null;\n+ this.last = null;\n+ deactivated = true;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olDragDown\")\n+ }\n+ return deactivated\n+ },\n+ adjustXY: function(evt) {\n+ var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);\n+ evt.xy.x -= pos[0];\n+ evt.xy.y -= pos[1]\n+ },\n+ addDocumentEvents: function() {\n+ OpenLayers.Element.addClass(document.body, \"olDragDown\");\n+ this.documentEvents = true;\n+ OpenLayers.Event.observe(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.observe(document, \"mouseup\", this._docUp)\n+ },\n+ removeDocumentEvents: function() {\n+ OpenLayers.Element.removeClass(document.body, \"olDragDown\");\n+ this.documentEvents = false;\n+ OpenLayers.Event.stopObserving(document, \"mousemove\", this._docMove);\n+ OpenLayers.Event.stopObserving(document, \"mouseup\", this._docUp)\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Drag\"\n+});\n OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {\n dragHandler: null,\n boxDivClassName: \"olHandlerBoxZoomBox\",\n boxOffsets: null,\n initialize: function(control, callbacks, options) {\n OpenLayers.Handler.prototype.initialize.apply(this, arguments);\n this.dragHandler = new OpenLayers.Handler.Drag(this, {\n@@ -28341,722 +23894,743 @@\n height: w3cBoxModel === false ? top + bottom : 0\n }\n }\n return this.boxOffsets\n },\n CLASS_NAME: \"OpenLayers.Handler.Box\"\n });\n-OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {\n- bounds: null,\n- div: null,\n- initialize: function(bounds, borderColor, borderWidth) {\n- this.bounds = bounds;\n- this.div = OpenLayers.Util.createDiv();\n- this.div.style.overflow = \"hidden\";\n- this.events = new OpenLayers.Events(this, this.div);\n- this.setBorder(borderColor, borderWidth)\n+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n+ EVENTMAP: {\n+ click: {\n+ in: \"click\",\n+ out: \"clickout\"\n+ },\n+ mousemove: {\n+ in: \"over\",\n+ out: \"out\"\n+ },\n+ dblclick: {\n+ in: \"dblclick\",\n+ out: null\n+ },\n+ mousedown: {\n+ in: null,\n+ out: null\n+ },\n+ mouseup: {\n+ in: null,\n+ out: null\n+ },\n+ touchstart: {\n+ in: \"click\",\n+ out: \"clickout\"\n+ }\n },\n- destroy: function() {\n- this.bounds = null;\n- this.div = null;\n- OpenLayers.Marker.prototype.destroy.apply(this, arguments)\n+ feature: null,\n+ lastFeature: null,\n+ down: null,\n+ up: null,\n+ clickTolerance: 4,\n+ geometryTypes: null,\n+ stopClick: true,\n+ stopDown: true,\n+ stopUp: false,\n+ initialize: function(control, layer, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.layer = layer\n },\n- setBorder: function(color, width) {\n- if (!color) {\n- color = \"red\"\n- }\n- if (!width) {\n- width = 2\n- }\n- this.div.style.border = width + \"px solid \" + color\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n },\n- draw: function(px, sz) {\n- OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);\n- return this.div\n+ touchmove: function(evt) {\n+ OpenLayers.Event.preventDefault(evt)\n },\n- onScreen: function() {\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsBounds(this.bounds, true, true)\n+ mousedown: function(evt) {\n+ if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n+ this.down = evt.xy\n }\n- return onScreen\n+ return this.handle(evt) ? !this.stopDown : true\n },\n- display: function(display) {\n- this.div.style.display = display ? \"\" : \"none\"\n+ mouseup: function(evt) {\n+ this.up = evt.xy;\n+ return this.handle(evt) ? !this.stopUp : true\n },\n- CLASS_NAME: \"OpenLayers.Marker.Box\"\n-});\n-OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {\n- distance: 20,\n- threshold: null,\n- features: null,\n- clusters: null,\n- clustering: false,\n- resolution: null,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- beforefeaturesadded: this.cacheFeatures,\n- featuresremoved: this.clearCache,\n- moveend: this.cluster,\n- scope: this\n- })\n- }\n- return activated\n+ click: function(evt) {\n+ return this.handle(evt) ? !this.stopClick : true\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.clearCache();\n- this.layer.events.un({\n- beforefeaturesadded: this.cacheFeatures,\n- featuresremoved: this.clearCache,\n- moveend: this.cluster,\n- scope: this\n- })\n+ mousemove: function(evt) {\n+ if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n+ return true\n }\n- return deactivated\n+ this.handle(evt);\n+ return true\n },\n- cacheFeatures: function(event) {\n- var propagate = true;\n- if (!this.clustering) {\n- this.clearCache();\n- this.features = event.features;\n- this.cluster();\n- propagate = false\n- }\n- return propagate\n+ dblclick: function(evt) {\n+ return !this.handle(evt)\n },\n- clearCache: function() {\n- if (!this.clustering) {\n- this.features = null\n- }\n+ geometryTypeMatches: function(feature) {\n+ return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n },\n- cluster: function(event) {\n- if ((!event || event.zoomChanged) && this.features) {\n- var resolution = this.layer.map.getResolution();\n- if (resolution != this.resolution || !this.clustersExist()) {\n- this.resolution = resolution;\n- var clusters = [];\n- var feature, clustered, cluster;\n- for (var i = 0; i < this.features.length; ++i) {\n- feature = this.features[i];\n- if (feature.geometry) {\n- clustered = false;\n- for (var j = clusters.length - 1; j >= 0; --j) {\n- cluster = clusters[j];\n- if (this.shouldCluster(cluster, feature)) {\n- this.addToCluster(cluster, feature);\n- clustered = true;\n- break\n- }\n- }\n- if (!clustered) {\n- clusters.push(this.createCluster(this.features[i]))\n- }\n+ handle: function(evt) {\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n+ }\n+ var type = evt.type;\n+ var handled = false;\n+ var previouslyIn = !!this.feature;\n+ var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n+ this.feature = this.layer.getFeatureFromEvent(evt);\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n+ }\n+ if (this.lastFeature && !this.lastFeature.layer) {\n+ this.lastFeature = null\n+ }\n+ if (this.feature) {\n+ if (type === \"touchstart\") {\n+ OpenLayers.Event.preventDefault(evt)\n+ }\n+ var inNew = this.feature != this.lastFeature;\n+ if (this.geometryTypeMatches(this.feature)) {\n+ if (previouslyIn && inNew) {\n+ if (this.lastFeature) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n+ this.triggerCallback(type, \"in\", [this.feature])\n+ } else if (!previouslyIn || click) {\n+ this.triggerCallback(type, \"in\", [this.feature])\n }\n- this.clustering = true;\n- this.layer.removeAllFeatures();\n- this.clustering = false;\n- if (clusters.length > 0) {\n- if (this.threshold > 1) {\n- var clone = clusters.slice();\n- clusters = [];\n- var candidate;\n- for (var i = 0, len = clone.length; i < len; ++i) {\n- candidate = clone[i];\n- if (candidate.attributes.count < this.threshold) {\n- Array.prototype.push.apply(clusters, candidate.cluster)\n- } else {\n- clusters.push(candidate)\n- }\n- }\n- }\n- this.clustering = true;\n- this.layer.addFeatures(clusters);\n- this.clustering = false\n+ this.lastFeature = this.feature;\n+ handled = true\n+ } else {\n+ if (this.lastFeature && (previouslyIn && inNew || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n- this.clusters = clusters\n+ this.feature = null\n }\n+ } else if (this.lastFeature && (previouslyIn || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n+ return handled\n },\n- clustersExist: function() {\n- var exist = false;\n- if (this.clusters && this.clusters.length > 0 && this.clusters.length == this.layer.features.length) {\n- exist = true;\n- for (var i = 0; i < this.clusters.length; ++i) {\n- if (this.clusters[i] != this.layer.features[i]) {\n- exist = false;\n- break\n+ triggerCallback: function(type, mode, args) {\n+ var key = this.EVENTMAP[type][mode];\n+ if (key) {\n+ if (type == \"click\" && this.up && this.down) {\n+ var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n+ if (dpx <= this.clickTolerance) {\n+ this.callback(key, args)\n }\n+ this.up = this.down = null\n+ } else {\n+ this.callback(key, args)\n }\n }\n- return exist\n- },\n- shouldCluster: function(cluster, feature) {\n- var cc = cluster.geometry.getBounds().getCenterLonLat();\n- var fc = feature.geometry.getBounds().getCenterLonLat();\n- var distance = Math.sqrt(Math.pow(cc.lon - fc.lon, 2) + Math.pow(cc.lat - fc.lat, 2)) / this.resolution;\n- return distance <= this.distance\n- },\n- addToCluster: function(cluster, feature) {\n- cluster.cluster.push(feature);\n- cluster.attributes.count += 1\n- },\n- createCluster: function(feature) {\n- var center = feature.geometry.getBounds().getCenterLonLat();\n- var cluster = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(center.lon, center.lat), {\n- count: 1\n- });\n- cluster.cluster = [feature];\n- return cluster\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Cluster\"\n-});\n-OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {\n- features: null,\n- length: 10,\n- num: null,\n- paging: false,\n activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- beforefeaturesadded: this.cacheFeatures,\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n scope: this\n- })\n+ });\n+ activated = true\n }\n return activated\n },\n deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.clearCache();\n- this.layer.events.un({\n- beforefeaturesadded: this.cacheFeatures,\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.feature = null;\n+ this.lastFeature = null;\n+ this.down = null;\n+ this.up = null;\n+ this.map.events.un({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n scope: this\n- })\n+ });\n+ deactivated = true\n }\n return deactivated\n },\n- cacheFeatures: function(event) {\n- if (!this.paging) {\n- this.clearCache();\n- this.features = event.features;\n- this.pageNext(event)\n- }\n- },\n- clearCache: function() {\n- if (this.features) {\n- for (var i = 0; i < this.features.length; ++i) {\n- this.features[i].destroy()\n- }\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop()\n }\n- this.features = null;\n- this.num = null\n- },\n- pageCount: function() {\n- var numFeatures = this.features ? this.features.length : 0;\n- return Math.ceil(numFeatures / this.length)\n },\n- pageNum: function() {\n- return this.num\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index)\n },\n- pageLength: function(newLength) {\n- if (newLength && newLength > 0) {\n- this.length = newLength\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n+ this.layer.setZIndex(index)\n+ } else {\n+ this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n }\n- return this.length\n },\n- pageNext: function(event) {\n- var changed = false;\n- if (this.features) {\n- if (this.num === null) {\n- this.num = -1\n+ CLASS_NAME: \"OpenLayers.Handler.Feature\"\n+});\n+OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {\n+ holeModifier: null,\n+ drawingHole: false,\n+ polygon: null,\n+ createFeature: function(pixel) {\n+ var lonlat = this.layer.getLonLatFromViewPortPx(pixel);\n+ var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);\n+ this.point = new OpenLayers.Feature.Vector(geometry);\n+ this.line = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LinearRing([this.point.geometry]));\n+ this.polygon = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([this.line.geometry]));\n+ this.callback(\"create\", [this.point.geometry, this.getSketch()]);\n+ this.point.geometry.clearBounds();\n+ this.layer.addFeatures([this.polygon, this.point], {\n+ silent: true\n+ })\n+ },\n+ addPoint: function(pixel) {\n+ if (!this.drawingHole && this.holeModifier && this.evt && this.evt[this.holeModifier]) {\n+ var geometry = this.point.geometry;\n+ var features = this.control.layer.features;\n+ var candidate, polygon;\n+ for (var i = features.length - 1; i >= 0; --i) {\n+ candidate = features[i].geometry;\n+ if ((candidate instanceof OpenLayers.Geometry.Polygon || candidate instanceof OpenLayers.Geometry.MultiPolygon) && candidate.intersects(geometry)) {\n+ polygon = features[i];\n+ this.control.layer.removeFeatures([polygon], {\n+ silent: true\n+ });\n+ this.control.layer.events.registerPriority(\"sketchcomplete\", this, this.finalizeInteriorRing);\n+ this.control.layer.events.registerPriority(\"sketchmodified\", this, this.enforceTopology);\n+ polygon.geometry.addComponent(this.line.geometry);\n+ this.polygon = polygon;\n+ this.drawingHole = true;\n+ break\n+ }\n }\n- var start = (this.num + 1) * this.length;\n- changed = this.page(start, event)\n }\n- return changed\n+ OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments)\n },\n- pagePrevious: function() {\n- var changed = false;\n- if (this.features) {\n- if (this.num === null) {\n- this.num = this.pageCount()\n- }\n- var start = (this.num - 1) * this.length;\n- changed = this.page(start)\n+ getCurrentPointIndex: function() {\n+ return this.line.geometry.components.length - 2\n+ },\n+ enforceTopology: function(event) {\n+ var point = event.vertex;\n+ var components = this.line.geometry.components;\n+ if (!this.polygon.geometry.intersects(point)) {\n+ var last = components[components.length - 3];\n+ point.x = last.x;\n+ point.y = last.y\n }\n- return changed\n },\n- page: function(start, event) {\n- var changed = false;\n- if (this.features) {\n- if (start >= 0 && start < this.features.length) {\n- var num = Math.floor(start / this.length);\n- if (num != this.num) {\n- this.paging = true;\n- var features = this.features.slice(start, start + this.length);\n- this.layer.removeFeatures(this.layer.features);\n- this.num = num;\n- if (event && event.features) {\n- event.features = features\n- } else {\n- this.layer.addFeatures(features)\n+ finishGeometry: function() {\n+ var index = this.line.geometry.components.length - 2;\n+ this.line.geometry.removeComponent(this.line.geometry.components[index]);\n+ this.removePoint();\n+ this.finalize()\n+ },\n+ finalizeInteriorRing: function() {\n+ var ring = this.line.geometry;\n+ var modified = ring.getArea() !== 0;\n+ if (modified) {\n+ var rings = this.polygon.geometry.components;\n+ for (var i = rings.length - 2; i >= 0; --i) {\n+ if (ring.intersects(rings[i])) {\n+ modified = false;\n+ break\n+ }\n+ }\n+ if (modified) {\n+ var target;\n+ outer: for (var i = rings.length - 2; i > 0; --i) {\n+ var points = rings[i].components;\n+ for (var j = 0, jj = points.length; j < jj; ++j) {\n+ if (ring.containsPoint(points[j])) {\n+ modified = false;\n+ break outer\n+ }\n }\n- this.paging = false;\n- changed = true\n }\n }\n }\n- return changed\n+ if (modified) {\n+ if (this.polygon.state !== OpenLayers.State.INSERT) {\n+ this.polygon.state = OpenLayers.State.UPDATE\n+ }\n+ } else {\n+ this.polygon.geometry.removeComponent(ring)\n+ }\n+ this.restoreFeature();\n+ return false\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Paging\"\n+ cancel: function() {\n+ if (this.drawingHole) {\n+ this.polygon.geometry.removeComponent(this.line.geometry);\n+ this.restoreFeature(true)\n+ }\n+ return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments)\n+ },\n+ restoreFeature: function(cancel) {\n+ this.control.layer.events.unregister(\"sketchcomplete\", this, this.finalizeInteriorRing);\n+ this.control.layer.events.unregister(\"sketchmodified\", this, this.enforceTopology);\n+ this.layer.removeFeatures([this.polygon], {\n+ silent: true\n+ });\n+ this.control.layer.addFeatures([this.polygon], {\n+ silent: true\n+ });\n+ this.drawingHole = false;\n+ if (!cancel) {\n+ this.control.layer.events.triggerEvent(\"sketchcomplete\", {\n+ feature: this.polygon\n+ })\n+ }\n+ },\n+ destroyFeature: function(force) {\n+ OpenLayers.Handler.Path.prototype.destroyFeature.call(this, force);\n+ this.polygon = null\n+ },\n+ drawFeature: function() {\n+ this.layer.drawFeature(this.polygon, this.style);\n+ this.layer.drawFeature(this.point, this.style)\n+ },\n+ getSketch: function() {\n+ return this.polygon\n+ },\n+ getGeometry: function() {\n+ var geometry = this.polygon && this.polygon.geometry;\n+ if (geometry && this.multi) {\n+ geometry = new OpenLayers.Geometry.MultiPolygon([geometry])\n+ }\n+ return geometry\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n });\n-OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {\n- filter: null,\n- cache: null,\n- caching: false,\n+OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {\n+ sides: 4,\n+ radius: null,\n+ snapAngle: null,\n+ snapToggle: \"shiftKey\",\n+ layerOptions: null,\n+ persist: false,\n+ irregular: false,\n+ citeCompliant: false,\n+ angle: null,\n+ fixedRadius: false,\n+ feature: null,\n+ layer: null,\n+ origin: null,\n+ initialize: function(control, callbacks, options) {\n+ if (!(options && options.layerOptions && options.layerOptions.styleMap)) {\n+ this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style[\"default\"], {})\n+ }\n+ OpenLayers.Handler.Drag.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.options = options ? options : {}\n+ },\n+ setOptions: function(newOptions) {\n+ OpenLayers.Util.extend(this.options, newOptions);\n+ OpenLayers.Util.extend(this, newOptions)\n+ },\n activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.cache = [];\n- this.layer.events.on({\n- beforefeaturesadded: this.handleAdd,\n- beforefeaturesremoved: this.handleRemove,\n- scope: this\n- })\n+ var activated = false;\n+ if (OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {\n+ var options = OpenLayers.Util.extend({\n+ displayInLayerSwitcher: false,\n+ calculateInRange: OpenLayers.Function.True,\n+ wrapDateLine: this.citeCompliant\n+ }, this.layerOptions);\n+ this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);\n+ this.map.addLayer(this.layer);\n+ activated = true\n }\n return activated\n },\n deactivate: function() {\n- this.cache = null;\n- if (this.layer && this.layer.events) {\n- this.layer.events.un({\n- beforefeaturesadded: this.handleAdd,\n- beforefeaturesremoved: this.handleRemove,\n- scope: this\n- })\n- }\n- return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments)\n- },\n- handleAdd: function(event) {\n- if (!this.caching && this.filter) {\n- var features = event.features;\n- event.features = [];\n- var feature;\n- for (var i = 0, ii = features.length; i < ii; ++i) {\n- feature = features[i];\n- if (this.filter.evaluate(feature)) {\n- event.features.push(feature)\n- } else {\n- this.cache.push(feature)\n+ var deactivated = false;\n+ if (OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {\n+ if (this.dragging) {\n+ this.cancel()\n+ }\n+ if (this.layer.map != null) {\n+ this.layer.destroy(false);\n+ if (this.feature) {\n+ this.feature.destroy()\n }\n }\n+ this.layer = null;\n+ this.feature = null;\n+ deactivated = true\n }\n+ return deactivated\n },\n- handleRemove: function(event) {\n- if (!this.caching) {\n- this.cache = []\n+ down: function(evt) {\n+ this.fixedRadius = !!this.radius;\n+ var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n+ this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n+ if (!this.fixedRadius || this.irregular) {\n+ this.radius = this.map.getResolution()\n }\n- },\n- setFilter: function(filter) {\n- this.filter = filter;\n- var previousCache = this.cache;\n- this.cache = [];\n- this.handleAdd({\n- features: this.layer.features\n+ if (this.persist) {\n+ this.clear()\n+ }\n+ this.feature = new OpenLayers.Feature.Vector;\n+ this.createGeometry();\n+ this.callback(\"create\", [this.origin, this.feature]);\n+ this.layer.addFeatures([this.feature], {\n+ silent: true\n });\n- if (this.cache.length > 0) {\n- this.caching = true;\n- this.layer.removeFeatures(this.cache.slice());\n- this.caching = false\n+ this.layer.drawFeature(this.feature, this.style)\n+ },\n+ move: function(evt) {\n+ var maploc = this.layer.getLonLatFromViewPortPx(evt.xy);\n+ var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);\n+ if (this.irregular) {\n+ var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;\n+ this.radius = Math.max(this.map.getResolution() / 2, ry)\n+ } else if (this.fixedRadius) {\n+ this.origin = point\n+ } else {\n+ this.calculateAngle(point, evt);\n+ this.radius = Math.max(this.map.getResolution() / 2, point.distanceTo(this.origin))\n }\n- if (previousCache.length > 0) {\n- var event = {\n- features: previousCache\n- };\n- this.handleAdd(event);\n- if (event.features.length > 0) {\n- this.caching = true;\n- this.layer.addFeatures(event.features);\n- this.caching = false\n+ this.modifyGeometry();\n+ if (this.irregular) {\n+ var dx = point.x - this.origin.x;\n+ var dy = point.y - this.origin.y;\n+ var ratio;\n+ if (dy == 0) {\n+ ratio = dx / (this.radius * Math.sqrt(2))\n+ } else {\n+ ratio = dx / dy\n }\n+ this.feature.geometry.resize(1, this.origin, ratio);\n+ this.feature.geometry.move(dx / 2, dy / 2)\n }\n+ this.layer.drawFeature(this.feature, this.style)\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Filter\"\n-});\n-OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {\n- force: false,\n- interval: 0,\n- timer: null,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- if (this.layer.visibility === true) {\n- this.start()\n- }\n- this.layer.events.on({\n- visibilitychanged: this.reset,\n- scope: this\n- })\n+ up: function(evt) {\n+ this.finalize();\n+ if (this.start == this.last) {\n+ this.callback(\"done\", [evt.xy])\n }\n- return activated\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.stop();\n- this.layer.events.un({\n- visibilitychanged: this.reset,\n- scope: this\n- })\n+ out: function(evt) {\n+ this.finalize()\n+ },\n+ createGeometry: function() {\n+ this.angle = Math.PI * (1 / this.sides - 1 / 2);\n+ if (this.snapAngle) {\n+ this.angle += this.snapAngle * (Math.PI / 180)\n }\n- return deactivated\n+ this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin, this.radius, this.sides, this.snapAngle)\n },\n- reset: function() {\n- if (this.layer.visibility === true) {\n- this.start()\n- } else {\n- this.stop()\n+ modifyGeometry: function() {\n+ var angle, point;\n+ var ring = this.feature.geometry.components[0];\n+ if (ring.components.length != this.sides + 1) {\n+ this.createGeometry();\n+ ring = this.feature.geometry.components[0]\n+ }\n+ for (var i = 0; i < this.sides; ++i) {\n+ point = ring.components[i];\n+ angle = this.angle + i * 2 * Math.PI / this.sides;\n+ point.x = this.origin.x + this.radius * Math.cos(angle);\n+ point.y = this.origin.y + this.radius * Math.sin(angle);\n+ point.clearBounds()\n }\n },\n- start: function() {\n- if (this.interval && typeof this.interval === \"number\" && this.interval > 0) {\n- this.timer = window.setInterval(OpenLayers.Function.bind(this.refresh, this), this.interval)\n+ calculateAngle: function(point, evt) {\n+ var alpha = Math.atan2(point.y - this.origin.y, point.x - this.origin.x);\n+ if (this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {\n+ var snapAngleRad = Math.PI / 180 * this.snapAngle;\n+ this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad\n+ } else {\n+ this.angle = alpha\n }\n },\n- refresh: function() {\n- if (this.layer && this.layer.refresh && typeof this.layer.refresh == \"function\") {\n- this.layer.refresh({\n- force: this.force\n- })\n+ cancel: function() {\n+ this.callback(\"cancel\", null);\n+ this.finalize()\n+ },\n+ finalize: function() {\n+ this.origin = null;\n+ this.radius = this.options.radius\n+ },\n+ clear: function() {\n+ if (this.layer) {\n+ this.layer.renderer.clear();\n+ this.layer.destroyFeatures()\n }\n },\n- stop: function() {\n- if (this.timer !== null) {\n- window.clearInterval(this.timer);\n- this.timer = null\n+ callback: function(name, args) {\n+ if (this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, [this.feature.geometry.clone()])\n+ }\n+ if (!this.persist && (name == \"done\" || name == \"cancel\")) {\n+ this.clear()\n }\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Refresh\"\n+ CLASS_NAME: \"OpenLayers.Handler.RegularPolygon\"\n });\n-OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {\n- events: null,\n- auto: false,\n- timer: null,\n- initialize: function(options) {\n- OpenLayers.Strategy.prototype.initialize.apply(this, [options]);\n- this.events = new OpenLayers.Events(this)\n+OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {\n+ callbacks: null,\n+ displaySystem: \"metric\",\n+ geodesic: false,\n+ displaySystemUnits: {\n+ geographic: [\"dd\"],\n+ english: [\"mi\", \"ft\", \"in\"],\n+ metric: [\"km\", \"m\"]\n },\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- if (this.auto) {\n- if (typeof this.auto === \"number\") {\n- this.timer = window.setInterval(OpenLayers.Function.bind(this.save, this), this.auto * 1e3)\n- } else {\n- this.layer.events.on({\n- featureadded: this.triggerSave,\n- afterfeaturemodified: this.triggerSave,\n- scope: this\n- })\n- }\n- }\n+ partialDelay: 300,\n+ delayedTrigger: null,\n+ persist: false,\n+ immediate: false,\n+ initialize: function(handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ var callbacks = {\n+ done: this.measureComplete,\n+ point: this.measurePartial\n+ };\n+ if (this.immediate) {\n+ callbacks.modify = this.measureImmediate\n }\n- return activated\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlerOptions = OpenLayers.Util.extend({\n+ persist: this.persist\n+ }, this.handlerOptions);\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions)\n },\n deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.auto) {\n- if (typeof this.auto === \"number\") {\n- window.clearInterval(this.timer)\n- } else {\n- this.layer.events.un({\n- featureadded: this.triggerSave,\n- afterfeaturemodified: this.triggerSave,\n- scope: this\n- })\n- }\n- }\n- }\n- return deactivated\n+ this.cancelDelay();\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- triggerSave: function(event) {\n- var feature = event.feature;\n- if (feature.state === OpenLayers.State.INSERT || feature.state === OpenLayers.State.UPDATE || feature.state === OpenLayers.State.DELETE) {\n- this.save([event.feature])\n+ cancel: function() {\n+ this.cancelDelay();\n+ this.handler.cancel()\n+ },\n+ setImmediate: function(immediate) {\n+ this.immediate = immediate;\n+ if (this.immediate) {\n+ this.callbacks.modify = this.measureImmediate\n+ } else {\n+ delete this.callbacks.modify\n }\n },\n- save: function(features) {\n- if (!features) {\n- features = this.layer.features\n+ updateHandler: function(handler, options) {\n+ var active = this.active;\n+ if (active) {\n+ this.deactivate()\n }\n- this.events.triggerEvent(\"start\", {\n- features: features\n- });\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var len = features.length;\n- var clones = new Array(len);\n- var orig, clone;\n- for (var i = 0; i < len; ++i) {\n- orig = features[i];\n- clone = orig.clone();\n- clone.fid = orig.fid;\n- clone.state = orig.state;\n- if (orig.url) {\n- clone.url = orig.url\n- }\n- clone._original = orig;\n- clone.geometry.transform(local, remote);\n- clones[i] = clone\n- }\n- features = clones\n+ this.handler = new handler(this, this.callbacks, options);\n+ if (active) {\n+ this.activate()\n }\n- this.layer.protocol.commit(features, {\n- callback: this.onCommit,\n- scope: this\n- })\n },\n- onCommit: function(response) {\n- var evt = {\n- response: response\n- };\n- if (response.success()) {\n- var features = response.reqFeatures;\n- var state, feature;\n- var destroys = [];\n- var insertIds = response.insertIds || [];\n- var j = 0;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- feature = feature._original || feature;\n- state = feature.state;\n- if (state) {\n- if (state == OpenLayers.State.DELETE) {\n- destroys.push(feature)\n- } else if (state == OpenLayers.State.INSERT) {\n- feature.fid = insertIds[j];\n- ++j\n- }\n- feature.state = null\n- }\n- }\n- if (destroys.length > 0) {\n- this.layer.destroyFeatures(destroys)\n- }\n- this.events.triggerEvent(\"success\", evt)\n+ measureComplete: function(geometry) {\n+ this.cancelDelay();\n+ this.measure(geometry, \"measure\")\n+ },\n+ measurePartial: function(point, geometry) {\n+ this.cancelDelay();\n+ geometry = geometry.clone();\n+ if (this.handler.freehandMode(this.handler.evt)) {\n+ this.measure(geometry, \"measurepartial\")\n } else {\n- this.events.triggerEvent(\"fail\", evt)\n+ this.delayedTrigger = window.setTimeout(OpenLayers.Function.bind(function() {\n+ this.delayedTrigger = null;\n+ this.measure(geometry, \"measurepartial\")\n+ }, this), this.partialDelay)\n }\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Save\"\n-});\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n- preload: false,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- refresh: this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load()\n- } else {\n- this.layer.events.on({\n- visibilitychanged: this.load,\n- scope: this\n- })\n- }\n+ measureImmediate: function(point, feature, drawing) {\n+ if (drawing && !this.handler.freehandMode(this.handler.evt)) {\n+ this.cancelDelay();\n+ this.measure(feature.geometry, \"measurepartial\")\n }\n- return activated\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- refresh: this.load,\n- visibilitychanged: this.load,\n- scope: this\n- })\n+ cancelDelay: function() {\n+ if (this.delayedTrigger !== null) {\n+ window.clearTimeout(this.delayedTrigger);\n+ this.delayedTrigger = null\n }\n- return deactivated\n },\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- visibilitychanged: this.load,\n- scope: this\n+ measure: function(geometry, eventType) {\n+ var stat, order;\n+ if (geometry.CLASS_NAME.indexOf(\"LineString\") > -1) {\n+ stat = this.getBestLength(geometry);\n+ order = 1\n+ } else {\n+ stat = this.getBestArea(geometry);\n+ order = 2\n+ }\n+ this.events.triggerEvent(eventType, {\n+ measure: stat[0],\n+ units: stat[1],\n+ order: order,\n+ geometry: geometry\n })\n },\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local)\n- }\n- }\n+ getBestArea: function(geometry) {\n+ var units = this.displaySystemUnits[this.displaySystem];\n+ var unit, area;\n+ for (var i = 0, len = units.length; i < len; ++i) {\n+ unit = units[i];\n+ area = this.getArea(geometry, unit);\n+ if (area > 1) {\n+ break\n }\n- layer.addFeatures(features)\n }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- })\n+ return [area, unit]\n },\n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n-});\n-OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n- bounds: null,\n- resolution: null,\n- ratio: 2,\n- resFactor: null,\n- response: null,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.call(this);\n- if (activated) {\n- this.layer.events.on({\n- moveend: this.update,\n- refresh: this.update,\n- visibilitychanged: this.update,\n- scope: this\n- });\n- this.update()\n+ getArea: function(geometry, units) {\n+ var area, geomUnits;\n+ if (this.geodesic) {\n+ area = geometry.getGeodesicArea(this.map.getProjectionObject());\n+ geomUnits = \"m\"\n+ } else {\n+ area = geometry.getArea();\n+ geomUnits = this.map.getUnits()\n }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- moveend: this.update,\n- refresh: this.update,\n- visibilitychanged: this.update,\n- scope: this\n- })\n+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n+ if (inPerDisplayUnit) {\n+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n+ area *= Math.pow(inPerMapUnit / inPerDisplayUnit, 2)\n }\n- return deactivated\n+ return area\n },\n- update: function(options) {\n- var mapBounds = this.getMapBounds();\n- if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) {\n- this.calculateBounds(mapBounds);\n- this.resolution = this.layer.map.getResolution();\n- this.triggerRead(options)\n+ getBestLength: function(geometry) {\n+ var units = this.displaySystemUnits[this.displaySystem];\n+ var unit, length;\n+ for (var i = 0, len = units.length; i < len; ++i) {\n+ unit = units[i];\n+ length = this.getLength(geometry, unit);\n+ if (length > 1) {\n+ break\n+ }\n }\n+ return [length, unit]\n },\n- getMapBounds: function() {\n- if (this.layer.map === null) {\n- return null\n+ getLength: function(geometry, units) {\n+ var length, geomUnits;\n+ if (this.geodesic) {\n+ length = geometry.getGeodesicLength(this.map.getProjectionObject());\n+ geomUnits = \"m\"\n+ } else {\n+ length = geometry.getLength();\n+ geomUnits = this.map.getUnits()\n }\n- var bounds = this.layer.map.getExtent();\n- if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) {\n- bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection)\n+ var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n+ if (inPerDisplayUnit) {\n+ var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n+ length *= inPerMapUnit / inPerDisplayUnit\n }\n- return bounds\n+ return length\n },\n- invalidBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds()\n- }\n- var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n- if (!invalid && this.resFactor) {\n- var ratio = this.resolution / this.layer.map.getResolution();\n- invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor\n+ CLASS_NAME: \"OpenLayers.Control.Measure\"\n+});\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n+ target: null,\n+ events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n+ startRegEx: /^mousedown|touchstart$/,\n+ cancelRegEx: /^touchmove$/,\n+ completeRegEx: /^mouseup|touchend$/,\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ })\n }\n- return invalid\n },\n- calculateBounds: function(mapBounds) {\n- if (!mapBounds) {\n- mapBounds = this.getMapBounds()\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick)\n }\n- var center = mapBounds.getCenterLonLat();\n- var dataWidth = mapBounds.getWidth() * this.ratio;\n- var dataHeight = mapBounds.getHeight() * this.ratio;\n- this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2)\n+ delete this.target\n },\n- triggerRead: function(options) {\n- if (this.response && !(options && options.noAbort === true)) {\n- this.layer.protocol.abort(this.response);\n- this.layer.events.triggerEvent(\"loadend\")\n- }\n- var evt = {\n- filter: this.createFilter()\n- };\n- this.layer.events.triggerEvent(\"loadstart\", evt);\n- this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({\n- filter: evt.filter,\n- callback: this.merge,\n- scope: this\n- }, options))\n+ getPressedButton: function(element) {\n+ var depth = 3,\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ button = element;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return button\n },\n- createFilter: function() {\n- var filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- value: this.bounds,\n- projection: this.layer.projection\n- });\n- if (this.layer.filter) {\n- filter = new OpenLayers.Filter.Logical({\n- type: OpenLayers.Filter.Logical.AND,\n- filters: [this.layer.filter, filter]\n- })\n- }\n- return filter\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === \"a\") {\n+ ignore = true;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return ignore\n },\n- merge: function(resp) {\n- this.layer.destroyFeatures();\n- if (resp.success()) {\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = this.layer.projection;\n- var local = this.layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local)\n- }\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ })\n }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n }\n- this.layer.addFeatures(features)\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt\n }\n- } else {\n- this.bounds = null\n }\n- this.response = null;\n- this.layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- })\n- },\n- CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n+ return propagate\n+ }\n });\n OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n controls: null,\n autoActivate: true,\n defaultControl: null,\n saveState: false,\n allowDepress: false,\n@@ -29237,372 +24811,1004 @@\n return this.getControlsBy(\"name\", match)\n },\n getControlsByClass: function(match) {\n return this.getControlsBy(\"CLASS_NAME\", match)\n },\n CLASS_NAME: \"OpenLayers.Control.Panel\"\n });\n-OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_TOOL,\n- out: false,\n- keyMask: null,\n- alwaysZoom: false,\n- zoomOnClick: true,\n- draw: function() {\n- this.handler = new OpenLayers.Handler.Box(this, {\n- done: this.zoomBox\n- }, {\n- keyMask: this.keyMask\n- })\n+OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_BUTTON,\n+ trigger: function() {},\n+ CLASS_NAME: \"OpenLayers.Control.Button\"\n+});\n+OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomIn()\n+ }\n },\n- zoomBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var bounds, targetCenterPx = position.getCenterPixel();\n- if (!this.out) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n- } else {\n- var pixWidth = position.right - position.left;\n- var pixHeight = position.bottom - position.top;\n- var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth);\n- var extent = this.map.getExtent();\n- var center = this.map.getLonLatFromPixel(targetCenterPx);\n- var xmin = center.lon - extent.getWidth() / 2 * zoomFactor;\n- var xmax = center.lon + extent.getWidth() / 2 * zoomFactor;\n- var ymin = center.lat - extent.getHeight() / 2 * zoomFactor;\n- var ymax = center.lat + extent.getHeight() / 2 * zoomFactor;\n- bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax)\n+ CLASS_NAME: \"OpenLayers.Control.ZoomIn\"\n+});\n+OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomOut()\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ZoomOut\"\n+});\n+OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {\n+ trigger: function() {\n+ if (this.map) {\n+ this.map.zoomToMaxExtent()\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ZoomToMaxExtent\"\n+});\n+OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n+ initialize: function(options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ this.addControls([new OpenLayers.Control.ZoomIn, new OpenLayers.Control.ZoomToMaxExtent, new OpenLayers.Control.ZoomOut])\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ZoomPanel\"\n+});\n+OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {\n+ autoActivate: true,\n+ layers: null,\n+ defaultHandlerOptions: {\n+ delay: 300,\n+ pixelTolerance: 4,\n+ stopMove: false,\n+ single: true,\n+ double: false,\n+ stopSingle: false,\n+ stopDouble: false\n+ },\n+ handlerMode: \"click\",\n+ setHandler: function(hm) {\n+ this.handlerMode = hm;\n+ this.resetHandler()\n+ },\n+ resetHandler: function() {\n+ if (this.handler) {\n+ this.handler.deactivate();\n+ this.handler.destroy();\n+ this.handler = null\n+ }\n+ if (this.handlerMode == \"hover\") {\n+ this.handler = new OpenLayers.Handler.Hover(this, {\n+ pause: this.handleEvent,\n+ move: this.reset\n+ }, this.handlerOptions)\n+ } else if (this.handlerMode == \"click\") {\n+ this.handler = new OpenLayers.Handler.Click(this, {\n+ click: this.handleEvent\n+ }, this.handlerOptions)\n+ } else if (this.handlerMode == \"move\") {\n+ this.handler = new OpenLayers.Handler.Hover(this, {\n+ pause: this.handleEvent,\n+ move: this.handleEvent\n+ }, this.handlerOptions)\n+ }\n+ if (this.handler) {\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ initialize: function(options) {\n+ options = options || {};\n+ options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.resetHandler()\n+ },\n+ handleEvent: function(evt) {\n+ if (evt == null) {\n+ this.reset();\n+ return\n+ }\n+ var lonLat = this.map.getLonLatFromPixel(evt.xy);\n+ if (!lonLat) {\n+ return\n+ }\n+ var layers = this.findLayers();\n+ if (layers.length > 0) {\n+ var infoLookup = {};\n+ var layer, idx;\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ layer = layers[i];\n+ idx = OpenLayers.Util.indexOf(this.map.layers, layer);\n+ infoLookup[idx] = layer.getFeatureInfo(lonLat)\n }\n- var lastZoom = this.map.getZoom(),\n- size = this.map.getSize(),\n- centerPx = {\n- x: size.w / 2,\n- y: size.h / 2\n- },\n- zoom = this.map.getZoomForExtent(bounds),\n- oldRes = this.map.getResolution(),\n- newRes = this.map.getResolutionForZoom(zoom);\n- if (oldRes == newRes) {\n- this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx))\n- } else {\n- var zoomOriginPx = {\n- x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes),\n- y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes)\n- };\n- this.map.zoomTo(zoom, zoomOriginPx)\n+ this.callback(infoLookup, lonLat, evt.xy)\n+ }\n+ },\n+ callback: function(infoLookup) {},\n+ reset: function(evt) {\n+ this.callback(null)\n+ },\n+ findLayers: function() {\n+ var candidates = this.layers || this.map.layers;\n+ var layers = [];\n+ var layer;\n+ for (var i = candidates.length - 1; i >= 0; --i) {\n+ layer = candidates[i];\n+ if (layer instanceof OpenLayers.Layer.UTFGrid) {\n+ layers.push(layer)\n }\n- if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n- this.map.zoomTo(lastZoom + (this.out ? -1 : 1))\n+ }\n+ return layers\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.UTFGrid\"\n+});\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+ isBaseLayer: false,\n+ isFixed: false,\n+ features: null,\n+ filter: null,\n+ selectedFeatures: null,\n+ unrenderedFeatures: null,\n+ reportError: true,\n+ style: null,\n+ styleMap: null,\n+ strategies: null,\n+ protocol: null,\n+ renderers: [\"SVG\", \"VML\", \"Canvas\"],\n+ renderer: null,\n+ rendererOptions: null,\n+ geometryType: null,\n+ drawn: false,\n+ ratio: 1,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.assignRenderer()\n+ }\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.renderer = null;\n+ this.displayError()\n+ }\n+ if (!this.styleMap) {\n+ this.styleMap = new OpenLayers.StyleMap\n+ }\n+ this.features = [];\n+ this.selectedFeatures = [];\n+ this.unrenderedFeatures = {};\n+ if (this.strategies) {\n+ for (var i = 0, len = this.strategies.length; i < len; i++) {\n+ this.strategies[i].setLayer(this)\n }\n- } else if (this.zoomOnClick) {\n- if (!this.out) {\n- this.map.zoomTo(this.map.getZoom() + 1, position)\n- } else {\n- this.map.zoomTo(this.map.getZoom() - 1, position)\n+ }\n+ },\n+ destroy: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoDestroy) {\n+ strategy.destroy()\n+ }\n }\n+ this.strategies = null\n+ }\n+ if (this.protocol) {\n+ if (this.protocol.autoDestroy) {\n+ this.protocol.destroy()\n+ }\n+ this.protocol = null\n+ }\n+ this.destroyFeatures();\n+ this.features = null;\n+ this.selectedFeatures = null;\n+ this.unrenderedFeatures = null;\n+ if (this.renderer) {\n+ this.renderer.destroy()\n }\n+ this.renderer = null;\n+ this.geometryType = null;\n+ this.drawn = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n-});\n-OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_TOOL,\n- panned: false,\n- interval: 0,\n- documentDrag: false,\n- kinetic: null,\n- enableKinetic: true,\n- kineticInterval: 10,\n- draw: function() {\n- if (this.enableKinetic && OpenLayers.Kinetic) {\n- var config = {\n- interval: this.kineticInterval\n- };\n- if (typeof this.enableKinetic === \"object\") {\n- config = OpenLayers.Util.extend(config, this.enableKinetic)\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+ var features = this.features;\n+ var len = features.length;\n+ var clonedFeatures = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ clonedFeatures[i] = features[i].clone()\n+ }\n+ obj.features = clonedFeatures;\n+ return obj\n+ },\n+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj)\n+ }\n+ },\n+ assignRenderer: function() {\n+ for (var i = 0, len = this.renderers.length; i < len; i++) {\n+ var rendererClass = this.renderers[i];\n+ var renderer = typeof rendererClass == \"function\" ? rendererClass : OpenLayers.Renderer[rendererClass];\n+ if (renderer && renderer.prototype.supported()) {\n+ this.renderer = new renderer(this.div, this.rendererOptions);\n+ break\n }\n- this.kinetic = new OpenLayers.Kinetic(config)\n }\n- this.handler = new OpenLayers.Handler.Drag(this, {\n- move: this.panMap,\n- done: this.panMapDone,\n- down: this.panMapStart\n- }, {\n- interval: this.interval,\n- documentDrag: this.documentDrag\n- })\n },\n- panMapStart: function() {\n- if (this.kinetic) {\n- this.kinetic.begin()\n+ displayError: function() {\n+ if (this.reportError) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n+ renderers: this.renderers.join(\"\\n\")\n+ }))\n }\n },\n- panMap: function(xy) {\n- if (this.kinetic) {\n- this.kinetic.update(xy)\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ if (!this.renderer) {\n+ this.map.removeLayer(this)\n+ } else {\n+ this.renderer.map = this.map;\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize)\n }\n- this.panned = true;\n- this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n- dragging: true,\n- animate: false\n- })\n },\n- panMapDone: function(xy) {\n- if (this.panned) {\n- var res = null;\n- if (this.kinetic) {\n- res = this.kinetic.end(xy)\n+ afterAdd: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.activate()\n+ }\n }\n- this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n- dragging: !!res,\n- animate: false\n- });\n- if (res) {\n- var self = this;\n- this.kinetic.move(res, function(x, y, end) {\n- self.map.pan(x, y, {\n- dragging: !end,\n- animate: false\n- })\n- })\n+ }\n+ },\n+ removeMap: function(map) {\n+ this.drawn = false;\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.deactivate()\n+ }\n }\n- this.panned = false\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.DragPan\"\n-});\n-OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n- dragPan: null,\n- dragPanOptions: null,\n- pinchZoom: null,\n- pinchZoomOptions: null,\n- documentDrag: false,\n- zoomBox: null,\n- zoomBoxEnabled: true,\n- zoomWheelEnabled: true,\n- mouseWheelOptions: null,\n- handleRightClicks: false,\n- zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n- autoActivate: true,\n- initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments)\n+ onMapResize: function() {\n+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize)\n },\n- destroy: function() {\n- this.deactivate();\n- if (this.dragPan) {\n- this.dragPan.destroy()\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ var coordSysUnchanged = true;\n+ if (!dragging) {\n+ this.renderer.root.style.visibility = \"hidden\";\n+ var viewSize = this.map.getSize(),\n+ viewWidth = viewSize.w,\n+ viewHeight = viewSize.h,\n+ offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2,\n+ offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2;\n+ offsetLeft += this.map.layerContainerOriginPx.x;\n+ offsetLeft = -Math.round(offsetLeft);\n+ offsetTop += this.map.layerContainerOriginPx.y;\n+ offsetTop = -Math.round(offsetTop);\n+ this.div.style.left = offsetLeft + \"px\";\n+ this.div.style.top = offsetTop + \"px\";\n+ var extent = this.map.getExtent().scale(this.ratio);\n+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+ this.renderer.root.style.visibility = \"visible\";\n+ if (OpenLayers.IS_GECKO === true) {\n+ this.div.scrollLeft = this.div.scrollLeft\n+ }\n+ if (!zoomChanged && coordSysUnchanged) {\n+ for (var i in this.unrenderedFeatures) {\n+ var feature = this.unrenderedFeatures[i];\n+ this.drawFeature(feature)\n+ }\n+ }\n }\n- this.dragPan = null;\n- if (this.zoomBox) {\n- this.zoomBox.destroy()\n+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n+ this.drawn = true;\n+ var feature;\n+ for (var i = 0, len = this.features.length; i < len; i++) {\n+ this.renderer.locked = i !== len - 1;\n+ feature = this.features[i];\n+ this.drawFeature(feature)\n+ }\n }\n- this.zoomBox = null;\n- if (this.pinchZoom) {\n- this.pinchZoom.destroy()\n+ },\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ var currentDisplay = this.div.style.display;\n+ if (currentDisplay != this.renderer.root.style.display) {\n+ this.renderer.root.style.display = currentDisplay\n }\n- this.pinchZoom = null;\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- this.dragPan.activate();\n- if (this.zoomWheelEnabled) {\n- this.handlers.wheel.activate()\n+ addFeatures: function(features, options) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n }\n- this.handlers.click.activate();\n- if (this.zoomBoxEnabled) {\n- this.zoomBox.activate()\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ var event = {\n+ features: features\n+ };\n+ var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n+ if (ret === false) {\n+ return\n+ }\n+ features = event.features\n }\n- if (this.pinchZoom) {\n- this.pinchZoom.activate()\n+ var featuresAdded = [];\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ if (i != features.length - 1) {\n+ this.renderer.locked = true\n+ } else {\n+ this.renderer.locked = false\n+ }\n+ var feature = features[i];\n+ if (this.geometryType && !(feature.geometry instanceof this.geometryType)) {\n+ throw new TypeError(\"addFeatures: component should be an \" + this.geometryType.prototype.CLASS_NAME)\n+ }\n+ feature.layer = this;\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style)\n+ }\n+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue\n+ }\n+ this.preFeatureInsert(feature)\n+ }\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n+ if (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature)\n+ }\n+ }\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ })\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n- deactivate: function() {\n- if (this.pinchZoom) {\n- this.pinchZoom.deactivate()\n+ removeFeatures: function(features, options) {\n+ if (!features || features.length === 0) {\n+ return\n+ }\n+ if (features === this.features) {\n+ return this.removeAllFeatures(options)\n+ }\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ if (features === this.selectedFeatures) {\n+ features = features.slice()\n+ }\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeaturesremoved\", {\n+ features: features\n+ })\n+ }\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ if (i != 0 && features[i - 1].geometry) {\n+ this.renderer.locked = true\n+ } else {\n+ this.renderer.locked = false\n+ }\n+ var feature = features[i];\n+ delete this.unrenderedFeatures[feature.id];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ this.features = OpenLayers.Util.removeItem(this.features, feature);\n+ feature.layer = null;\n+ if (feature.geometry) {\n+ this.renderer.eraseFeatures(feature)\n+ }\n+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n+ OpenLayers.Util.removeItem(this.selectedFeatures, feature)\n+ }\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ }\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ })\n }\n- this.zoomBox.deactivate();\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.handlers.wheel.deactivate();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- draw: function() {\n- if (this.handleRightClicks) {\n- this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False\n+ removeAllFeatures: function(options) {\n+ var notify = !options || !options.silent;\n+ var features = this.features;\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeaturesremoved\", {\n+ features: features\n+ })\n }\n- var clickCallbacks = {\n- click: this.defaultClick,\n- dblclick: this.defaultDblClick,\n- dblrightclick: this.defaultDblRightClick\n- };\n- var clickOptions = {\n- double: true,\n- stopDouble: true\n- };\n- this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n- this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions));\n- this.zoomBox = new OpenLayers.Control.ZoomBox({\n- map: this.map,\n- keyMask: this.zoomBoxKeyMask\n- });\n- this.dragPan.draw();\n- this.zoomBox.draw();\n- var wheelOptions = this.map.fractionalZoom ? {} : {\n- cumulative: false,\n- interval: 50,\n- maxDelta: 6\n- };\n- this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, {\n- up: this.wheelUp,\n- down: this.wheelDown\n- }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions));\n- if (OpenLayers.Control.PinchZoom) {\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions))\n+ var feature;\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ feature = features[i];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ feature.layer = null;\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ }\n+ this.renderer.clear();\n+ this.features = [];\n+ this.unrenderedFeatures = {};\n+ this.selectedFeatures = [];\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ })\n }\n },\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut()\n+ destroyFeatures: function(features, options) {\n+ var all = features == undefined;\n+ if (all) {\n+ features = this.features\n+ }\n+ if (features) {\n+ this.removeFeatures(features, options);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ features[i].destroy()\n+ }\n }\n },\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy)\n+ drawFeature: function(feature, style) {\n+ if (!this.drawn) {\n+ return\n+ }\n+ if (typeof style != \"object\") {\n+ if (!style && feature.state === OpenLayers.State.DELETE) {\n+ style = \"delete\"\n+ }\n+ var renderIntent = style || feature.renderIntent;\n+ style = feature.style || this.style;\n+ if (!style) {\n+ style = this.styleMap.createSymbolizer(feature, renderIntent)\n+ }\n+ }\n+ var drawn = this.renderer.drawFeature(feature, style);\n+ if (drawn === false || drawn === null) {\n+ this.unrenderedFeatures[feature.id] = feature\n+ } else {\n+ delete this.unrenderedFeatures[feature.id]\n+ }\n },\n- defaultDblRightClick: function(evt) {\n- this.map.zoomTo(this.map.zoom - 1, evt.xy)\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features)\n },\n- wheelChange: function(evt, deltaZ) {\n- if (!this.map.fractionalZoom) {\n- deltaZ = Math.round(deltaZ)\n+ getFeatureFromEvent: function(evt) {\n+ if (!this.renderer) {\n+ throw new Error(\"getFeatureFromEvent called on layer with no \" + \"renderer. This usually means you destroyed a \" + \"layer, but not some handler which is associated \" + \"with it.\")\n }\n- var currentZoom = this.map.getZoom(),\n- newZoom = currentZoom + deltaZ;\n- newZoom = Math.max(newZoom, 0);\n- newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n- if (newZoom === currentZoom) {\n- return\n+ var feature = null;\n+ var featureId = this.renderer.getFeatureIdFromEvent(evt);\n+ if (featureId) {\n+ if (typeof featureId === \"string\") {\n+ feature = this.getFeatureById(featureId)\n+ } else {\n+ feature = featureId\n+ }\n }\n- this.map.zoomTo(newZoom, evt.xy)\n+ return feature\n },\n- wheelUp: function(evt, delta) {\n- this.wheelChange(evt, delta || 1)\n+ getFeatureBy: function(property, value) {\n+ var feature = null;\n+ for (var i = 0, len = this.features.length; i < len; ++i) {\n+ if (this.features[i][property] == value) {\n+ feature = this.features[i];\n+ break\n+ }\n+ }\n+ return feature\n },\n- wheelDown: function(evt, delta) {\n- this.wheelChange(evt, delta || -1)\n+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy(\"id\", featureId)\n },\n- disableZoomBox: function() {\n- this.zoomBoxEnabled = false;\n- this.zoomBox.deactivate()\n+ getFeatureByFid: function(featureFid) {\n+ return this.getFeatureBy(\"fid\", featureFid)\n },\n- enableZoomBox: function() {\n- this.zoomBoxEnabled = true;\n- if (this.active) {\n- this.zoomBox.activate()\n+ getFeaturesByAttribute: function(attrName, attrValue) {\n+ var i, feature, len = this.features.length,\n+ foundFeatures = [];\n+ for (i = 0; i < len; i++) {\n+ feature = this.features[i];\n+ if (feature && feature.attributes) {\n+ if (feature.attributes[attrName] === attrValue) {\n+ foundFeatures.push(feature)\n+ }\n+ }\n }\n+ return foundFeatures\n },\n- disableZoomWheel: function() {\n- this.zoomWheelEnabled = false;\n- this.handlers.wheel.deactivate()\n- },\n- enableZoomWheel: function() {\n- this.zoomWheelEnabled = true;\n- if (this.active) {\n- this.handlers.wheel.activate()\n+ onFeatureInsert: function(feature) {},\n+ preFeatureInsert: function(feature) {},\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ var features = this.features;\n+ if (features && features.length > 0) {\n+ var geometry = null;\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ geometry = features[i].geometry;\n+ if (geometry) {\n+ if (maxExtent === null) {\n+ maxExtent = new OpenLayers.Bounds\n+ }\n+ maxExtent.extend(geometry.getBounds())\n+ }\n+ }\n }\n+ return maxExtent\n },\n- CLASS_NAME: \"OpenLayers.Control.Navigation\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector\"\n });\n-OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {\n+OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {\n+ DEFAULTS: {\n+ tolerance: 10,\n+ node: true,\n+ edge: true,\n+ vertex: true\n+ },\n+ greedy: true,\n+ precedence: [\"node\", \"vertex\", \"edge\"],\n+ resolution: null,\n+ geoToleranceCache: null,\n+ layer: null,\n+ feature: null,\n+ point: null,\n initialize: function(options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n- this.addControls([new OpenLayers.Control.Navigation, new OpenLayers.Control.ZoomBox])\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.options = options || {};\n+ if (this.options.layer) {\n+ this.setLayer(this.options.layer)\n+ }\n+ var defaults = OpenLayers.Util.extend({}, this.options.defaults);\n+ this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);\n+ this.setTargets(this.options.targets);\n+ if (this.targets.length === 0 && this.layer) {\n+ this.addTargetLayer(this.layer)\n+ }\n+ this.geoToleranceCache = {}\n },\n- draw: function() {\n- var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n- if (this.defaultControl === null) {\n- this.defaultControl = this.controls[0]\n+ setLayer: function(layer) {\n+ if (this.active) {\n+ this.deactivate();\n+ this.layer = layer;\n+ this.activate()\n+ } else {\n+ this.layer = layer\n }\n- return div\n },\n- CLASS_NAME: \"OpenLayers.Control.NavToolbar\"\n+ setTargets: function(targets) {\n+ this.targets = [];\n+ if (targets && targets.length) {\n+ var target;\n+ for (var i = 0, len = targets.length; i < len; ++i) {\n+ target = targets[i];\n+ if (target instanceof OpenLayers.Layer.Vector) {\n+ this.addTargetLayer(target)\n+ } else {\n+ this.addTarget(target)\n+ }\n+ }\n+ }\n+ },\n+ addTargetLayer: function(layer) {\n+ this.addTarget({\n+ layer: layer\n+ })\n+ },\n+ addTarget: function(target) {\n+ target = OpenLayers.Util.applyDefaults(target, this.defaults);\n+ target.nodeTolerance = target.nodeTolerance || target.tolerance;\n+ target.vertexTolerance = target.vertexTolerance || target.tolerance;\n+ target.edgeTolerance = target.edgeTolerance || target.tolerance;\n+ this.targets.push(target)\n+ },\n+ removeTargetLayer: function(layer) {\n+ var target;\n+ for (var i = this.targets.length - 1; i >= 0; --i) {\n+ target = this.targets[i];\n+ if (target.layer === layer) {\n+ this.removeTarget(target)\n+ }\n+ }\n+ },\n+ removeTarget: function(target) {\n+ return OpenLayers.Util.removeItem(this.targets, target)\n+ },\n+ activate: function() {\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.on({\n+ sketchstarted: this.onSketchModified,\n+ sketchmodified: this.onSketchModified,\n+ vertexmodified: this.onVertexModified,\n+ scope: this\n+ })\n+ }\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.un({\n+ sketchstarted: this.onSketchModified,\n+ sketchmodified: this.onSketchModified,\n+ vertexmodified: this.onVertexModified,\n+ scope: this\n+ })\n+ }\n+ }\n+ this.feature = null;\n+ this.point = null;\n+ return deactivated\n+ },\n+ onSketchModified: function(event) {\n+ this.feature = event.feature;\n+ this.considerSnapping(event.vertex, event.vertex)\n+ },\n+ onVertexModified: function(event) {\n+ this.feature = event.feature;\n+ var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);\n+ this.considerSnapping(event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat))\n+ },\n+ considerSnapping: function(point, loc) {\n+ var best = {\n+ rank: Number.POSITIVE_INFINITY,\n+ dist: Number.POSITIVE_INFINITY,\n+ x: null,\n+ y: null\n+ };\n+ var snapped = false;\n+ var result, target;\n+ for (var i = 0, len = this.targets.length; i < len; ++i) {\n+ target = this.targets[i];\n+ result = this.testTarget(target, loc);\n+ if (result) {\n+ if (this.greedy) {\n+ best = result;\n+ best.target = target;\n+ snapped = true;\n+ break\n+ } else {\n+ if (result.rank < best.rank || result.rank === best.rank && result.dist < best.dist) {\n+ best = result;\n+ best.target = target;\n+ snapped = true\n+ }\n+ }\n+ }\n+ }\n+ if (snapped) {\n+ var proceed = this.events.triggerEvent(\"beforesnap\", {\n+ point: point,\n+ x: best.x,\n+ y: best.y,\n+ distance: best.dist,\n+ layer: best.target.layer,\n+ snapType: this.precedence[best.rank]\n+ });\n+ if (proceed !== false) {\n+ point.x = best.x;\n+ point.y = best.y;\n+ this.point = point;\n+ this.events.triggerEvent(\"snap\", {\n+ point: point,\n+ snapType: this.precedence[best.rank],\n+ layer: best.target.layer,\n+ distance: best.dist\n+ })\n+ } else {\n+ snapped = false\n+ }\n+ }\n+ if (this.point && !snapped) {\n+ point.x = loc.x;\n+ point.y = loc.y;\n+ this.point = null;\n+ this.events.triggerEvent(\"unsnap\", {\n+ point: point\n+ })\n+ }\n+ },\n+ testTarget: function(target, loc) {\n+ var resolution = this.layer.map.getResolution();\n+ if (\"minResolution\" in target) {\n+ if (resolution < target.minResolution) {\n+ return null\n+ }\n+ }\n+ if (\"maxResolution\" in target) {\n+ if (resolution >= target.maxResolution) {\n+ return null\n+ }\n+ }\n+ var tolerance = {\n+ node: this.getGeoTolerance(target.nodeTolerance, resolution),\n+ vertex: this.getGeoTolerance(target.vertexTolerance, resolution),\n+ edge: this.getGeoTolerance(target.edgeTolerance, resolution)\n+ };\n+ var maxTolerance = Math.max(tolerance.node, tolerance.vertex, tolerance.edge);\n+ var result = {\n+ rank: Number.POSITIVE_INFINITY,\n+ dist: Number.POSITIVE_INFINITY\n+ };\n+ var eligible = false;\n+ var features = target.layer.features;\n+ var feature, type, vertices, vertex, closest, dist, found;\n+ var numTypes = this.precedence.length;\n+ var ll = new OpenLayers.LonLat(loc.x, loc.y);\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ if (feature !== this.feature && !feature._sketch && feature.state !== OpenLayers.State.DELETE && (!target.filter || target.filter.evaluate(feature))) {\n+ if (feature.atPoint(ll, maxTolerance, maxTolerance)) {\n+ for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) {\n+ type = this.precedence[j];\n+ if (target[type]) {\n+ if (type === \"edge\") {\n+ closest = feature.geometry.distanceTo(loc, {\n+ details: true\n+ });\n+ dist = closest.distance;\n+ if (dist <= tolerance[type] && dist < result.dist) {\n+ result = {\n+ rank: j,\n+ dist: dist,\n+ x: closest.x0,\n+ y: closest.y0\n+ };\n+ eligible = true;\n+ break\n+ }\n+ } else {\n+ vertices = feature.geometry.getVertices(type === \"node\");\n+ found = false;\n+ for (var k = 0, klen = vertices.length; k < klen; ++k) {\n+ vertex = vertices[k];\n+ dist = vertex.distanceTo(loc);\n+ if (dist <= tolerance[type] && (j < result.rank || j === result.rank && dist < result.dist)) {\n+ result = {\n+ rank: j,\n+ dist: dist,\n+ x: vertex.x,\n+ y: vertex.y\n+ };\n+ eligible = true;\n+ found = true\n+ }\n+ }\n+ if (found) {\n+ break\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return eligible ? result : null\n+ },\n+ getGeoTolerance: function(tolerance, resolution) {\n+ if (resolution !== this.resolution) {\n+ this.resolution = resolution;\n+ this.geoToleranceCache = {}\n+ }\n+ var geoTolerance = this.geoToleranceCache[tolerance];\n+ if (geoTolerance === undefined) {\n+ geoTolerance = tolerance * resolution;\n+ this.geoToleranceCache[tolerance] = geoTolerance\n+ }\n+ return geoTolerance\n+ },\n+ destroy: function() {\n+ if (this.active) {\n+ this.deactivate()\n+ }\n+ delete this.layer;\n+ delete this.targets;\n+ OpenLayers.Control.prototype.destroy.call(this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Snapping\"\n });\n-OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {\n- fetchEvent: \"tileloadstart\",\n+OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n+ hover: false,\n+ requestEncoding: \"KVP\",\n+ drillDown: false,\n+ maxFeatures: 10,\n+ clickCallback: \"click\",\n layers: null,\n- autoActivate: true,\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- var i, layers = this.layers || map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.addLayer({\n- layer: layers[i]\n- })\n+ queryVisible: true,\n+ infoFormat: \"text/html\",\n+ vendorParams: {},\n+ format: null,\n+ formatOptions: null,\n+ handler: null,\n+ hoverRequest: null,\n+ pending: 0,\n+ initialize: function(options) {\n+ options = options || {};\n+ options.handlerOptions = options.handlerOptions || {};\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions)\n }\n- if (!this.layers) {\n- map.events.on({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n- scope: this\n- })\n+ if (this.drillDown === true) {\n+ this.hover = false\n+ }\n+ if (this.hover) {\n+ this.handler = new OpenLayers.Handler.Hover(this, {\n+ move: this.cancelHover,\n+ pause: this.getInfoForHover\n+ }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, {\n+ delay: 250\n+ }))\n+ } else {\n+ var callbacks = {};\n+ callbacks[this.clickCallback] = this.getInfoForClick;\n+ this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {})\n }\n },\n- addLayer: function(evt) {\n- evt.layer.events.register(this.fetchEvent, this, this.fetch)\n+ getInfoForClick: function(evt) {\n+ this.request(evt.xy, {})\n },\n- removeLayer: function(evt) {\n- evt.layer.events.unregister(this.fetchEvent, this, this.fetch)\n+ getInfoForHover: function(evt) {\n+ this.request(evt.xy, {\n+ hover: true\n+ })\n },\n- fetch: function(evt) {\n- if (this.active && window.localStorage && evt.tile instanceof OpenLayers.Tile.Image) {\n- var tile = evt.tile,\n- url = tile.url;\n- if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && url.indexOf(OpenLayers.ProxyHost) === 0) {\n- url = OpenLayers.Control.CacheWrite.urlMap[url]\n+ cancelHover: function() {\n+ if (this.hoverRequest) {\n+ --this.pending;\n+ if (this.pending <= 0) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ this.pending = 0\n }\n- var dataURI = window.localStorage.getItem(\"olCache_\" + url);\n- if (dataURI) {\n- tile.url = dataURI;\n- if (evt.type === \"tileerror\") {\n- tile.setImgSrc(dataURI)\n+ this.hoverRequest.abort();\n+ this.hoverRequest = null\n+ }\n+ },\n+ findLayers: function() {\n+ var candidates = this.layers || this.map.layers;\n+ var layers = [];\n+ var layer;\n+ for (var i = candidates.length - 1; i >= 0; --i) {\n+ layer = candidates[i];\n+ if (layer instanceof OpenLayers.Layer.WMTS && layer.requestEncoding === this.requestEncoding && (!this.queryVisible || layer.getVisibility())) {\n+ layers.push(layer);\n+ if (!this.drillDown || this.hover) {\n+ break\n }\n }\n }\n+ return layers\n },\n- destroy: function() {\n- if (this.layers || this.map) {\n- var i, layers = this.layers || this.map.layers;\n- for (i = layers.length - 1; i >= 0; --i) {\n- this.removeLayer({\n- layer: layers[i]\n- })\n+ buildRequestOptions: function(layer, xy) {\n+ var loc = this.map.getLonLatFromPixel(xy);\n+ var getTileUrl = layer.getURL(new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat));\n+ var params = OpenLayers.Util.getParameters(getTileUrl);\n+ var tileInfo = layer.getTileInfo(loc);\n+ OpenLayers.Util.extend(params, {\n+ service: \"WMTS\",\n+ version: layer.version,\n+ request: \"GetFeatureInfo\",\n+ infoFormat: this.infoFormat,\n+ i: tileInfo.i,\n+ j: tileInfo.j\n+ });\n+ OpenLayers.Util.applyDefaults(params, this.vendorParams);\n+ return {\n+ url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,\n+ params: OpenLayers.Util.upperCaseObject(params),\n+ callback: function(request) {\n+ this.handleResponse(xy, request, layer)\n+ },\n+ scope: this\n+ }\n+ },\n+ request: function(xy, options) {\n+ options = options || {};\n+ var layers = this.findLayers();\n+ if (layers.length > 0) {\n+ var issue, layer;\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ layer = layers[i];\n+ issue = this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: xy,\n+ layer: layer\n+ });\n+ if (issue !== false) {\n+ ++this.pending;\n+ var requestOptions = this.buildRequestOptions(layer, xy);\n+ var request = OpenLayers.Request.GET(requestOptions);\n+ if (options.hover === true) {\n+ this.hoverRequest = request\n+ }\n+ }\n+ }\n+ if (this.pending > 0) {\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\")\n }\n }\n- if (this.map) {\n- this.map.events.un({\n- addlayer: this.addLayer,\n- removeLayer: this.removeLayer,\n- scope: this\n+ },\n+ handleResponse: function(xy, request, layer) {\n+ --this.pending;\n+ if (this.pending <= 0) {\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ this.pending = 0\n+ }\n+ if (request.status && (request.status < 200 || request.status >= 300)) {\n+ this.events.triggerEvent(\"exception\", {\n+ xy: xy,\n+ request: request,\n+ layer: layer\n })\n+ } else {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n+ }\n+ var features, except;\n+ try {\n+ features = this.format.read(doc)\n+ } catch (error) {\n+ except = true;\n+ this.events.triggerEvent(\"exception\", {\n+ xy: xy,\n+ request: request,\n+ error: error,\n+ layer: layer\n+ })\n+ }\n+ if (!except) {\n+ this.events.triggerEvent(\"getfeatureinfo\", {\n+ text: request.responseText,\n+ features: features,\n+ request: request,\n+ xy: xy,\n+ layer: layer\n+ })\n+ }\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Control.CacheRead\"\n+ CLASS_NAME: \"OpenLayers.Control.WMTSGetFeatureInfo\"\n });\n OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {\n documentDrag: false,\n geometryTypes: null,\n clickout: true,\n toggle: true,\n standalone: false,\n@@ -30054,228 +26260,296 @@\n },\n CLASS_NAME: \"OpenLayers.Control.ModifyFeature\"\n });\n OpenLayers.Control.ModifyFeature.RESHAPE = 1;\n OpenLayers.Control.ModifyFeature.RESIZE = 2;\n OpenLayers.Control.ModifyFeature.ROTATE = 4;\n OpenLayers.Control.ModifyFeature.DRAG = 8;\n-OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n- layer: null,\n- callbacks: null,\n- multi: false,\n- featureAdded: function() {},\n- initialize: function(layer, handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.callbacks = OpenLayers.Util.extend({\n- done: this.drawFeature,\n- modify: function(vertex, feature) {\n- this.layer.events.triggerEvent(\"sketchmodified\", {\n- vertex: vertex,\n- feature: feature\n- })\n- },\n- create: function(vertex, feature) {\n- this.layer.events.triggerEvent(\"sketchstarted\", {\n- vertex: vertex,\n- feature: feature\n- })\n- }\n- }, this.callbacks);\n- this.layer = layer;\n- this.handlerOptions = this.handlerOptions || {};\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n- renderers: layer.renderers,\n- rendererOptions: layer.rendererOptions\n- });\n- if (!(\"multi\" in this.handlerOptions)) {\n- this.handlerOptions.multi = this.multi\n- }\n- var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;\n- if (sketchStyle) {\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n- styleMap: new OpenLayers.StyleMap({\n- default: sketchStyle\n- })\n- })\n- }\n- this.handler = new handler(this, this.callbacks, this.handlerOptions)\n- },\n- drawFeature: function(geometry) {\n- var feature = new OpenLayers.Feature.Vector(geometry);\n- var proceed = this.layer.events.triggerEvent(\"sketchcomplete\", {\n- feature: feature\n- });\n- if (proceed !== false) {\n- feature.state = OpenLayers.State.INSERT;\n- this.layer.addFeatures([feature]);\n- this.featureAdded(feature);\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- })\n- }\n- },\n- insertXY: function(x, y) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertXY(x, y)\n- }\n- },\n- insertDeltaXY: function(dx, dy) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDeltaXY(dx, dy)\n- }\n- },\n- insertDirectionLength: function(direction, length) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDirectionLength(direction, length)\n- }\n+OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_TOOL,\n+ out: false,\n+ keyMask: null,\n+ alwaysZoom: false,\n+ zoomOnClick: true,\n+ draw: function() {\n+ this.handler = new OpenLayers.Handler.Box(this, {\n+ done: this.zoomBox\n+ }, {\n+ keyMask: this.keyMask\n+ })\n },\n- insertDeflectionLength: function(deflection, length) {\n- if (this.handler && this.handler.line) {\n- this.handler.insertDeflectionLength(deflection, length)\n+ zoomBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var bounds, targetCenterPx = position.getCenterPixel();\n+ if (!this.out) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n+ } else {\n+ var pixWidth = position.right - position.left;\n+ var pixHeight = position.bottom - position.top;\n+ var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth);\n+ var extent = this.map.getExtent();\n+ var center = this.map.getLonLatFromPixel(targetCenterPx);\n+ var xmin = center.lon - extent.getWidth() / 2 * zoomFactor;\n+ var xmax = center.lon + extent.getWidth() / 2 * zoomFactor;\n+ var ymin = center.lat - extent.getHeight() / 2 * zoomFactor;\n+ var ymax = center.lat + extent.getHeight() / 2 * zoomFactor;\n+ bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax)\n+ }\n+ var lastZoom = this.map.getZoom(),\n+ size = this.map.getSize(),\n+ centerPx = {\n+ x: size.w / 2,\n+ y: size.h / 2\n+ },\n+ zoom = this.map.getZoomForExtent(bounds),\n+ oldRes = this.map.getResolution(),\n+ newRes = this.map.getResolutionForZoom(zoom);\n+ if (oldRes == newRes) {\n+ this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx))\n+ } else {\n+ var zoomOriginPx = {\n+ x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes),\n+ y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes)\n+ };\n+ this.map.zoomTo(zoom, zoomOriginPx)\n+ }\n+ if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {\n+ this.map.zoomTo(lastZoom + (this.out ? -1 : 1))\n+ }\n+ } else if (this.zoomOnClick) {\n+ if (!this.out) {\n+ this.map.zoomTo(this.map.getZoom() + 1, position)\n+ } else {\n+ this.map.zoomTo(this.map.getZoom() - 1, position)\n+ }\n }\n },\n- undo: function() {\n- return this.handler.undo && this.handler.undo()\n- },\n- redo: function() {\n- return this.handler.redo && this.handler.redo()\n- },\n- finishSketch: function() {\n- this.handler.finishGeometry()\n- },\n- cancel: function() {\n- this.handler.cancel()\n- },\n- CLASS_NAME: \"OpenLayers.Control.DrawFeature\"\n+ CLASS_NAME: \"OpenLayers.Control.ZoomBox\"\n });\n-OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {\n- center: null,\n- zoom: null,\n- layers: null,\n- displayProjection: null,\n- getParameters: function(url) {\n- url = url || window.location.href;\n- var parameters = OpenLayers.Util.getParameters(url);\n- var index = url.indexOf(\"#\");\n- if (index > 0) {\n- url = \"?\" + url.substring(index + 1, url.length);\n- OpenLayers.Util.extend(parameters, OpenLayers.Util.getParameters(url))\n- }\n- return parameters\n- },\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- for (var i = 0, len = this.map.controls.length; i < len; i++) {\n- var control = this.map.controls[i];\n- if (control != this && control.CLASS_NAME == \"OpenLayers.Control.ArgParser\") {\n- if (control.displayProjection != this.displayProjection) {\n- this.displayProjection = control.displayProjection\n- }\n- break\n+OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_TOOL,\n+ panned: false,\n+ interval: 0,\n+ documentDrag: false,\n+ kinetic: null,\n+ enableKinetic: true,\n+ kineticInterval: 10,\n+ draw: function() {\n+ if (this.enableKinetic && OpenLayers.Kinetic) {\n+ var config = {\n+ interval: this.kineticInterval\n+ };\n+ if (typeof this.enableKinetic === \"object\") {\n+ config = OpenLayers.Util.extend(config, this.enableKinetic)\n }\n+ this.kinetic = new OpenLayers.Kinetic(config)\n }\n- if (i == this.map.controls.length) {\n- var args = this.getParameters();\n- if (args.layers) {\n- this.layers = args.layers;\n- this.map.events.register(\"addlayer\", this, this.configureLayers);\n- this.configureLayers()\n- }\n- if (args.lat && args.lon) {\n- this.center = new OpenLayers.LonLat(parseFloat(args.lon), parseFloat(args.lat));\n- if (args.zoom) {\n- this.zoom = parseFloat(args.zoom)\n- }\n- this.map.events.register(\"changebaselayer\", this, this.setCenter);\n- this.setCenter()\n- }\n+ this.handler = new OpenLayers.Handler.Drag(this, {\n+ move: this.panMap,\n+ done: this.panMapDone,\n+ down: this.panMapStart\n+ }, {\n+ interval: this.interval,\n+ documentDrag: this.documentDrag\n+ })\n+ },\n+ panMapStart: function() {\n+ if (this.kinetic) {\n+ this.kinetic.begin()\n }\n },\n- setCenter: function() {\n- if (this.map.baseLayer) {\n- this.map.events.unregister(\"changebaselayer\", this, this.setCenter);\n- if (this.displayProjection) {\n- this.center.transform(this.displayProjection, this.map.getProjectionObject())\n- }\n- this.map.setCenter(this.center, this.zoom)\n+ panMap: function(xy) {\n+ if (this.kinetic) {\n+ this.kinetic.update(xy)\n }\n+ this.panned = true;\n+ this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n+ dragging: true,\n+ animate: false\n+ })\n },\n- configureLayers: function() {\n- if (this.layers.length == this.map.layers.length) {\n- this.map.events.unregister(\"addlayer\", this, this.configureLayers);\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- var c = this.layers.charAt(i);\n- if (c == \"B\") {\n- this.map.setBaseLayer(layer)\n- } else if (c == \"T\" || c == \"F\") {\n- layer.setVisibility(c == \"T\")\n- }\n+ panMapDone: function(xy) {\n+ if (this.panned) {\n+ var res = null;\n+ if (this.kinetic) {\n+ res = this.kinetic.end(xy)\n }\n+ this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {\n+ dragging: !!res,\n+ animate: false\n+ });\n+ if (res) {\n+ var self = this;\n+ this.kinetic.move(res, function(x, y, end) {\n+ self.map.pan(x, y, {\n+ dragging: !end,\n+ animate: false\n+ })\n+ })\n+ }\n+ this.panned = false\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.ArgParser\"\n+ CLASS_NAME: \"OpenLayers.Control.DragPan\"\n });\n-OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {\n- geolocation: null,\n- available: \"geolocation\" in navigator,\n- bind: true,\n- watch: false,\n- geolocationOptions: null,\n+OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {\n+ dragPan: null,\n+ dragPanOptions: null,\n+ pinchZoom: null,\n+ pinchZoomOptions: null,\n+ documentDrag: false,\n+ zoomBox: null,\n+ zoomBoxEnabled: true,\n+ zoomWheelEnabled: true,\n+ mouseWheelOptions: null,\n+ handleRightClicks: false,\n+ zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,\n+ autoActivate: true,\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments)\n+ },\n destroy: function() {\n this.deactivate();\n+ if (this.dragPan) {\n+ this.dragPan.destroy()\n+ }\n+ this.dragPan = null;\n+ if (this.zoomBox) {\n+ this.zoomBox.destroy()\n+ }\n+ this.zoomBox = null;\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy()\n+ }\n+ this.pinchZoom = null;\n OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n activate: function() {\n- if (this.available && !this.geolocation) {\n- this.geolocation = navigator.geolocation\n+ this.dragPan.activate();\n+ if (this.zoomWheelEnabled) {\n+ this.handlers.wheel.activate()\n }\n- if (!this.geolocation) {\n- this.events.triggerEvent(\"locationuncapable\");\n- return false\n+ this.handlers.click.activate();\n+ if (this.zoomBoxEnabled) {\n+ this.zoomBox.activate()\n }\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- if (this.watch) {\n- this.watchId = this.geolocation.watchPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions)\n- } else {\n- this.getCurrentLocation()\n- }\n- return true\n+ if (this.pinchZoom) {\n+ this.pinchZoom.activate()\n }\n- return false\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n deactivate: function() {\n- if (this.active && this.watchId !== null) {\n- this.geolocation.clearWatch(this.watchId)\n+ if (this.pinchZoom) {\n+ this.pinchZoom.deactivate()\n }\n+ this.zoomBox.deactivate();\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.handlers.wheel.deactivate();\n return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- geolocate: function(position) {\n- var center = new OpenLayers.LonLat(position.coords.longitude, position.coords.latitude).transform(new OpenLayers.Projection(\"EPSG:4326\"), this.map.getProjectionObject());\n- if (this.bind) {\n- this.map.setCenter(center)\n+ draw: function() {\n+ if (this.handleRightClicks) {\n+ this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False\n+ }\n+ var clickCallbacks = {\n+ click: this.defaultClick,\n+ dblclick: this.defaultDblClick,\n+ dblrightclick: this.defaultDblRightClick\n+ };\n+ var clickOptions = {\n+ double: true,\n+ stopDouble: true\n+ };\n+ this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n+ this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions));\n+ this.zoomBox = new OpenLayers.Control.ZoomBox({\n+ map: this.map,\n+ keyMask: this.zoomBoxKeyMask\n+ });\n+ this.dragPan.draw();\n+ this.zoomBox.draw();\n+ var wheelOptions = this.map.fractionalZoom ? {} : {\n+ cumulative: false,\n+ interval: 50,\n+ maxDelta: 6\n+ };\n+ this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, {\n+ up: this.wheelUp,\n+ down: this.wheelDown\n+ }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions));\n+ if (OpenLayers.Control.PinchZoom) {\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions))\n }\n- this.events.triggerEvent(\"locationupdated\", {\n- position: position,\n- point: new OpenLayers.Geometry.Point(center.lon, center.lat)\n- })\n },\n- getCurrentLocation: function() {\n- if (!this.active || this.watch) {\n- return false\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut()\n }\n- this.geolocation.getCurrentPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions);\n- return true\n },\n- failure: function(error) {\n- this.events.triggerEvent(\"locationfailed\", {\n- error: error\n- })\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy)\n },\n- CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n+ defaultDblRightClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom - 1, evt.xy)\n+ },\n+ wheelChange: function(evt, deltaZ) {\n+ if (!this.map.fractionalZoom) {\n+ deltaZ = Math.round(deltaZ)\n+ }\n+ var currentZoom = this.map.getZoom(),\n+ newZoom = currentZoom + deltaZ;\n+ newZoom = Math.max(newZoom, 0);\n+ newZoom = Math.min(newZoom, this.map.getNumZoomLevels());\n+ if (newZoom === currentZoom) {\n+ return\n+ }\n+ this.map.zoomTo(newZoom, evt.xy)\n+ },\n+ wheelUp: function(evt, delta) {\n+ this.wheelChange(evt, delta || 1)\n+ },\n+ wheelDown: function(evt, delta) {\n+ this.wheelChange(evt, delta || -1)\n+ },\n+ disableZoomBox: function() {\n+ this.zoomBoxEnabled = false;\n+ this.zoomBox.deactivate()\n+ },\n+ enableZoomBox: function() {\n+ this.zoomBoxEnabled = true;\n+ if (this.active) {\n+ this.zoomBox.activate()\n+ }\n+ },\n+ disableZoomWheel: function() {\n+ this.zoomWheelEnabled = false;\n+ this.handlers.wheel.deactivate()\n+ },\n+ enableZoomWheel: function() {\n+ this.zoomWheelEnabled = true;\n+ if (this.active) {\n+ this.handlers.wheel.activate()\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Navigation\"\n });\n OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {\n layers: null,\n imageFormat: \"image/png\",\n quotaRegEx: /quota/i,\n setMap: function(map) {\n OpenLayers.Control.prototype.setMap.apply(this, arguments);\n@@ -30376,638 +26650,215 @@\n key = window.localStorage.key(i);\n if (key.substr(0, 8) === \"olCache_\") {\n window.localStorage.removeItem(key)\n }\n }\n };\n OpenLayers.Control.CacheWrite.urlMap = {};\n-OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_BUTTON,\n- trigger: function() {},\n- CLASS_NAME: \"OpenLayers.Control.Button\"\n-});\n-OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, {\n- slideFactor: 50,\n- slideRatio: null,\n- direction: null,\n- initialize: function(direction, options) {\n- this.direction = direction;\n- this.CLASS_NAME += this.direction;\n- OpenLayers.Control.prototype.initialize.apply(this, [options])\n- },\n- trigger: function() {\n- if (this.map) {\n- var getSlideFactor = OpenLayers.Function.bind(function(dim) {\n- return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor\n- }, this);\n- switch (this.direction) {\n- case OpenLayers.Control.Pan.NORTH:\n- this.map.pan(0, -getSlideFactor(\"h\"));\n- break;\n- case OpenLayers.Control.Pan.SOUTH:\n- this.map.pan(0, getSlideFactor(\"h\"));\n- break;\n- case OpenLayers.Control.Pan.WEST:\n- this.map.pan(-getSlideFactor(\"w\"), 0);\n- break;\n- case OpenLayers.Control.Pan.EAST:\n- this.map.pan(getSlideFactor(\"w\"), 0);\n- break\n- }\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Control.Pan\"\n-});\n-OpenLayers.Control.Pan.NORTH = \"North\";\n-OpenLayers.Control.Pan.SOUTH = \"South\";\n-OpenLayers.Control.Pan.EAST = \"East\";\n-OpenLayers.Control.Pan.WEST = \"West\";\n-OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n- slideFactor: 50,\n- slideRatio: null,\n+OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {\n+ type: OpenLayers.Control.TYPE_TOGGLE,\n+ previous: null,\n+ previousOptions: null,\n+ next: null,\n+ nextOptions: null,\n+ limit: 50,\n+ autoActivate: true,\n+ clearOnDeactivate: false,\n+ registry: null,\n+ nextStack: null,\n+ previousStack: null,\n+ listeners: null,\n+ restoring: false,\n initialize: function(options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n- var options = {\n- slideFactor: this.slideFactor,\n- slideRatio: this.slideRatio\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.registry = OpenLayers.Util.extend({\n+ moveend: this.getState\n+ }, this.registry);\n+ var previousOptions = {\n+ trigger: OpenLayers.Function.bind(this.previousTrigger, this),\n+ displayClass: this.displayClass + \" \" + this.displayClass + \"Previous\"\n };\n- this.addControls([new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)])\n- },\n- CLASS_NAME: \"OpenLayers.Control.PanPanel\"\n-});\n-OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, {\n- trigger: function() {\n- if (this.map) {\n- this.map.zoomIn()\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Control.ZoomIn\"\n-});\n-OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, {\n- trigger: function() {\n- if (this.map) {\n- this.map.zoomOut()\n- }\n+ OpenLayers.Util.extend(previousOptions, this.previousOptions);\n+ this.previous = new OpenLayers.Control.Button(previousOptions);\n+ var nextOptions = {\n+ trigger: OpenLayers.Function.bind(this.nextTrigger, this),\n+ displayClass: this.displayClass + \" \" + this.displayClass + \"Next\"\n+ };\n+ OpenLayers.Util.extend(nextOptions, this.nextOptions);\n+ this.next = new OpenLayers.Control.Button(nextOptions);\n+ this.clear()\n },\n- CLASS_NAME: \"OpenLayers.Control.ZoomOut\"\n-});\n-OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, {\n- trigger: function() {\n- if (this.map) {\n- this.map.zoomToMaxExtent()\n+ onPreviousChange: function(state, length) {\n+ if (state && !this.previous.active) {\n+ this.previous.activate()\n+ } else if (!state && this.previous.active) {\n+ this.previous.deactivate()\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.ZoomToMaxExtent\"\n-});\n-OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n- initialize: function(options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n- this.addControls([new OpenLayers.Control.ZoomIn, new OpenLayers.Control.ZoomToMaxExtent, new OpenLayers.Control.ZoomOut])\n- },\n- CLASS_NAME: \"OpenLayers.Control.ZoomPanel\"\n-});\n-OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {\n- protocol: null,\n- multipleKey: null,\n- toggleKey: null,\n- modifiers: null,\n- multiple: false,\n- click: true,\n- single: true,\n- clickout: true,\n- toggle: false,\n- clickTolerance: 5,\n- hover: false,\n- box: false,\n- maxFeatures: 10,\n- features: null,\n- hoverFeature: null,\n- handlers: null,\n- hoverResponse: null,\n- filterType: OpenLayers.Filter.Spatial.BBOX,\n- initialize: function(options) {\n- options.handlerOptions = options.handlerOptions || {};\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.features = {};\n- this.handlers = {};\n- if (this.click) {\n- this.handlers.click = new OpenLayers.Handler.Click(this, {\n- click: this.selectClick\n- }, this.handlerOptions.click || {})\n- }\n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(this, {\n- done: this.selectBox\n- }, OpenLayers.Util.extend(this.handlerOptions.box, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- }))\n- }\n- if (this.hover) {\n- this.handlers.hover = new OpenLayers.Handler.Hover(this, {\n- move: this.cancelHover,\n- pause: this.selectHover\n- }, OpenLayers.Util.extend(this.handlerOptions.hover, {\n- delay: 250,\n- pixelTolerance: 2\n- }))\n+ onNextChange: function(state, length) {\n+ if (state && !this.next.active) {\n+ this.next.activate()\n+ } else if (!state && this.next.active) {\n+ this.next.deactivate()\n }\n },\n- activate: function() {\n- if (!this.active) {\n- for (var i in this.handlers) {\n- this.handlers[i].activate()\n- }\n+ destroy: function() {\n+ OpenLayers.Control.prototype.destroy.apply(this);\n+ this.previous.destroy();\n+ this.next.destroy();\n+ this.deactivate();\n+ for (var prop in this) {\n+ this[prop] = null\n }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n- deactivate: function() {\n- if (this.active) {\n- for (var i in this.handlers) {\n- this.handlers[i].deactivate()\n- }\n- }\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n+ setMap: function(map) {\n+ this.map = map;\n+ this.next.setMap(map);\n+ this.previous.setMap(map)\n },\n- selectClick: function(evt) {\n- var bounds = this.pixelToBounds(evt.xy);\n- this.setModifiers(evt);\n- this.request(bounds, {\n- single: this.single\n- })\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ this.next.draw();\n+ this.previous.draw()\n },\n- selectBox: function(position) {\n- var bounds;\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n+ previousTrigger: function() {\n+ var current = this.previousStack.shift();\n+ var state = this.previousStack.shift();\n+ if (state != undefined) {\n+ this.nextStack.unshift(current);\n+ this.previousStack.unshift(state);\n+ this.restoring = true;\n+ this.restore(state);\n+ this.restoring = false;\n+ this.onNextChange(this.nextStack[0], this.nextStack.length);\n+ this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n } else {\n- if (this.click) {\n- return\n- }\n- bounds = this.pixelToBounds(position)\n+ this.previousStack.unshift(current)\n }\n- this.setModifiers(this.handlers.box.dragHandler.evt);\n- this.request(bounds)\n- },\n- selectHover: function(evt) {\n- var bounds = this.pixelToBounds(evt.xy);\n- this.request(bounds, {\n- single: true,\n- hover: true\n- })\n+ return state\n },\n- cancelHover: function() {\n- if (this.hoverResponse) {\n- this.protocol.abort(this.hoverResponse);\n- this.hoverResponse = null;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n+ nextTrigger: function() {\n+ var state = this.nextStack.shift();\n+ if (state != undefined) {\n+ this.previousStack.unshift(state);\n+ this.restoring = true;\n+ this.restore(state);\n+ this.restoring = false;\n+ this.onNextChange(this.nextStack[0], this.nextStack.length);\n+ this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n }\n+ return state\n },\n- request: function(bounds, options) {\n- options = options || {};\n- var filter = new OpenLayers.Filter.Spatial({\n- type: this.filterType,\n- value: bounds\n- });\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n- var response = this.protocol.read({\n- maxFeatures: options.single == true ? this.maxFeatures : undefined,\n- filter: filter,\n- callback: function(result) {\n- if (result.success()) {\n- if (result.features.length) {\n- if (options.single == true) {\n- this.selectBestFeature(result.features, bounds.getCenterLonLat(), options)\n- } else {\n- this.select(result.features)\n- }\n- } else if (options.hover) {\n- this.hoverSelect()\n- } else {\n- this.events.triggerEvent(\"clickout\");\n- if (this.clickout) {\n- this.unselectAll()\n- }\n- }\n- }\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n- },\n- scope: this\n- });\n- if (options.hover == true) {\n- this.hoverResponse = response\n- }\n+ clear: function() {\n+ this.previousStack = [];\n+ this.previous.deactivate();\n+ this.nextStack = [];\n+ this.next.deactivate()\n },\n- selectBestFeature: function(features, clickPosition, options) {\n- options = options || {};\n- if (features.length) {\n- var point = new OpenLayers.Geometry.Point(clickPosition.lon, clickPosition.lat);\n- var feature, resultFeature, dist;\n- var minDist = Number.MAX_VALUE;\n- for (var i = 0; i < features.length; ++i) {\n- feature = features[i];\n- if (feature.geometry) {\n- dist = point.distanceTo(feature.geometry, {\n- edge: false\n- });\n- if (dist < minDist) {\n- minDist = dist;\n- resultFeature = feature;\n- if (minDist == 0) {\n- break\n- }\n- }\n- }\n- }\n- if (options.hover == true) {\n- this.hoverSelect(resultFeature)\n- } else {\n- this.select(resultFeature || features)\n- }\n+ getState: function() {\n+ return {\n+ center: this.map.getCenter(),\n+ resolution: this.map.getResolution(),\n+ projection: this.map.getProjectionObject(),\n+ units: this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units\n }\n },\n- setModifiers: function(evt) {\n- this.modifiers = {\n- multiple: this.multiple || this.multipleKey && evt[this.multipleKey],\n- toggle: this.toggle || this.toggleKey && evt[this.toggleKey]\n+ restore: function(state) {\n+ var center, zoom;\n+ if (this.map.getProjectionObject() == state.projection) {\n+ zoom = this.map.getZoomForResolution(state.resolution);\n+ center = state.center\n+ } else {\n+ center = state.center.clone();\n+ center.transform(state.projection, this.map.getProjectionObject());\n+ var sourceUnits = state.units;\n+ var targetUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units;\n+ var resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n+ zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution)\n }\n+ this.map.setCenter(center, zoom)\n },\n- select: function(features) {\n- if (!this.modifiers.multiple && !this.modifiers.toggle) {\n- this.unselectAll()\n- }\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- var cont = this.events.triggerEvent(\"beforefeaturesselected\", {\n- features: features\n- });\n- if (cont !== false) {\n- var selectedFeatures = [];\n- var feature;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- if (this.features[feature.fid || feature.id]) {\n- if (this.modifiers.toggle) {\n- this.unselect(this.features[feature.fid || feature.id])\n+ setListeners: function() {\n+ this.listeners = {};\n+ for (var type in this.registry) {\n+ this.listeners[type] = OpenLayers.Function.bind(function() {\n+ if (!this.restoring) {\n+ var state = this.registry[type].apply(this, arguments);\n+ this.previousStack.unshift(state);\n+ if (this.previousStack.length > 1) {\n+ this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n }\n- } else {\n- cont = this.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- this.features[feature.fid || feature.id] = feature;\n- selectedFeatures.push(feature);\n- this.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- })\n+ if (this.previousStack.length > this.limit + 1) {\n+ this.previousStack.pop()\n+ }\n+ if (this.nextStack.length > 0) {\n+ this.nextStack = [];\n+ this.onNextChange(null, 0)\n }\n }\n- }\n- this.events.triggerEvent(\"featuresselected\", {\n- features: selectedFeatures\n- })\n- }\n- },\n- hoverSelect: function(feature) {\n- var fid = feature ? feature.fid || feature.id : null;\n- var hfid = this.hoverFeature ? this.hoverFeature.fid || this.hoverFeature.id : null;\n- if (hfid && hfid != fid) {\n- this.events.triggerEvent(\"outfeature\", {\n- feature: this.hoverFeature\n- });\n- this.hoverFeature = null\n- }\n- if (fid && fid != hfid) {\n- this.events.triggerEvent(\"hoverfeature\", {\n- feature: feature\n- });\n- this.hoverFeature = feature\n- }\n- },\n- unselect: function(feature) {\n- delete this.features[feature.fid || feature.id];\n- this.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n- })\n- },\n- unselectAll: function() {\n- for (var fid in this.features) {\n- this.unselect(this.features[fid])\n- }\n- },\n- setMap: function(map) {\n- for (var i in this.handlers) {\n- this.handlers[i].setMap(map)\n- }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments)\n- },\n- pixelToBounds: function(pixel) {\n- var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2);\n- var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2);\n- var ll = this.map.getLonLatFromPixel(llPx);\n- var ur = this.map.getLonLatFromPixel(urPx);\n- return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat)\n- },\n- CLASS_NAME: \"OpenLayers.Control.GetFeature\"\n-});\n-OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {\n- DEFAULTS: {\n- tolerance: 10,\n- node: true,\n- edge: true,\n- vertex: true\n- },\n- greedy: true,\n- precedence: [\"node\", \"vertex\", \"edge\"],\n- resolution: null,\n- geoToleranceCache: null,\n- layer: null,\n- feature: null,\n- point: null,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.options = options || {};\n- if (this.options.layer) {\n- this.setLayer(this.options.layer)\n- }\n- var defaults = OpenLayers.Util.extend({}, this.options.defaults);\n- this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);\n- this.setTargets(this.options.targets);\n- if (this.targets.length === 0 && this.layer) {\n- this.addTargetLayer(this.layer)\n- }\n- this.geoToleranceCache = {}\n- },\n- setLayer: function(layer) {\n- if (this.active) {\n- this.deactivate();\n- this.layer = layer;\n- this.activate()\n- } else {\n- this.layer = layer\n- }\n- },\n- setTargets: function(targets) {\n- this.targets = [];\n- if (targets && targets.length) {\n- var target;\n- for (var i = 0, len = targets.length; i < len; ++i) {\n- target = targets[i];\n- if (target instanceof OpenLayers.Layer.Vector) {\n- this.addTargetLayer(target)\n- } else {\n- this.addTarget(target)\n- }\n- }\n- }\n- },\n- addTargetLayer: function(layer) {\n- this.addTarget({\n- layer: layer\n- })\n- },\n- addTarget: function(target) {\n- target = OpenLayers.Util.applyDefaults(target, this.defaults);\n- target.nodeTolerance = target.nodeTolerance || target.tolerance;\n- target.vertexTolerance = target.vertexTolerance || target.tolerance;\n- target.edgeTolerance = target.edgeTolerance || target.tolerance;\n- this.targets.push(target)\n- },\n- removeTargetLayer: function(layer) {\n- var target;\n- for (var i = this.targets.length - 1; i >= 0; --i) {\n- target = this.targets[i];\n- if (target.layer === layer) {\n- this.removeTarget(target)\n- }\n+ return true\n+ }, this)\n }\n },\n- removeTarget: function(target) {\n- return OpenLayers.Util.removeItem(this.targets, target)\n- },\n activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- if (this.layer && this.layer.events) {\n- this.layer.events.on({\n- sketchstarted: this.onSketchModified,\n- sketchmodified: this.onSketchModified,\n- vertexmodified: this.onVertexModified,\n- scope: this\n- })\n+ var activated = false;\n+ if (this.map) {\n+ if (OpenLayers.Control.prototype.activate.apply(this)) {\n+ if (this.listeners == null) {\n+ this.setListeners()\n+ }\n+ for (var type in this.listeners) {\n+ this.map.events.register(type, this, this.listeners[type])\n+ }\n+ activated = true;\n+ if (this.previousStack.length == 0) {\n+ this.initStack()\n+ }\n }\n }\n return activated\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.layer && this.layer.events) {\n- this.layer.events.un({\n- sketchstarted: this.onSketchModified,\n- sketchmodified: this.onSketchModified,\n- vertexmodified: this.onVertexModified,\n- scope: this\n- })\n- }\n+ initStack: function() {\n+ if (this.map.getCenter()) {\n+ this.listeners.moveend()\n }\n- this.feature = null;\n- this.point = null;\n- return deactivated\n- },\n- onSketchModified: function(event) {\n- this.feature = event.feature;\n- this.considerSnapping(event.vertex, event.vertex)\n- },\n- onVertexModified: function(event) {\n- this.feature = event.feature;\n- var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);\n- this.considerSnapping(event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat))\n },\n- considerSnapping: function(point, loc) {\n- var best = {\n- rank: Number.POSITIVE_INFINITY,\n- dist: Number.POSITIVE_INFINITY,\n- x: null,\n- y: null\n- };\n- var snapped = false;\n- var result, target;\n- for (var i = 0, len = this.targets.length; i < len; ++i) {\n- target = this.targets[i];\n- result = this.testTarget(target, loc);\n- if (result) {\n- if (this.greedy) {\n- best = result;\n- best.target = target;\n- snapped = true;\n- break\n- } else {\n- if (result.rank < best.rank || result.rank === best.rank && result.dist < best.dist) {\n- best = result;\n- best.target = target;\n- snapped = true\n- }\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (this.map) {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this)) {\n+ for (var type in this.listeners) {\n+ this.map.events.unregister(type, this, this.listeners[type])\n }\n- }\n- }\n- if (snapped) {\n- var proceed = this.events.triggerEvent(\"beforesnap\", {\n- point: point,\n- x: best.x,\n- y: best.y,\n- distance: best.dist,\n- layer: best.target.layer,\n- snapType: this.precedence[best.rank]\n- });\n- if (proceed !== false) {\n- point.x = best.x;\n- point.y = best.y;\n- this.point = point;\n- this.events.triggerEvent(\"snap\", {\n- point: point,\n- snapType: this.precedence[best.rank],\n- layer: best.target.layer,\n- distance: best.dist\n- })\n- } else {\n- snapped = false\n- }\n- }\n- if (this.point && !snapped) {\n- point.x = loc.x;\n- point.y = loc.y;\n- this.point = null;\n- this.events.triggerEvent(\"unsnap\", {\n- point: point\n- })\n- }\n- },\n- testTarget: function(target, loc) {\n- var resolution = this.layer.map.getResolution();\n- if (\"minResolution\" in target) {\n- if (resolution < target.minResolution) {\n- return null\n- }\n- }\n- if (\"maxResolution\" in target) {\n- if (resolution >= target.maxResolution) {\n- return null\n- }\n- }\n- var tolerance = {\n- node: this.getGeoTolerance(target.nodeTolerance, resolution),\n- vertex: this.getGeoTolerance(target.vertexTolerance, resolution),\n- edge: this.getGeoTolerance(target.edgeTolerance, resolution)\n- };\n- var maxTolerance = Math.max(tolerance.node, tolerance.vertex, tolerance.edge);\n- var result = {\n- rank: Number.POSITIVE_INFINITY,\n- dist: Number.POSITIVE_INFINITY\n- };\n- var eligible = false;\n- var features = target.layer.features;\n- var feature, type, vertices, vertex, closest, dist, found;\n- var numTypes = this.precedence.length;\n- var ll = new OpenLayers.LonLat(loc.x, loc.y);\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- if (feature !== this.feature && !feature._sketch && feature.state !== OpenLayers.State.DELETE && (!target.filter || target.filter.evaluate(feature))) {\n- if (feature.atPoint(ll, maxTolerance, maxTolerance)) {\n- for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) {\n- type = this.precedence[j];\n- if (target[type]) {\n- if (type === \"edge\") {\n- closest = feature.geometry.distanceTo(loc, {\n- details: true\n- });\n- dist = closest.distance;\n- if (dist <= tolerance[type] && dist < result.dist) {\n- result = {\n- rank: j,\n- dist: dist,\n- x: closest.x0,\n- y: closest.y0\n- };\n- eligible = true;\n- break\n- }\n- } else {\n- vertices = feature.geometry.getVertices(type === \"node\");\n- found = false;\n- for (var k = 0, klen = vertices.length; k < klen; ++k) {\n- vertex = vertices[k];\n- dist = vertex.distanceTo(loc);\n- if (dist <= tolerance[type] && (j < result.rank || j === result.rank && dist < result.dist)) {\n- result = {\n- rank: j,\n- dist: dist,\n- x: vertex.x,\n- y: vertex.y\n- };\n- eligible = true;\n- found = true\n- }\n- }\n- if (found) {\n- break\n- }\n- }\n- }\n- }\n+ if (this.clearOnDeactivate) {\n+ this.clear()\n }\n+ deactivated = true\n }\n }\n- return eligible ? result : null\n- },\n- getGeoTolerance: function(tolerance, resolution) {\n- if (resolution !== this.resolution) {\n- this.resolution = resolution;\n- this.geoToleranceCache = {}\n- }\n- var geoTolerance = this.geoToleranceCache[tolerance];\n- if (geoTolerance === undefined) {\n- geoTolerance = tolerance * resolution;\n- this.geoToleranceCache[tolerance] = geoTolerance\n- }\n- return geoTolerance\n- },\n- destroy: function() {\n- if (this.active) {\n- this.deactivate()\n- }\n- delete this.layer;\n- delete this.targets;\n- OpenLayers.Control.prototype.destroy.call(this)\n+ return deactivated\n },\n- CLASS_NAME: \"OpenLayers.Control.Snapping\"\n+ CLASS_NAME: \"OpenLayers.Control.NavigationHistory\"\n });\n-OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n hover: false,\n- requestEncoding: \"KVP\",\n drillDown: false,\n maxFeatures: 10,\n clickCallback: \"click\",\n+ output: \"features\",\n layers: null,\n- queryVisible: true,\n+ queryVisible: false,\n+ url: null,\n+ layerUrls: null,\n infoFormat: \"text/html\",\n vendorParams: {},\n format: null,\n formatOptions: null,\n handler: null,\n hoverRequest: null,\n- pending: 0,\n initialize: function(options) {\n options = options || {};\n options.handlerOptions = options.handlerOptions || {};\n OpenLayers.Control.prototype.initialize.apply(this, [options]);\n if (!this.format) {\n this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions)\n }\n@@ -31024,136 +26875,678 @@\n } else {\n var callbacks = {};\n callbacks[this.clickCallback] = this.getInfoForClick;\n this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {})\n }\n },\n getInfoForClick: function(evt) {\n+ this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: evt.xy\n+ });\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n this.request(evt.xy, {})\n },\n getInfoForHover: function(evt) {\n+ this.events.triggerEvent(\"beforegetfeatureinfo\", {\n+ xy: evt.xy\n+ });\n this.request(evt.xy, {\n hover: true\n })\n },\n cancelHover: function() {\n if (this.hoverRequest) {\n- --this.pending;\n- if (this.pending <= 0) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.pending = 0\n- }\n this.hoverRequest.abort();\n this.hoverRequest = null\n }\n },\n findLayers: function() {\n var candidates = this.layers || this.map.layers;\n var layers = [];\n- var layer;\n+ var layer, url;\n for (var i = candidates.length - 1; i >= 0; --i) {\n layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.WMTS && layer.requestEncoding === this.requestEncoding && (!this.queryVisible || layer.getVisibility())) {\n- layers.push(layer);\n- if (!this.drillDown || this.hover) {\n- break\n+ if (layer instanceof OpenLayers.Layer.WMS && (!this.queryVisible || layer.getVisibility())) {\n+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n+ if (this.drillDown === false && !this.url) {\n+ this.url = url\n+ }\n+ if (this.drillDown === true || this.urlMatches(url)) {\n+ layers.push(layer)\n }\n }\n }\n return layers\n },\n- buildRequestOptions: function(layer, xy) {\n- var loc = this.map.getLonLatFromPixel(xy);\n- var getTileUrl = layer.getURL(new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat));\n- var params = OpenLayers.Util.getParameters(getTileUrl);\n- var tileInfo = layer.getTileInfo(loc);\n- OpenLayers.Util.extend(params, {\n- service: \"WMTS\",\n- version: layer.version,\n+ urlMatches: function(url) {\n+ var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);\n+ if (!matches && this.layerUrls) {\n+ for (var i = 0, len = this.layerUrls.length; i < len; ++i) {\n+ if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {\n+ matches = true;\n+ break\n+ }\n+ }\n+ }\n+ return matches\n+ },\n+ buildWMSOptions: function(url, layers, clickPosition, format) {\n+ var layerNames = [],\n+ styleNames = [];\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ if (layers[i].params.LAYERS != null) {\n+ layerNames = layerNames.concat(layers[i].params.LAYERS);\n+ styleNames = styleNames.concat(this.getStyleNames(layers[i]))\n+ }\n+ }\n+ var firstLayer = layers[0];\n+ var projection = this.map.getProjection();\n+ var layerProj = firstLayer.projection;\n+ if (layerProj && layerProj.equals(this.map.getProjectionObject())) {\n+ projection = layerProj.getCode()\n+ }\n+ var params = OpenLayers.Util.extend({\n+ service: \"WMS\",\n+ version: firstLayer.params.VERSION,\n request: \"GetFeatureInfo\",\n- infoFormat: this.infoFormat,\n- i: tileInfo.i,\n- j: tileInfo.j\n+ exceptions: firstLayer.params.EXCEPTIONS,\n+ bbox: this.map.getExtent().toBBOX(null, firstLayer.reverseAxisOrder()),\n+ feature_count: this.maxFeatures,\n+ height: this.map.getSize().h,\n+ width: this.map.getSize().w,\n+ format: format,\n+ info_format: firstLayer.params.INFO_FORMAT || this.infoFormat\n+ }, parseFloat(firstLayer.params.VERSION) >= 1.3 ? {\n+ crs: projection,\n+ i: parseInt(clickPosition.x),\n+ j: parseInt(clickPosition.y)\n+ } : {\n+ srs: projection,\n+ x: parseInt(clickPosition.x),\n+ y: parseInt(clickPosition.y)\n });\n+ if (layerNames.length != 0) {\n+ params = OpenLayers.Util.extend({\n+ layers: layerNames,\n+ query_layers: layerNames,\n+ styles: styleNames\n+ }, params)\n+ }\n OpenLayers.Util.applyDefaults(params, this.vendorParams);\n return {\n- url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,\n+ url: url,\n params: OpenLayers.Util.upperCaseObject(params),\n callback: function(request) {\n- this.handleResponse(xy, request, layer)\n+ this.handleResponse(clickPosition, request, url)\n },\n scope: this\n }\n },\n- request: function(xy, options) {\n- options = options || {};\n+ getStyleNames: function(layer) {\n+ var styleNames;\n+ if (layer.params.STYLES) {\n+ styleNames = layer.params.STYLES\n+ } else {\n+ if (OpenLayers.Util.isArray(layer.params.LAYERS)) {\n+ styleNames = new Array(layer.params.LAYERS.length)\n+ } else {\n+ styleNames = layer.params.LAYERS.replace(/[^,]/g, \"\")\n+ }\n+ }\n+ return styleNames\n+ },\n+ request: function(clickPosition, options) {\n var layers = this.findLayers();\n- if (layers.length > 0) {\n- var issue, layer;\n+ if (layers.length == 0) {\n+ this.events.triggerEvent(\"nogetfeatureinfo\");\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ return\n+ }\n+ options = options || {};\n+ if (this.drillDown === false) {\n+ var wmsOptions = this.buildWMSOptions(this.url, layers, clickPosition, layers[0].params.FORMAT);\n+ var request = OpenLayers.Request.GET(wmsOptions);\n+ if (options.hover === true) {\n+ this.hoverRequest = request\n+ }\n+ } else {\n+ this._requestCount = 0;\n+ this._numRequests = 0;\n+ this.features = [];\n+ var services = {},\n+ url;\n for (var i = 0, len = layers.length; i < len; i++) {\n- layer = layers[i];\n- issue = this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: xy,\n- layer: layer\n- });\n- if (issue !== false) {\n- ++this.pending;\n- var requestOptions = this.buildRequestOptions(layer, xy);\n- var request = OpenLayers.Request.GET(requestOptions);\n- if (options.hover === true) {\n- this.hoverRequest = request\n- }\n+ var layer = layers[i];\n+ var service, found = false;\n+ url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n+ if (url in services) {\n+ services[url].push(layer)\n+ } else {\n+ this._numRequests++;\n+ services[url] = [layer]\n }\n }\n- if (this.pending > 0) {\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\")\n+ var layers;\n+ for (var url in services) {\n+ layers = services[url];\n+ var wmsOptions = this.buildWMSOptions(url, layers, clickPosition, layers[0].params.FORMAT);\n+ OpenLayers.Request.GET(wmsOptions)\n }\n }\n },\n- handleResponse: function(xy, request, layer) {\n- --this.pending;\n- if (this.pending <= 0) {\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.pending = 0\n+ triggerGetFeatureInfo: function(request, xy, features) {\n+ this.events.triggerEvent(\"getfeatureinfo\", {\n+ text: request.responseText,\n+ features: features,\n+ request: request,\n+ xy: xy\n+ });\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n+ },\n+ handleResponse: function(xy, request, url) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- if (request.status && (request.status < 200 || request.status >= 300)) {\n- this.events.triggerEvent(\"exception\", {\n- xy: xy,\n- request: request,\n- layer: layer\n+ var features = this.format.read(doc);\n+ if (this.drillDown === false) {\n+ this.triggerGetFeatureInfo(request, xy, features)\n+ } else {\n+ this._requestCount++;\n+ if (this.output === \"object\") {\n+ this._features = (this._features || []).concat({\n+ url: url,\n+ features: features\n+ })\n+ } else {\n+ this._features = (this._features || []).concat(features)\n+ }\n+ if (this._requestCount === this._numRequests) {\n+ this.triggerGetFeatureInfo(request, xy, this._features.concat());\n+ delete this._features;\n+ delete this._requestCount;\n+ delete this._numRequests\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.WMSGetFeatureInfo\"\n+});\n+OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {\n+ geometryTypes: null,\n+ onStart: function(feature, pixel) {},\n+ onDrag: function(feature, pixel) {},\n+ onComplete: function(feature, pixel) {},\n+ onEnter: function(feature) {},\n+ onLeave: function(feature) {},\n+ documentDrag: false,\n+ layer: null,\n+ feature: null,\n+ dragCallbacks: {},\n+ featureCallbacks: {},\n+ lastPixel: null,\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.layer = layer;\n+ this.handlers = {\n+ drag: new OpenLayers.Handler.Drag(this, OpenLayers.Util.extend({\n+ down: this.downFeature,\n+ move: this.moveFeature,\n+ up: this.upFeature,\n+ out: this.cancel,\n+ done: this.doneDragging\n+ }, this.dragCallbacks), {\n+ documentDrag: this.documentDrag\n+ }),\n+ feature: new OpenLayers.Handler.Feature(this, this.layer, OpenLayers.Util.extend({\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature,\n+ over: this.overFeature,\n+ out: this.outFeature\n+ }, this.featureCallbacks), {\n+ geometryTypes: this.geometryTypes\n })\n+ }\n+ },\n+ clickFeature: function(feature) {\n+ if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {\n+ this.handlers.drag.dragstart(this.handlers.feature.evt);\n+ this.handlers.drag.stopDown = false\n+ }\n+ },\n+ clickoutFeature: function(feature) {\n+ if (this.handlers.feature.touch && this.over) {\n+ this.outFeature(feature);\n+ this.handlers.drag.stopDown = true\n+ }\n+ },\n+ destroy: function() {\n+ this.layer = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, [])\n+ },\n+ activate: function() {\n+ return this.handlers.feature.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments)\n+ },\n+ deactivate: function() {\n+ this.handlers.drag.deactivate();\n+ this.handlers.feature.deactivate();\n+ this.feature = null;\n+ this.dragging = false;\n+ this.lastPixel = null;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n+ },\n+ overFeature: function(feature) {\n+ var activated = false;\n+ if (!this.handlers.drag.dragging) {\n+ this.feature = feature;\n+ this.handlers.drag.activate();\n+ activated = true;\n+ this.over = true;\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n+ this.onEnter(feature)\n } else {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ if (this.feature.id == feature.id) {\n+ this.over = true\n+ } else {\n+ this.over = false\n }\n- var features, except;\n- try {\n- features = this.format.read(doc)\n- } catch (error) {\n- except = true;\n- this.events.triggerEvent(\"exception\", {\n- xy: xy,\n- request: request,\n- error: error,\n- layer: layer\n+ }\n+ return activated\n+ },\n+ downFeature: function(pixel) {\n+ this.lastPixel = pixel;\n+ this.onStart(this.feature, pixel)\n+ },\n+ moveFeature: function(pixel) {\n+ var res = this.map.getResolution();\n+ this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), res * (this.lastPixel.y - pixel.y));\n+ this.layer.drawFeature(this.feature);\n+ this.lastPixel = pixel;\n+ this.onDrag(this.feature, pixel)\n+ },\n+ upFeature: function(pixel) {\n+ if (!this.over) {\n+ this.handlers.drag.deactivate()\n+ }\n+ },\n+ doneDragging: function(pixel) {\n+ this.onComplete(this.feature, pixel)\n+ },\n+ outFeature: function(feature) {\n+ if (!this.handlers.drag.dragging) {\n+ this.over = false;\n+ this.handlers.drag.deactivate();\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n+ this.onLeave(feature);\n+ this.feature = null\n+ } else {\n+ if (this.feature.id == feature.id) {\n+ this.over = false\n+ }\n+ }\n+ },\n+ cancel: function() {\n+ this.handlers.drag.deactivate();\n+ this.over = false\n+ },\n+ setMap: function(map) {\n+ this.handlers.drag.setMap(map);\n+ this.handlers.feature.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.DragFeature\"\n+});\n+OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {\n+ center: null,\n+ zoom: null,\n+ layers: null,\n+ displayProjection: null,\n+ getParameters: function(url) {\n+ url = url || window.location.href;\n+ var parameters = OpenLayers.Util.getParameters(url);\n+ var index = url.indexOf(\"#\");\n+ if (index > 0) {\n+ url = \"?\" + url.substring(index + 1, url.length);\n+ OpenLayers.Util.extend(parameters, OpenLayers.Util.getParameters(url))\n+ }\n+ return parameters\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ for (var i = 0, len = this.map.controls.length; i < len; i++) {\n+ var control = this.map.controls[i];\n+ if (control != this && control.CLASS_NAME == \"OpenLayers.Control.ArgParser\") {\n+ if (control.displayProjection != this.displayProjection) {\n+ this.displayProjection = control.displayProjection\n+ }\n+ break\n+ }\n+ }\n+ if (i == this.map.controls.length) {\n+ var args = this.getParameters();\n+ if (args.layers) {\n+ this.layers = args.layers;\n+ this.map.events.register(\"addlayer\", this, this.configureLayers);\n+ this.configureLayers()\n+ }\n+ if (args.lat && args.lon) {\n+ this.center = new OpenLayers.LonLat(parseFloat(args.lon), parseFloat(args.lat));\n+ if (args.zoom) {\n+ this.zoom = parseFloat(args.zoom)\n+ }\n+ this.map.events.register(\"changebaselayer\", this, this.setCenter);\n+ this.setCenter()\n+ }\n+ }\n+ },\n+ setCenter: function() {\n+ if (this.map.baseLayer) {\n+ this.map.events.unregister(\"changebaselayer\", this, this.setCenter);\n+ if (this.displayProjection) {\n+ this.center.transform(this.displayProjection, this.map.getProjectionObject())\n+ }\n+ this.map.setCenter(this.center, this.zoom)\n+ }\n+ },\n+ configureLayers: function() {\n+ if (this.layers.length == this.map.layers.length) {\n+ this.map.events.unregister(\"addlayer\", this, this.configureLayers);\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ var c = this.layers.charAt(i);\n+ if (c == \"B\") {\n+ this.map.setBaseLayer(layer)\n+ } else if (c == \"T\" || c == \"F\") {\n+ layer.setVisibility(c == \"T\")\n+ }\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ArgParser\"\n+});\n+OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {\n+ argParserClass: OpenLayers.Control.ArgParser,\n+ element: null,\n+ anchor: false,\n+ base: \"\",\n+ displayProjection: null,\n+ initialize: function(element, base, options) {\n+ if (element !== null && typeof element == \"object\" && !OpenLayers.Util.isElement(element)) {\n+ options = element;\n+ this.base = document.location.href;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (this.element != null) {\n+ this.element = OpenLayers.Util.getElement(this.element)\n+ }\n+ } else {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.element = OpenLayers.Util.getElement(element);\n+ this.base = base || document.location.href\n+ }\n+ },\n+ destroy: function() {\n+ if (this.element && this.element.parentNode == this.div) {\n+ this.div.removeChild(this.element);\n+ this.element = null\n+ }\n+ if (this.map) {\n+ this.map.events.unregister(\"moveend\", this, this.updateLink)\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ for (var i = 0, len = this.map.controls.length; i < len; i++) {\n+ var control = this.map.controls[i];\n+ if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {\n+ if (control.displayProjection != this.displayProjection) {\n+ this.displayProjection = control.displayProjection\n+ }\n+ break\n+ }\n+ }\n+ if (i == this.map.controls.length) {\n+ this.map.addControl(new this.argParserClass({\n+ displayProjection: this.displayProjection\n+ }))\n+ }\n+ },\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.element && !this.anchor) {\n+ this.element = document.createElement(\"a\");\n+ this.element.innerHTML = OpenLayers.i18n(\"Permalink\");\n+ this.element.href = \"\";\n+ this.div.appendChild(this.element)\n+ }\n+ this.map.events.on({\n+ moveend: this.updateLink,\n+ changelayer: this.updateLink,\n+ changebaselayer: this.updateLink,\n+ scope: this\n+ });\n+ this.updateLink();\n+ return this.div\n+ },\n+ updateLink: function() {\n+ var separator = this.anchor ? \"#\" : \"?\";\n+ var href = this.base;\n+ var anchor = null;\n+ if (href.indexOf(\"#\") != -1 && this.anchor == false) {\n+ anchor = href.substring(href.indexOf(\"#\"), href.length)\n+ }\n+ if (href.indexOf(separator) != -1) {\n+ href = href.substring(0, href.indexOf(separator))\n+ }\n+ var splits = href.split(\"#\");\n+ href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams());\n+ if (anchor) {\n+ href += anchor\n+ }\n+ if (this.anchor && !this.element) {\n+ window.location.href = href\n+ } else {\n+ this.element.href = href\n+ }\n+ },\n+ createParams: function(center, zoom, layers) {\n+ center = center || this.map.getCenter();\n+ var params = OpenLayers.Util.getParameters(this.base);\n+ if (center) {\n+ params.zoom = zoom || this.map.getZoom();\n+ var lat = center.lat;\n+ var lon = center.lon;\n+ if (this.displayProjection) {\n+ var mapPosition = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ }, this.map.getProjectionObject(), this.displayProjection);\n+ lon = mapPosition.x;\n+ lat = mapPosition.y\n+ }\n+ params.lat = Math.round(lat * 1e5) / 1e5;\n+ params.lon = Math.round(lon * 1e5) / 1e5;\n+ layers = layers || this.map.layers;\n+ params.layers = \"\";\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ if (layer.isBaseLayer) {\n+ params.layers += layer == this.map.baseLayer ? \"B\" : \"0\"\n+ } else {\n+ params.layers += layer.getVisibility() ? \"T\" : \"F\"\n+ }\n+ }\n+ }\n+ return params\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Permalink\"\n+});\n+OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n+ layer: null,\n+ callbacks: null,\n+ multi: false,\n+ featureAdded: function() {},\n+ initialize: function(layer, handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.callbacks = OpenLayers.Util.extend({\n+ done: this.drawFeature,\n+ modify: function(vertex, feature) {\n+ this.layer.events.triggerEvent(\"sketchmodified\", {\n+ vertex: vertex,\n+ feature: feature\n+ })\n+ },\n+ create: function(vertex, feature) {\n+ this.layer.events.triggerEvent(\"sketchstarted\", {\n+ vertex: vertex,\n+ feature: feature\n })\n }\n- if (!except) {\n- this.events.triggerEvent(\"getfeatureinfo\", {\n- text: request.responseText,\n- features: features,\n- request: request,\n- xy: xy,\n- layer: layer\n+ }, this.callbacks);\n+ this.layer = layer;\n+ this.handlerOptions = this.handlerOptions || {};\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n+ renderers: layer.renderers,\n+ rendererOptions: layer.rendererOptions\n+ });\n+ if (!(\"multi\" in this.handlerOptions)) {\n+ this.handlerOptions.multi = this.multi\n+ }\n+ var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;\n+ if (sketchStyle) {\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n+ styleMap: new OpenLayers.StyleMap({\n+ default: sketchStyle\n })\n+ })\n+ }\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions)\n+ },\n+ drawFeature: function(geometry) {\n+ var feature = new OpenLayers.Feature.Vector(geometry);\n+ var proceed = this.layer.events.triggerEvent(\"sketchcomplete\", {\n+ feature: feature\n+ });\n+ if (proceed !== false) {\n+ feature.state = OpenLayers.State.INSERT;\n+ this.layer.addFeatures([feature]);\n+ this.featureAdded(feature);\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ })\n+ }\n+ },\n+ insertXY: function(x, y) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertXY(x, y)\n+ }\n+ },\n+ insertDeltaXY: function(dx, dy) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeltaXY(dx, dy)\n+ }\n+ },\n+ insertDirectionLength: function(direction, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDirectionLength(direction, length)\n+ }\n+ },\n+ insertDeflectionLength: function(deflection, length) {\n+ if (this.handler && this.handler.line) {\n+ this.handler.insertDeflectionLength(deflection, length)\n+ }\n+ },\n+ undo: function() {\n+ return this.handler.undo && this.handler.undo()\n+ },\n+ redo: function() {\n+ return this.handler.redo && this.handler.redo()\n+ },\n+ finishSketch: function() {\n+ this.handler.finishGeometry()\n+ },\n+ cancel: function() {\n+ this.handler.cancel()\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.DrawFeature\"\n+});\n+OpenLayers.Control.EditingToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {\n+ citeCompliant: false,\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ this.addControls([new OpenLayers.Control.Navigation]);\n+ var controls = [new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {\n+ displayClass: \"olControlDrawFeaturePoint\",\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n+ }\n+ }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {\n+ displayClass: \"olControlDrawFeaturePath\",\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n+ }\n+ }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {\n+ displayClass: \"olControlDrawFeaturePolygon\",\n+ handlerOptions: {\n+ citeCompliant: this.citeCompliant\n }\n+ })];\n+ this.addControls(controls)\n+ },\n+ draw: function() {\n+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n+ if (this.defaultControl === null) {\n+ this.defaultControl = this.controls[0]\n }\n+ return div\n },\n- CLASS_NAME: \"OpenLayers.Control.WMTSGetFeatureInfo\"\n+ CLASS_NAME: \"OpenLayers.Control.EditingToolbar\"\n+});\n+OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n+ separator: \", \",\n+ template: \"${layers}\",\n+ destroy: function() {\n+ this.map.events.un({\n+ removelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ changebaselayer: this.updateAttribution,\n+ scope: this\n+ });\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ this.map.events.on({\n+ changebaselayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ removelayer: this.updateAttribution,\n+ scope: this\n+ });\n+ this.updateAttribution();\n+ return this.div\n+ },\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution)\n+ }\n+ }\n+ }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ })\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n });\n OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {\n type: OpenLayers.Control.TYPE_TOOL,\n pinchOrigin: null,\n currentCenter: null,\n autoActivate: true,\n preserveCenter: false,\n@@ -31192,14 +27585,519 @@\n location.lat -= resolution * (size.h / 2 - zoomPixel.y);\n this.map.div.clientWidth = this.map.div.clientWidth;\n this.map.setCenter(location, zoom)\n }\n },\n CLASS_NAME: \"OpenLayers.Control.PinchZoom\"\n });\n+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {\n+ autoActivate: true,\n+ slideFactor: 75,\n+ observeElement: null,\n+ draw: function() {\n+ var observeElement = this.observeElement || document;\n+ this.handler = new OpenLayers.Handler.Keyboard(this, {\n+ keydown: this.defaultKeyPress\n+ }, {\n+ observeElement: observeElement\n+ })\n+ },\n+ defaultKeyPress: function(evt) {\n+ var size, handled = true;\n+ var target = OpenLayers.Event.element(evt);\n+ if (target && (target.tagName == \"INPUT\" || target.tagName == \"TEXTAREA\" || target.tagName == \"SELECT\")) {\n+ return\n+ }\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_LEFT:\n+ this.map.pan(-this.slideFactor, 0);\n+ break;\n+ case OpenLayers.Event.KEY_RIGHT:\n+ this.map.pan(this.slideFactor, 0);\n+ break;\n+ case OpenLayers.Event.KEY_UP:\n+ this.map.pan(0, -this.slideFactor);\n+ break;\n+ case OpenLayers.Event.KEY_DOWN:\n+ this.map.pan(0, this.slideFactor);\n+ break;\n+ case 33:\n+ size = this.map.getSize();\n+ this.map.pan(0, -.75 * size.h);\n+ break;\n+ case 34:\n+ size = this.map.getSize();\n+ this.map.pan(0, .75 * size.h);\n+ break;\n+ case 35:\n+ size = this.map.getSize();\n+ this.map.pan(.75 * size.w, 0);\n+ break;\n+ case 36:\n+ size = this.map.getSize();\n+ this.map.pan(-.75 * size.w, 0);\n+ break;\n+ case 43:\n+ case 61:\n+ case 187:\n+ case 107:\n+ this.map.zoomIn();\n+ break;\n+ case 45:\n+ case 109:\n+ case 189:\n+ case 95:\n+ this.map.zoomOut();\n+ break;\n+ default:\n+ handled = false\n+ }\n+ if (handled) {\n+ OpenLayers.Event.stop(evt)\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.KeyboardDefaults\"\n+});\n+OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {\n+ geometryTypes: null,\n+ layer: null,\n+ preserveAspectRatio: false,\n+ rotate: true,\n+ feature: null,\n+ renderIntent: \"temporary\",\n+ rotationHandleSymbolizer: null,\n+ box: null,\n+ center: null,\n+ scale: 1,\n+ ratio: 1,\n+ rotation: 0,\n+ handles: null,\n+ rotationHandles: null,\n+ dragControl: null,\n+ irregular: false,\n+ initialize: function(layer, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.layer = layer;\n+ if (!this.rotationHandleSymbolizer) {\n+ this.rotationHandleSymbolizer = {\n+ stroke: false,\n+ pointRadius: 10,\n+ fillOpacity: 0,\n+ cursor: \"pointer\"\n+ }\n+ }\n+ this.createBox();\n+ this.createControl()\n+ },\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.dragControl.activate();\n+ this.layer.addFeatures([this.box]);\n+ this.rotate && this.layer.addFeatures(this.rotationHandles);\n+ this.layer.addFeatures(this.handles);\n+ activated = true\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.layer.removeFeatures(this.handles);\n+ this.rotate && this.layer.removeFeatures(this.rotationHandles);\n+ this.layer.removeFeatures([this.box]);\n+ this.dragControl.deactivate();\n+ deactivated = true\n+ }\n+ return deactivated\n+ },\n+ setMap: function(map) {\n+ this.dragControl.setMap(map);\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ },\n+ setFeature: function(feature, initialParams) {\n+ initialParams = OpenLayers.Util.applyDefaults(initialParams, {\n+ rotation: 0,\n+ scale: 1,\n+ ratio: 1\n+ });\n+ var oldRotation = this.rotation;\n+ var oldCenter = this.center;\n+ OpenLayers.Util.extend(this, initialParams);\n+ var cont = this.events.triggerEvent(\"beforesetfeature\", {\n+ feature: feature\n+ });\n+ if (cont === false) {\n+ return\n+ }\n+ this.feature = feature;\n+ this.activate();\n+ this._setfeature = true;\n+ var featureBounds = this.feature.geometry.getBounds();\n+ this.box.move(featureBounds.getCenterLonLat());\n+ this.box.geometry.rotate(-oldRotation, oldCenter);\n+ this._angle = 0;\n+ var ll;\n+ if (this.rotation) {\n+ var geom = feature.geometry.clone();\n+ geom.rotate(-this.rotation, this.center);\n+ var box = new OpenLayers.Feature.Vector(geom.getBounds().toGeometry());\n+ box.geometry.rotate(this.rotation, this.center);\n+ this.box.geometry.rotate(this.rotation, this.center);\n+ this.box.move(box.geometry.getBounds().getCenterLonLat());\n+ var llGeom = box.geometry.components[0].components[0];\n+ ll = llGeom.getBounds().getCenterLonLat()\n+ } else {\n+ ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom)\n+ }\n+ this.handles[0].move(ll);\n+ delete this._setfeature;\n+ this.events.triggerEvent(\"setfeature\", {\n+ feature: feature\n+ })\n+ },\n+ unsetFeature: function() {\n+ if (this.active) {\n+ this.deactivate()\n+ } else {\n+ this.feature = null;\n+ this.rotation = 0;\n+ this.scale = 1;\n+ this.ratio = 1\n+ }\n+ },\n+ createBox: function() {\n+ var control = this;\n+ this.center = new OpenLayers.Geometry.Point(0, 0);\n+ this.box = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-1, -1), new OpenLayers.Geometry.Point(0, -1), new OpenLayers.Geometry.Point(1, -1), new OpenLayers.Geometry.Point(1, 0), new OpenLayers.Geometry.Point(1, 1), new OpenLayers.Geometry.Point(0, 1), new OpenLayers.Geometry.Point(-1, 1), new OpenLayers.Geometry.Point(-1, 0), new OpenLayers.Geometry.Point(-1, -1)]), null, typeof this.renderIntent == \"string\" ? null : this.renderIntent);\n+ this.box.geometry.move = function(x, y) {\n+ control._moving = true;\n+ OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);\n+ control.center.move(x, y);\n+ delete control._moving\n+ };\n+ var vertexMoveFn = function(x, y) {\n+ OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.move(x, y);\n+ this._handle.geometry.move(x, y)\n+ };\n+ var vertexResizeFn = function(scale, center, ratio) {\n+ OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.resize(scale, center, ratio);\n+ this._handle.geometry.resize(scale, center, ratio)\n+ };\n+ var vertexRotateFn = function(angle, center) {\n+ OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);\n+ this._rotationHandle && this._rotationHandle.geometry.rotate(angle, center);\n+ this._handle.geometry.rotate(angle, center)\n+ };\n+ var handleMoveFn = function(x, y) {\n+ var oldX = this.x,\n+ oldY = this.y;\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ if (control._moving) {\n+ return\n+ }\n+ var evt = control.dragControl.handlers.drag.evt;\n+ var preserveAspectRatio = !control._setfeature && control.preserveAspectRatio;\n+ var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);\n+ var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);\n+ var centerGeometry = control.center;\n+ this.rotate(-control.rotation, centerGeometry);\n+ oldGeom.rotate(-control.rotation, centerGeometry);\n+ var dx1 = this.x - centerGeometry.x;\n+ var dy1 = this.y - centerGeometry.y;\n+ var dx0 = dx1 - (this.x - oldGeom.x);\n+ var dy0 = dy1 - (this.y - oldGeom.y);\n+ if (control.irregular && !control._setfeature) {\n+ dx1 -= (this.x - oldGeom.x) / 2;\n+ dy1 -= (this.y - oldGeom.y) / 2\n+ }\n+ this.x = oldX;\n+ this.y = oldY;\n+ var scale, ratio = 1;\n+ if (reshape) {\n+ scale = Math.abs(dy0) < 1e-5 ? 1 : dy1 / dy0;\n+ ratio = (Math.abs(dx0) < 1e-5 ? 1 : dx1 / dx0) / scale\n+ } else {\n+ var l0 = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n+ var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n+ scale = l1 / l0\n+ }\n+ control._moving = true;\n+ control.box.geometry.rotate(-control.rotation, centerGeometry);\n+ delete control._moving;\n+ control.box.geometry.resize(scale, centerGeometry, ratio);\n+ control.box.geometry.rotate(control.rotation, centerGeometry);\n+ control.transformFeature({\n+ scale: scale,\n+ ratio: ratio\n+ });\n+ if (control.irregular && !control._setfeature) {\n+ var newCenter = centerGeometry.clone();\n+ newCenter.x += Math.abs(oldX - centerGeometry.x) < 1e-5 ? 0 : this.x - oldX;\n+ newCenter.y += Math.abs(oldY - centerGeometry.y) < 1e-5 ? 0 : this.y - oldY;\n+ control.box.geometry.move(this.x - oldX, this.y - oldY);\n+ control.transformFeature({\n+ center: newCenter\n+ })\n+ }\n+ };\n+ var rotationHandleMoveFn = function(x, y) {\n+ var oldX = this.x,\n+ oldY = this.y;\n+ OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n+ if (control._moving) {\n+ return\n+ }\n+ var evt = control.dragControl.handlers.drag.evt;\n+ var constrain = evt && evt.shiftKey ? 45 : 1;\n+ var centerGeometry = control.center;\n+ var dx1 = this.x - centerGeometry.x;\n+ var dy1 = this.y - centerGeometry.y;\n+ var dx0 = dx1 - x;\n+ var dy0 = dy1 - y;\n+ this.x = oldX;\n+ this.y = oldY;\n+ var a0 = Math.atan2(dy0, dx0);\n+ var a1 = Math.atan2(dy1, dx1);\n+ var angle = a1 - a0;\n+ angle *= 180 / Math.PI;\n+ control._angle = (control._angle + angle) % 360;\n+ var diff = control.rotation % constrain;\n+ if (Math.abs(control._angle) >= constrain || diff !== 0) {\n+ angle = Math.round(control._angle / constrain) * constrain - diff;\n+ control._angle = 0;\n+ control.box.geometry.rotate(angle, centerGeometry);\n+ control.transformFeature({\n+ rotation: angle\n+ })\n+ }\n+ };\n+ var handles = new Array(8);\n+ var rotationHandles = new Array(4);\n+ var geom, handle, rotationHandle;\n+ var positions = [\"sw\", \"s\", \"se\", \"e\", \"ne\", \"n\", \"nw\", \"w\"];\n+ for (var i = 0; i < 8; ++i) {\n+ geom = this.box.geometry.components[i];\n+ handle = new OpenLayers.Feature.Vector(geom.clone(), {\n+ role: positions[i] + \"-resize\"\n+ }, typeof this.renderIntent == \"string\" ? null : this.renderIntent);\n+ if (i % 2 == 0) {\n+ rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {\n+ role: positions[i] + \"-rotate\"\n+ }, typeof this.rotationHandleSymbolizer == \"string\" ? null : this.rotationHandleSymbolizer);\n+ rotationHandle.geometry.move = rotationHandleMoveFn;\n+ geom._rotationHandle = rotationHandle;\n+ rotationHandles[i / 2] = rotationHandle\n+ }\n+ geom.move = vertexMoveFn;\n+ geom.resize = vertexResizeFn;\n+ geom.rotate = vertexRotateFn;\n+ handle.geometry.move = handleMoveFn;\n+ geom._handle = handle;\n+ handles[i] = handle\n+ }\n+ this.rotationHandles = rotationHandles;\n+ this.handles = handles\n+ },\n+ createControl: function() {\n+ var control = this;\n+ this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {\n+ documentDrag: true,\n+ moveFeature: function(pixel) {\n+ if (this.feature === control.feature) {\n+ this.feature = control.box\n+ }\n+ OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments)\n+ },\n+ onDrag: function(feature, pixel) {\n+ if (feature === control.box) {\n+ control.transformFeature({\n+ center: control.center\n+ })\n+ }\n+ },\n+ onStart: function(feature, pixel) {\n+ var eligible = !control.geometryTypes || OpenLayers.Util.indexOf(control.geometryTypes, feature.geometry.CLASS_NAME) !== -1;\n+ var i = OpenLayers.Util.indexOf(control.handles, feature);\n+ i += OpenLayers.Util.indexOf(control.rotationHandles, feature);\n+ if (feature !== control.feature && feature !== control.box && i == -2 && eligible) {\n+ control.setFeature(feature)\n+ }\n+ },\n+ onComplete: function(feature, pixel) {\n+ control.events.triggerEvent(\"transformcomplete\", {\n+ feature: control.feature\n+ })\n+ }\n+ })\n+ },\n+ drawHandles: function() {\n+ var layer = this.layer;\n+ for (var i = 0; i < 8; ++i) {\n+ if (this.rotate && i % 2 === 0) {\n+ layer.drawFeature(this.rotationHandles[i / 2], this.rotationHandleSymbolizer)\n+ }\n+ layer.drawFeature(this.handles[i], this.renderIntent)\n+ }\n+ },\n+ transformFeature: function(mods) {\n+ if (!this._setfeature) {\n+ this.scale *= mods.scale || 1;\n+ this.ratio *= mods.ratio || 1;\n+ var oldRotation = this.rotation;\n+ this.rotation = (this.rotation + (mods.rotation || 0)) % 360;\n+ if (this.events.triggerEvent(\"beforetransform\", mods) !== false) {\n+ var feature = this.feature;\n+ var geom = feature.geometry;\n+ var center = this.center;\n+ geom.rotate(-oldRotation, center);\n+ if (mods.scale || mods.ratio) {\n+ geom.resize(mods.scale, center, mods.ratio)\n+ } else if (mods.center) {\n+ feature.move(mods.center.getBounds().getCenterLonLat())\n+ }\n+ geom.rotate(this.rotation, center);\n+ this.layer.drawFeature(feature);\n+ feature.toState(OpenLayers.State.UPDATE);\n+ this.events.triggerEvent(\"transform\", mods)\n+ }\n+ }\n+ this.layer.drawFeature(this.box, this.renderIntent);\n+ this.drawHandles()\n+ },\n+ destroy: function() {\n+ var geom;\n+ for (var i = 0; i < 8; ++i) {\n+ geom = this.box.geometry.components[i];\n+ geom._handle.destroy();\n+ geom._handle = null;\n+ geom._rotationHandle && geom._rotationHandle.destroy();\n+ geom._rotationHandle = null\n+ }\n+ this.center = null;\n+ this.feature = null;\n+ this.handles = null;\n+ this.rotationHandleSymbolizer = null;\n+ this.rotationHandles = null;\n+ this.box.destroy();\n+ this.box = null;\n+ this.layer = null;\n+ this.dragControl.destroy();\n+ this.dragControl = null;\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.TransformFeature\"\n+});\n+OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {\n+ initialize: function(options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ this.addControls([new OpenLayers.Control.Navigation, new OpenLayers.Control.ZoomBox])\n+ },\n+ draw: function() {\n+ var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n+ if (this.defaultControl === null) {\n+ this.defaultControl = this.controls[0]\n+ }\n+ return div\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.NavToolbar\"\n+});\n+OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {\n+ maxWidth: 100,\n+ topOutUnits: \"km\",\n+ topInUnits: \"m\",\n+ bottomOutUnits: \"mi\",\n+ bottomInUnits: \"ft\",\n+ eTop: null,\n+ eBottom: null,\n+ geodesic: false,\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.eTop) {\n+ this.eTop = document.createElement(\"div\");\n+ this.eTop.className = this.displayClass + \"Top\";\n+ var theLen = this.topInUnits.length;\n+ this.div.appendChild(this.eTop);\n+ if (this.topOutUnits == \"\" || this.topInUnits == \"\") {\n+ this.eTop.style.visibility = \"hidden\"\n+ } else {\n+ this.eTop.style.visibility = \"visible\"\n+ }\n+ this.eBottom = document.createElement(\"div\");\n+ this.eBottom.className = this.displayClass + \"Bottom\";\n+ this.div.appendChild(this.eBottom);\n+ if (this.bottomOutUnits == \"\" || this.bottomInUnits == \"\") {\n+ this.eBottom.style.visibility = \"hidden\"\n+ } else {\n+ this.eBottom.style.visibility = \"visible\"\n+ }\n+ }\n+ this.map.events.register(\"moveend\", this, this.update);\n+ this.update();\n+ return this.div\n+ },\n+ getBarLen: function(maxLen) {\n+ var digits = parseInt(Math.log(maxLen) / Math.log(10));\n+ var pow10 = Math.pow(10, digits);\n+ var firstChar = parseInt(maxLen / pow10);\n+ var barLen;\n+ if (firstChar > 5) {\n+ barLen = 5\n+ } else if (firstChar > 2) {\n+ barLen = 2\n+ } else {\n+ barLen = 1\n+ }\n+ return barLen * pow10\n+ },\n+ update: function() {\n+ var res = this.map.getResolution();\n+ if (!res) {\n+ return\n+ }\n+ var curMapUnits = this.map.getUnits();\n+ var inches = OpenLayers.INCHES_PER_UNIT;\n+ var maxSizeData = this.maxWidth * res * inches[curMapUnits];\n+ var geodesicRatio = 1;\n+ if (this.geodesic === true) {\n+ var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || 1e-6) * this.maxWidth;\n+ var maxSizeKilometers = maxSizeData / inches[\"km\"];\n+ geodesicRatio = maxSizeGeodesic / maxSizeKilometers;\n+ maxSizeData *= geodesicRatio\n+ }\n+ var topUnits;\n+ var bottomUnits;\n+ if (maxSizeData > 1e5) {\n+ topUnits = this.topOutUnits;\n+ bottomUnits = this.bottomOutUnits\n+ } else {\n+ topUnits = this.topInUnits;\n+ bottomUnits = this.bottomInUnits\n+ }\n+ var topMax = maxSizeData / inches[topUnits];\n+ var bottomMax = maxSizeData / inches[bottomUnits];\n+ var topRounded = this.getBarLen(topMax);\n+ var bottomRounded = this.getBarLen(bottomMax);\n+ topMax = topRounded / inches[curMapUnits] * inches[topUnits];\n+ bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];\n+ var topPx = topMax / res / geodesicRatio;\n+ var bottomPx = bottomMax / res / geodesicRatio;\n+ if (this.eBottom.style.visibility == \"visible\") {\n+ this.eBottom.style.width = Math.round(bottomPx) + \"px\";\n+ this.eBottom.innerHTML = bottomRounded + \" \" + bottomUnits\n+ }\n+ if (this.eTop.style.visibility == \"visible\") {\n+ this.eTop.style.width = Math.round(topPx) + \"px\";\n+ this.eTop.innerHTML = topRounded + \" \" + topUnits\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.ScaleLine\"\n+});\n OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {\n element: null,\n ovmap: null,\n size: {\n w: 180,\n h: 90\n },\n@@ -31559,319 +28457,376 @@\n x: Math.round(1 / res * (lonlat.lon - extent.left)),\n y: Math.round(1 / res * (extent.top - lonlat.lat))\n }\n }\n },\n CLASS_NAME: \"OpenLayers.Control.OverviewMap\"\n });\n-OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {\n- hover: false,\n- drillDown: false,\n- maxFeatures: 10,\n- clickCallback: \"click\",\n- output: \"features\",\n- layers: null,\n- queryVisible: false,\n- url: null,\n- layerUrls: null,\n- infoFormat: \"text/html\",\n- vendorParams: {},\n- format: null,\n- formatOptions: null,\n- handler: null,\n- hoverRequest: null,\n- initialize: function(options) {\n- options = options || {};\n- options.handlerOptions = options.handlerOptions || {};\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (!this.format) {\n- this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions)\n+OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {\n+ geolocation: null,\n+ available: \"geolocation\" in navigator,\n+ bind: true,\n+ watch: false,\n+ geolocationOptions: null,\n+ destroy: function() {\n+ this.deactivate();\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ activate: function() {\n+ if (this.available && !this.geolocation) {\n+ this.geolocation = navigator.geolocation\n }\n- if (this.drillDown === true) {\n- this.hover = false\n+ if (!this.geolocation) {\n+ this.events.triggerEvent(\"locationuncapable\");\n+ return false\n }\n- if (this.hover) {\n- this.handler = new OpenLayers.Handler.Hover(this, {\n- move: this.cancelHover,\n- pause: this.getInfoForHover\n- }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, {\n- delay: 250\n- }))\n- } else {\n- var callbacks = {};\n- callbacks[this.clickCallback] = this.getInfoForClick;\n- this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {})\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ if (this.watch) {\n+ this.watchId = this.geolocation.watchPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions)\n+ } else {\n+ this.getCurrentLocation()\n+ }\n+ return true\n }\n+ return false\n },\n- getInfoForClick: function(evt) {\n- this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: evt.xy\n- });\n- OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n- this.request(evt.xy, {})\n+ deactivate: function() {\n+ if (this.active && this.watchId !== null) {\n+ this.geolocation.clearWatch(this.watchId)\n+ }\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- getInfoForHover: function(evt) {\n- this.events.triggerEvent(\"beforegetfeatureinfo\", {\n- xy: evt.xy\n- });\n- this.request(evt.xy, {\n- hover: true\n+ geolocate: function(position) {\n+ var center = new OpenLayers.LonLat(position.coords.longitude, position.coords.latitude).transform(new OpenLayers.Projection(\"EPSG:4326\"), this.map.getProjectionObject());\n+ if (this.bind) {\n+ this.map.setCenter(center)\n+ }\n+ this.events.triggerEvent(\"locationupdated\", {\n+ position: position,\n+ point: new OpenLayers.Geometry.Point(center.lon, center.lat)\n })\n },\n- cancelHover: function() {\n- if (this.hoverRequest) {\n- this.hoverRequest.abort();\n- this.hoverRequest = null\n+ getCurrentLocation: function() {\n+ if (!this.active || this.watch) {\n+ return false\n }\n+ this.geolocation.getCurrentPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions);\n+ return true\n },\n- findLayers: function() {\n- var candidates = this.layers || this.map.layers;\n- var layers = [];\n- var layer, url;\n- for (var i = candidates.length - 1; i >= 0; --i) {\n- layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.WMS && (!this.queryVisible || layer.getVisibility())) {\n- url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n- if (this.drillDown === false && !this.url) {\n- this.url = url\n- }\n- if (this.drillDown === true || this.urlMatches(url)) {\n- layers.push(layer)\n- }\n- }\n- }\n- return layers\n+ failure: function(error) {\n+ this.events.triggerEvent(\"locationfailed\", {\n+ error: error\n+ })\n },\n- urlMatches: function(url) {\n- var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);\n- if (!matches && this.layerUrls) {\n- for (var i = 0, len = this.layerUrls.length; i < len; ++i) {\n- if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {\n- matches = true;\n- break\n- }\n- }\n- }\n- return matches\n+ CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n+});\n+OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {\n+ autoActivate: true,\n+ intervals: [45, 30, 20, 10, 5, 2, 1, .5, .2, .1, .05, .01, .005, .002, .001],\n+ displayInLayerSwitcher: true,\n+ visible: true,\n+ numPoints: 50,\n+ targetSize: 200,\n+ layerName: null,\n+ labelled: true,\n+ labelFormat: \"dm\",\n+ lineSymbolizer: {\n+ strokeColor: \"#333\",\n+ strokeWidth: 1,\n+ strokeOpacity: .5\n },\n- buildWMSOptions: function(url, layers, clickPosition, format) {\n- var layerNames = [],\n- styleNames = [];\n- for (var i = 0, len = layers.length; i < len; i++) {\n- if (layers[i].params.LAYERS != null) {\n- layerNames = layerNames.concat(layers[i].params.LAYERS);\n- styleNames = styleNames.concat(this.getStyleNames(layers[i]))\n- }\n- }\n- var firstLayer = layers[0];\n- var projection = this.map.getProjection();\n- var layerProj = firstLayer.projection;\n- if (layerProj && layerProj.equals(this.map.getProjectionObject())) {\n- projection = layerProj.getCode()\n+ labelSymbolizer: {},\n+ gratLayer: null,\n+ initialize: function(options) {\n+ options = options || {};\n+ options.layerName = options.layerName || OpenLayers.i18n(\"Graticule\");\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.labelSymbolizer.stroke = false;\n+ this.labelSymbolizer.fill = false;\n+ this.labelSymbolizer.label = \"${label}\";\n+ this.labelSymbolizer.labelAlign = \"${labelAlign}\";\n+ this.labelSymbolizer.labelXOffset = \"${xOffset}\";\n+ this.labelSymbolizer.labelYOffset = \"${yOffset}\"\n+ },\n+ destroy: function() {\n+ this.deactivate();\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.gratLayer) {\n+ this.gratLayer.destroy();\n+ this.gratLayer = null\n }\n- var params = OpenLayers.Util.extend({\n- service: \"WMS\",\n- version: firstLayer.params.VERSION,\n- request: \"GetFeatureInfo\",\n- exceptions: firstLayer.params.EXCEPTIONS,\n- bbox: this.map.getExtent().toBBOX(null, firstLayer.reverseAxisOrder()),\n- feature_count: this.maxFeatures,\n- height: this.map.getSize().h,\n- width: this.map.getSize().w,\n- format: format,\n- info_format: firstLayer.params.INFO_FORMAT || this.infoFormat\n- }, parseFloat(firstLayer.params.VERSION) >= 1.3 ? {\n- crs: projection,\n- i: parseInt(clickPosition.x),\n- j: parseInt(clickPosition.y)\n- } : {\n- srs: projection,\n- x: parseInt(clickPosition.x),\n- y: parseInt(clickPosition.y)\n- });\n- if (layerNames.length != 0) {\n- params = OpenLayers.Util.extend({\n- layers: layerNames,\n- query_layers: layerNames,\n- styles: styleNames\n- }, params)\n+ },\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.gratLayer) {\n+ var gratStyle = new OpenLayers.Style({}, {\n+ rules: [new OpenLayers.Rule({\n+ symbolizer: {\n+ Point: this.labelSymbolizer,\n+ Line: this.lineSymbolizer\n+ }\n+ })]\n+ });\n+ this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {\n+ styleMap: new OpenLayers.StyleMap({\n+ default: gratStyle\n+ }),\n+ visibility: this.visible,\n+ displayInLayerSwitcher: this.displayInLayerSwitcher\n+ })\n }\n- OpenLayers.Util.applyDefaults(params, this.vendorParams);\n- return {\n- url: url,\n- params: OpenLayers.Util.upperCaseObject(params),\n- callback: function(request) {\n- this.handleResponse(clickPosition, request, url)\n- },\n- scope: this\n+ return this.div\n+ },\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.map.addLayer(this.gratLayer);\n+ this.map.events.register(\"moveend\", this, this.update);\n+ this.update();\n+ return true\n+ } else {\n+ return false\n }\n },\n- getStyleNames: function(layer) {\n- var styleNames;\n- if (layer.params.STYLES) {\n- styleNames = layer.params.STYLES\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.map.events.unregister(\"moveend\", this, this.update);\n+ this.map.removeLayer(this.gratLayer);\n+ return true\n } else {\n- if (OpenLayers.Util.isArray(layer.params.LAYERS)) {\n- styleNames = new Array(layer.params.LAYERS.length)\n- } else {\n- styleNames = layer.params.LAYERS.replace(/[^,]/g, \"\")\n- }\n+ return false\n }\n- return styleNames\n },\n- request: function(clickPosition, options) {\n- var layers = this.findLayers();\n- if (layers.length == 0) {\n- this.events.triggerEvent(\"nogetfeatureinfo\");\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\");\n+ update: function() {\n+ var mapBounds = this.map.getExtent();\n+ if (!mapBounds) {\n return\n }\n- options = options || {};\n- if (this.drillDown === false) {\n- var wmsOptions = this.buildWMSOptions(this.url, layers, clickPosition, layers[0].params.FORMAT);\n- var request = OpenLayers.Request.GET(wmsOptions);\n- if (options.hover === true) {\n- this.hoverRequest = request\n- }\n- } else {\n- this._requestCount = 0;\n- this._numRequests = 0;\n- this.features = [];\n- var services = {},\n- url;\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var service, found = false;\n- url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;\n- if (url in services) {\n- services[url].push(layer)\n- } else {\n- this._numRequests++;\n- services[url] = [layer]\n- }\n- }\n- var layers;\n- for (var url in services) {\n- layers = services[url];\n- var wmsOptions = this.buildWMSOptions(url, layers, clickPosition, layers[0].params.FORMAT);\n- OpenLayers.Request.GET(wmsOptions)\n- }\n+ this.gratLayer.destroyFeatures();\n+ var llProj = new OpenLayers.Projection(\"EPSG:4326\");\n+ var mapProj = this.map.getProjectionObject();\n+ var mapRes = this.map.getResolution();\n+ if (mapProj.proj && mapProj.proj.projName == \"longlat\") {\n+ this.numPoints = 1\n }\n- },\n- triggerGetFeatureInfo: function(request, xy, features) {\n- this.events.triggerEvent(\"getfeatureinfo\", {\n- text: request.responseText,\n- features: features,\n- request: request,\n- xy: xy\n- });\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n- },\n- handleResponse: function(xy, request, url) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ var mapCenter = this.map.getCenter();\n+ var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);\n+ OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);\n+ var testSq = this.targetSize * mapRes;\n+ testSq *= testSq;\n+ var llInterval;\n+ for (var i = 0; i < this.intervals.length; ++i) {\n+ llInterval = this.intervals[i];\n+ var delta = llInterval / 2;\n+ var p1 = mapCenterLL.offset({\n+ x: -delta,\n+ y: -delta\n+ });\n+ var p2 = mapCenterLL.offset({\n+ x: delta,\n+ y: delta\n+ });\n+ OpenLayers.Projection.transform(p1, llProj, mapProj);\n+ OpenLayers.Projection.transform(p2, llProj, mapProj);\n+ var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);\n+ if (distSq <= testSq) {\n+ break\n+ }\n }\n- var features = this.format.read(doc);\n- if (this.drillDown === false) {\n- this.triggerGetFeatureInfo(request, xy, features)\n- } else {\n- this._requestCount++;\n- if (this.output === \"object\") {\n- this._features = (this._features || []).concat({\n- url: url,\n- features: features\n- })\n- } else {\n- this._features = (this._features || []).concat(features)\n+ mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval;\n+ mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval;\n+ var iter = 0;\n+ var centerLonPoints = [mapCenterLL.clone()];\n+ var newPoint = mapCenterLL.clone();\n+ var mapXY;\n+ do {\n+ newPoint = newPoint.offset({\n+ x: 0,\n+ y: llInterval\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLonPoints.unshift(newPoint)\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: 0,\n+ y: -llInterval\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLonPoints.push(newPoint)\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n+ iter = 0;\n+ var centerLatPoints = [mapCenterLL.clone()];\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: -llInterval,\n+ y: 0\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLatPoints.unshift(newPoint)\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n+ newPoint = mapCenterLL.clone();\n+ do {\n+ newPoint = newPoint.offset({\n+ x: llInterval,\n+ y: 0\n+ });\n+ mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n+ centerLatPoints.push(newPoint)\n+ } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n+ var lines = [];\n+ for (var i = 0; i < centerLatPoints.length; ++i) {\n+ var lon = centerLatPoints[i].x;\n+ var pointList = [];\n+ var labelPoint = null;\n+ var latEnd = Math.min(centerLonPoints[0].y, 90);\n+ var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);\n+ var latDelta = (latEnd - latStart) / this.numPoints;\n+ var lat = latStart;\n+ for (var j = 0; j <= this.numPoints; ++j) {\n+ var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n+ gridPoint.transform(llProj, mapProj);\n+ pointList.push(gridPoint);\n+ lat += latDelta;\n+ if (gridPoint.y >= mapBounds.bottom && !labelPoint) {\n+ labelPoint = gridPoint\n+ }\n }\n- if (this._requestCount === this._numRequests) {\n- this.triggerGetFeatureInfo(request, xy, this._features.concat());\n- delete this._features;\n- delete this._requestCount;\n- delete this._numRequests\n+ if (this.labelled) {\n+ var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom);\n+ var labelAttrs = {\n+ value: lon,\n+ label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, \"lon\", this.labelFormat) : \"\",\n+ labelAlign: \"cb\",\n+ xOffset: 0,\n+ yOffset: 2\n+ };\n+ this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs))\n }\n+ var geom = new OpenLayers.Geometry.LineString(pointList);\n+ lines.push(new OpenLayers.Feature.Vector(geom))\n }\n- },\n- CLASS_NAME: \"OpenLayers.Control.WMSGetFeatureInfo\"\n-});\n-OpenLayers.Control.EditingToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {\n- citeCompliant: false,\n- initialize: function(layer, options) {\n- OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n- this.addControls([new OpenLayers.Control.Navigation]);\n- var controls = [new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {\n- displayClass: \"olControlDrawFeaturePoint\",\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n+ for (var j = 0; j < centerLonPoints.length; ++j) {\n+ lat = centerLonPoints[j].y;\n+ if (lat < -90 || lat > 90) {\n+ continue\n }\n- }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {\n- displayClass: \"olControlDrawFeaturePath\",\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n+ var pointList = [];\n+ var lonStart = centerLatPoints[0].x;\n+ var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;\n+ var lonDelta = (lonEnd - lonStart) / this.numPoints;\n+ var lon = lonStart;\n+ var labelPoint = null;\n+ for (var i = 0; i <= this.numPoints; ++i) {\n+ var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n+ gridPoint.transform(llProj, mapProj);\n+ pointList.push(gridPoint);\n+ lon += lonDelta;\n+ if (gridPoint.x < mapBounds.right) {\n+ labelPoint = gridPoint\n+ }\n }\n- }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {\n- displayClass: \"olControlDrawFeaturePolygon\",\n- handlerOptions: {\n- citeCompliant: this.citeCompliant\n+ if (this.labelled) {\n+ var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);\n+ var labelAttrs = {\n+ value: lat,\n+ label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, \"lat\", this.labelFormat) : \"\",\n+ labelAlign: \"rb\",\n+ xOffset: -2,\n+ yOffset: 2\n+ };\n+ this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs))\n }\n- })];\n- this.addControls(controls)\n- },\n- draw: function() {\n- var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);\n- if (this.defaultControl === null) {\n- this.defaultControl = this.controls[0]\n+ var geom = new OpenLayers.Geometry.LineString(pointList);\n+ lines.push(new OpenLayers.Feature.Vector(geom))\n }\n- return div\n+ this.gratLayer.addFeatures(lines)\n },\n- CLASS_NAME: \"OpenLayers.Control.EditingToolbar\"\n+ CLASS_NAME: \"OpenLayers.Control.Graticule\"\n });\n-OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {\n+ autoActivate: true,\n+ element: null,\n+ prefix: \"\",\n separator: \", \",\n- template: \"${layers}\",\n+ suffix: \"\",\n+ numDigits: 5,\n+ granularity: 10,\n+ emptyString: null,\n+ lastXy: null,\n+ displayProjection: null,\n destroy: function() {\n- this.map.events.un({\n- removelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- changebaselayer: this.updateAttribution,\n- scope: this\n- });\n+ this.deactivate();\n OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.map.events.register(\"mousemove\", this, this.redraw);\n+ this.map.events.register(\"mouseout\", this, this.reset);\n+ this.redraw();\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.map.events.unregister(\"mousemove\", this, this.redraw);\n+ this.map.events.unregister(\"mouseout\", this, this.reset);\n+ this.element.innerHTML = \"\";\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n draw: function() {\n OpenLayers.Control.prototype.draw.apply(this, arguments);\n- this.map.events.on({\n- changebaselayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- removelayer: this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n+ if (!this.element) {\n+ this.div.left = \"\";\n+ this.div.top = \"\";\n+ this.element = this.div\n+ }\n return this.div\n },\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution)\n- }\n- }\n+ redraw: function(evt) {\n+ var lonLat;\n+ if (evt == null) {\n+ this.reset();\n+ return\n+ } else {\n+ if (this.lastXy == null || Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) {\n+ this.lastXy = evt.xy;\n+ return\n }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- })\n+ lonLat = this.map.getLonLatFromPixel(evt.xy);\n+ if (!lonLat) {\n+ return\n+ }\n+ if (this.displayProjection) {\n+ lonLat.transform(this.map.getProjectionObject(), this.displayProjection)\n+ }\n+ this.lastXy = evt.xy\n+ }\n+ var newHtml = this.formatOutput(lonLat);\n+ if (newHtml != this.element.innerHTML) {\n+ this.element.innerHTML = newHtml\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+ reset: function(evt) {\n+ if (this.emptyString != null) {\n+ this.element.innerHTML = this.emptyString\n+ }\n+ },\n+ formatOutput: function(lonLat) {\n+ var digits = parseInt(this.numDigits);\n+ var newHtml = this.prefix + lonLat.lon.toFixed(digits) + this.separator + lonLat.lat.toFixed(digits) + this.suffix;\n+ return newHtml\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.MousePosition\"\n });\n OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {\n slideFactor: 50,\n slideRatio: null,\n buttons: null,\n position: null,\n initialize: function(options) {\n@@ -31958,356 +28913,14 @@\n getSlideFactor: function(dim) {\n return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor\n },\n CLASS_NAME: \"OpenLayers.Control.PanZoom\"\n });\n OpenLayers.Control.PanZoom.X = 4;\n OpenLayers.Control.PanZoom.Y = 4;\n-OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {\n- argParserClass: OpenLayers.Control.ArgParser,\n- element: null,\n- anchor: false,\n- base: \"\",\n- displayProjection: null,\n- initialize: function(element, base, options) {\n- if (element !== null && typeof element == \"object\" && !OpenLayers.Util.isElement(element)) {\n- options = element;\n- this.base = document.location.href;\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (this.element != null) {\n- this.element = OpenLayers.Util.getElement(this.element)\n- }\n- } else {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.element = OpenLayers.Util.getElement(element);\n- this.base = base || document.location.href\n- }\n- },\n- destroy: function() {\n- if (this.element && this.element.parentNode == this.div) {\n- this.div.removeChild(this.element);\n- this.element = null\n- }\n- if (this.map) {\n- this.map.events.unregister(\"moveend\", this, this.updateLink)\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n- },\n- setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- for (var i = 0, len = this.map.controls.length; i < len; i++) {\n- var control = this.map.controls[i];\n- if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {\n- if (control.displayProjection != this.displayProjection) {\n- this.displayProjection = control.displayProjection\n- }\n- break\n- }\n- }\n- if (i == this.map.controls.length) {\n- this.map.addControl(new this.argParserClass({\n- displayProjection: this.displayProjection\n- }))\n- }\n- },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.element && !this.anchor) {\n- this.element = document.createElement(\"a\");\n- this.element.innerHTML = OpenLayers.i18n(\"Permalink\");\n- this.element.href = \"\";\n- this.div.appendChild(this.element)\n- }\n- this.map.events.on({\n- moveend: this.updateLink,\n- changelayer: this.updateLink,\n- changebaselayer: this.updateLink,\n- scope: this\n- });\n- this.updateLink();\n- return this.div\n- },\n- updateLink: function() {\n- var separator = this.anchor ? \"#\" : \"?\";\n- var href = this.base;\n- var anchor = null;\n- if (href.indexOf(\"#\") != -1 && this.anchor == false) {\n- anchor = href.substring(href.indexOf(\"#\"), href.length)\n- }\n- if (href.indexOf(separator) != -1) {\n- href = href.substring(0, href.indexOf(separator))\n- }\n- var splits = href.split(\"#\");\n- href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams());\n- if (anchor) {\n- href += anchor\n- }\n- if (this.anchor && !this.element) {\n- window.location.href = href\n- } else {\n- this.element.href = href\n- }\n- },\n- createParams: function(center, zoom, layers) {\n- center = center || this.map.getCenter();\n- var params = OpenLayers.Util.getParameters(this.base);\n- if (center) {\n- params.zoom = zoom || this.map.getZoom();\n- var lat = center.lat;\n- var lon = center.lon;\n- if (this.displayProjection) {\n- var mapPosition = OpenLayers.Projection.transform({\n- x: lon,\n- y: lat\n- }, this.map.getProjectionObject(), this.displayProjection);\n- lon = mapPosition.x;\n- lat = mapPosition.y\n- }\n- params.lat = Math.round(lat * 1e5) / 1e5;\n- params.lon = Math.round(lon * 1e5) / 1e5;\n- layers = layers || this.map.layers;\n- params.layers = \"\";\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- if (layer.isBaseLayer) {\n- params.layers += layer == this.map.baseLayer ? \"B\" : \"0\"\n- } else {\n- params.layers += layer.getVisibility() ? \"T\" : \"F\"\n- }\n- }\n- }\n- return params\n- },\n- CLASS_NAME: \"OpenLayers.Control.Permalink\"\n-});\n-OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {\n- layer: null,\n- source: null,\n- sourceOptions: null,\n- tolerance: null,\n- edge: true,\n- deferDelete: false,\n- mutual: true,\n- targetFilter: null,\n- sourceFilter: null,\n- handler: null,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.options = options || {};\n- if (this.options.source) {\n- this.setSource(this.options.source)\n- }\n- },\n- setSource: function(layer) {\n- if (this.active) {\n- this.deactivate();\n- if (this.handler) {\n- this.handler.destroy();\n- delete this.handler\n- }\n- this.source = layer;\n- this.activate()\n- } else {\n- this.source = layer\n- }\n- },\n- activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- if (!this.source) {\n- if (!this.handler) {\n- this.handler = new OpenLayers.Handler.Path(this, {\n- done: function(geometry) {\n- this.onSketchComplete({\n- feature: new OpenLayers.Feature.Vector(geometry)\n- })\n- }\n- }, {\n- layerOptions: this.sourceOptions\n- })\n- }\n- this.handler.activate()\n- } else if (this.source.events) {\n- this.source.events.on({\n- sketchcomplete: this.onSketchComplete,\n- afterfeaturemodified: this.afterFeatureModified,\n- scope: this\n- })\n- }\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- if (this.source && this.source.events) {\n- this.source.events.un({\n- sketchcomplete: this.onSketchComplete,\n- afterfeaturemodified: this.afterFeatureModified,\n- scope: this\n- })\n- }\n- }\n- return deactivated\n- },\n- onSketchComplete: function(event) {\n- this.feature = null;\n- return !this.considerSplit(event.feature)\n- },\n- afterFeatureModified: function(event) {\n- if (event.modified) {\n- var feature = event.feature;\n- if (typeof feature.geometry.split === \"function\") {\n- this.feature = event.feature;\n- this.considerSplit(event.feature)\n- }\n- }\n- },\n- removeByGeometry: function(features, geometry) {\n- for (var i = 0, len = features.length; i < len; ++i) {\n- if (features[i].geometry === geometry) {\n- features.splice(i, 1);\n- break\n- }\n- }\n- },\n- isEligible: function(target) {\n- if (!target.geometry) {\n- return false\n- } else {\n- return target.state !== OpenLayers.State.DELETE && typeof target.geometry.split === \"function\" && this.feature !== target && (!this.targetFilter || this.targetFilter.evaluate(target.attributes))\n- }\n- },\n- considerSplit: function(feature) {\n- var sourceSplit = false;\n- var targetSplit = false;\n- if (!this.sourceFilter || this.sourceFilter.evaluate(feature.attributes)) {\n- var features = this.layer && this.layer.features || [];\n- var target, results, proceed;\n- var additions = [],\n- removals = [];\n- var mutual = this.layer === this.source && this.mutual;\n- var options = {\n- edge: this.edge,\n- tolerance: this.tolerance,\n- mutual: mutual\n- };\n- var sourceParts = [feature.geometry];\n- var targetFeature, targetParts;\n- var source, parts;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- targetFeature = features[i];\n- if (this.isEligible(targetFeature)) {\n- targetParts = [targetFeature.geometry];\n- for (var j = 0; j < sourceParts.length; ++j) {\n- source = sourceParts[j];\n- for (var k = 0; k < targetParts.length; ++k) {\n- target = targetParts[k];\n- if (source.getBounds().intersectsBounds(target.getBounds())) {\n- results = source.split(target, options);\n- if (results) {\n- proceed = this.events.triggerEvent(\"beforesplit\", {\n- source: feature,\n- target: targetFeature\n- });\n- if (proceed !== false) {\n- if (mutual) {\n- parts = results[0];\n- if (parts.length > 1) {\n- parts.unshift(j, 1);\n- Array.prototype.splice.apply(sourceParts, parts);\n- j += parts.length - 3\n- }\n- results = results[1]\n- }\n- if (results.length > 1) {\n- results.unshift(k, 1);\n- Array.prototype.splice.apply(targetParts, results);\n- k += results.length - 3\n- }\n- }\n- }\n- }\n- }\n- }\n- if (targetParts && targetParts.length > 1) {\n- this.geomsToFeatures(targetFeature, targetParts);\n- this.events.triggerEvent(\"split\", {\n- original: targetFeature,\n- features: targetParts\n- });\n- Array.prototype.push.apply(additions, targetParts);\n- removals.push(targetFeature);\n- targetSplit = true\n- }\n- }\n- }\n- if (sourceParts && sourceParts.length > 1) {\n- this.geomsToFeatures(feature, sourceParts);\n- this.events.triggerEvent(\"split\", {\n- original: feature,\n- features: sourceParts\n- });\n- Array.prototype.push.apply(additions, sourceParts);\n- removals.push(feature);\n- sourceSplit = true\n- }\n- if (sourceSplit || targetSplit) {\n- if (this.deferDelete) {\n- var feat, destroys = [];\n- for (var i = 0, len = removals.length; i < len; ++i) {\n- feat = removals[i];\n- if (feat.state === OpenLayers.State.INSERT) {\n- destroys.push(feat)\n- } else {\n- feat.state = OpenLayers.State.DELETE;\n- this.layer.drawFeature(feat)\n- }\n- }\n- this.layer.destroyFeatures(destroys, {\n- silent: true\n- });\n- for (var i = 0, len = additions.length; i < len; ++i) {\n- additions[i].state = OpenLayers.State.INSERT\n- }\n- } else {\n- this.layer.destroyFeatures(removals, {\n- silent: true\n- })\n- }\n- this.layer.addFeatures(additions, {\n- silent: true\n- });\n- this.events.triggerEvent(\"aftersplit\", {\n- source: feature,\n- features: additions\n- })\n- }\n- }\n- return sourceSplit\n- },\n- geomsToFeatures: function(feature, geoms) {\n- var clone = feature.clone();\n- delete clone.geometry;\n- var newFeature;\n- for (var i = 0, len = geoms.length; i < len; ++i) {\n- newFeature = clone.clone();\n- newFeature.geometry = geoms[i];\n- newFeature.state = OpenLayers.State.INSERT;\n- geoms[i] = newFeature\n- }\n- },\n- destroy: function() {\n- if (this.active) {\n- this.deactivate()\n- }\n- OpenLayers.Control.prototype.destroy.call(this)\n- },\n- CLASS_NAME: \"OpenLayers.Control.Split\"\n-});\n OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {\n zoomStopWidth: 18,\n zoomStopHeight: 11,\n slider: null,\n sliderEvents: null,\n zoombarDiv: null,\n zoomWorldIcon: false,\n@@ -32516,860 +29129,1468 @@\n },\n moveZoomBar: function() {\n var newTop = (this.map.getNumZoomLevels() - 1 - this.map.getZoom()) * this.zoomStopHeight + this.startTop + 1;\n this.slider.style.top = newTop + \"px\"\n },\n CLASS_NAME: \"OpenLayers.Control.PanZoomBar\"\n });\n-OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {\n- geometryTypes: null,\n- onStart: function(feature, pixel) {},\n- onDrag: function(feature, pixel) {},\n- onComplete: function(feature, pixel) {},\n- onEnter: function(feature) {},\n- onLeave: function(feature) {},\n- documentDrag: false,\n- layer: null,\n- feature: null,\n- dragCallbacks: {},\n- featureCallbacks: {},\n- lastPixel: null,\n- initialize: function(layer, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.layer = layer;\n- this.handlers = {\n- drag: new OpenLayers.Handler.Drag(this, OpenLayers.Util.extend({\n- down: this.downFeature,\n- move: this.moveFeature,\n- up: this.upFeature,\n- out: this.cancel,\n- done: this.doneDragging\n- }, this.dragCallbacks), {\n- documentDrag: this.documentDrag\n- }),\n- feature: new OpenLayers.Handler.Feature(this, this.layer, OpenLayers.Util.extend({\n- click: this.clickFeature,\n- clickout: this.clickoutFeature,\n- over: this.overFeature,\n- out: this.outFeature\n- }, this.featureCallbacks), {\n- geometryTypes: this.geometryTypes\n- })\n+OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, {\n+ slideFactor: 50,\n+ slideRatio: null,\n+ direction: null,\n+ initialize: function(direction, options) {\n+ this.direction = direction;\n+ this.CLASS_NAME += this.direction;\n+ OpenLayers.Control.prototype.initialize.apply(this, [options])\n+ },\n+ trigger: function() {\n+ if (this.map) {\n+ var getSlideFactor = OpenLayers.Function.bind(function(dim) {\n+ return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor\n+ }, this);\n+ switch (this.direction) {\n+ case OpenLayers.Control.Pan.NORTH:\n+ this.map.pan(0, -getSlideFactor(\"h\"));\n+ break;\n+ case OpenLayers.Control.Pan.SOUTH:\n+ this.map.pan(0, getSlideFactor(\"h\"));\n+ break;\n+ case OpenLayers.Control.Pan.WEST:\n+ this.map.pan(-getSlideFactor(\"w\"), 0);\n+ break;\n+ case OpenLayers.Control.Pan.EAST:\n+ this.map.pan(getSlideFactor(\"w\"), 0);\n+ break\n+ }\n }\n },\n- clickFeature: function(feature) {\n- if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {\n- this.handlers.drag.dragstart(this.handlers.feature.evt);\n- this.handlers.drag.stopDown = false\n+ CLASS_NAME: \"OpenLayers.Control.Pan\"\n+});\n+OpenLayers.Control.Pan.NORTH = \"North\";\n+OpenLayers.Control.Pan.SOUTH = \"South\";\n+OpenLayers.Control.Pan.EAST = \"East\";\n+OpenLayers.Control.Pan.WEST = \"West\";\n+OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, {\n+ slideFactor: 50,\n+ slideRatio: null,\n+ initialize: function(options) {\n+ OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);\n+ var options = {\n+ slideFactor: this.slideFactor,\n+ slideRatio: this.slideRatio\n+ };\n+ this.addControls([new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)])\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.PanPanel\"\n+});\n+OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {\n+ fetchEvent: \"tileloadstart\",\n+ layers: null,\n+ autoActivate: true,\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ var i, layers = this.layers || map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.addLayer({\n+ layer: layers[i]\n+ })\n+ }\n+ if (!this.layers) {\n+ map.events.on({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ })\n }\n },\n- clickoutFeature: function(feature) {\n- if (this.handlers.feature.touch && this.over) {\n- this.outFeature(feature);\n- this.handlers.drag.stopDown = true\n+ addLayer: function(evt) {\n+ evt.layer.events.register(this.fetchEvent, this, this.fetch)\n+ },\n+ removeLayer: function(evt) {\n+ evt.layer.events.unregister(this.fetchEvent, this, this.fetch)\n+ },\n+ fetch: function(evt) {\n+ if (this.active && window.localStorage && evt.tile instanceof OpenLayers.Tile.Image) {\n+ var tile = evt.tile,\n+ url = tile.url;\n+ if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && url.indexOf(OpenLayers.ProxyHost) === 0) {\n+ url = OpenLayers.Control.CacheWrite.urlMap[url]\n+ }\n+ var dataURI = window.localStorage.getItem(\"olCache_\" + url);\n+ if (dataURI) {\n+ tile.url = dataURI;\n+ if (evt.type === \"tileerror\") {\n+ tile.setImgSrc(dataURI)\n+ }\n+ }\n }\n },\n destroy: function() {\n- this.layer = null;\n- OpenLayers.Control.prototype.destroy.apply(this, [])\n+ if (this.layers || this.map) {\n+ var i, layers = this.layers || this.map.layers;\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ this.removeLayer({\n+ layer: layers[i]\n+ })\n+ }\n+ }\n+ if (this.map) {\n+ this.map.events.un({\n+ addlayer: this.addLayer,\n+ removeLayer: this.removeLayer,\n+ scope: this\n+ })\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- return this.handlers.feature.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments)\n+ CLASS_NAME: \"OpenLayers.Control.CacheRead\"\n+});\n+OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {\n+ element: null,\n+ geodesic: false,\n+ initialize: function(element, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.element = OpenLayers.Util.getElement(element)\n },\n- deactivate: function() {\n- this.handlers.drag.deactivate();\n- this.handlers.feature.deactivate();\n- this.feature = null;\n- this.dragging = false;\n- this.lastPixel = null;\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (!this.element) {\n+ this.element = document.createElement(\"div\");\n+ this.div.appendChild(this.element)\n+ }\n+ this.map.events.register(\"moveend\", this, this.updateScale);\n+ this.updateScale();\n+ return this.div\n },\n- overFeature: function(feature) {\n- var activated = false;\n- if (!this.handlers.drag.dragging) {\n- this.feature = feature;\n- this.handlers.drag.activate();\n- activated = true;\n- this.over = true;\n- OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n- this.onEnter(feature)\n- } else {\n- if (this.feature.id == feature.id) {\n- this.over = true\n- } else {\n- this.over = false\n+ updateScale: function() {\n+ var scale;\n+ if (this.geodesic === true) {\n+ var units = this.map.getUnits();\n+ if (!units) {\n+ return\n }\n+ var inches = OpenLayers.INCHES_PER_UNIT;\n+ scale = (this.map.getGeodesicPixelSize().w || 1e-6) * inches[\"km\"] * OpenLayers.DOTS_PER_INCH\n+ } else {\n+ scale = this.map.getScale()\n }\n- return activated\n+ if (!scale) {\n+ return\n+ }\n+ if (scale >= 9500 && scale <= 95e4) {\n+ scale = Math.round(scale / 1e3) + \"K\"\n+ } else if (scale >= 95e4) {\n+ scale = Math.round(scale / 1e6) + \"M\"\n+ } else {\n+ scale = Math.round(scale)\n+ }\n+ this.element.innerHTML = OpenLayers.i18n(\"Scale = 1 : ${scaleDenom}\", {\n+ scaleDenom: scale\n+ })\n },\n- downFeature: function(pixel) {\n- this.lastPixel = pixel;\n- this.onStart(this.feature, pixel)\n+ CLASS_NAME: \"OpenLayers.Control.Scale\"\n+});\n+OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n+ layerStates: null,\n+ layersDiv: null,\n+ baseLayersDiv: null,\n+ baseLayers: null,\n+ dataLbl: null,\n+ dataLayersDiv: null,\n+ dataLayers: null,\n+ minimizeDiv: null,\n+ maximizeDiv: null,\n+ ascending: true,\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments);\n+ this.layerStates = []\n },\n- moveFeature: function(pixel) {\n- var res = this.map.getResolution();\n- this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), res * (this.lastPixel.y - pixel.y));\n- this.layer.drawFeature(this.feature);\n- this.lastPixel = pixel;\n- this.onDrag(this.feature, pixel)\n+ destroy: function() {\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+ this.map.events.un({\n+ buttonclick: this.onButtonClick,\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- upFeature: function(pixel) {\n- if (!this.over) {\n- this.handlers.drag.deactivate()\n+ setMap: function(map) {\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n+ this.map.events.on({\n+ addlayer: this.redraw,\n+ changelayer: this.redraw,\n+ removelayer: this.redraw,\n+ changebaselayer: this.redraw,\n+ scope: this\n+ });\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick)\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n }\n },\n- doneDragging: function(pixel) {\n- this.onComplete(this.feature, pixel)\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this);\n+ this.loadContents();\n+ if (!this.outsideViewport) {\n+ this.minimizeControl()\n+ }\n+ this.redraw();\n+ return this.div\n },\n- outFeature: function(feature) {\n- if (!this.handlers.drag.dragging) {\n- this.over = false;\n- this.handlers.drag.deactivate();\n- OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + \"Over\");\n- this.onLeave(feature);\n- this.feature = null\n- } else {\n- if (this.feature.id == feature.id) {\n- this.over = false\n+ onButtonClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.minimizeDiv) {\n+ this.minimizeControl()\n+ } else if (button === this.maximizeDiv) {\n+ this.maximizeControl()\n+ } else if (button._layerSwitcher === this.id) {\n+ if (button[\"for\"]) {\n+ button = document.getElementById(button[\"for\"])\n+ }\n+ if (!button.disabled) {\n+ if (button.type == \"radio\") {\n+ button.checked = true;\n+ this.map.setBaseLayer(this.map.getLayer(button._layer))\n+ } else {\n+ button.checked = !button.checked;\n+ this.updateMap()\n+ }\n }\n }\n },\n- cancel: function() {\n- this.handlers.drag.deactivate();\n- this.over = false\n+ clearLayersArray: function(layersType) {\n+ this[layersType + \"LayersDiv\"].innerHTML = \"\";\n+ this[layersType + \"Layers\"] = []\n },\n- setMap: function(map) {\n- this.handlers.drag.setMap(map);\n- this.handlers.feature.setMap(map);\n- OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ checkRedraw: function() {\n+ if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) {\n+ return true\n+ }\n+ for (var i = 0, len = this.layerStates.length; i < len; i++) {\n+ var layerState = this.layerStates[i];\n+ var layer = this.map.layers[i];\n+ if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) {\n+ return true\n+ }\n+ }\n+ return false\n },\n- CLASS_NAME: \"OpenLayers.Control.DragFeature\"\n-});\n-OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {\n- maxWidth: 100,\n- topOutUnits: \"km\",\n- topInUnits: \"m\",\n- bottomOutUnits: \"mi\",\n- bottomInUnits: \"ft\",\n- eTop: null,\n- eBottom: null,\n- geodesic: false,\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.eTop) {\n- this.eTop = document.createElement(\"div\");\n- this.eTop.className = this.displayClass + \"Top\";\n- var theLen = this.topInUnits.length;\n- this.div.appendChild(this.eTop);\n- if (this.topOutUnits == \"\" || this.topInUnits == \"\") {\n- this.eTop.style.visibility = \"hidden\"\n- } else {\n- this.eTop.style.visibility = \"visible\"\n+ redraw: function() {\n+ if (!this.checkRedraw()) {\n+ return this.div\n+ }\n+ this.clearLayersArray(\"base\");\n+ this.clearLayersArray(\"data\");\n+ var containsOverlays = false;\n+ var containsBaseLayers = false;\n+ var len = this.map.layers.length;\n+ this.layerStates = new Array(len);\n+ for (var i = 0; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ this.layerStates[i] = {\n+ name: layer.name,\n+ visibility: layer.visibility,\n+ inRange: layer.inRange,\n+ id: layer.id\n }\n- this.eBottom = document.createElement(\"div\");\n- this.eBottom.className = this.displayClass + \"Bottom\";\n- this.div.appendChild(this.eBottom);\n- if (this.bottomOutUnits == \"\" || this.bottomInUnits == \"\") {\n- this.eBottom.style.visibility = \"hidden\"\n- } else {\n- this.eBottom.style.visibility = \"visible\"\n+ }\n+ var layers = this.map.layers.slice();\n+ if (!this.ascending) {\n+ layers.reverse()\n+ }\n+ for (var i = 0, len = layers.length; i < len; i++) {\n+ var layer = layers[i];\n+ var baseLayer = layer.isBaseLayer;\n+ if (layer.displayInLayerSwitcher) {\n+ if (baseLayer) {\n+ containsBaseLayers = true\n+ } else {\n+ containsOverlays = true\n+ }\n+ var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility();\n+ var inputElem = document.createElement(\"input\"),\n+ inputId = OpenLayers.Util.createUniqueID(this.id + \"_input_\");\n+ inputElem.id = inputId;\n+ inputElem.name = baseLayer ? this.id + \"_baseLayers\" : layer.name;\n+ inputElem.type = baseLayer ? \"radio\" : \"checkbox\";\n+ inputElem.value = layer.name;\n+ inputElem.checked = checked;\n+ inputElem.defaultChecked = checked;\n+ inputElem.className = \"olButton\";\n+ inputElem._layer = layer.id;\n+ inputElem._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ inputElem.disabled = true\n+ }\n+ var labelSpan = document.createElement(\"label\");\n+ labelSpan[\"for\"] = inputElem.id;\n+ OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n+ labelSpan._layer = layer.id;\n+ labelSpan._layerSwitcher = this.id;\n+ if (!baseLayer && !layer.inRange) {\n+ labelSpan.style.color = \"gray\"\n+ }\n+ labelSpan.innerHTML = layer.name;\n+ labelSpan.style.verticalAlign = baseLayer ? \"bottom\" : \"baseline\";\n+ var br = document.createElement(\"br\");\n+ var groupArray = baseLayer ? this.baseLayers : this.dataLayers;\n+ groupArray.push({\n+ layer: layer,\n+ inputElem: inputElem,\n+ labelSpan: labelSpan\n+ });\n+ var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv;\n+ groupDiv.appendChild(inputElem);\n+ groupDiv.appendChild(labelSpan);\n+ groupDiv.appendChild(br)\n }\n }\n- this.map.events.register(\"moveend\", this, this.update);\n- this.update();\n+ this.dataLbl.style.display = containsOverlays ? \"\" : \"none\";\n+ this.baseLbl.style.display = containsBaseLayers ? \"\" : \"none\";\n return this.div\n },\n- getBarLen: function(maxLen) {\n- var digits = parseInt(Math.log(maxLen) / Math.log(10));\n- var pow10 = Math.pow(10, digits);\n- var firstChar = parseInt(maxLen / pow10);\n- var barLen;\n- if (firstChar > 5) {\n- barLen = 5\n- } else if (firstChar > 2) {\n- barLen = 2\n- } else {\n- barLen = 1\n+ updateMap: function() {\n+ for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n+ var layerEntry = this.baseLayers[i];\n+ if (layerEntry.inputElem.checked) {\n+ this.map.setBaseLayer(layerEntry.layer, false)\n+ }\n+ }\n+ for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n+ var layerEntry = this.dataLayers[i];\n+ layerEntry.layer.setVisibility(layerEntry.inputElem.checked)\n }\n- return barLen * pow10\n },\n- update: function() {\n- var res = this.map.getResolution();\n- if (!res) {\n- return\n+ maximizeControl: function(e) {\n+ this.div.style.width = \"\";\n+ this.div.style.height = \"\";\n+ this.showControls(false);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e)\n }\n- var curMapUnits = this.map.getUnits();\n- var inches = OpenLayers.INCHES_PER_UNIT;\n- var maxSizeData = this.maxWidth * res * inches[curMapUnits];\n- var geodesicRatio = 1;\n- if (this.geodesic === true) {\n- var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || 1e-6) * this.maxWidth;\n- var maxSizeKilometers = maxSizeData / inches[\"km\"];\n- geodesicRatio = maxSizeGeodesic / maxSizeKilometers;\n- maxSizeData *= geodesicRatio\n+ },\n+ minimizeControl: function(e) {\n+ this.div.style.width = \"0px\";\n+ this.div.style.height = \"0px\";\n+ this.showControls(true);\n+ if (e != null) {\n+ OpenLayers.Event.stop(e)\n }\n- var topUnits;\n- var bottomUnits;\n- if (maxSizeData > 1e5) {\n- topUnits = this.topOutUnits;\n- bottomUnits = this.bottomOutUnits\n+ },\n+ showControls: function(minimize) {\n+ this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n+ this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n+ this.layersDiv.style.display = minimize ? \"none\" : \"\"\n+ },\n+ loadContents: function() {\n+ this.layersDiv = document.createElement(\"div\");\n+ this.layersDiv.id = this.id + \"_layersDiv\";\n+ OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n+ this.baseLbl = document.createElement(\"div\");\n+ this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n+ OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n+ this.baseLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n+ this.dataLbl = document.createElement(\"div\");\n+ this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n+ OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n+ this.dataLayersDiv = document.createElement(\"div\");\n+ OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n+ if (this.ascending) {\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv);\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv)\n } else {\n- topUnits = this.topInUnits;\n- bottomUnits = this.bottomInUnits\n+ this.layersDiv.appendChild(this.dataLbl);\n+ this.layersDiv.appendChild(this.dataLayersDiv);\n+ this.layersDiv.appendChild(this.baseLbl);\n+ this.layersDiv.appendChild(this.baseLayersDiv)\n }\n- var topMax = maxSizeData / inches[topUnits];\n- var bottomMax = maxSizeData / inches[bottomUnits];\n- var topRounded = this.getBarLen(topMax);\n- var bottomRounded = this.getBarLen(bottomMax);\n- topMax = topRounded / inches[curMapUnits] * inches[topUnits];\n- bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];\n- var topPx = topMax / res / geodesicRatio;\n- var bottomPx = bottomMax / res / geodesicRatio;\n- if (this.eBottom.style.visibility == \"visible\") {\n- this.eBottom.style.width = Math.round(bottomPx) + \"px\";\n- this.eBottom.innerHTML = bottomRounded + \" \" + bottomUnits\n+ this.div.appendChild(this.layersDiv);\n+ var img = OpenLayers.Util.getImageLocation(\"layer-switcher-maximize.png\");\n+ this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MaximizeDiv\", null, null, img, \"absolute\");\n+ OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n+ this.maximizeDiv.style.display = \"none\";\n+ this.div.appendChild(this.maximizeDiv);\n+ var img = OpenLayers.Util.getImageLocation(\"layer-switcher-minimize.png\");\n+ this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MinimizeDiv\", null, null, img, \"absolute\");\n+ OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n+ this.minimizeDiv.style.display = \"none\";\n+ this.div.appendChild(this.minimizeDiv)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+});\n+OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n+ zoomInText: \"+\",\n+ zoomInId: \"olZoomInLink\",\n+ zoomOutText: \"\u2212\",\n+ zoomOutId: \"olZoomOutLink\",\n+ draw: function() {\n+ var div = OpenLayers.Control.prototype.draw.apply(this),\n+ links = this.getOrCreateLinks(div),\n+ zoomIn = links.zoomIn,\n+ zoomOut = links.zoomOut,\n+ eventsInstance = this.map.events;\n+ if (zoomOut.parentNode !== div) {\n+ eventsInstance = this.events;\n+ eventsInstance.attachToElement(zoomOut.parentNode)\n }\n- if (this.eTop.style.visibility == \"visible\") {\n- this.eTop.style.width = Math.round(topPx) + \"px\";\n- this.eTop.innerHTML = topRounded + \" \" + topUnits\n+ eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n+ this.zoomInLink = zoomIn;\n+ this.zoomOutLink = zoomOut;\n+ return div\n+ },\n+ getOrCreateLinks: function(el) {\n+ var zoomIn = document.getElementById(this.zoomInId),\n+ zoomOut = document.getElementById(this.zoomOutId);\n+ if (!zoomIn) {\n+ zoomIn = document.createElement(\"a\");\n+ zoomIn.href = \"#zoomIn\";\n+ zoomIn.appendChild(document.createTextNode(this.zoomInText));\n+ zoomIn.className = \"olControlZoomIn\";\n+ el.appendChild(zoomIn)\n+ }\n+ OpenLayers.Element.addClass(zoomIn, \"olButton\");\n+ if (!zoomOut) {\n+ zoomOut = document.createElement(\"a\");\n+ zoomOut.href = \"#zoomOut\";\n+ zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n+ zoomOut.className = \"olControlZoomOut\";\n+ el.appendChild(zoomOut)\n+ }\n+ OpenLayers.Element.addClass(zoomOut, \"olButton\");\n+ return {\n+ zoomIn: zoomIn,\n+ zoomOut: zoomOut\n }\n },\n- CLASS_NAME: \"OpenLayers.Control.ScaleLine\"\n+ onZoomClick: function(evt) {\n+ var button = evt.buttonElement;\n+ if (button === this.zoomInLink) {\n+ this.map.zoomIn()\n+ } else if (button === this.zoomOutLink) {\n+ this.map.zoomOut()\n+ }\n+ },\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onZoomClick)\n+ }\n+ delete this.zoomInLink;\n+ delete this.zoomOutLink;\n+ OpenLayers.Control.prototype.destroy.apply(this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Zoom\"\n });\n-OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {\n- geometryTypes: null,\n+OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {\n layer: null,\n- preserveAspectRatio: false,\n- rotate: true,\n- feature: null,\n- renderIntent: \"temporary\",\n- rotationHandleSymbolizer: null,\n- box: null,\n- center: null,\n- scale: 1,\n- ratio: 1,\n- rotation: 0,\n- handles: null,\n- rotationHandles: null,\n- dragControl: null,\n- irregular: false,\n- initialize: function(layer, options) {\n+ source: null,\n+ sourceOptions: null,\n+ tolerance: null,\n+ edge: true,\n+ deferDelete: false,\n+ mutual: true,\n+ targetFilter: null,\n+ sourceFilter: null,\n+ handler: null,\n+ initialize: function(options) {\n OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.layer = layer;\n- if (!this.rotationHandleSymbolizer) {\n- this.rotationHandleSymbolizer = {\n- stroke: false,\n- pointRadius: 10,\n- fillOpacity: 0,\n- cursor: \"pointer\"\n+ this.options = options || {};\n+ if (this.options.source) {\n+ this.setSource(this.options.source)\n+ }\n+ },\n+ setSource: function(layer) {\n+ if (this.active) {\n+ this.deactivate();\n+ if (this.handler) {\n+ this.handler.destroy();\n+ delete this.handler\n }\n+ this.source = layer;\n+ this.activate()\n+ } else {\n+ this.source = layer\n }\n- this.createBox();\n- this.createControl()\n },\n activate: function() {\n- var activated = false;\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.dragControl.activate();\n- this.layer.addFeatures([this.box]);\n- this.rotate && this.layer.addFeatures(this.rotationHandles);\n- this.layer.addFeatures(this.handles);\n- activated = true\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ if (!this.source) {\n+ if (!this.handler) {\n+ this.handler = new OpenLayers.Handler.Path(this, {\n+ done: function(geometry) {\n+ this.onSketchComplete({\n+ feature: new OpenLayers.Feature.Vector(geometry)\n+ })\n+ }\n+ }, {\n+ layerOptions: this.sourceOptions\n+ })\n+ }\n+ this.handler.activate()\n+ } else if (this.source.events) {\n+ this.source.events.on({\n+ sketchcomplete: this.onSketchComplete,\n+ afterfeaturemodified: this.afterFeatureModified,\n+ scope: this\n+ })\n+ }\n }\n return activated\n },\n deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.layer.removeFeatures(this.handles);\n- this.rotate && this.layer.removeFeatures(this.rotationHandles);\n- this.layer.removeFeatures([this.box]);\n- this.dragControl.deactivate();\n- deactivated = true\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.source && this.source.events) {\n+ this.source.events.un({\n+ sketchcomplete: this.onSketchComplete,\n+ afterfeaturemodified: this.afterFeatureModified,\n+ scope: this\n+ })\n+ }\n }\n return deactivated\n },\n- setMap: function(map) {\n- this.dragControl.setMap(map);\n- OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ onSketchComplete: function(event) {\n+ this.feature = null;\n+ return !this.considerSplit(event.feature)\n },\n- setFeature: function(feature, initialParams) {\n- initialParams = OpenLayers.Util.applyDefaults(initialParams, {\n- rotation: 0,\n- scale: 1,\n- ratio: 1\n- });\n- var oldRotation = this.rotation;\n- var oldCenter = this.center;\n- OpenLayers.Util.extend(this, initialParams);\n- var cont = this.events.triggerEvent(\"beforesetfeature\", {\n- feature: feature\n- });\n- if (cont === false) {\n- return\n+ afterFeatureModified: function(event) {\n+ if (event.modified) {\n+ var feature = event.feature;\n+ if (typeof feature.geometry.split === \"function\") {\n+ this.feature = event.feature;\n+ this.considerSplit(event.feature)\n+ }\n }\n- this.feature = feature;\n- this.activate();\n- this._setfeature = true;\n- var featureBounds = this.feature.geometry.getBounds();\n- this.box.move(featureBounds.getCenterLonLat());\n- this.box.geometry.rotate(-oldRotation, oldCenter);\n- this._angle = 0;\n- var ll;\n- if (this.rotation) {\n- var geom = feature.geometry.clone();\n- geom.rotate(-this.rotation, this.center);\n- var box = new OpenLayers.Feature.Vector(geom.getBounds().toGeometry());\n- box.geometry.rotate(this.rotation, this.center);\n- this.box.geometry.rotate(this.rotation, this.center);\n- this.box.move(box.geometry.getBounds().getCenterLonLat());\n- var llGeom = box.geometry.components[0].components[0];\n- ll = llGeom.getBounds().getCenterLonLat()\n- } else {\n- ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom)\n+ },\n+ removeByGeometry: function(features, geometry) {\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ if (features[i].geometry === geometry) {\n+ features.splice(i, 1);\n+ break\n+ }\n }\n- this.handles[0].move(ll);\n- delete this._setfeature;\n- this.events.triggerEvent(\"setfeature\", {\n- feature: feature\n- })\n },\n- unsetFeature: function() {\n- if (this.active) {\n- this.deactivate()\n+ isEligible: function(target) {\n+ if (!target.geometry) {\n+ return false\n } else {\n- this.feature = null;\n- this.rotation = 0;\n- this.scale = 1;\n- this.ratio = 1\n+ return target.state !== OpenLayers.State.DELETE && typeof target.geometry.split === \"function\" && this.feature !== target && (!this.targetFilter || this.targetFilter.evaluate(target.attributes))\n }\n },\n- createBox: function() {\n- var control = this;\n- this.center = new OpenLayers.Geometry.Point(0, 0);\n- this.box = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-1, -1), new OpenLayers.Geometry.Point(0, -1), new OpenLayers.Geometry.Point(1, -1), new OpenLayers.Geometry.Point(1, 0), new OpenLayers.Geometry.Point(1, 1), new OpenLayers.Geometry.Point(0, 1), new OpenLayers.Geometry.Point(-1, 1), new OpenLayers.Geometry.Point(-1, 0), new OpenLayers.Geometry.Point(-1, -1)]), null, typeof this.renderIntent == \"string\" ? null : this.renderIntent);\n- this.box.geometry.move = function(x, y) {\n- control._moving = true;\n- OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);\n- control.center.move(x, y);\n- delete control._moving\n- };\n- var vertexMoveFn = function(x, y) {\n- OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.move(x, y);\n- this._handle.geometry.move(x, y)\n- };\n- var vertexResizeFn = function(scale, center, ratio) {\n- OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.resize(scale, center, ratio);\n- this._handle.geometry.resize(scale, center, ratio)\n- };\n- var vertexRotateFn = function(angle, center) {\n- OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);\n- this._rotationHandle && this._rotationHandle.geometry.rotate(angle, center);\n- this._handle.geometry.rotate(angle, center)\n- };\n- var handleMoveFn = function(x, y) {\n- var oldX = this.x,\n- oldY = this.y;\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- if (control._moving) {\n- return\n- }\n- var evt = control.dragControl.handlers.drag.evt;\n- var preserveAspectRatio = !control._setfeature && control.preserveAspectRatio;\n- var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);\n- var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);\n- var centerGeometry = control.center;\n- this.rotate(-control.rotation, centerGeometry);\n- oldGeom.rotate(-control.rotation, centerGeometry);\n- var dx1 = this.x - centerGeometry.x;\n- var dy1 = this.y - centerGeometry.y;\n- var dx0 = dx1 - (this.x - oldGeom.x);\n- var dy0 = dy1 - (this.y - oldGeom.y);\n- if (control.irregular && !control._setfeature) {\n- dx1 -= (this.x - oldGeom.x) / 2;\n- dy1 -= (this.y - oldGeom.y) / 2\n+ considerSplit: function(feature) {\n+ var sourceSplit = false;\n+ var targetSplit = false;\n+ if (!this.sourceFilter || this.sourceFilter.evaluate(feature.attributes)) {\n+ var features = this.layer && this.layer.features || [];\n+ var target, results, proceed;\n+ var additions = [],\n+ removals = [];\n+ var mutual = this.layer === this.source && this.mutual;\n+ var options = {\n+ edge: this.edge,\n+ tolerance: this.tolerance,\n+ mutual: mutual\n+ };\n+ var sourceParts = [feature.geometry];\n+ var targetFeature, targetParts;\n+ var source, parts;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ targetFeature = features[i];\n+ if (this.isEligible(targetFeature)) {\n+ targetParts = [targetFeature.geometry];\n+ for (var j = 0; j < sourceParts.length; ++j) {\n+ source = sourceParts[j];\n+ for (var k = 0; k < targetParts.length; ++k) {\n+ target = targetParts[k];\n+ if (source.getBounds().intersectsBounds(target.getBounds())) {\n+ results = source.split(target, options);\n+ if (results) {\n+ proceed = this.events.triggerEvent(\"beforesplit\", {\n+ source: feature,\n+ target: targetFeature\n+ });\n+ if (proceed !== false) {\n+ if (mutual) {\n+ parts = results[0];\n+ if (parts.length > 1) {\n+ parts.unshift(j, 1);\n+ Array.prototype.splice.apply(sourceParts, parts);\n+ j += parts.length - 3\n+ }\n+ results = results[1]\n+ }\n+ if (results.length > 1) {\n+ results.unshift(k, 1);\n+ Array.prototype.splice.apply(targetParts, results);\n+ k += results.length - 3\n+ }\n+ }\n+ }\n+ }\n+ }\n+ }\n+ if (targetParts && targetParts.length > 1) {\n+ this.geomsToFeatures(targetFeature, targetParts);\n+ this.events.triggerEvent(\"split\", {\n+ original: targetFeature,\n+ features: targetParts\n+ });\n+ Array.prototype.push.apply(additions, targetParts);\n+ removals.push(targetFeature);\n+ targetSplit = true\n+ }\n+ }\n }\n- this.x = oldX;\n- this.y = oldY;\n- var scale, ratio = 1;\n- if (reshape) {\n- scale = Math.abs(dy0) < 1e-5 ? 1 : dy1 / dy0;\n- ratio = (Math.abs(dx0) < 1e-5 ? 1 : dx1 / dx0) / scale\n- } else {\n- var l0 = Math.sqrt(dx0 * dx0 + dy0 * dy0);\n- var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);\n- scale = l1 / l0\n+ if (sourceParts && sourceParts.length > 1) {\n+ this.geomsToFeatures(feature, sourceParts);\n+ this.events.triggerEvent(\"split\", {\n+ original: feature,\n+ features: sourceParts\n+ });\n+ Array.prototype.push.apply(additions, sourceParts);\n+ removals.push(feature);\n+ sourceSplit = true\n }\n- control._moving = true;\n- control.box.geometry.rotate(-control.rotation, centerGeometry);\n- delete control._moving;\n- control.box.geometry.resize(scale, centerGeometry, ratio);\n- control.box.geometry.rotate(control.rotation, centerGeometry);\n- control.transformFeature({\n- scale: scale,\n- ratio: ratio\n- });\n- if (control.irregular && !control._setfeature) {\n- var newCenter = centerGeometry.clone();\n- newCenter.x += Math.abs(oldX - centerGeometry.x) < 1e-5 ? 0 : this.x - oldX;\n- newCenter.y += Math.abs(oldY - centerGeometry.y) < 1e-5 ? 0 : this.y - oldY;\n- control.box.geometry.move(this.x - oldX, this.y - oldY);\n- control.transformFeature({\n- center: newCenter\n+ if (sourceSplit || targetSplit) {\n+ if (this.deferDelete) {\n+ var feat, destroys = [];\n+ for (var i = 0, len = removals.length; i < len; ++i) {\n+ feat = removals[i];\n+ if (feat.state === OpenLayers.State.INSERT) {\n+ destroys.push(feat)\n+ } else {\n+ feat.state = OpenLayers.State.DELETE;\n+ this.layer.drawFeature(feat)\n+ }\n+ }\n+ this.layer.destroyFeatures(destroys, {\n+ silent: true\n+ });\n+ for (var i = 0, len = additions.length; i < len; ++i) {\n+ additions[i].state = OpenLayers.State.INSERT\n+ }\n+ } else {\n+ this.layer.destroyFeatures(removals, {\n+ silent: true\n+ })\n+ }\n+ this.layer.addFeatures(additions, {\n+ silent: true\n+ });\n+ this.events.triggerEvent(\"aftersplit\", {\n+ source: feature,\n+ features: additions\n })\n }\n- };\n- var rotationHandleMoveFn = function(x, y) {\n- var oldX = this.x,\n- oldY = this.y;\n- OpenLayers.Geometry.Point.prototype.move.call(this, x, y);\n- if (control._moving) {\n- return\n- }\n- var evt = control.dragControl.handlers.drag.evt;\n- var constrain = evt && evt.shiftKey ? 45 : 1;\n- var centerGeometry = control.center;\n- var dx1 = this.x - centerGeometry.x;\n- var dy1 = this.y - centerGeometry.y;\n- var dx0 = dx1 - x;\n- var dy0 = dy1 - y;\n- this.x = oldX;\n- this.y = oldY;\n- var a0 = Math.atan2(dy0, dx0);\n- var a1 = Math.atan2(dy1, dx1);\n- var angle = a1 - a0;\n- angle *= 180 / Math.PI;\n- control._angle = (control._angle + angle) % 360;\n- var diff = control.rotation % constrain;\n- if (Math.abs(control._angle) >= constrain || diff !== 0) {\n- angle = Math.round(control._angle / constrain) * constrain - diff;\n- control._angle = 0;\n- control.box.geometry.rotate(angle, centerGeometry);\n- control.transformFeature({\n- rotation: angle\n- })\n+ }\n+ return sourceSplit\n+ },\n+ geomsToFeatures: function(feature, geoms) {\n+ var clone = feature.clone();\n+ delete clone.geometry;\n+ var newFeature;\n+ for (var i = 0, len = geoms.length; i < len; ++i) {\n+ newFeature = clone.clone();\n+ newFeature.geometry = geoms[i];\n+ newFeature.state = OpenLayers.State.INSERT;\n+ geoms[i] = newFeature\n+ }\n+ },\n+ destroy: function() {\n+ if (this.active) {\n+ this.deactivate()\n+ }\n+ OpenLayers.Control.prototype.destroy.call(this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Split\"\n+});\n+OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {\n+ protocol: null,\n+ multipleKey: null,\n+ toggleKey: null,\n+ modifiers: null,\n+ multiple: false,\n+ click: true,\n+ single: true,\n+ clickout: true,\n+ toggle: false,\n+ clickTolerance: 5,\n+ hover: false,\n+ box: false,\n+ maxFeatures: 10,\n+ features: null,\n+ hoverFeature: null,\n+ handlers: null,\n+ hoverResponse: null,\n+ filterType: OpenLayers.Filter.Spatial.BBOX,\n+ initialize: function(options) {\n+ options.handlerOptions = options.handlerOptions || {};\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.features = {};\n+ this.handlers = {};\n+ if (this.click) {\n+ this.handlers.click = new OpenLayers.Handler.Click(this, {\n+ click: this.selectClick\n+ }, this.handlerOptions.click || {})\n+ }\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(this, {\n+ done: this.selectBox\n+ }, OpenLayers.Util.extend(this.handlerOptions.box, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ }))\n+ }\n+ if (this.hover) {\n+ this.handlers.hover = new OpenLayers.Handler.Hover(this, {\n+ move: this.cancelHover,\n+ pause: this.selectHover\n+ }, OpenLayers.Util.extend(this.handlerOptions.hover, {\n+ delay: 250,\n+ pixelTolerance: 2\n+ }))\n+ }\n+ },\n+ activate: function() {\n+ if (!this.active) {\n+ for (var i in this.handlers) {\n+ this.handlers[i].activate()\n }\n- };\n- var handles = new Array(8);\n- var rotationHandles = new Array(4);\n- var geom, handle, rotationHandle;\n- var positions = [\"sw\", \"s\", \"se\", \"e\", \"ne\", \"n\", \"nw\", \"w\"];\n- for (var i = 0; i < 8; ++i) {\n- geom = this.box.geometry.components[i];\n- handle = new OpenLayers.Feature.Vector(geom.clone(), {\n- role: positions[i] + \"-resize\"\n- }, typeof this.renderIntent == \"string\" ? null : this.renderIntent);\n- if (i % 2 == 0) {\n- rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), {\n- role: positions[i] + \"-rotate\"\n- }, typeof this.rotationHandleSymbolizer == \"string\" ? null : this.rotationHandleSymbolizer);\n- rotationHandle.geometry.move = rotationHandleMoveFn;\n- geom._rotationHandle = rotationHandle;\n- rotationHandles[i / 2] = rotationHandle\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments)\n+ },\n+ deactivate: function() {\n+ if (this.active) {\n+ for (var i in this.handlers) {\n+ this.handlers[i].deactivate()\n }\n- geom.move = vertexMoveFn;\n- geom.resize = vertexResizeFn;\n- geom.rotate = vertexRotateFn;\n- handle.geometry.move = handleMoveFn;\n- geom._handle = handle;\n- handles[i] = handle\n }\n- this.rotationHandles = rotationHandles;\n- this.handles = handles\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- createControl: function() {\n- var control = this;\n- this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {\n- documentDrag: true,\n- moveFeature: function(pixel) {\n- if (this.feature === control.feature) {\n- this.feature = control.box\n- }\n- OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments)\n- },\n- onDrag: function(feature, pixel) {\n- if (feature === control.box) {\n- control.transformFeature({\n- center: control.center\n- })\n+ selectClick: function(evt) {\n+ var bounds = this.pixelToBounds(evt.xy);\n+ this.setModifiers(evt);\n+ this.request(bounds, {\n+ single: this.single\n+ })\n+ },\n+ selectBox: function(position) {\n+ var bounds;\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat)\n+ } else {\n+ if (this.click) {\n+ return\n+ }\n+ bounds = this.pixelToBounds(position)\n+ }\n+ this.setModifiers(this.handlers.box.dragHandler.evt);\n+ this.request(bounds)\n+ },\n+ selectHover: function(evt) {\n+ var bounds = this.pixelToBounds(evt.xy);\n+ this.request(bounds, {\n+ single: true,\n+ hover: true\n+ })\n+ },\n+ cancelHover: function() {\n+ if (this.hoverResponse) {\n+ this.protocol.abort(this.hoverResponse);\n+ this.hoverResponse = null;\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n+ }\n+ },\n+ request: function(bounds, options) {\n+ options = options || {};\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: this.filterType,\n+ value: bounds\n+ });\n+ OpenLayers.Element.addClass(this.map.viewPortDiv, \"olCursorWait\");\n+ var response = this.protocol.read({\n+ maxFeatures: options.single == true ? this.maxFeatures : undefined,\n+ filter: filter,\n+ callback: function(result) {\n+ if (result.success()) {\n+ if (result.features.length) {\n+ if (options.single == true) {\n+ this.selectBestFeature(result.features, bounds.getCenterLonLat(), options)\n+ } else {\n+ this.select(result.features)\n+ }\n+ } else if (options.hover) {\n+ this.hoverSelect()\n+ } else {\n+ this.events.triggerEvent(\"clickout\");\n+ if (this.clickout) {\n+ this.unselectAll()\n+ }\n+ }\n }\n+ OpenLayers.Element.removeClass(this.map.viewPortDiv, \"olCursorWait\")\n },\n- onStart: function(feature, pixel) {\n- var eligible = !control.geometryTypes || OpenLayers.Util.indexOf(control.geometryTypes, feature.geometry.CLASS_NAME) !== -1;\n- var i = OpenLayers.Util.indexOf(control.handles, feature);\n- i += OpenLayers.Util.indexOf(control.rotationHandles, feature);\n- if (feature !== control.feature && feature !== control.box && i == -2 && eligible) {\n- control.setFeature(feature)\n+ scope: this\n+ });\n+ if (options.hover == true) {\n+ this.hoverResponse = response\n+ }\n+ },\n+ selectBestFeature: function(features, clickPosition, options) {\n+ options = options || {};\n+ if (features.length) {\n+ var point = new OpenLayers.Geometry.Point(clickPosition.lon, clickPosition.lat);\n+ var feature, resultFeature, dist;\n+ var minDist = Number.MAX_VALUE;\n+ for (var i = 0; i < features.length; ++i) {\n+ feature = features[i];\n+ if (feature.geometry) {\n+ dist = point.distanceTo(feature.geometry, {\n+ edge: false\n+ });\n+ if (dist < minDist) {\n+ minDist = dist;\n+ resultFeature = feature;\n+ if (minDist == 0) {\n+ break\n+ }\n+ }\n }\n- },\n- onComplete: function(feature, pixel) {\n- control.events.triggerEvent(\"transformcomplete\", {\n- feature: control.feature\n- })\n }\n- })\n- },\n- drawHandles: function() {\n- var layer = this.layer;\n- for (var i = 0; i < 8; ++i) {\n- if (this.rotate && i % 2 === 0) {\n- layer.drawFeature(this.rotationHandles[i / 2], this.rotationHandleSymbolizer)\n+ if (options.hover == true) {\n+ this.hoverSelect(resultFeature)\n+ } else {\n+ this.select(resultFeature || features)\n }\n- layer.drawFeature(this.handles[i], this.renderIntent)\n }\n },\n- transformFeature: function(mods) {\n- if (!this._setfeature) {\n- this.scale *= mods.scale || 1;\n- this.ratio *= mods.ratio || 1;\n- var oldRotation = this.rotation;\n- this.rotation = (this.rotation + (mods.rotation || 0)) % 360;\n- if (this.events.triggerEvent(\"beforetransform\", mods) !== false) {\n- var feature = this.feature;\n- var geom = feature.geometry;\n- var center = this.center;\n- geom.rotate(-oldRotation, center);\n- if (mods.scale || mods.ratio) {\n- geom.resize(mods.scale, center, mods.ratio)\n- } else if (mods.center) {\n- feature.move(mods.center.getBounds().getCenterLonLat())\n+ setModifiers: function(evt) {\n+ this.modifiers = {\n+ multiple: this.multiple || this.multipleKey && evt[this.multipleKey],\n+ toggle: this.toggle || this.toggleKey && evt[this.toggleKey]\n+ }\n+ },\n+ select: function(features) {\n+ if (!this.modifiers.multiple && !this.modifiers.toggle) {\n+ this.unselectAll()\n+ }\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ var cont = this.events.triggerEvent(\"beforefeaturesselected\", {\n+ features: features\n+ });\n+ if (cont !== false) {\n+ var selectedFeatures = [];\n+ var feature;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ if (this.features[feature.fid || feature.id]) {\n+ if (this.modifiers.toggle) {\n+ this.unselect(this.features[feature.fid || feature.id])\n+ }\n+ } else {\n+ cont = this.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ this.features[feature.fid || feature.id] = feature;\n+ selectedFeatures.push(feature);\n+ this.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ })\n+ }\n }\n- geom.rotate(this.rotation, center);\n- this.layer.drawFeature(feature);\n- feature.toState(OpenLayers.State.UPDATE);\n- this.events.triggerEvent(\"transform\", mods)\n }\n+ this.events.triggerEvent(\"featuresselected\", {\n+ features: selectedFeatures\n+ })\n }\n- this.layer.drawFeature(this.box, this.renderIntent);\n- this.drawHandles()\n },\n- destroy: function() {\n- var geom;\n- for (var i = 0; i < 8; ++i) {\n- geom = this.box.geometry.components[i];\n- geom._handle.destroy();\n- geom._handle = null;\n- geom._rotationHandle && geom._rotationHandle.destroy();\n- geom._rotationHandle = null\n+ hoverSelect: function(feature) {\n+ var fid = feature ? feature.fid || feature.id : null;\n+ var hfid = this.hoverFeature ? this.hoverFeature.fid || this.hoverFeature.id : null;\n+ if (hfid && hfid != fid) {\n+ this.events.triggerEvent(\"outfeature\", {\n+ feature: this.hoverFeature\n+ });\n+ this.hoverFeature = null\n+ }\n+ if (fid && fid != hfid) {\n+ this.events.triggerEvent(\"hoverfeature\", {\n+ feature: feature\n+ });\n+ this.hoverFeature = feature\n }\n- this.center = null;\n- this.feature = null;\n- this.handles = null;\n- this.rotationHandleSymbolizer = null;\n- this.rotationHandles = null;\n- this.box.destroy();\n- this.box = null;\n- this.layer = null;\n- this.dragControl.destroy();\n- this.dragControl = null;\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Control.TransformFeature\"\n-});\n-OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {\n- autoActivate: true,\n- slideFactor: 75,\n- observeElement: null,\n- draw: function() {\n- var observeElement = this.observeElement || document;\n- this.handler = new OpenLayers.Handler.Keyboard(this, {\n- keydown: this.defaultKeyPress\n- }, {\n- observeElement: observeElement\n+ unselect: function(feature) {\n+ delete this.features[feature.fid || feature.id];\n+ this.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n })\n },\n- defaultKeyPress: function(evt) {\n- var size, handled = true;\n- var target = OpenLayers.Event.element(evt);\n- if (target && (target.tagName == \"INPUT\" || target.tagName == \"TEXTAREA\" || target.tagName == \"SELECT\")) {\n- return\n- }\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_LEFT:\n- this.map.pan(-this.slideFactor, 0);\n- break;\n- case OpenLayers.Event.KEY_RIGHT:\n- this.map.pan(this.slideFactor, 0);\n- break;\n- case OpenLayers.Event.KEY_UP:\n- this.map.pan(0, -this.slideFactor);\n- break;\n- case OpenLayers.Event.KEY_DOWN:\n- this.map.pan(0, this.slideFactor);\n- break;\n- case 33:\n- size = this.map.getSize();\n- this.map.pan(0, -.75 * size.h);\n- break;\n- case 34:\n- size = this.map.getSize();\n- this.map.pan(0, .75 * size.h);\n- break;\n- case 35:\n- size = this.map.getSize();\n- this.map.pan(.75 * size.w, 0);\n- break;\n- case 36:\n- size = this.map.getSize();\n- this.map.pan(-.75 * size.w, 0);\n- break;\n- case 43:\n- case 61:\n- case 187:\n- case 107:\n- this.map.zoomIn();\n- break;\n- case 45:\n- case 109:\n- case 189:\n- case 95:\n- this.map.zoomOut();\n- break;\n- default:\n- handled = false\n+ unselectAll: function() {\n+ for (var fid in this.features) {\n+ this.unselect(this.features[fid])\n }\n- if (handled) {\n- OpenLayers.Event.stop(evt)\n+ },\n+ setMap: function(map) {\n+ for (var i in this.handlers) {\n+ this.handlers[i].setMap(map)\n }\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Control.KeyboardDefaults\"\n+ pixelToBounds: function(pixel) {\n+ var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2);\n+ var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2);\n+ var ll = this.map.getLonLatFromPixel(llPx);\n+ var ur = this.map.getLonLatFromPixel(urPx);\n+ return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.GetFeature\"\n });\n-OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, {\n+OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {\n+ dragPan: null,\n+ dragPanOptions: null,\n+ pinchZoom: null,\n+ pinchZoomOptions: null,\n+ clickHandlerOptions: null,\n+ documentDrag: false,\n autoActivate: true,\n- layers: null,\n- defaultHandlerOptions: {\n- delay: 300,\n- pixelTolerance: 4,\n- stopMove: false,\n- single: true,\n- double: false,\n- stopSingle: false,\n- stopDouble: false\n- },\n- handlerMode: \"click\",\n- setHandler: function(hm) {\n- this.handlerMode = hm;\n- this.resetHandler()\n+ initialize: function(options) {\n+ this.handlers = {};\n+ OpenLayers.Control.prototype.initialize.apply(this, arguments)\n },\n- resetHandler: function() {\n- if (this.handler) {\n- this.handler.deactivate();\n- this.handler.destroy();\n- this.handler = null\n+ destroy: function() {\n+ this.deactivate();\n+ if (this.dragPan) {\n+ this.dragPan.destroy()\n }\n- if (this.handlerMode == \"hover\") {\n- this.handler = new OpenLayers.Handler.Hover(this, {\n- pause: this.handleEvent,\n- move: this.reset\n- }, this.handlerOptions)\n- } else if (this.handlerMode == \"click\") {\n- this.handler = new OpenLayers.Handler.Click(this, {\n- click: this.handleEvent\n- }, this.handlerOptions)\n- } else if (this.handlerMode == \"move\") {\n- this.handler = new OpenLayers.Handler.Hover(this, {\n- pause: this.handleEvent,\n- move: this.handleEvent\n- }, this.handlerOptions)\n+ this.dragPan = null;\n+ if (this.pinchZoom) {\n+ this.pinchZoom.destroy();\n+ delete this.pinchZoom\n }\n- if (this.handler) {\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ this.dragPan.activate();\n+ this.handlers.click.activate();\n+ this.pinchZoom.activate();\n return true\n- } else {\n- return false\n }\n+ return false\n },\n- initialize: function(options) {\n- options = options || {};\n- options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions;\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.resetHandler()\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ this.dragPan.deactivate();\n+ this.handlers.click.deactivate();\n+ this.pinchZoom.deactivate();\n+ return true\n+ }\n+ return false\n },\n- handleEvent: function(evt) {\n- if (evt == null) {\n- this.reset();\n- return\n+ draw: function() {\n+ var clickCallbacks = {\n+ click: this.defaultClick,\n+ dblclick: this.defaultDblClick\n+ };\n+ var clickOptions = OpenLayers.Util.extend({\n+ double: true,\n+ stopDouble: true,\n+ pixelTolerance: 2\n+ }, this.clickHandlerOptions);\n+ this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n+ this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n+ map: this.map,\n+ documentDrag: this.documentDrag\n+ }, this.dragPanOptions));\n+ this.dragPan.draw();\n+ this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n+ map: this.map\n+ }, this.pinchZoomOptions))\n+ },\n+ defaultClick: function(evt) {\n+ if (evt.lastTouches && evt.lastTouches.length == 2) {\n+ this.map.zoomOut()\n }\n- var lonLat = this.map.getLonLatFromPixel(evt.xy);\n- if (!lonLat) {\n- return\n+ },\n+ defaultDblClick: function(evt) {\n+ this.map.zoomTo(this.map.zoom + 1, evt.xy)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n+});\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n+ },\n+ isBaseLayer: true,\n+ encodeBBOX: false,\n+ noMagic: false,\n+ yx: {},\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\"\n }\n- var layers = this.findLayers();\n- if (layers.length > 0) {\n- var infoLookup = {};\n- var layer, idx;\n- for (var i = 0, len = layers.length; i < len; i++) {\n- layer = layers[i];\n- idx = OpenLayers.Util.indexOf(this.map.layers, layer);\n- infoLookup[idx] = layer.getFeatureInfo(lonLat)\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n+ if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ if (options == null || !options.isBaseLayer) {\n+ this.isBaseLayer = false\n+ }\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n }\n- this.callback(infoLookup, lonLat, evt.xy)\n }\n },\n- callback: function(infoLookup) {},\n- reset: function(evt) {\n- this.callback(null)\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- findLayers: function() {\n- var candidates = this.layers || this.map.layers;\n- var layers = [];\n- var layer;\n- for (var i = candidates.length - 1; i >= 0; --i) {\n- layer = candidates[i];\n- if (layer instanceof OpenLayers.Layer.UTFGrid) {\n- layers.push(layer)\n- }\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString\n+ },\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ },\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n+ var value = projectionCode == \"none\" ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value\n+ } else {\n+ this.params.SRS = value\n }\n- return layers\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n+ }\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n },\n- CLASS_NAME: \"OpenLayers.Control.UTFGrid\"\n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n });\n-OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {\n- autoActivate: true,\n- element: null,\n- prefix: \"\",\n- separator: \", \",\n- suffix: \"\",\n- numDigits: 5,\n- granularity: 10,\n- emptyString: null,\n- lastXy: null,\n- displayProjection: null,\n+OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {\n+ clearOnDeactivate: false,\n+ layers: null,\n+ callbacks: null,\n+ selectionSymbolizer: {\n+ Polygon: {\n+ fillColor: \"#FF0000\",\n+ stroke: false\n+ },\n+ Line: {\n+ strokeColor: \"#FF0000\",\n+ strokeWidth: 2\n+ },\n+ Point: {\n+ graphicName: \"square\",\n+ fillColor: \"#FF0000\",\n+ pointRadius: 5\n+ }\n+ },\n+ layerOptions: null,\n+ sketchStyle: null,\n+ wfsCache: {},\n+ layerCache: {},\n+ initialize: function(handler, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.callbacks = OpenLayers.Util.extend({\n+ done: this.select,\n+ click: this.select\n+ }, this.callbacks);\n+ this.handlerOptions = this.handlerOptions || {};\n+ this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {\n+ displayInLayerSwitcher: false,\n+ tileOptions: {\n+ maxGetUrlLength: 2048\n+ }\n+ });\n+ if (this.sketchStyle) {\n+ this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n+ styleMap: new OpenLayers.StyleMap({\n+ default: this.sketchStyle\n+ })\n+ })\n+ }\n+ this.handler = new handler(this, this.callbacks, this.handlerOptions)\n+ },\n destroy: function() {\n- this.deactivate();\n+ for (var key in this.layerCache) {\n+ delete this.layerCache[key]\n+ }\n+ for (var key in this.wfsCache) {\n+ delete this.wfsCache[key]\n+ }\n OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.map.events.register(\"mousemove\", this, this.redraw);\n- this.map.events.register(\"mouseout\", this, this.reset);\n- this.redraw();\n- return true\n+ coupleLayerVisiblity: function(evt) {\n+ this.setVisibility(evt.object.getVisibility())\n+ },\n+ createSelectionLayer: function(source) {\n+ var selectionLayer;\n+ if (!this.layerCache[source.id]) {\n+ selectionLayer = new OpenLayers.Layer.WMS(source.name, source.url, source.params, OpenLayers.Util.applyDefaults(this.layerOptions, source.getOptions()));\n+ this.layerCache[source.id] = selectionLayer;\n+ if (this.layerOptions.displayInLayerSwitcher === false) {\n+ source.events.on({\n+ visibilitychanged: this.coupleLayerVisiblity,\n+ scope: selectionLayer\n+ })\n+ }\n+ this.map.addLayer(selectionLayer)\n } else {\n- return false\n+ selectionLayer = this.layerCache[source.id]\n }\n+ return selectionLayer\n },\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.map.events.unregister(\"mousemove\", this, this.redraw);\n- this.map.events.unregister(\"mouseout\", this, this.reset);\n- this.element.innerHTML = \"\";\n- return true\n- } else {\n- return false\n+ createSLD: function(layer, filters, geometryAttributes) {\n+ var sld = {\n+ version: \"1.0.0\",\n+ namedLayers: {}\n+ };\n+ var layerNames = [layer.params.LAYERS].join(\",\").split(\",\");\n+ for (var i = 0, len = layerNames.length; i < len; i++) {\n+ var name = layerNames[i];\n+ sld.namedLayers[name] = {\n+ name: name,\n+ userStyles: []\n+ };\n+ var symbolizer = this.selectionSymbolizer;\n+ var geometryAttribute = geometryAttributes[i];\n+ if (geometryAttribute.type.indexOf(\"Polygon\") >= 0) {\n+ symbolizer = {\n+ Polygon: this.selectionSymbolizer[\"Polygon\"]\n+ }\n+ } else if (geometryAttribute.type.indexOf(\"LineString\") >= 0) {\n+ symbolizer = {\n+ Line: this.selectionSymbolizer[\"Line\"]\n+ }\n+ } else if (geometryAttribute.type.indexOf(\"Point\") >= 0) {\n+ symbolizer = {\n+ Point: this.selectionSymbolizer[\"Point\"]\n+ }\n+ }\n+ var filter = filters[i];\n+ sld.namedLayers[name].userStyles.push({\n+ name: \"default\",\n+ rules: [new OpenLayers.Rule({\n+ symbolizer: symbolizer,\n+ filter: filter,\n+ maxScaleDenominator: layer.options.minScale\n+ })]\n+ })\n }\n+ return new OpenLayers.Format.SLD({\n+ srsName: this.map.getProjection()\n+ }).write(sld)\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.element) {\n- this.div.left = \"\";\n- this.div.top = \"\";\n- this.element = this.div\n+ parseDescribeLayer: function(request) {\n+ var format = new OpenLayers.Format.WMSDescribeLayer;\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- return this.div\n+ var describeLayer = format.read(doc);\n+ var typeNames = [];\n+ var url = null;\n+ for (var i = 0, len = describeLayer.length; i < len; i++) {\n+ if (describeLayer[i].owsType == \"WFS\") {\n+ typeNames.push(describeLayer[i].typeName);\n+ url = describeLayer[i].owsURL\n+ }\n+ }\n+ var options = {\n+ url: url,\n+ params: {\n+ SERVICE: \"WFS\",\n+ TYPENAME: typeNames.toString(),\n+ REQUEST: \"DescribeFeatureType\",\n+ VERSION: \"1.0.0\"\n+ },\n+ callback: function(request) {\n+ var format = new OpenLayers.Format.WFSDescribeFeatureType;\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n+ }\n+ var describeFeatureType = format.read(doc);\n+ this.control.wfsCache[this.layer.id] = describeFeatureType;\n+ this.control._queue && this.control.applySelection()\n+ },\n+ scope: this\n+ };\n+ OpenLayers.Request.GET(options)\n },\n- redraw: function(evt) {\n- var lonLat;\n- if (evt == null) {\n- this.reset();\n- return\n- } else {\n- if (this.lastXy == null || Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) {\n- this.lastXy = evt.xy;\n- return\n+ getGeometryAttributes: function(layer) {\n+ var result = [];\n+ var cache = this.wfsCache[layer.id];\n+ for (var i = 0, len = cache.featureTypes.length; i < len; i++) {\n+ var typeName = cache.featureTypes[i];\n+ var properties = typeName.properties;\n+ for (var j = 0, lenj = properties.length; j < lenj; j++) {\n+ var property = properties[j];\n+ var type = property.type;\n+ if (type.indexOf(\"LineString\") >= 0 || type.indexOf(\"GeometryAssociationType\") >= 0 || type.indexOf(\"GeometryPropertyType\") >= 0 || type.indexOf(\"Point\") >= 0 || type.indexOf(\"Polygon\") >= 0) {\n+ result.push(property)\n+ }\n }\n- lonLat = this.map.getLonLatFromPixel(evt.xy);\n- if (!lonLat) {\n- return\n+ }\n+ return result\n+ },\n+ activate: function() {\n+ var activated = OpenLayers.Control.prototype.activate.call(this);\n+ if (activated) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer && !this.wfsCache[layer.id]) {\n+ var options = {\n+ url: layer.url,\n+ params: {\n+ SERVICE: \"WMS\",\n+ VERSION: layer.params.VERSION,\n+ LAYERS: layer.params.LAYERS,\n+ REQUEST: \"DescribeLayer\"\n+ },\n+ callback: this.parseDescribeLayer,\n+ scope: {\n+ layer: layer,\n+ control: this\n+ }\n+ };\n+ OpenLayers.Request.GET(options)\n+ }\n }\n- if (this.displayProjection) {\n- lonLat.transform(this.map.getProjectionObject(), this.displayProjection)\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ if (layer && this.clearOnDeactivate === true) {\n+ var layerCache = this.layerCache;\n+ var selectionLayer = layerCache[layer.id];\n+ if (selectionLayer) {\n+ layer.events.un({\n+ visibilitychanged: this.coupleLayerVisiblity,\n+ scope: selectionLayer\n+ });\n+ selectionLayer.destroy();\n+ delete layerCache[layer.id]\n+ }\n+ }\n }\n- this.lastXy = evt.xy\n }\n- var newHtml = this.formatOutput(lonLat);\n- if (newHtml != this.element.innerHTML) {\n- this.element.innerHTML = newHtml\n+ return deactivated\n+ },\n+ setLayers: function(layers) {\n+ if (this.active) {\n+ this.deactivate();\n+ this.layers = layers;\n+ this.activate()\n+ } else {\n+ this.layers = layers\n }\n },\n- reset: function(evt) {\n- if (this.emptyString != null) {\n- this.element.innerHTML = this.emptyString\n+ createFilter: function(geometryAttribute, geometry) {\n+ var filter = null;\n+ if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {\n+ if (this.handler.irregular === true) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ property: geometryAttribute.name,\n+ value: geometry.getBounds()\n+ })\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ })\n+ }\n+ } else if (this.handler instanceof OpenLayers.Handler.Polygon) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ })\n+ } else if (this.handler instanceof OpenLayers.Handler.Path) {\n+ if (geometryAttribute.type.indexOf(\"Point\") >= 0) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.DWITHIN,\n+ property: geometryAttribute.name,\n+ distance: this.map.getExtent().getWidth() * .01,\n+ distanceUnits: this.map.getUnits(),\n+ value: geometry\n+ })\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ })\n+ }\n+ } else if (this.handler instanceof OpenLayers.Handler.Click) {\n+ if (geometryAttribute.type.indexOf(\"Polygon\") >= 0) {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: geometryAttribute.name,\n+ value: geometry\n+ })\n+ } else {\n+ filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.DWITHIN,\n+ property: geometryAttribute.name,\n+ distance: this.map.getExtent().getWidth() * .01,\n+ distanceUnits: this.map.getUnits(),\n+ value: geometry\n+ })\n+ }\n }\n+ return filter\n },\n- formatOutput: function(lonLat) {\n- var digits = parseInt(this.numDigits);\n- var newHtml = this.prefix + lonLat.lon.toFixed(digits) + this.separator + lonLat.lat.toFixed(digits) + this.suffix;\n- return newHtml\n+ select: function(geometry) {\n+ this._queue = function() {\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ var layer = this.layers[i];\n+ var geometryAttributes = this.getGeometryAttributes(layer);\n+ var filters = [];\n+ for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) {\n+ var geometryAttribute = geometryAttributes[j];\n+ if (geometryAttribute !== null) {\n+ if (!(geometry instanceof OpenLayers.Geometry)) {\n+ var point = this.map.getLonLatFromPixel(geometry.xy);\n+ geometry = new OpenLayers.Geometry.Point(point.lon, point.lat)\n+ }\n+ var filter = this.createFilter(geometryAttribute, geometry);\n+ if (filter !== null) {\n+ filters.push(filter)\n+ }\n+ }\n+ }\n+ var selectionLayer = this.createSelectionLayer(layer);\n+ this.events.triggerEvent(\"selected\", {\n+ layer: layer,\n+ filters: filters\n+ });\n+ var sld = this.createSLD(layer, filters, geometryAttributes);\n+ selectionLayer.mergeNewParams({\n+ SLD_BODY: sld\n+ });\n+ delete this._queue\n+ }\n+ };\n+ this.applySelection()\n },\n- CLASS_NAME: \"OpenLayers.Control.MousePosition\"\n-});\n-OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n- zoomInText: \"+\",\n- zoomInId: \"olZoomInLink\",\n- zoomOutText: \"\u2212\",\n- zoomOutId: \"olZoomOutLink\",\n- draw: function() {\n- var div = OpenLayers.Control.prototype.draw.apply(this),\n- links = this.getOrCreateLinks(div),\n- zoomIn = links.zoomIn,\n- zoomOut = links.zoomOut,\n- eventsInstance = this.map.events;\n- if (zoomOut.parentNode !== div) {\n- eventsInstance = this.events;\n- eventsInstance.attachToElement(zoomOut.parentNode)\n+ applySelection: function() {\n+ var canApply = true;\n+ for (var i = 0, len = this.layers.length; i < len; i++) {\n+ if (!this.wfsCache[this.layers[i].id]) {\n+ canApply = false;\n+ break\n+ }\n }\n- eventsInstance.register(\"buttonclick\", this, this.onZoomClick);\n- this.zoomInLink = zoomIn;\n- this.zoomOutLink = zoomOut;\n- return div\n+ canApply && this._queue.call(this)\n },\n- getOrCreateLinks: function(el) {\n- var zoomIn = document.getElementById(this.zoomInId),\n- zoomOut = document.getElementById(this.zoomOutId);\n- if (!zoomIn) {\n- zoomIn = document.createElement(\"a\");\n- zoomIn.href = \"#zoomIn\";\n- zoomIn.appendChild(document.createTextNode(this.zoomInText));\n- zoomIn.className = \"olControlZoomIn\";\n- el.appendChild(zoomIn)\n- }\n- OpenLayers.Element.addClass(zoomIn, \"olButton\");\n- if (!zoomOut) {\n- zoomOut = document.createElement(\"a\");\n- zoomOut.href = \"#zoomOut\";\n- zoomOut.appendChild(document.createTextNode(this.zoomOutText));\n- zoomOut.className = \"olControlZoomOut\";\n- el.appendChild(zoomOut)\n+ CLASS_NAME: \"OpenLayers.Control.SLDSelect\"\n+});\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ displayInLayerSwitcher: false,\n+ layers: null,\n+ display: function() {},\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature\n+ }\n }\n- OpenLayers.Element.addClass(zoomOut, \"olButton\");\n- return {\n- zoomIn: zoomIn,\n- zoomOut: zoomOut\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer)\n+ },\n+ removeMap: function(map) {\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n+ },\n+ collectRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer)\n+ }\n }\n },\n- onZoomClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.zoomInLink) {\n- this.map.zoomIn()\n- } else if (button === this.zoomOutLink) {\n- this.map.zoomOut()\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer)\n+ }\n }\n },\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onZoomClick)\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots()\n }\n- delete this.zoomInLink;\n- delete this.zoomOutLink;\n- OpenLayers.Control.prototype.destroy.apply(this)\n },\n- CLASS_NAME: \"OpenLayers.Control.Zoom\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n });\n OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n multipleKey: null,\n toggleKey: null,\n multiple: false,\n clickout: true,\n toggle: false,\n@@ -33658,1799 +30879,3993 @@\n this.handlers.feature.layer = this.layer;\n if (isActive) {\n this.activate()\n }\n },\n CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n });\n-OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {\n- type: OpenLayers.Control.TYPE_TOGGLE,\n- previous: null,\n- previousOptions: null,\n- next: null,\n- nextOptions: null,\n- limit: 50,\n- autoActivate: true,\n- clearOnDeactivate: false,\n- registry: null,\n- nextStack: null,\n- previousStack: null,\n- listeners: null,\n- restoring: false,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.registry = OpenLayers.Util.extend({\n- moveend: this.getState\n- }, this.registry);\n- var previousOptions = {\n- trigger: OpenLayers.Function.bind(this.previousTrigger, this),\n- displayClass: this.displayClass + \" \" + this.displayClass + \"Previous\"\n- };\n- OpenLayers.Util.extend(previousOptions, this.previousOptions);\n- this.previous = new OpenLayers.Control.Button(previousOptions);\n- var nextOptions = {\n- trigger: OpenLayers.Function.bind(this.nextTrigger, this),\n- displayClass: this.displayClass + \" \" + this.displayClass + \"Next\"\n- };\n- OpenLayers.Util.extend(nextOptions, this.nextOptions);\n- this.next = new OpenLayers.Control.Button(nextOptions);\n- this.clear()\n+OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n+ url: null,\n+ utfgridResolution: 2,\n+ json: null,\n+ format: null,\n+ destroy: function() {\n+ this.clear();\n+ OpenLayers.Tile.prototype.destroy.apply(this, arguments)\n },\n- onPreviousChange: function(state, length) {\n- if (state && !this.previous.active) {\n- this.previous.activate()\n- } else if (!state && this.previous.active) {\n- this.previous.deactivate()\n+ draw: function() {\n+ var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n+ if (drawn) {\n+ if (this.isLoading) {\n+ this.abortLoading();\n+ this.events.triggerEvent(\"reload\")\n+ } else {\n+ this.isLoading = true;\n+ this.events.triggerEvent(\"loadstart\")\n+ }\n+ this.url = this.layer.getURL(this.bounds);\n+ if (this.layer.useJSONP) {\n+ var ols = new OpenLayers.Protocol.Script({\n+ url: this.url,\n+ callback: function(response) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ this.json = response.data\n+ },\n+ scope: this\n+ });\n+ ols.read();\n+ this.request = ols\n+ } else {\n+ this.request = OpenLayers.Request.GET({\n+ url: this.url,\n+ callback: function(response) {\n+ this.isLoading = false;\n+ this.events.triggerEvent(\"loadend\");\n+ if (response.status === 200) {\n+ this.parseData(response.responseText)\n+ }\n+ },\n+ scope: this\n+ })\n+ }\n+ } else {\n+ this.unload()\n }\n+ return drawn\n },\n- onNextChange: function(state, length) {\n- if (state && !this.next.active) {\n- this.next.activate()\n- } else if (!state && this.next.active) {\n- this.next.deactivate()\n+ abortLoading: function() {\n+ if (this.request) {\n+ this.request.abort();\n+ delete this.request\n }\n+ this.isLoading = false\n },\n- destroy: function() {\n- OpenLayers.Control.prototype.destroy.apply(this);\n- this.previous.destroy();\n- this.next.destroy();\n- this.deactivate();\n- for (var prop in this) {\n- this[prop] = null\n+ getFeatureInfo: function(i, j) {\n+ var info = null;\n+ if (this.json) {\n+ var id = this.getFeatureId(i, j);\n+ if (id !== null) {\n+ info = {\n+ id: id,\n+ data: this.json.data[id]\n+ }\n+ }\n }\n+ return info\n },\n- setMap: function(map) {\n- this.map = map;\n- this.next.setMap(map);\n- this.previous.setMap(map)\n+ getFeatureId: function(i, j) {\n+ var id = null;\n+ if (this.json) {\n+ var resolution = this.utfgridResolution;\n+ var row = Math.floor(j / resolution);\n+ var col = Math.floor(i / resolution);\n+ var charCode = this.json.grid[row].charCodeAt(col);\n+ var index = this.indexFromCharCode(charCode);\n+ var keys = this.json.keys;\n+ if (!isNaN(index) && index in keys) {\n+ id = keys[index]\n+ }\n+ }\n+ return id\n+ },\n+ indexFromCharCode: function(charCode) {\n+ if (charCode >= 93) {\n+ charCode--\n+ }\n+ if (charCode >= 35) {\n+ charCode--\n+ }\n+ return charCode - 32\n+ },\n+ parseData: function(str) {\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.JSON\n+ }\n+ this.json = this.format.read(str)\n+ },\n+ clear: function() {\n+ this.json = null\n },\n+ CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n+});\n+OpenLayers.Tile.Image.IFrame = {\n+ useIFrame: null,\n+ blankImageUrl: \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7\",\n draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- this.next.draw();\n- this.previous.draw()\n+ var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);\n+ if (draw) {\n+ var url = this.layer.getURL(this.bounds);\n+ var usedIFrame = this.useIFrame;\n+ this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && url.length > this.maxGetUrlLength;\n+ var fromIFrame = usedIFrame && !this.useIFrame;\n+ var toIFrame = !usedIFrame && this.useIFrame;\n+ if (fromIFrame || toIFrame) {\n+ if (this.imgDiv && this.imgDiv.parentNode === this.frame) {\n+ this.frame.removeChild(this.imgDiv)\n+ }\n+ this.imgDiv = null;\n+ if (fromIFrame) {\n+ this.frame.removeChild(this.frame.firstChild)\n+ }\n+ }\n+ }\n+ return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments)\n },\n- previousTrigger: function() {\n- var current = this.previousStack.shift();\n- var state = this.previousStack.shift();\n- if (state != undefined) {\n- this.nextStack.unshift(current);\n- this.previousStack.unshift(state);\n- this.restoring = true;\n- this.restore(state);\n- this.restoring = false;\n- this.onNextChange(this.nextStack[0], this.nextStack.length);\n- this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n+ getImage: function() {\n+ if (this.useIFrame === true) {\n+ if (!this.frame.childNodes.length) {\n+ var eventPane = document.createElement(\"div\"),\n+ style = eventPane.style;\n+ style.position = \"absolute\";\n+ style.width = \"100%\";\n+ style.height = \"100%\";\n+ style.zIndex = 1;\n+ style.backgroundImage = \"url(\" + this.blankImageUrl + \")\";\n+ this.frame.appendChild(eventPane)\n+ }\n+ var id = this.id + \"_iFrame\",\n+ iframe;\n+ if (parseFloat(navigator.appVersion.split(\"MSIE\")[1]) < 9) {\n+ iframe = document.createElement('<iframe name=\"' + id + '\">');\n+ iframe.style.backgroundColor = \"#FFFFFF\";\n+ iframe.style.filter = \"chroma(color=#FFFFFF)\"\n+ } else {\n+ iframe = document.createElement(\"iframe\");\n+ iframe.style.backgroundColor = \"transparent\";\n+ iframe.name = id\n+ }\n+ iframe.scrolling = \"no\";\n+ iframe.marginWidth = \"0px\";\n+ iframe.marginHeight = \"0px\";\n+ iframe.frameBorder = \"0\";\n+ iframe.style.position = \"absolute\";\n+ iframe.style.width = \"100%\";\n+ iframe.style.height = \"100%\";\n+ if (this.layer.opacity < 1) {\n+ OpenLayers.Util.modifyDOMElement(iframe, null, null, null, null, null, null, this.layer.opacity)\n+ }\n+ this.frame.appendChild(iframe);\n+ this.imgDiv = iframe;\n+ return iframe\n } else {\n- this.previousStack.unshift(current)\n+ return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments)\n }\n- return state\n },\n- nextTrigger: function() {\n- var state = this.nextStack.shift();\n- if (state != undefined) {\n- this.previousStack.unshift(state);\n- this.restoring = true;\n- this.restore(state);\n- this.restoring = false;\n- this.onNextChange(this.nextStack[0], this.nextStack.length);\n- this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n+ createRequestForm: function() {\n+ var form = document.createElement(\"form\");\n+ form.method = \"POST\";\n+ var cacheId = this.layer.params[\"_OLSALT\"];\n+ cacheId = (cacheId ? cacheId + \"_\" : \"\") + this.bounds.toBBOX();\n+ form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);\n+ form.target = this.id + \"_iFrame\";\n+ var imageSize = this.layer.getImageSize(),\n+ params = OpenLayers.Util.getParameters(this.url),\n+ field;\n+ for (var par in params) {\n+ field = document.createElement(\"input\");\n+ field.type = \"hidden\";\n+ field.name = par;\n+ field.value = params[par];\n+ form.appendChild(field)\n }\n- return state\n+ return form\n },\n- clear: function() {\n- this.previousStack = [];\n- this.previous.deactivate();\n- this.nextStack = [];\n- this.next.deactivate()\n+ setImgSrc: function(url) {\n+ if (this.useIFrame === true) {\n+ if (url) {\n+ var form = this.createRequestForm();\n+ this.frame.appendChild(form);\n+ form.submit();\n+ this.frame.removeChild(form)\n+ } else if (this.imgDiv.parentNode === this.frame) {\n+ this.frame.removeChild(this.imgDiv);\n+ this.imgDiv = null\n+ }\n+ } else {\n+ OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments)\n+ }\n },\n- getState: function() {\n- return {\n- center: this.map.getCenter(),\n- resolution: this.map.getResolution(),\n- projection: this.map.getProjectionObject(),\n- units: this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units\n+ onImageLoad: function() {\n+ OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments);\n+ if (this.useIFrame === true) {\n+ this.imgDiv.style.opacity = 1;\n+ this.frame.style.opacity = this.layer.opacity\n }\n },\n- restore: function(state) {\n- var center, zoom;\n- if (this.map.getProjectionObject() == state.projection) {\n- zoom = this.map.getZoomForResolution(state.resolution);\n- center = state.center\n- } else {\n- center = state.center.clone();\n- center.transform(state.projection, this.map.getProjectionObject());\n- var sourceUnits = state.units;\n- var targetUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units;\n- var resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;\n- zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution)\n+ createBackBuffer: function() {\n+ var backBuffer;\n+ if (this.useIFrame === false) {\n+ backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this)\n }\n- this.map.setCenter(center, zoom)\n+ return backBuffer\n+ }\n+};\n+OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {\n+ bounds: null,\n+ div: null,\n+ initialize: function(bounds, borderColor, borderWidth) {\n+ this.bounds = bounds;\n+ this.div = OpenLayers.Util.createDiv();\n+ this.div.style.overflow = \"hidden\";\n+ this.events = new OpenLayers.Events(this, this.div);\n+ this.setBorder(borderColor, borderWidth)\n },\n- setListeners: function() {\n- this.listeners = {};\n- for (var type in this.registry) {\n- this.listeners[type] = OpenLayers.Function.bind(function() {\n- if (!this.restoring) {\n- var state = this.registry[type].apply(this, arguments);\n- this.previousStack.unshift(state);\n- if (this.previousStack.length > 1) {\n- this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1)\n- }\n- if (this.previousStack.length > this.limit + 1) {\n- this.previousStack.pop()\n- }\n- if (this.nextStack.length > 0) {\n- this.nextStack = [];\n- this.onNextChange(null, 0)\n- }\n- }\n- return true\n- }, this)\n+ destroy: function() {\n+ this.bounds = null;\n+ this.div = null;\n+ OpenLayers.Marker.prototype.destroy.apply(this, arguments)\n+ },\n+ setBorder: function(color, width) {\n+ if (!color) {\n+ color = \"red\"\n+ }\n+ if (!width) {\n+ width = 2\n }\n+ this.div.style.border = width + \"px solid \" + color\n },\n- activate: function() {\n- var activated = false;\n+ draw: function(px, sz) {\n+ OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);\n+ return this.div\n+ },\n+ onScreen: function() {\n+ var onScreen = false;\n if (this.map) {\n- if (OpenLayers.Control.prototype.activate.apply(this)) {\n- if (this.listeners == null) {\n- this.setListeners()\n+ var screenBounds = this.map.getExtent();\n+ onScreen = screenBounds.containsBounds(this.bounds, true, true)\n+ }\n+ return onScreen\n+ },\n+ display: function(display) {\n+ this.div.style.display = display ? \"\" : \"none\"\n+ },\n+ CLASS_NAME: \"OpenLayers.Marker.Box\"\n+});\n+OpenLayers.Popup.Anchored = OpenLayers.Class(OpenLayers.Popup, {\n+ relativePosition: null,\n+ keepInMap: true,\n+ anchor: null,\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n+ var newArguments = [id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback];\n+ OpenLayers.Popup.prototype.initialize.apply(this, newArguments);\n+ this.anchor = anchor != null ? anchor : {\n+ size: new OpenLayers.Size(0, 0),\n+ offset: new OpenLayers.Pixel(0, 0)\n+ }\n+ },\n+ destroy: function() {\n+ this.anchor = null;\n+ this.relativePosition = null;\n+ OpenLayers.Popup.prototype.destroy.apply(this, arguments)\n+ },\n+ show: function() {\n+ this.updatePosition();\n+ OpenLayers.Popup.prototype.show.apply(this, arguments)\n+ },\n+ moveTo: function(px) {\n+ var oldRelativePosition = this.relativePosition;\n+ this.relativePosition = this.calculateRelativePosition(px);\n+ OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));\n+ if (this.relativePosition != oldRelativePosition) {\n+ this.updateRelativePosition()\n+ }\n+ },\n+ setSize: function(contentSize) {\n+ OpenLayers.Popup.prototype.setSize.apply(this, arguments);\n+ if (this.lonlat && this.map) {\n+ var px = this.map.getLayerPxFromLonLat(this.lonlat);\n+ this.moveTo(px)\n+ }\n+ },\n+ calculateRelativePosition: function(px) {\n+ var lonlat = this.map.getLonLatFromLayerPx(px);\n+ var extent = this.map.getExtent();\n+ var quadrant = extent.determineQuadrant(lonlat);\n+ return OpenLayers.Bounds.oppositeQuadrant(quadrant)\n+ },\n+ updateRelativePosition: function() {},\n+ calculateNewPx: function(px) {\n+ var newPx = px.offset(this.anchor.offset);\n+ var size = this.size || this.contentSize;\n+ var top = this.relativePosition.charAt(0) == \"t\";\n+ newPx.y += top ? -size.h : this.anchor.size.h;\n+ var left = this.relativePosition.charAt(1) == \"l\";\n+ newPx.x += left ? -size.w : this.anchor.size.w;\n+ return newPx\n+ },\n+ CLASS_NAME: \"OpenLayers.Popup.Anchored\"\n+});\n+OpenLayers.Popup.Framed = OpenLayers.Class(OpenLayers.Popup.Anchored, {\n+ imageSrc: null,\n+ imageSize: null,\n+ isAlphaImage: false,\n+ positionBlocks: null,\n+ blocks: null,\n+ fixedRelativePosition: false,\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n+ OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);\n+ if (this.fixedRelativePosition) {\n+ this.updateRelativePosition();\n+ this.calculateRelativePosition = function(px) {\n+ return this.relativePosition\n+ }\n+ }\n+ this.contentDiv.style.position = \"absolute\";\n+ this.contentDiv.style.zIndex = 1;\n+ if (closeBox) {\n+ this.closeDiv.style.zIndex = 1\n+ }\n+ this.groupDiv.style.position = \"absolute\";\n+ this.groupDiv.style.top = \"0px\";\n+ this.groupDiv.style.left = \"0px\";\n+ this.groupDiv.style.height = \"100%\";\n+ this.groupDiv.style.width = \"100%\"\n+ },\n+ destroy: function() {\n+ this.imageSrc = null;\n+ this.imageSize = null;\n+ this.isAlphaImage = null;\n+ this.fixedRelativePosition = false;\n+ this.positionBlocks = null;\n+ for (var i = 0; i < this.blocks.length; i++) {\n+ var block = this.blocks[i];\n+ if (block.image) {\n+ block.div.removeChild(block.image)\n+ }\n+ block.image = null;\n+ if (block.div) {\n+ this.groupDiv.removeChild(block.div)\n+ }\n+ block.div = null\n+ }\n+ this.blocks = null;\n+ OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments)\n+ },\n+ setBackgroundColor: function(color) {},\n+ setBorder: function() {},\n+ setOpacity: function(opacity) {},\n+ setSize: function(contentSize) {\n+ OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);\n+ this.updateBlocks()\n+ },\n+ updateRelativePosition: function() {\n+ this.padding = this.positionBlocks[this.relativePosition].padding;\n+ if (this.closeDiv) {\n+ var contentDivPadding = this.getContentDivPadding();\n+ this.closeDiv.style.right = contentDivPadding.right + this.padding.right + \"px\";\n+ this.closeDiv.style.top = contentDivPadding.top + this.padding.top + \"px\"\n+ }\n+ this.updateBlocks()\n+ },\n+ calculateNewPx: function(px) {\n+ var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(this, arguments);\n+ newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);\n+ return newPx\n+ },\n+ createBlocks: function() {\n+ this.blocks = [];\n+ var firstPosition = null;\n+ for (var key in this.positionBlocks) {\n+ firstPosition = key;\n+ break\n+ }\n+ var position = this.positionBlocks[firstPosition];\n+ for (var i = 0; i < position.blocks.length; i++) {\n+ var block = {};\n+ this.blocks.push(block);\n+ var divId = this.id + \"_FrameDecorationDiv_\" + i;\n+ block.div = OpenLayers.Util.createDiv(divId, null, null, null, \"absolute\", null, \"hidden\", null);\n+ var imgId = this.id + \"_FrameDecorationImg_\" + i;\n+ var imageCreator = this.isAlphaImage ? OpenLayers.Util.createAlphaImageDiv : OpenLayers.Util.createImage;\n+ block.image = imageCreator(imgId, null, this.imageSize, this.imageSrc, \"absolute\", null, null, null);\n+ block.div.appendChild(block.image);\n+ this.groupDiv.appendChild(block.div)\n+ }\n+ },\n+ updateBlocks: function() {\n+ if (!this.blocks) {\n+ this.createBlocks()\n+ }\n+ if (this.size && this.relativePosition) {\n+ var position = this.positionBlocks[this.relativePosition];\n+ for (var i = 0; i < position.blocks.length; i++) {\n+ var positionBlock = position.blocks[i];\n+ var block = this.blocks[i];\n+ var l = positionBlock.anchor.left;\n+ var b = positionBlock.anchor.bottom;\n+ var r = positionBlock.anchor.right;\n+ var t = positionBlock.anchor.top;\n+ var w = isNaN(positionBlock.size.w) ? this.size.w - (r + l) : positionBlock.size.w;\n+ var h = isNaN(positionBlock.size.h) ? this.size.h - (b + t) : positionBlock.size.h;\n+ block.div.style.width = (w < 0 ? 0 : w) + \"px\";\n+ block.div.style.height = (h < 0 ? 0 : h) + \"px\";\n+ block.div.style.left = l != null ? l + \"px\" : \"\";\n+ block.div.style.bottom = b != null ? b + \"px\" : \"\";\n+ block.div.style.right = r != null ? r + \"px\" : \"\";\n+ block.div.style.top = t != null ? t + \"px\" : \"\";\n+ block.image.style.left = positionBlock.position.x + \"px\";\n+ block.image.style.top = positionBlock.position.y + \"px\"\n+ }\n+ this.contentDiv.style.left = this.padding.left + \"px\";\n+ this.contentDiv.style.top = this.padding.top + \"px\"\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Popup.Framed\"\n+});\n+OpenLayers.Popup.FramedCloud = OpenLayers.Class(OpenLayers.Popup.Framed, {\n+ contentDisplayClass: \"olFramedCloudPopupContent\",\n+ autoSize: true,\n+ panMapIfOutOfView: true,\n+ imageSize: new OpenLayers.Size(1276, 736),\n+ isAlphaImage: false,\n+ fixedRelativePosition: false,\n+ positionBlocks: {\n+ tl: {\n+ offset: new OpenLayers.Pixel(44, 0),\n+ padding: new OpenLayers.Bounds(8, 40, 8, 9),\n+ blocks: [{\n+ size: new OpenLayers.Size(\"auto\", \"auto\"),\n+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, {\n+ size: new OpenLayers.Size(22, \"auto\"),\n+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, {\n+ size: new OpenLayers.Size(\"auto\", 19),\n+ anchor: new OpenLayers.Bounds(0, 32, 22, null),\n+ position: new OpenLayers.Pixel(0, -631)\n+ }, {\n+ size: new OpenLayers.Size(22, 18),\n+ anchor: new OpenLayers.Bounds(null, 32, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -632)\n+ }, {\n+ size: new OpenLayers.Size(81, 35),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(0, -688)\n+ }]\n+ },\n+ tr: {\n+ offset: new OpenLayers.Pixel(-45, 0),\n+ padding: new OpenLayers.Bounds(8, 40, 8, 9),\n+ blocks: [{\n+ size: new OpenLayers.Size(\"auto\", \"auto\"),\n+ anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, {\n+ size: new OpenLayers.Size(22, \"auto\"),\n+ anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, {\n+ size: new OpenLayers.Size(\"auto\", 19),\n+ anchor: new OpenLayers.Bounds(0, 32, 22, null),\n+ position: new OpenLayers.Pixel(0, -631)\n+ }, {\n+ size: new OpenLayers.Size(22, 19),\n+ anchor: new OpenLayers.Bounds(null, 32, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -631)\n+ }, {\n+ size: new OpenLayers.Size(81, 35),\n+ anchor: new OpenLayers.Bounds(0, 0, null, null),\n+ position: new OpenLayers.Pixel(-215, -687)\n+ }]\n+ },\n+ bl: {\n+ offset: new OpenLayers.Pixel(45, 0),\n+ padding: new OpenLayers.Bounds(8, 9, 8, 40),\n+ blocks: [{\n+ size: new OpenLayers.Size(\"auto\", \"auto\"),\n+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, {\n+ size: new OpenLayers.Size(22, \"auto\"),\n+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, {\n+ size: new OpenLayers.Size(\"auto\", 21),\n+ anchor: new OpenLayers.Bounds(0, 0, 22, null),\n+ position: new OpenLayers.Pixel(0, -629)\n+ }, {\n+ size: new OpenLayers.Size(22, 21),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -629)\n+ }, {\n+ size: new OpenLayers.Size(81, 33),\n+ anchor: new OpenLayers.Bounds(null, null, 0, 0),\n+ position: new OpenLayers.Pixel(-101, -674)\n+ }]\n+ },\n+ br: {\n+ offset: new OpenLayers.Pixel(-44, 0),\n+ padding: new OpenLayers.Bounds(8, 9, 8, 40),\n+ blocks: [{\n+ size: new OpenLayers.Size(\"auto\", \"auto\"),\n+ anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n+ position: new OpenLayers.Pixel(0, 0)\n+ }, {\n+ size: new OpenLayers.Size(22, \"auto\"),\n+ anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n+ position: new OpenLayers.Pixel(-1238, 0)\n+ }, {\n+ size: new OpenLayers.Size(\"auto\", 21),\n+ anchor: new OpenLayers.Bounds(0, 0, 22, null),\n+ position: new OpenLayers.Pixel(0, -629)\n+ }, {\n+ size: new OpenLayers.Size(22, 21),\n+ anchor: new OpenLayers.Bounds(null, 0, 0, null),\n+ position: new OpenLayers.Pixel(-1238, -629)\n+ }, {\n+ size: new OpenLayers.Size(81, 33),\n+ anchor: new OpenLayers.Bounds(0, null, null, 0),\n+ position: new OpenLayers.Pixel(-311, -674)\n+ }]\n+ }\n+ },\n+ minSize: new OpenLayers.Size(105, 10),\n+ maxSize: new OpenLayers.Size(1200, 660),\n+ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {\n+ this.imageSrc = OpenLayers.Util.getImageLocation(\"cloud-popup-relative.png\");\n+ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n+ this.contentDiv.className = this.contentDisplayClass\n+ },\n+ CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n+});\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ sphericalMercator: false,\n+ zoomOffset: 0,\n+ serverResolutions: null,\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options)\n+ }\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = \"\" + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url)\n+ }\n+ return OpenLayers.String.format(url, xyz)\n+ },\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = (x % limit + limit) % limit\n+ }\n+ return {\n+ x: x,\n+ y: y,\n+ z: z\n+ }\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+});\n+OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ url: null,\n+ tileOrigin: null,\n+ tileSize: new OpenLayers.Size(256, 256),\n+ useArcGISServer: true,\n+ type: \"png\",\n+ useScales: false,\n+ overrideDPI: false,\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ if (this.resolutions) {\n+ this.serverResolutions = this.resolutions;\n+ this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0])\n+ }\n+ if (this.layerInfo) {\n+ var info = this.layerInfo;\n+ var startingTileExtent = new OpenLayers.Bounds(info.fullExtent.xmin, info.fullExtent.ymin, info.fullExtent.xmax, info.fullExtent.ymax);\n+ this.projection = \"EPSG:\" + info.spatialReference.wkid;\n+ this.sphericalMercator = info.spatialReference.wkid == 102100;\n+ this.units = info.units == \"esriFeet\" ? \"ft\" : \"m\";\n+ if (!!info.tileInfo) {\n+ this.tileSize = new OpenLayers.Size(info.tileInfo.width || info.tileInfo.cols, info.tileInfo.height || info.tileInfo.rows);\n+ this.tileOrigin = new OpenLayers.LonLat(info.tileInfo.origin.x, info.tileInfo.origin.y);\n+ var upperLeft = new OpenLayers.Geometry.Point(startingTileExtent.left, startingTileExtent.top);\n+ var bottomRight = new OpenLayers.Geometry.Point(startingTileExtent.right, startingTileExtent.bottom);\n+ if (this.useScales) {\n+ this.scales = []\n+ } else {\n+ this.resolutions = []\n }\n- for (var type in this.listeners) {\n- this.map.events.register(type, this, this.listeners[type])\n+ this.lods = [];\n+ for (var key in info.tileInfo.lods) {\n+ if (info.tileInfo.lods.hasOwnProperty(key)) {\n+ var lod = info.tileInfo.lods[key];\n+ if (this.useScales) {\n+ this.scales.push(lod.scale)\n+ } else {\n+ this.resolutions.push(lod.resolution)\n+ }\n+ var start = this.getContainingTileCoords(upperLeft, lod.resolution);\n+ lod.startTileCol = start.x;\n+ lod.startTileRow = start.y;\n+ var end = this.getContainingTileCoords(bottomRight, lod.resolution);\n+ lod.endTileCol = end.x;\n+ lod.endTileRow = end.y;\n+ this.lods.push(lod)\n+ }\n }\n- activated = true;\n- if (this.previousStack.length == 0) {\n- this.initStack()\n+ this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);\n+ this.serverResolutions = this.resolutions;\n+ if (this.overrideDPI && info.tileInfo.dpi) {\n+ OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi\n }\n }\n }\n- return activated\n },\n- initStack: function() {\n- if (this.map.getCenter()) {\n- this.listeners.moveend()\n+ getContainingTileCoords: function(point, res) {\n+ return new OpenLayers.Pixel(Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0))\n+ },\n+ calculateMaxExtentWithLOD: function(lod) {\n+ var numTileCols = lod.endTileCol - lod.startTileCol + 1;\n+ var numTileRows = lod.endTileRow - lod.startTileRow + 1;\n+ var minX = this.tileOrigin.lon + lod.startTileCol * this.tileSize.w * lod.resolution;\n+ var maxX = minX + numTileCols * this.tileSize.w * lod.resolution;\n+ var maxY = this.tileOrigin.lat - lod.startTileRow * this.tileSize.h * lod.resolution;\n+ var minY = maxY - numTileRows * this.tileSize.h * lod.resolution;\n+ return new OpenLayers.Bounds(minX, minY, maxX, maxY)\n+ },\n+ calculateMaxExtentWithExtent: function(extent, res) {\n+ var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);\n+ var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);\n+ var start = this.getContainingTileCoords(upperLeft, res);\n+ var end = this.getContainingTileCoords(bottomRight, res);\n+ var lod = {\n+ resolution: res,\n+ startTileCol: start.x,\n+ startTileRow: start.y,\n+ endTileCol: end.x,\n+ endTileRow: end.y\n+ };\n+ return this.calculateMaxExtentWithLOD(lod)\n+ },\n+ getUpperLeftTileCoord: function(res) {\n+ var upperLeft = new OpenLayers.Geometry.Point(this.maxExtent.left, this.maxExtent.top);\n+ return this.getContainingTileCoords(upperLeft, res)\n+ },\n+ getLowerRightTileCoord: function(res) {\n+ var bottomRight = new OpenLayers.Geometry.Point(this.maxExtent.right, this.maxExtent.bottom);\n+ return this.getContainingTileCoords(bottomRight, res)\n+ },\n+ getMaxExtentForResolution: function(res) {\n+ var start = this.getUpperLeftTileCoord(res);\n+ var end = this.getLowerRightTileCoord(res);\n+ var numTileCols = end.x - start.x + 1;\n+ var numTileRows = end.y - start.y + 1;\n+ var minX = this.tileOrigin.lon + start.x * this.tileSize.w * res;\n+ var maxX = minX + numTileCols * this.tileSize.w * res;\n+ var maxY = this.tileOrigin.lat - start.y * this.tileSize.h * res;\n+ var minY = maxY - numTileRows * this.tileSize.h * res;\n+ return new OpenLayers.Bounds(minX, minY, maxX, maxY)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options)\n }\n+ return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj])\n },\n- deactivate: function() {\n- var deactivated = false;\n- if (this.map) {\n- if (OpenLayers.Control.prototype.deactivate.apply(this)) {\n- for (var type in this.listeners) {\n- this.map.events.unregister(type, this, this.listeners[type])\n- }\n- if (this.clearOnDeactivate) {\n- this.clear()\n- }\n- deactivated = true\n+ initGriddedTiles: function(bounds) {\n+ delete this._tileOrigin;\n+ OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments)\n+ },\n+ getMaxExtent: function() {\n+ var resolution = this.map.getResolution();\n+ return this.maxExtent = this.getMaxExtentForResolution(resolution)\n+ },\n+ getTileOrigin: function() {\n+ if (!this._tileOrigin) {\n+ var extent = this.getMaxExtent();\n+ this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom)\n+ }\n+ return this._tileOrigin\n+ },\n+ getURL: function(bounds) {\n+ var res = this.getResolution();\n+ var originTileX = this.tileOrigin.lon + res * this.tileSize.w / 2;\n+ var originTileY = this.tileOrigin.lat - res * this.tileSize.h / 2;\n+ var center = bounds.getCenterLonLat();\n+ var point = {\n+ x: center.lon,\n+ y: center.lat\n+ };\n+ var x = Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)));\n+ var y = Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)));\n+ var z = this.map.getZoom();\n+ if (this.lods) {\n+ var lod = this.lods[this.map.getZoom()];\n+ if (x < lod.startTileCol || x > lod.endTileCol || (y < lod.startTileRow || y > lod.endTileRow)) {\n+ return null\n+ }\n+ } else {\n+ var start = this.getUpperLeftTileCoord(res);\n+ var end = this.getLowerRightTileCoord(res);\n+ if (x < start.x || x >= end.x || (y < start.y || y >= end.y)) {\n+ return null\n }\n }\n- return deactivated\n+ var url = this.url;\n+ var s = \"\" + x + y + z;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(s, url)\n+ }\n+ if (this.useArcGISServer) {\n+ url = url + \"/tile/${z}/${y}/${x}\"\n+ } else {\n+ x = \"C\" + OpenLayers.Number.zeroPad(x, 8, 16);\n+ y = \"R\" + OpenLayers.Number.zeroPad(y, 8, 16);\n+ z = \"L\" + OpenLayers.Number.zeroPad(z, 2, 10);\n+ url = url + \"/${z}/${y}/${x}.\" + this.type\n+ }\n+ url = OpenLayers.String.format(url, {\n+ x: x,\n+ y: y,\n+ z: z\n+ });\n+ return OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(this.params))\n },\n- CLASS_NAME: \"OpenLayers.Control.NavigationHistory\"\n+ CLASS_NAME: \"OpenLayers.Layer.ArcGISCache\"\n });\n-OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {\n- dragPan: null,\n- dragPanOptions: null,\n- pinchZoom: null,\n- pinchZoomOptions: null,\n- clickHandlerOptions: null,\n- documentDrag: false,\n- autoActivate: true,\n+OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ isBaseLayer: false,\n+ projection: new OpenLayers.Projection(\"EPSG:900913\"),\n+ useJSONP: false,\n+ tileClass: OpenLayers.Tile.UTFGrid,\n initialize: function(options) {\n- this.handlers = {};\n- OpenLayers.Control.prototype.initialize.apply(this, arguments)\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [options.name, options.url, {}, options]);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ utfgridResolution: this.utfgridResolution\n+ }, this.tileOptions)\n+ },\n+ createBackBuffer: function() {},\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.UTFGrid(this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getFeatureInfo: function(location) {\n+ var info = null;\n+ var tileInfo = this.getTileData(location);\n+ if (tileInfo && tileInfo.tile) {\n+ info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j)\n+ }\n+ return info\n+ },\n+ getFeatureId: function(location) {\n+ var id = null;\n+ var info = this.getTileData(location);\n+ if (info.tile) {\n+ id = info.tile.getFeatureId(info.i, info.j)\n+ }\n+ return id\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.UTFGrid\"\n+});\n+OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ DEFAULT_PARAMS: {\n+ i: \"jpeg\",\n+ map: \"\"\n+ },\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n+ this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS)\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var mapRes = this.map.getResolution();\n+ var scale = Math.round(this.map.getScale() * 1e4) / 1e4;\n+ var pX = Math.round(bounds.left / mapRes);\n+ var pY = -Math.round(bounds.top / mapRes);\n+ return this.getFullRequestString({\n+ t: pY,\n+ l: pX,\n+ s: scale\n+ })\n+ },\n+ calculateGridLayout: function(bounds, origin, resolution) {\n+ var tilelon = resolution * this.tileSize.w;\n+ var tilelat = resolution * this.tileSize.h;\n+ var offsetlon = bounds.left;\n+ var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n+ var offsetlat = bounds.top;\n+ var tilerow = Math.floor(offsetlat / tilelat) + this.buffer;\n+ return {\n+ tilelon: tilelon,\n+ tilelat: tilelat,\n+ startcol: tilecol,\n+ startrow: tilerow\n+ }\n+ },\n+ getTileBoundsForGridIndex: function(row, col) {\n+ var origin = this.getTileOrigin();\n+ var tileLayout = this.gridLayout;\n+ var tilelon = tileLayout.tilelon;\n+ var tilelat = tileLayout.tilelat;\n+ var minX = (tileLayout.startcol + col) * tilelon;\n+ var minY = (tileLayout.startrow - row) * tilelat;\n+ return new OpenLayers.Bounds(minX, minY, minX + tilelon, minY + tilelat)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.KaMap(this.name, this.url, this.params, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ if (this.tileSize != null) {\n+ obj.tileSize = this.tileSize.clone()\n+ }\n+ obj.grid = [];\n+ return obj\n+ },\n+ getTileBounds: function(viewPortPx) {\n+ var resolution = this.getResolution();\n+ var tileMapWidth = resolution * this.tileSize.w;\n+ var tileMapHeight = resolution * this.tileSize.h;\n+ var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n+ var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);\n+ var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);\n+ return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.KaMap\"\n+});\n+OpenLayers.Layer.SphericalMercator = {\n+ getExtent: function() {\n+ var extent = null;\n+ if (this.sphericalMercator) {\n+ extent = this.map.calculateBounds()\n+ } else {\n+ extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this)\n+ }\n+ return extent\n+ },\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments)\n+ },\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments)\n+ },\n+ initMercatorParameters: function() {\n+ this.RESOLUTIONS = [];\n+ var maxResolution = 156543.03390625;\n+ for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {\n+ this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom)\n+ }\n+ this.units = \"m\";\n+ this.projection = this.projection || \"EPSG:900913\"\n+ },\n+ forwardMercator: function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(lon, lat) {\n+ var point = OpenLayers.Projection.transform({\n+ x: lon,\n+ y: lat\n+ }, gg, sm);\n+ return new OpenLayers.LonLat(point.x, point.y)\n+ }\n+ }(),\n+ inverseMercator: function() {\n+ var gg = new OpenLayers.Projection(\"EPSG:4326\");\n+ var sm = new OpenLayers.Projection(\"EPSG:900913\");\n+ return function(x, y) {\n+ var point = OpenLayers.Projection.transform({\n+ x: x,\n+ y: y\n+ }, sm, gg);\n+ return new OpenLayers.LonLat(point.x, point.y)\n+ }\n+ }()\n+};\n+OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ mode: \"map\",\n+ map_imagetype: \"png\"\n+ },\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n+ this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS);\n+ if (options == null || options.isBaseLayer == null) {\n+ this.isBaseLayer = this.params.transparent != \"true\" && this.params.transparent != true\n+ }\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.MapServer(this.name, this.url, this.params, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top];\n+ var imageSize = this.getImageSize();\n+ var url = this.getFullRequestString({\n+ mapext: extent,\n+ imgext: extent,\n+ map_size: [imageSize.w, imageSize.h],\n+ imgx: imageSize.w / 2,\n+ imgy: imageSize.h / 2,\n+ imgxy: [imageSize.w, imageSize.h]\n+ });\n+ return url\n+ },\n+ getFullRequestString: function(newParams, altUrl) {\n+ var url = altUrl == null ? this.url : altUrl;\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(paramsString, url)\n+ }\n+ var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key]\n+ }\n+ }\n+ paramsString = OpenLayers.Util.getParameterString(allParams);\n+ var requestString = url;\n+ paramsString = paramsString.replace(/,/g, \"+\");\n+ if (paramsString != \"\") {\n+ var lastServerChar = url.charAt(url.length - 1);\n+ if (lastServerChar == \"&\" || lastServerChar == \"?\") {\n+ requestString += paramsString\n+ } else {\n+ if (url.indexOf(\"?\") == -1) {\n+ requestString += \"?\" + paramsString\n+ } else {\n+ requestString += \"&\" + paramsString\n+ }\n+ }\n+ }\n+ return requestString\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.MapServer\"\n+});\n+OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {\n+ isBaseLayer: false,\n+ markers: null,\n+ drawn: false,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ this.markers = []\n },\n destroy: function() {\n- this.deactivate();\n- if (this.dragPan) {\n- this.dragPan.destroy()\n+ this.clearMarkers();\n+ this.markers = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+ },\n+ setOpacity: function(opacity) {\n+ if (opacity != this.opacity) {\n+ this.opacity = opacity;\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ this.markers[i].setOpacity(this.opacity)\n+ }\n }\n- this.dragPan = null;\n- if (this.pinchZoom) {\n- this.pinchZoom.destroy();\n- delete this.pinchZoom\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ if (zoomChanged || !this.drawn) {\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ this.drawMarker(this.markers[i])\n+ }\n+ this.drawn = true\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.dragPan.activate();\n- this.handlers.click.activate();\n- this.pinchZoom.activate();\n- return true\n+ addMarker: function(marker) {\n+ this.markers.push(marker);\n+ if (this.opacity < 1) {\n+ marker.setOpacity(this.opacity)\n+ }\n+ if (this.map && this.map.getExtent()) {\n+ marker.map = this.map;\n+ this.drawMarker(marker)\n }\n- return false\n },\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.dragPan.deactivate();\n- this.handlers.click.deactivate();\n- this.pinchZoom.deactivate();\n- return true\n+ removeMarker: function(marker) {\n+ if (this.markers && this.markers.length) {\n+ OpenLayers.Util.removeItem(this.markers, marker);\n+ marker.erase()\n }\n- return false\n },\n- draw: function() {\n- var clickCallbacks = {\n- click: this.defaultClick,\n- dblclick: this.defaultDblClick\n- };\n- var clickOptions = OpenLayers.Util.extend({\n- double: true,\n- stopDouble: true,\n- pixelTolerance: 2\n- }, this.clickHandlerOptions);\n- this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);\n- this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({\n- map: this.map,\n- documentDrag: this.documentDrag\n- }, this.dragPanOptions));\n- this.dragPan.draw();\n- this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({\n- map: this.map\n- }, this.pinchZoomOptions))\n+ clearMarkers: function() {\n+ if (this.markers != null) {\n+ while (this.markers.length > 0) {\n+ this.removeMarker(this.markers[0])\n+ }\n+ }\n },\n- defaultClick: function(evt) {\n- if (evt.lastTouches && evt.lastTouches.length == 2) {\n- this.map.zoomOut()\n+ drawMarker: function(marker) {\n+ var px = this.map.getLayerPxFromLonLat(marker.lonlat);\n+ if (px == null) {\n+ marker.display(false)\n+ } else {\n+ if (!marker.isDrawn()) {\n+ var markerImg = marker.draw(px);\n+ this.div.appendChild(markerImg)\n+ } else if (marker.icon) {\n+ marker.icon.moveTo(px)\n+ }\n }\n },\n- defaultDblClick: function(evt) {\n- this.map.zoomTo(this.map.zoom + 1, evt.xy)\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ if (this.markers && this.markers.length > 0) {\n+ var maxExtent = new OpenLayers.Bounds;\n+ for (var i = 0, len = this.markers.length; i < len; i++) {\n+ var marker = this.markers[i];\n+ maxExtent.extend(marker.lonlat)\n+ }\n+ }\n+ return maxExtent\n },\n- CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n+ CLASS_NAME: \"OpenLayers.Layer.Markers\"\n });\n-OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {\n- layerStates: null,\n- layersDiv: null,\n- baseLayersDiv: null,\n- baseLayers: null,\n- dataLbl: null,\n- dataLayersDiv: null,\n- dataLayers: null,\n- minimizeDiv: null,\n- maximizeDiv: null,\n- ascending: true,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, arguments);\n- this.layerStates = []\n+OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {\n+ location: null,\n+ features: null,\n+ formatOptions: null,\n+ selectedFeature: null,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);\n+ this.features = []\n },\n destroy: function() {\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n- this.map.events.un({\n- buttonclick: this.onButtonClick,\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n- });\n- this.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n+ this.clearFeatures();\n+ this.features = null\n+ },\n+ loadText: function() {\n+ if (!this.loaded) {\n+ if (this.location != null) {\n+ var onFail = function(e) {\n+ this.events.triggerEvent(\"loadend\")\n+ };\n+ this.events.triggerEvent(\"loadstart\");\n+ OpenLayers.Request.GET({\n+ url: this.location,\n+ success: this.parseData,\n+ failure: onFail,\n+ scope: this\n+ });\n+ this.loaded = true\n+ }\n+ }\n+ },\n+ moveTo: function(bounds, zoomChanged, minor) {\n+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n+ if (this.visibility && !this.loaded) {\n+ this.loadText()\n+ }\n+ },\n+ parseData: function(ajaxRequest) {\n+ var text = ajaxRequest.responseText;\n+ var options = {};\n+ OpenLayers.Util.extend(options, this.formatOptions);\n+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n+ options.externalProjection = this.projection;\n+ options.internalProjection = this.map.getProjectionObject()\n+ }\n+ var parser = new OpenLayers.Format.Text(options);\n+ var features = parser.read(text);\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ var data = {};\n+ var feature = features[i];\n+ var location;\n+ var iconSize, iconOffset;\n+ location = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y);\n+ if (feature.style.graphicWidth && feature.style.graphicHeight) {\n+ iconSize = new OpenLayers.Size(feature.style.graphicWidth, feature.style.graphicHeight)\n+ }\n+ if (feature.style.graphicXOffset !== undefined && feature.style.graphicYOffset !== undefined) {\n+ iconOffset = new OpenLayers.Pixel(feature.style.graphicXOffset, feature.style.graphicYOffset)\n+ }\n+ if (feature.style.externalGraphic != null) {\n+ data.icon = new OpenLayers.Icon(feature.style.externalGraphic, iconSize, iconOffset)\n+ } else {\n+ data.icon = OpenLayers.Marker.defaultIcon();\n+ if (iconSize != null) {\n+ data.icon.setSize(iconSize)\n+ }\n+ }\n+ if (feature.attributes.title != null && feature.attributes.description != null) {\n+ data[\"popupContentHTML\"] = \"<h2>\" + feature.attributes.title + \"</h2>\" + \"<p>\" + feature.attributes.description + \"</p>\"\n+ }\n+ data[\"overflow\"] = feature.attributes.overflow || \"auto\";\n+ var markerFeature = new OpenLayers.Feature(this, location, data);\n+ this.features.push(markerFeature);\n+ var marker = markerFeature.createMarker();\n+ if (feature.attributes.title != null && feature.attributes.description != null) {\n+ marker.events.register(\"click\", markerFeature, this.markerClick)\n+ }\n+ this.addMarker(marker)\n+ }\n+ this.events.triggerEvent(\"loadend\")\n+ },\n+ markerClick: function(evt) {\n+ var sameMarkerClicked = this == this.layer.selectedFeature;\n+ this.layer.selectedFeature = !sameMarkerClicked ? this : null;\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i])\n+ }\n+ if (!sameMarkerClicked) {\n+ this.layer.map.addPopup(this.createPopup())\n+ }\n+ OpenLayers.Event.stop(evt)\n+ },\n+ clearFeatures: function() {\n+ if (this.features != null) {\n+ while (this.features.length > 0) {\n+ var feature = this.features[0];\n+ OpenLayers.Util.removeItem(this.features, feature);\n+ feature.destroy()\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Text\"\n+});\n+OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ serviceVersion: \"1.0.0\",\n+ layername: null,\n+ type: null,\n+ isBaseLayer: true,\n+ tileOrigin: null,\n+ serverResolutions: null,\n+ zoomOffset: 0,\n+ initialize: function(name, url, options) {\n+ var newArguments = [];\n+ newArguments.push(name, url, {}, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.TMS(this.name, this.url, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+ var path = this.serviceVersion + \"/\" + this.layername + \"/\" + z + \"/\" + x + \"/\" + y + \".\" + this.type;\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url)\n+ }\n+ return url + path\n },\n setMap: function(map) {\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- this.map.events.on({\n- addlayer: this.redraw,\n- changelayer: this.redraw,\n- removelayer: this.redraw,\n- changebaselayer: this.redraw,\n- scope: this\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.bottom)\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.TMS\"\n+});\n+OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {\n+ IMAGE_EXTENSIONS: {\n+ jpeg: \"jpg\",\n+ gif: \"gif\",\n+ png: \"png\",\n+ png8: \"png\",\n+ png24: \"png\",\n+ dithered: \"png\"\n+ },\n+ DEFAULT_FORMAT: \"jpeg\",\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);\n+ this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT]\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var mapRes = this.map.getResolution();\n+ var scale = Math.round(this.map.getScale() * 1e4) / 1e4;\n+ var pX = Math.round(bounds.left / mapRes);\n+ var pY = -Math.round(bounds.top / mapRes);\n+ var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;\n+ var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;\n+ var components = [\"/\", this.params.map, \"/\", scale, \"/\", this.params.g.replace(/\\s/g, \"_\"), \"/def/t\", metaY, \"/l\", metaX, \"/t\", pY, \"l\", pX, \".\", this.extension];\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(components.join(\"\"), url)\n+ }\n+ return url + components.join(\"\")\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.KaMapCache\"\n+});\n+OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({\n+ initialize: function() {},\n+ initResolutions: function() {\n+ var props = [\"minZoomLevel\", \"maxZoomLevel\", \"numZoomLevels\"];\n+ for (var i = 0, len = props.length; i < len; i++) {\n+ var property = props[i];\n+ this[property] = this.options[property] != null ? this.options[property] : this.map[property]\n+ }\n+ if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) {\n+ this.minZoomLevel = this.MIN_ZOOM_LEVEL\n+ }\n+ var desiredZoomLevels;\n+ var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;\n+ if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) {\n+ desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1\n+ } else {\n+ desiredZoomLevels = this.numZoomLevels\n+ }\n+ if (desiredZoomLevels != null) {\n+ this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels)\n+ } else {\n+ this.numZoomLevels = limitZoomLevels\n+ }\n+ this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;\n+ if (this.RESOLUTIONS != null) {\n+ var resolutionsIndex = 0;\n+ this.resolutions = [];\n+ for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {\n+ this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]\n+ }\n+ this.maxResolution = this.resolutions[0];\n+ this.minResolution = this.resolutions[this.resolutions.length - 1]\n+ }\n+ },\n+ getResolution: function() {\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getResolution.apply(this, arguments)\n+ } else {\n+ var resolution = null;\n+ var viewSize = this.map.getSize();\n+ var extent = this.getExtent();\n+ if (viewSize != null && extent != null) {\n+ resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h)\n+ }\n+ return resolution\n+ }\n+ },\n+ getExtent: function() {\n+ var size = this.map.getSize();\n+ var tl = this.getLonLatFromViewPortPx({\n+ x: 0,\n+ y: 0\n });\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick)\n+ var br = this.getLonLatFromViewPortPx({\n+ x: size.w,\n+ y: size.h\n+ });\n+ if (tl != null && br != null) {\n+ return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat)\n } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n+ return null\n }\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this);\n- this.loadContents();\n- if (!this.outsideViewport) {\n- this.minimizeControl()\n+ getZoomForResolution: function(resolution) {\n+ if (this.resolutions != null) {\n+ return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments)\n+ } else {\n+ var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);\n+ return this.getZoomForExtent(extent)\n }\n- this.redraw();\n- return this.div\n },\n- onButtonClick: function(evt) {\n- var button = evt.buttonElement;\n- if (button === this.minimizeDiv) {\n- this.minimizeControl()\n- } else if (button === this.maximizeDiv) {\n- this.maximizeControl()\n- } else if (button._layerSwitcher === this.id) {\n- if (button[\"for\"]) {\n- button = document.getElementById(button[\"for\"])\n+ getOLZoomFromMapObjectZoom: function(moZoom) {\n+ var zoom = null;\n+ if (moZoom != null) {\n+ zoom = moZoom - this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom))\n }\n- if (!button.disabled) {\n- if (button.type == \"radio\") {\n- button.checked = true;\n- this.map.setBaseLayer(this.map.getLayer(button._layer))\n- } else {\n- button.checked = !button.checked;\n- this.updateMap()\n- }\n+ }\n+ return zoom\n+ },\n+ getMapObjectZoomFromOLZoom: function(olZoom) {\n+ var zoom = null;\n+ if (olZoom != null) {\n+ zoom = olZoom + this.minZoomLevel;\n+ if (this.map.baseLayer !== this) {\n+ zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom))\n }\n }\n+ return zoom\n },\n- clearLayersArray: function(layersType) {\n- this[layersType + \"LayersDiv\"].innerHTML = \"\";\n- this[layersType + \"Layers\"] = []\n+ CLASS_NAME: \"OpenLayers.Layer.FixedZoomLevels\"\n+});\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ name: \"OpenStreetMap\",\n+ url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+ sphericalMercator: true,\n+ wrapDateLine: true,\n+ tileOptions: null,\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options && this.options.tileOptions)\n },\n- checkRedraw: function() {\n- if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) {\n- return true\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n }\n- for (var i = 0, len = this.layerStates.length; i < len; i++) {\n- var layerState = this.layerStates[i];\n- var layer = this.map.layers[i];\n- if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) {\n- return true\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n+});\n+OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ useHttpTile: false,\n+ singleTile: false,\n+ useOverlay: false,\n+ useAsyncOverlay: true,\n+ TILE_PARAMS: {\n+ operation: \"GETTILEIMAGE\",\n+ version: \"1.2.0\"\n+ },\n+ SINGLE_TILE_PARAMS: {\n+ operation: \"GETMAPIMAGE\",\n+ format: \"PNG\",\n+ locale: \"en\",\n+ clip: \"1\",\n+ version: \"1.0.0\"\n+ },\n+ OVERLAY_PARAMS: {\n+ operation: \"GETDYNAMICMAPOVERLAYIMAGE\",\n+ format: \"PNG\",\n+ locale: \"en\",\n+ clip: \"1\",\n+ version: \"2.0.0\"\n+ },\n+ FOLDER_PARAMS: {\n+ tileColumnsPerFolder: 30,\n+ tileRowsPerFolder: 30,\n+ format: \"png\",\n+ querystring: null\n+ },\n+ defaultSize: new OpenLayers.Size(300, 300),\n+ tileOriginCorner: \"tl\",\n+ initialize: function(name, url, params, options) {\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n+ if (options == null || options.isBaseLayer == null) {\n+ this.isBaseLayer = this.transparent != \"true\" && this.transparent != true\n+ }\n+ if (options && options.useOverlay != null) {\n+ this.useOverlay = options.useOverlay\n+ }\n+ if (this.singleTile) {\n+ if (this.useOverlay) {\n+ OpenLayers.Util.applyDefaults(this.params, this.OVERLAY_PARAMS);\n+ if (!this.useAsyncOverlay) {\n+ this.params.version = \"1.0.0\"\n+ }\n+ } else {\n+ OpenLayers.Util.applyDefaults(this.params, this.SINGLE_TILE_PARAMS)\n+ }\n+ } else {\n+ if (this.useHttpTile) {\n+ OpenLayers.Util.applyDefaults(this.params, this.FOLDER_PARAMS)\n+ } else {\n+ OpenLayers.Util.applyDefaults(this.params, this.TILE_PARAMS)\n }\n+ this.setTileSize(this.defaultSize)\n }\n- return false\n },\n- redraw: function() {\n- if (!this.checkRedraw()) {\n- return this.div\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.MapGuide(this.name, this.url, this.params, this.getOptions())\n }\n- this.clearLayersArray(\"base\");\n- this.clearLayersArray(\"data\");\n- var containsOverlays = false;\n- var containsBaseLayers = false;\n- var len = this.map.layers.length;\n- this.layerStates = new Array(len);\n- for (var i = 0; i < len; i++) {\n- var layer = this.map.layers[i];\n- this.layerStates[i] = {\n- name: layer.name,\n- visibility: layer.visibility,\n- inRange: layer.inRange,\n- id: layer.id\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ var url;\n+ var center = bounds.getCenterLonLat();\n+ var mapSize = this.map.getSize();\n+ if (this.singleTile) {\n+ var params = {\n+ setdisplaydpi: OpenLayers.DOTS_PER_INCH,\n+ setdisplayheight: mapSize.h * this.ratio,\n+ setdisplaywidth: mapSize.w * this.ratio,\n+ setviewcenterx: center.lon,\n+ setviewcentery: center.lat,\n+ setviewscale: this.map.getScale()\n+ };\n+ if (this.useOverlay && !this.useAsyncOverlay) {\n+ var getVisParams = {};\n+ getVisParams = OpenLayers.Util.extend(getVisParams, params);\n+ getVisParams.operation = \"GETVISIBLEMAPEXTENT\";\n+ getVisParams.version = \"1.0.0\";\n+ getVisParams.session = this.params.session;\n+ getVisParams.mapName = this.params.mapName;\n+ getVisParams.format = \"text/xml\";\n+ url = this.getFullRequestString(getVisParams);\n+ OpenLayers.Request.GET({\n+ url: url,\n+ async: false\n+ })\n+ }\n+ url = this.getFullRequestString(params)\n+ } else {\n+ var currentRes = this.map.getResolution();\n+ var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);\n+ colidx = Math.round(colidx / this.tileSize.w);\n+ var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);\n+ rowidx = Math.round(rowidx / this.tileSize.h);\n+ if (this.useHttpTile) {\n+ url = this.getImageFilePath({\n+ tilecol: colidx,\n+ tilerow: rowidx,\n+ scaleindex: this.resolutions.length - this.map.zoom - 1\n+ })\n+ } else {\n+ url = this.getFullRequestString({\n+ tilecol: colidx,\n+ tilerow: rowidx,\n+ scaleindex: this.resolutions.length - this.map.zoom - 1\n+ })\n }\n }\n- var layers = this.map.layers.slice();\n- if (!this.ascending) {\n- layers.reverse()\n+ return url\n+ },\n+ getFullRequestString: function(newParams, altUrl) {\n+ var url = altUrl == null ? this.url : altUrl;\n+ if (typeof url == \"object\") {\n+ url = url[Math.floor(Math.random() * url.length)]\n }\n- for (var i = 0, len = layers.length; i < len; i++) {\n- var layer = layers[i];\n- var baseLayer = layer.isBaseLayer;\n- if (layer.displayInLayerSwitcher) {\n- if (baseLayer) {\n- containsBaseLayers = true\n+ var requestString = url;\n+ var allParams = OpenLayers.Util.extend({}, this.params);\n+ allParams = OpenLayers.Util.extend(allParams, newParams);\n+ var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));\n+ for (var key in allParams) {\n+ if (key.toUpperCase() in urlParams) {\n+ delete allParams[key]\n+ }\n+ }\n+ var paramsString = OpenLayers.Util.getParameterString(allParams);\n+ paramsString = paramsString.replace(/,/g, \"+\");\n+ if (paramsString != \"\") {\n+ var lastServerChar = url.charAt(url.length - 1);\n+ if (lastServerChar == \"&\" || lastServerChar == \"?\") {\n+ requestString += paramsString\n+ } else {\n+ if (url.indexOf(\"?\") == -1) {\n+ requestString += \"?\" + paramsString\n } else {\n- containsOverlays = true\n+ requestString += \"&\" + paramsString\n }\n- var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility();\n- var inputElem = document.createElement(\"input\"),\n- inputId = OpenLayers.Util.createUniqueID(this.id + \"_input_\");\n- inputElem.id = inputId;\n- inputElem.name = baseLayer ? this.id + \"_baseLayers\" : layer.name;\n- inputElem.type = baseLayer ? \"radio\" : \"checkbox\";\n- inputElem.value = layer.name;\n- inputElem.checked = checked;\n- inputElem.defaultChecked = checked;\n- inputElem.className = \"olButton\";\n- inputElem._layer = layer.id;\n- inputElem._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- inputElem.disabled = true\n+ }\n+ }\n+ return requestString\n+ },\n+ getImageFilePath: function(newParams, altUrl) {\n+ var url = altUrl == null ? this.url : altUrl;\n+ if (typeof url == \"object\") {\n+ url = url[Math.floor(Math.random() * url.length)]\n+ }\n+ var requestString = url;\n+ var tileRowGroup = \"\";\n+ var tileColGroup = \"\";\n+ if (newParams.tilerow < 0) {\n+ tileRowGroup = \"-\"\n+ }\n+ if (newParams.tilerow == 0) {\n+ tileRowGroup += \"0\"\n+ } else {\n+ tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder\n+ }\n+ if (newParams.tilecol < 0) {\n+ tileColGroup = \"-\"\n+ }\n+ if (newParams.tilecol == 0) {\n+ tileColGroup += \"0\"\n+ } else {\n+ tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder\n+ }\n+ var tilePath = \"/S\" + Math.floor(newParams.scaleindex) + \"/\" + this.params.basemaplayergroupname + \"/R\" + tileRowGroup + \"/C\" + tileColGroup + \"/\" + newParams.tilerow % this.params.tileRowsPerFolder + \"_\" + newParams.tilecol % this.params.tileColumnsPerFolder + \".\" + this.params.format;\n+ if (this.params.querystring) {\n+ tilePath += \"?\" + this.params.querystring\n+ }\n+ requestString += tilePath;\n+ return requestString\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.MapGuide\"\n+});\n+OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {\n+ drawMarker: function(marker) {\n+ var topleft = this.map.getLayerPxFromLonLat({\n+ lon: marker.bounds.left,\n+ lat: marker.bounds.top\n+ });\n+ var botright = this.map.getLayerPxFromLonLat({\n+ lon: marker.bounds.right,\n+ lat: marker.bounds.bottom\n+ });\n+ if (botright == null || topleft == null) {\n+ marker.display(false)\n+ } else {\n+ var markerDiv = marker.draw(topleft, {\n+ w: Math.max(1, botright.x - topleft.x),\n+ h: Math.max(1, botright.y - topleft.y)\n+ });\n+ if (!marker.drawn) {\n+ this.div.appendChild(markerDiv);\n+ marker.drawn = true\n+ }\n+ }\n+ },\n+ removeMarker: function(marker) {\n+ OpenLayers.Util.removeItem(this.markers, marker);\n+ if (marker.div != null && marker.div.parentNode == this.div) {\n+ this.div.removeChild(marker.div)\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Boxes\"\n+});\n+OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {\n+ location: null,\n+ features: null,\n+ formatOptions: null,\n+ selectedFeature: null,\n+ icon: null,\n+ popupSize: null,\n+ useFeedTitle: true,\n+ initialize: function(name, location, options) {\n+ OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);\n+ this.location = location;\n+ this.features = []\n+ },\n+ destroy: function() {\n+ OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n+ this.clearFeatures();\n+ this.features = null\n+ },\n+ loadRSS: function() {\n+ if (!this.loaded) {\n+ this.events.triggerEvent(\"loadstart\");\n+ OpenLayers.Request.GET({\n+ url: this.location,\n+ success: this.parseData,\n+ scope: this\n+ });\n+ this.loaded = true\n+ }\n+ },\n+ moveTo: function(bounds, zoomChanged, minor) {\n+ OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n+ if (this.visibility && !this.loaded) {\n+ this.loadRSS()\n+ }\n+ },\n+ parseData: function(ajaxRequest) {\n+ var doc = ajaxRequest.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText)\n+ }\n+ if (this.useFeedTitle) {\n+ var name = null;\n+ try {\n+ name = doc.getElementsByTagNameNS(\"*\", \"title\")[0].firstChild.nodeValue\n+ } catch (e) {\n+ try {\n+ name = doc.getElementsByTagName(\"title\")[0].firstChild.nodeValue\n+ } catch (e) {}\n+ }\n+ if (name) {\n+ this.setName(name)\n+ }\n+ }\n+ var options = {};\n+ OpenLayers.Util.extend(options, this.formatOptions);\n+ if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n+ options.externalProjection = this.projection;\n+ options.internalProjection = this.map.getProjectionObject()\n+ }\n+ var format = new OpenLayers.Format.GeoRSS(options);\n+ var features = format.read(doc);\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ var data = {};\n+ var feature = features[i];\n+ if (!feature.geometry) {\n+ continue\n+ }\n+ var title = feature.attributes.title ? feature.attributes.title : \"Untitled\";\n+ var description = feature.attributes.description ? feature.attributes.description : \"No description.\";\n+ var link = feature.attributes.link ? feature.attributes.link : \"\";\n+ var location = feature.geometry.getBounds().getCenterLonLat();\n+ data.icon = this.icon == null ? OpenLayers.Marker.defaultIcon() : this.icon.clone();\n+ data.popupSize = this.popupSize ? this.popupSize.clone() : new OpenLayers.Size(250, 120);\n+ if (title || description) {\n+ data.title = title;\n+ data.description = description;\n+ var contentHTML = '<div class=\"olLayerGeoRSSClose\">[x]</div>';\n+ contentHTML += '<div class=\"olLayerGeoRSSTitle\">';\n+ if (link) {\n+ contentHTML += '<a class=\"link\" href=\"' + link + '\" target=\"_blank\">'\n }\n- var labelSpan = document.createElement(\"label\");\n- labelSpan[\"for\"] = inputElem.id;\n- OpenLayers.Element.addClass(labelSpan, \"labelSpan olButton\");\n- labelSpan._layer = layer.id;\n- labelSpan._layerSwitcher = this.id;\n- if (!baseLayer && !layer.inRange) {\n- labelSpan.style.color = \"gray\"\n+ contentHTML += title;\n+ if (link) {\n+ contentHTML += \"</a>\"\n }\n- labelSpan.innerHTML = layer.name;\n- labelSpan.style.verticalAlign = baseLayer ? \"bottom\" : \"baseline\";\n- var br = document.createElement(\"br\");\n- var groupArray = baseLayer ? this.baseLayers : this.dataLayers;\n- groupArray.push({\n- layer: layer,\n- inputElem: inputElem,\n- labelSpan: labelSpan\n- });\n- var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv;\n- groupDiv.appendChild(inputElem);\n- groupDiv.appendChild(labelSpan);\n- groupDiv.appendChild(br)\n+ contentHTML += \"</div>\";\n+ contentHTML += '<div style=\"\" class=\"olLayerGeoRSSDescription\">';\n+ contentHTML += description;\n+ contentHTML += \"</div>\";\n+ data[\"popupContentHTML\"] = contentHTML\n }\n+ var feature = new OpenLayers.Feature(this, location, data);\n+ this.features.push(feature);\n+ var marker = feature.createMarker();\n+ marker.events.register(\"click\", feature, this.markerClick);\n+ this.addMarker(marker)\n }\n- this.dataLbl.style.display = containsOverlays ? \"\" : \"none\";\n- this.baseLbl.style.display = containsBaseLayers ? \"\" : \"none\";\n- return this.div\n+ this.events.triggerEvent(\"loadend\")\n },\n- updateMap: function() {\n- for (var i = 0, len = this.baseLayers.length; i < len; i++) {\n- var layerEntry = this.baseLayers[i];\n- if (layerEntry.inputElem.checked) {\n- this.map.setBaseLayer(layerEntry.layer, false)\n+ markerClick: function(evt) {\n+ var sameMarkerClicked = this == this.layer.selectedFeature;\n+ this.layer.selectedFeature = !sameMarkerClicked ? this : null;\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i])\n+ }\n+ if (!sameMarkerClicked) {\n+ var popup = this.createPopup();\n+ OpenLayers.Event.observe(popup.div, \"click\", OpenLayers.Function.bind(function() {\n+ for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n+ this.layer.map.removePopup(this.layer.map.popups[i])\n+ }\n+ }, this));\n+ this.layer.map.addPopup(popup)\n+ }\n+ OpenLayers.Event.stop(evt)\n+ },\n+ clearFeatures: function() {\n+ if (this.features != null) {\n+ while (this.features.length > 0) {\n+ var feature = this.features[0];\n+ OpenLayers.Util.removeItem(this.features, feature);\n+ feature.destroy()\n }\n }\n- for (var i = 0, len = this.dataLayers.length; i < len; i++) {\n- var layerEntry = this.dataLayers[i];\n- layerEntry.layer.setVisibility(layerEntry.inputElem.checked)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.GeoRSS\"\n+});\n+OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ ClientVersion: \"9.2\",\n+ ServiceName: \"\"\n+ },\n+ featureCoordSys: \"4326\",\n+ filterCoordSys: \"4326\",\n+ layers: null,\n+ async: true,\n+ name: \"ArcIMS\",\n+ isBaseLayer: true,\n+ DEFAULT_OPTIONS: {\n+ tileSize: new OpenLayers.Size(512, 512),\n+ featureCoordSys: \"4326\",\n+ filterCoordSys: \"4326\",\n+ layers: null,\n+ isBaseLayer: true,\n+ async: true,\n+ name: \"ArcIMS\"\n+ },\n+ initialize: function(name, url, options) {\n+ this.tileSize = new OpenLayers.Size(512, 512);\n+ this.params = OpenLayers.Util.applyDefaults({\n+ ServiceName: options.serviceName\n+ }, this.DEFAULT_PARAMS);\n+ this.options = OpenLayers.Util.applyDefaults(options, this.DEFAULT_OPTIONS);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, this.params, options]);\n+ if (this.transparent) {\n+ if (!this.isBaseLayer) {\n+ this.isBaseLayer = false\n+ }\n+ if (this.format == \"image/jpeg\") {\n+ this.format = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n+ }\n+ }\n+ if (this.options.layers === null) {\n+ this.options.layers = []\n }\n },\n- maximizeControl: function(e) {\n- this.div.style.width = \"\";\n- this.div.style.height = \"\";\n- this.showControls(false);\n- if (e != null) {\n- OpenLayers.Event.stop(e)\n+ getURL: function(bounds) {\n+ var url = \"\";\n+ bounds = this.adjustBounds(bounds);\n+ var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {\n+ requesttype: \"image\",\n+ envelope: bounds.toArray(),\n+ tileSize: this.tileSize\n+ }));\n+ var req = new OpenLayers.Request.POST({\n+ url: this.getFullRequestString(),\n+ data: axlReq.write(),\n+ async: false\n+ });\n+ if (req != null) {\n+ var doc = req.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = req.responseText\n+ }\n+ var axlResp = new OpenLayers.Format.ArcXML;\n+ var arcxml = axlResp.read(doc);\n+ url = this.getUrlOrImage(arcxml.image.output)\n }\n+ return url\n },\n- minimizeControl: function(e) {\n- this.div.style.width = \"0px\";\n- this.div.style.height = \"0px\";\n- this.showControls(true);\n- if (e != null) {\n- OpenLayers.Event.stop(e)\n+ getURLasync: function(bounds, callback, scope) {\n+ bounds = this.adjustBounds(bounds);\n+ var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {\n+ requesttype: \"image\",\n+ envelope: bounds.toArray(),\n+ tileSize: this.tileSize\n+ }));\n+ OpenLayers.Request.POST({\n+ url: this.getFullRequestString(),\n+ async: true,\n+ data: axlReq.write(),\n+ callback: function(req) {\n+ var doc = req.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = req.responseText\n+ }\n+ var axlResp = new OpenLayers.Format.ArcXML;\n+ var arcxml = axlResp.read(doc);\n+ callback.call(scope, this.getUrlOrImage(arcxml.image.output))\n+ },\n+ scope: this\n+ })\n+ },\n+ getUrlOrImage: function(output) {\n+ var ret = \"\";\n+ if (output.url) {\n+ ret = output.url\n+ } else if (output.data) {\n+ ret = \"data:image/\" + output.type + \";base64,\" + output.data\n }\n+ return ret\n },\n- showControls: function(minimize) {\n- this.maximizeDiv.style.display = minimize ? \"\" : \"none\";\n- this.minimizeDiv.style.display = minimize ? \"none\" : \"\";\n- this.layersDiv.style.display = minimize ? \"none\" : \"\"\n+ setLayerQuery: function(id, querydef) {\n+ for (var lyr = 0; lyr < this.options.layers.length; lyr++) {\n+ if (id == this.options.layers[lyr].id) {\n+ this.options.layers[lyr].query = querydef;\n+ return\n+ }\n+ }\n+ this.options.layers.push({\n+ id: id,\n+ visible: true,\n+ query: querydef\n+ })\n },\n- loadContents: function() {\n- this.layersDiv = document.createElement(\"div\");\n- this.layersDiv.id = this.id + \"_layersDiv\";\n- OpenLayers.Element.addClass(this.layersDiv, \"layersDiv\");\n- this.baseLbl = document.createElement(\"div\");\n- this.baseLbl.innerHTML = OpenLayers.i18n(\"Base Layer\");\n- OpenLayers.Element.addClass(this.baseLbl, \"baseLbl\");\n- this.baseLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.baseLayersDiv, \"baseLayersDiv\");\n- this.dataLbl = document.createElement(\"div\");\n- this.dataLbl.innerHTML = OpenLayers.i18n(\"Overlays\");\n- OpenLayers.Element.addClass(this.dataLbl, \"dataLbl\");\n- this.dataLayersDiv = document.createElement(\"div\");\n- OpenLayers.Element.addClass(this.dataLayersDiv, \"dataLayersDiv\");\n- if (this.ascending) {\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv);\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv)\n+ getFeatureInfo: function(geometry, layer, options) {\n+ var buffer = options.buffer || 1;\n+ var callback = options.callback || function() {};\n+ var scope = options.scope || window;\n+ var requestOptions = {};\n+ OpenLayers.Util.extend(requestOptions, this.options);\n+ requestOptions.requesttype = \"feature\";\n+ if (geometry instanceof OpenLayers.LonLat) {\n+ requestOptions.polygon = null;\n+ requestOptions.envelope = [geometry.lon - buffer, geometry.lat - buffer, geometry.lon + buffer, geometry.lat + buffer]\n+ } else if (geometry instanceof OpenLayers.Geometry.Polygon) {\n+ requestOptions.envelope = null;\n+ requestOptions.polygon = geometry\n+ }\n+ var arcxml = new OpenLayers.Format.ArcXML(requestOptions);\n+ OpenLayers.Util.extend(arcxml.request.get_feature, options);\n+ arcxml.request.get_feature.layer = layer.id;\n+ if (typeof layer.query.accuracy == \"number\") {\n+ arcxml.request.get_feature.query.accuracy = layer.query.accuracy\n } else {\n- this.layersDiv.appendChild(this.dataLbl);\n- this.layersDiv.appendChild(this.dataLayersDiv);\n- this.layersDiv.appendChild(this.baseLbl);\n- this.layersDiv.appendChild(this.baseLayersDiv)\n+ var mapCenter = this.map.getCenter();\n+ var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);\n+ viewPx.x++;\n+ var mapOffCenter = this.map.getLonLatFromPixel(viewPx);\n+ arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon\n }\n- this.div.appendChild(this.layersDiv);\n- var img = OpenLayers.Util.getImageLocation(\"layer-switcher-maximize.png\");\n- this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MaximizeDiv\", null, null, img, \"absolute\");\n- OpenLayers.Element.addClass(this.maximizeDiv, \"maximizeDiv olButton\");\n- this.maximizeDiv.style.display = \"none\";\n- this.div.appendChild(this.maximizeDiv);\n- var img = OpenLayers.Util.getImageLocation(\"layer-switcher-minimize.png\");\n- this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(\"OpenLayers_Control_MinimizeDiv\", null, null, img, \"absolute\");\n- OpenLayers.Element.addClass(this.minimizeDiv, \"minimizeDiv olButton\");\n- this.minimizeDiv.style.display = \"none\";\n- this.div.appendChild(this.minimizeDiv)\n+ arcxml.request.get_feature.query.where = layer.query.where;\n+ arcxml.request.get_feature.query.spatialfilter.relation = \"area_intersection\";\n+ OpenLayers.Request.POST({\n+ url: this.getFullRequestString({\n+ CustomService: \"Query\"\n+ }),\n+ data: arcxml.write(),\n+ callback: function(request) {\n+ var response = arcxml.parseResponse(request.responseText);\n+ if (!arcxml.iserror()) {\n+ callback.call(scope, response.features)\n+ } else {\n+ callback.call(scope, null)\n+ }\n+ }\n+ })\n },\n- CLASS_NAME: \"OpenLayers.Control.LayerSwitcher\"\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcIMS(this.name, this.url, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.ArcIMS\"\n });\n-OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {\n- autoActivate: true,\n- intervals: [45, 30, 20, 10, 5, 2, 1, .5, .2, .1, .05, .01, .005, .002, .001],\n- displayInLayerSwitcher: true,\n- visible: true,\n- numPoints: 50,\n- targetSize: 200,\n- layerName: null,\n- labelled: true,\n- labelFormat: \"dm\",\n- lineSymbolizer: {\n- strokeColor: \"#333\",\n- strokeWidth: 1,\n- strokeOpacity: .5\n+OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ format: \"image/png\",\n+ serverResolutions: null,\n+ initialize: function(name, url, layername, options) {\n+ this.layername = layername;\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, {}, options]);\n+ this.extension = this.format.split(\"/\")[1].toLowerCase();\n+ this.extension = this.extension == \"jpg\" ? \"jpeg\" : this.extension\n },\n- labelSymbolizer: {},\n- gratLayer: null,\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.TileCache(this.name, this.url, this.layername, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ var res = this.getServerResolution();\n+ var bbox = this.maxExtent;\n+ var size = this.tileSize;\n+ var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));\n+ var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));\n+ var tileZ = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom();\n+ var components = [this.layername, OpenLayers.Number.zeroPad(tileZ, 2), OpenLayers.Number.zeroPad(parseInt(tileX / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileX / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileX) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY) % 1e3, 3) + \".\" + this.extension];\n+ var path = components.join(\"/\");\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url)\n+ }\n+ url = url.charAt(url.length - 1) == \"/\" ? url : url + \"/\";\n+ return url + path\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.TileCache\"\n+});\n+OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ size: null,\n+ isBaseLayer: true,\n+ standardTileSize: 256,\n+ tileOriginCorner: \"tl\",\n+ numberOfTiers: 0,\n+ tileCountUpToTier: null,\n+ tierSizeInTiles: null,\n+ tierImageSize: null,\n+ initialize: function(name, url, size, options) {\n+ this.initializeZoomify(size);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, size, {}, options])\n+ },\n+ initializeZoomify: function(size) {\n+ var imageSize = size.clone();\n+ this.size = size.clone();\n+ var tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));\n+ this.tierSizeInTiles = [tiles];\n+ this.tierImageSize = [imageSize];\n+ while (imageSize.w > this.standardTileSize || imageSize.h > this.standardTileSize) {\n+ imageSize = new OpenLayers.Size(Math.floor(imageSize.w / 2), Math.floor(imageSize.h / 2));\n+ tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));\n+ this.tierSizeInTiles.push(tiles);\n+ this.tierImageSize.push(imageSize)\n+ }\n+ this.tierSizeInTiles.reverse();\n+ this.tierImageSize.reverse();\n+ this.numberOfTiers = this.tierSizeInTiles.length;\n+ var resolutions = [1];\n+ this.tileCountUpToTier = [0];\n+ for (var i = 1; i < this.numberOfTiers; i++) {\n+ resolutions.unshift(Math.pow(2, i));\n+ this.tileCountUpToTier.push(this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + this.tileCountUpToTier[i - 1])\n+ }\n+ if (!this.serverResolutions) {\n+ this.serverResolutions = resolutions\n+ }\n+ },\n+ destroy: function() {\n+ OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);\n+ this.tileCountUpToTier.length = 0;\n+ this.tierSizeInTiles.length = 0;\n+ this.tierImageSize.length = 0\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Zoomify(this.name, this.url, this.size, this.options)\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getZoomForResolution(res);\n+ var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];\n+ var path = \"TileGroup\" + Math.floor(tileIndex / 256) + \"/\" + z + \"-\" + x + \"-\" + y + \".jpg\";\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ url = this.selectUrl(path, url)\n+ }\n+ return url + path\n+ },\n+ getImageSize: function() {\n+ if (arguments.length > 0) {\n+ var bounds = this.adjustBounds(arguments[0]);\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n+ var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getZoomForResolution(res);\n+ var w = this.standardTileSize;\n+ var h = this.standardTileSize;\n+ if (x == this.tierSizeInTiles[z].w - 1) {\n+ var w = this.tierImageSize[z].w % this.standardTileSize\n+ }\n+ if (y == this.tierSizeInTiles[z].h - 1) {\n+ var h = this.tierImageSize[z].h % this.standardTileSize\n+ }\n+ return new OpenLayers.Size(w, h)\n+ } else {\n+ return this.tileSize\n+ }\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.top)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Zoomify\"\n+});\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ key: null,\n+ serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n+ metadata: null,\n+ protocolRegex: /^http:/i,\n+ type: \"Road\",\n+ culture: \"en-US\",\n+ metadataParams: null,\n+ tileOptions: null,\n+ protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n initialize: function(options) {\n- options = options || {};\n- options.layerName = options.layerName || OpenLayers.i18n(\"Graticule\");\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.labelSymbolizer.stroke = false;\n- this.labelSymbolizer.fill = false;\n- this.labelSymbolizer.label = \"${label}\";\n- this.labelSymbolizer.labelAlign = \"${labelAlign}\";\n- this.labelSymbolizer.labelXOffset = \"${xOffset}\";\n- this.labelSymbolizer.labelYOffset = \"${yOffset}\"\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options.tileOptions);\n+ this.loadMetadata()\n+ },\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script)\n+ },\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n+ }\n+ this.addOptions({\n+ maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n+ numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw()\n+ }\n+ this.updateAttribution()\n+ },\n+ getURL: function(bounds) {\n+ if (!this.url) {\n+ return\n+ }\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = \"0\";\n+ var mask = 1 << i - 1;\n+ if ((x & mask) != 0) {\n+ digit++\n+ }\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++\n+ }\n+ quadDigits.push(digit)\n+ }\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl(\"\" + x + y + z, this.url);\n+ return OpenLayers.String.format(url, {\n+ quadkey: quadKey\n+ })\n+ },\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ return\n+ }\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \"\n+ }\n+ }\n+ }\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ })\n+ },\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options)\n+ }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n destroy: function() {\n- this.deactivate();\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.gratLayer) {\n- this.gratLayer.destroy();\n- this.gratLayer = null\n+ this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+});\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined;\n+ delete this._callbackId\n+};\n+OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {},\n+ isBaseLayer: true,\n+ lzd: null,\n+ zoomLevels: null,\n+ initialize: function(name, url, lzd, zoomLevels, params, options) {\n+ this.lzd = lzd;\n+ this.zoomLevels = zoomLevels;\n+ var newArguments = [];\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS)\n+ },\n+ getZoom: function() {\n+ var zoom = this.map.getZoom();\n+ var extent = this.map.getMaxExtent();\n+ zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);\n+ return zoom\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var zoom = this.getZoom();\n+ var extent = this.map.getMaxExtent();\n+ var deg = this.lzd / Math.pow(2, this.getZoom());\n+ var x = Math.floor((bounds.left - extent.left) / deg);\n+ var y = Math.floor((bounds.bottom - extent.bottom) / deg);\n+ if (this.map.getResolution() <= this.lzd / 512 && this.getZoom() <= this.zoomLevels) {\n+ return this.getFullRequestString({\n+ L: zoom,\n+ X: x,\n+ Y: y\n+ })\n+ } else {\n+ return OpenLayers.Util.getImageLocation(\"blank.gif\")\n }\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.gratLayer) {\n- var gratStyle = new OpenLayers.Style({}, {\n- rules: [new OpenLayers.Rule({\n- symbolizer: {\n- Point: this.labelSymbolizer,\n- Line: this.lineSymbolizer\n+ CLASS_NAME: \"OpenLayers.Layer.WorldWind\"\n+});\n+OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {\n+ smoothDragPan: true,\n+ isBaseLayer: true,\n+ isFixed: true,\n+ pane: null,\n+ mapObject: null,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (this.pane == null) {\n+ this.pane = OpenLayers.Util.createDiv(this.div.id + \"_EventPane\")\n+ }\n+ },\n+ destroy: function() {\n+ this.mapObject = null;\n+ this.pane = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;\n+ this.pane.style.display = this.div.style.display;\n+ this.pane.style.width = \"100%\";\n+ this.pane.style.height = \"100%\";\n+ if (OpenLayers.BROWSER_NAME == \"msie\") {\n+ this.pane.style.background = \"url(\" + OpenLayers.Util.getImageLocation(\"blank.gif\") + \")\"\n+ }\n+ if (this.isFixed) {\n+ this.map.viewPortDiv.appendChild(this.pane)\n+ } else {\n+ this.map.layerContainerDiv.appendChild(this.pane)\n+ }\n+ this.loadMapObject();\n+ if (this.mapObject == null) {\n+ this.loadWarningMessage()\n+ }\n+ },\n+ removeMap: function(map) {\n+ if (this.pane && this.pane.parentNode) {\n+ this.pane.parentNode.removeChild(this.pane)\n+ }\n+ OpenLayers.Layer.prototype.removeMap.apply(this, arguments)\n+ },\n+ loadWarningMessage: function() {\n+ this.div.style.backgroundColor = \"darkblue\";\n+ var viewSize = this.map.getSize();\n+ var msgW = Math.min(viewSize.w, 300);\n+ var msgH = Math.min(viewSize.h, 200);\n+ var size = new OpenLayers.Size(msgW, msgH);\n+ var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);\n+ var topLeft = centerPx.add(-size.w / 2, -size.h / 2);\n+ var div = OpenLayers.Util.createDiv(this.name + \"_warning\", topLeft, size, null, null, null, \"auto\");\n+ div.style.padding = \"7px\";\n+ div.style.backgroundColor = \"yellow\";\n+ div.innerHTML = this.getWarningHTML();\n+ this.div.appendChild(div)\n+ },\n+ getWarningHTML: function() {\n+ return \"\"\n+ },\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ this.pane.style.display = this.div.style.display\n+ },\n+ setZIndex: function(zIndex) {\n+ OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);\n+ this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1\n+ },\n+ moveByPx: function(dx, dy) {\n+ OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);\n+ if (this.dragPanMapObject) {\n+ this.dragPanMapObject(dx, -dy)\n+ } else {\n+ this.moveTo(this.map.getCachedCenter())\n+ }\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ if (this.mapObject != null) {\n+ var newCenter = this.map.getCenter();\n+ var newZoom = this.map.getZoom();\n+ if (newCenter != null) {\n+ var moOldCenter = this.getMapObjectCenter();\n+ var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);\n+ var moOldZoom = this.getMapObjectZoom();\n+ var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);\n+ if (!newCenter.equals(oldCenter) || newZoom != oldZoom) {\n+ if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) {\n+ var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);\n+ var newPx = this.map.getViewPortPxFromLonLat(newCenter);\n+ this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y)\n+ } else {\n+ var center = this.getMapObjectLonLatFromOLLonLat(newCenter);\n+ var zoom = this.getMapObjectZoomFromOLZoom(newZoom);\n+ this.setMapObjectCenter(center, zoom, dragging)\n }\n- })]\n- });\n- this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {\n- styleMap: new OpenLayers.StyleMap({\n- default: gratStyle\n- }),\n- visibility: this.visible,\n- displayInLayerSwitcher: this.displayInLayerSwitcher\n- })\n+ }\n+ }\n }\n- return this.div\n },\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- this.map.addLayer(this.gratLayer);\n- this.map.events.register(\"moveend\", this, this.update);\n- this.update();\n- return true\n+ getLonLatFromViewPortPx: function(viewPortPx) {\n+ var lonlat = null;\n+ if (this.mapObject != null && this.getMapObjectCenter() != null) {\n+ var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);\n+ var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);\n+ lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat)\n+ }\n+ return lonlat\n+ },\n+ getViewPortPxFromLonLat: function(lonlat) {\n+ var viewPortPx = null;\n+ if (this.mapObject != null && this.getMapObjectCenter() != null) {\n+ var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);\n+ var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);\n+ viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel)\n+ }\n+ return viewPortPx\n+ },\n+ getOLLonLatFromMapObjectLonLat: function(moLonLat) {\n+ var olLonLat = null;\n+ if (moLonLat != null) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ olLonLat = new OpenLayers.LonLat(lon, lat)\n+ }\n+ return olLonLat\n+ },\n+ getMapObjectLonLatFromOLLonLat: function(olLonLat) {\n+ var moLatLng = null;\n+ if (olLonLat != null) {\n+ moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat)\n+ }\n+ return moLatLng\n+ },\n+ getOLPixelFromMapObjectPixel: function(moPixel) {\n+ var olPixel = null;\n+ if (moPixel != null) {\n+ var x = this.getXFromMapObjectPixel(moPixel);\n+ var y = this.getYFromMapObjectPixel(moPixel);\n+ olPixel = new OpenLayers.Pixel(x, y)\n+ }\n+ return olPixel\n+ },\n+ getMapObjectPixelFromOLPixel: function(olPixel) {\n+ var moPixel = null;\n+ if (olPixel != null) {\n+ moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y)\n+ }\n+ return moPixel\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.EventPane\"\n+});\n+OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ format: \"png\"\n+ },\n+ isBaseLayer: true,\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n+ if (this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ if (options == null || !options.isBaseLayer) {\n+ this.isBaseLayer = false\n+ }\n+ if (this.params.FORMAT == \"jpg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"gif\" : \"png\"\n+ }\n+ }\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, this.url, this.params, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var projWords = this.projection.getCode().split(\":\");\n+ var srid = projWords[projWords.length - 1];\n+ var imageSize = this.getImageSize();\n+ var newParams = {\n+ BBOX: bounds.toBBOX(),\n+ SIZE: imageSize.w + \",\" + imageSize.h,\n+ F: \"image\",\n+ BBOXSR: srid,\n+ IMAGESR: srid\n+ };\n+ if (this.layerDefs) {\n+ var layerDefStrList = [];\n+ var layerID;\n+ for (layerID in this.layerDefs) {\n+ if (this.layerDefs.hasOwnProperty(layerID)) {\n+ if (this.layerDefs[layerID]) {\n+ layerDefStrList.push(layerID);\n+ layerDefStrList.push(\":\");\n+ layerDefStrList.push(this.layerDefs[layerID]);\n+ layerDefStrList.push(\";\")\n+ }\n+ }\n+ }\n+ if (layerDefStrList.length > 0) {\n+ newParams[\"LAYERDEFS\"] = layerDefStrList.join(\"\")\n+ }\n+ }\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString\n+ },\n+ setLayerFilter: function(id, queryDef) {\n+ if (!this.layerDefs) {\n+ this.layerDefs = {}\n+ }\n+ if (queryDef) {\n+ this.layerDefs[id] = queryDef\n } else {\n- return false\n+ delete this.layerDefs[id]\n }\n },\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- this.map.events.unregister(\"moveend\", this, this.update);\n- this.map.removeLayer(this.gratLayer);\n- return true\n+ clearLayerFilter: function(id) {\n+ if (id) {\n+ delete this.layerDefs[id]\n } else {\n- return false\n+ delete this.layerDefs\n }\n },\n- update: function() {\n- var mapBounds = this.map.getExtent();\n- if (!mapBounds) {\n- return\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.ArcGIS93Rest\"\n+});\n+OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ version: \"1.0.0\",\n+ requestEncoding: \"KVP\",\n+ url: null,\n+ layer: null,\n+ matrixSet: null,\n+ style: null,\n+ format: \"image/jpeg\",\n+ tileOrigin: null,\n+ tileFullExtent: null,\n+ formatSuffix: null,\n+ matrixIds: null,\n+ dimensions: null,\n+ params: null,\n+ zoomOffset: 0,\n+ serverResolutions: null,\n+ formatSuffixMap: {\n+ \"image/png\": \"png\",\n+ \"image/png8\": \"png\",\n+ \"image/png24\": \"png\",\n+ \"image/png32\": \"png\",\n+ png: \"png\",\n+ \"image/jpeg\": \"jpg\",\n+ \"image/jpg\": \"jpg\",\n+ jpeg: \"jpg\",\n+ jpg: \"jpg\"\n+ },\n+ matrix: null,\n+ initialize: function(config) {\n+ var required = {\n+ url: true,\n+ layer: true,\n+ style: true,\n+ matrixSet: true\n+ };\n+ for (var prop in required) {\n+ if (!(prop in config)) {\n+ throw new Error(\"Missing property '\" + prop + \"' in layer configuration.\")\n+ }\n }\n- this.gratLayer.destroyFeatures();\n- var llProj = new OpenLayers.Projection(\"EPSG:4326\");\n- var mapProj = this.map.getProjectionObject();\n- var mapRes = this.map.getResolution();\n- if (mapProj.proj && mapProj.proj.projName == \"longlat\") {\n- this.numPoints = 1\n+ config.params = OpenLayers.Util.upperCaseObject(config.params);\n+ var args = [config.name, config.url, config.params, config];\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);\n+ if (!this.formatSuffix) {\n+ this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split(\"/\").pop()\n }\n- var mapCenter = this.map.getCenter();\n- var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);\n- OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);\n- var testSq = this.targetSize * mapRes;\n- testSq *= testSq;\n- var llInterval;\n- for (var i = 0; i < this.intervals.length; ++i) {\n- llInterval = this.intervals[i];\n- var delta = llInterval / 2;\n- var p1 = mapCenterLL.offset({\n- x: -delta,\n- y: -delta\n- });\n- var p2 = mapCenterLL.offset({\n- x: delta,\n- y: delta\n- });\n- OpenLayers.Projection.transform(p1, llProj, mapProj);\n- OpenLayers.Projection.transform(p2, llProj, mapProj);\n- var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);\n- if (distSq <= testSq) {\n- break\n+ if (this.matrixIds) {\n+ var len = this.matrixIds.length;\n+ if (len && typeof this.matrixIds[0] === \"string\") {\n+ var ids = this.matrixIds;\n+ this.matrixIds = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ this.matrixIds[i] = {\n+ identifier: ids[i]\n+ }\n+ }\n }\n }\n- mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval;\n- mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval;\n- var iter = 0;\n- var centerLonPoints = [mapCenterLL.clone()];\n- var newPoint = mapCenterLL.clone();\n- var mapXY;\n- do {\n- newPoint = newPoint.offset({\n- x: 0,\n- y: llInterval\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLonPoints.unshift(newPoint)\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: 0,\n- y: -llInterval\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLonPoints.push(newPoint)\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n- iter = 0;\n- var centerLatPoints = [mapCenterLL.clone()];\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: -llInterval,\n- y: 0\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLatPoints.unshift(newPoint)\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n- newPoint = mapCenterLL.clone();\n- do {\n- newPoint = newPoint.offset({\n- x: llInterval,\n- y: 0\n- });\n- mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);\n- centerLatPoints.push(newPoint)\n- } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3);\n- var lines = [];\n- for (var i = 0; i < centerLatPoints.length; ++i) {\n- var lon = centerLatPoints[i].x;\n- var pointList = [];\n- var labelPoint = null;\n- var latEnd = Math.min(centerLonPoints[0].y, 90);\n- var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);\n- var latDelta = (latEnd - latStart) / this.numPoints;\n- var lat = latStart;\n- for (var j = 0; j <= this.numPoints; ++j) {\n- var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n- gridPoint.transform(llProj, mapProj);\n- pointList.push(gridPoint);\n- lat += latDelta;\n- if (gridPoint.y >= mapBounds.bottom && !labelPoint) {\n- labelPoint = gridPoint\n+ },\n+ setMap: function() {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments)\n+ },\n+ updateMatrixProperties: function() {\n+ this.matrix = this.getMatrix();\n+ if (this.matrix) {\n+ if (this.matrix.topLeftCorner) {\n+ this.tileOrigin = this.matrix.topLeftCorner\n+ }\n+ if (this.matrix.tileWidth && this.matrix.tileHeight) {\n+ this.tileSize = new OpenLayers.Size(this.matrix.tileWidth, this.matrix.tileHeight)\n+ }\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top)\n+ }\n+ if (!this.tileFullExtent) {\n+ this.tileFullExtent = this.maxExtent\n+ }\n+ }\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ if (zoomChanged || !this.matrix) {\n+ this.updateMatrixProperties()\n+ }\n+ return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMTS(this.options)\n+ }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getIdentifier: function() {\n+ return this.getServerZoom()\n+ },\n+ getMatrix: function() {\n+ var matrix;\n+ if (!this.matrixIds || this.matrixIds.length === 0) {\n+ matrix = {\n+ identifier: this.getIdentifier()\n+ }\n+ } else {\n+ if (\"scaleDenominator\" in this.matrixIds[0]) {\n+ var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * this.getServerResolution() / 28e-5;\n+ var diff = Number.POSITIVE_INFINITY;\n+ var delta;\n+ for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {\n+ delta = Math.abs(1 - this.matrixIds[i].scaleDenominator / denom);\n+ if (delta < diff) {\n+ diff = delta;\n+ matrix = this.matrixIds[i]\n+ }\n+ }\n+ } else {\n+ matrix = this.matrixIds[this.getIdentifier()]\n+ }\n+ }\n+ return matrix\n+ },\n+ getTileInfo: function(loc) {\n+ var res = this.getServerResolution();\n+ var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);\n+ var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);\n+ var col = Math.floor(fx);\n+ var row = Math.floor(fy);\n+ return {\n+ col: col,\n+ row: row,\n+ i: Math.floor((fx - col) * this.tileSize.w),\n+ j: Math.floor((fy - row) * this.tileSize.h)\n+ }\n+ },\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var url = \"\";\n+ if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {\n+ var center = bounds.getCenterLonLat();\n+ var info = this.getTileInfo(center);\n+ var matrixId = this.matrix.identifier;\n+ var dimensions = this.dimensions,\n+ params;\n+ if (OpenLayers.Util.isArray(this.url)) {\n+ url = this.selectUrl([this.version, this.style, this.matrixSet, this.matrix.identifier, info.row, info.col].join(\",\"), this.url)\n+ } else {\n+ url = this.url\n+ }\n+ if (this.requestEncoding.toUpperCase() === \"REST\") {\n+ params = this.params;\n+ if (url.indexOf(\"{\") !== -1) {\n+ var template = url.replace(/\\{/g, \"${\");\n+ var context = {\n+ style: this.style,\n+ Style: this.style,\n+ TileMatrixSet: this.matrixSet,\n+ TileMatrix: this.matrix.identifier,\n+ TileRow: info.row,\n+ TileCol: info.col\n+ };\n+ if (dimensions) {\n+ var dimension, i;\n+ for (i = dimensions.length - 1; i >= 0; --i) {\n+ dimension = dimensions[i];\n+ context[dimension] = params[dimension.toUpperCase()]\n+ }\n+ }\n+ url = OpenLayers.String.format(template, context)\n+ } else {\n+ var path = this.version + \"/\" + this.layer + \"/\" + this.style + \"/\";\n+ if (dimensions) {\n+ for (var i = 0; i < dimensions.length; i++) {\n+ if (params[dimensions[i]]) {\n+ path = path + params[dimensions[i]] + \"/\"\n+ }\n+ }\n+ }\n+ path = path + this.matrixSet + \"/\" + this.matrix.identifier + \"/\" + info.row + \"/\" + info.col + \".\" + this.formatSuffix;\n+ if (!url.match(/\\/$/)) {\n+ url = url + \"/\"\n+ }\n+ url = url + path\n+ }\n+ } else if (this.requestEncoding.toUpperCase() === \"KVP\") {\n+ params = {\n+ SERVICE: \"WMTS\",\n+ REQUEST: \"GetTile\",\n+ VERSION: this.version,\n+ LAYER: this.layer,\n+ STYLE: this.style,\n+ TILEMATRIXSET: this.matrixSet,\n+ TILEMATRIX: this.matrix.identifier,\n+ TILEROW: info.row,\n+ TILECOL: info.col,\n+ FORMAT: this.format\n+ };\n+ url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params])\n+ }\n+ }\n+ return url\n+ },\n+ mergeNewParams: function(newParams) {\n+ if (this.requestEncoding.toUpperCase() === \"KVP\") {\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, [OpenLayers.Util.upperCaseObject(newParams)])\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.WMTS\"\n+});\n+OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ dx: null,\n+ dy: null,\n+ ratio: 1.5,\n+ maxFeatures: 250,\n+ rotation: 0,\n+ origin: null,\n+ gridBounds: null,\n+ initialize: function(config) {\n+ config = config || {};\n+ OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config])\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ map.events.register(\"moveend\", this, this.onMoveEnd)\n+ },\n+ removeMap: function(map) {\n+ map.events.unregister(\"moveend\", this, this.onMoveEnd);\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n+ },\n+ setRatio: function(ratio) {\n+ this.ratio = ratio;\n+ this.updateGrid(true)\n+ },\n+ setMaxFeatures: function(maxFeatures) {\n+ this.maxFeatures = maxFeatures;\n+ this.updateGrid(true)\n+ },\n+ setSpacing: function(dx, dy) {\n+ this.dx = dx;\n+ this.dy = dy || dx;\n+ this.updateGrid(true)\n+ },\n+ setOrigin: function(origin) {\n+ this.origin = origin;\n+ this.updateGrid(true)\n+ },\n+ getOrigin: function() {\n+ if (!this.origin) {\n+ this.origin = this.map.getExtent().getCenterLonLat()\n+ }\n+ return this.origin\n+ },\n+ setRotation: function(rotation) {\n+ this.rotation = rotation;\n+ this.updateGrid(true)\n+ },\n+ onMoveEnd: function() {\n+ this.updateGrid()\n+ },\n+ getViewBounds: function() {\n+ var bounds = this.map.getExtent();\n+ if (this.rotation) {\n+ var origin = this.getOrigin();\n+ var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n+ var rect = bounds.toGeometry();\n+ rect.rotate(-this.rotation, rotationOrigin);\n+ bounds = rect.getBounds()\n+ }\n+ return bounds\n+ },\n+ updateGrid: function(force) {\n+ if (force || this.invalidBounds()) {\n+ var viewBounds = this.getViewBounds();\n+ var origin = this.getOrigin();\n+ var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n+ var viewBoundsWidth = viewBounds.getWidth();\n+ var viewBoundsHeight = viewBounds.getHeight();\n+ var aspectRatio = viewBoundsWidth / viewBoundsHeight;\n+ var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);\n+ var maxWidth = maxHeight * aspectRatio;\n+ var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);\n+ var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);\n+ var center = viewBounds.getCenterLonLat();\n+ this.gridBounds = new OpenLayers.Bounds(center.lon - gridWidth / 2, center.lat - gridHeight / 2, center.lon + gridWidth / 2, center.lat + gridHeight / 2);\n+ var rows = Math.floor(gridHeight / this.dy);\n+ var cols = Math.floor(gridWidth / this.dx);\n+ var gridLeft = origin.lon + this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx);\n+ var gridBottom = origin.lat + this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy);\n+ var features = new Array(rows * cols);\n+ var x, y, point;\n+ for (var i = 0; i < cols; ++i) {\n+ x = gridLeft + i * this.dx;\n+ for (var j = 0; j < rows; ++j) {\n+ y = gridBottom + j * this.dy;\n+ point = new OpenLayers.Geometry.Point(x, y);\n+ if (this.rotation) {\n+ point.rotate(this.rotation, rotationOrigin)\n+ }\n+ features[i * rows + j] = new OpenLayers.Feature.Vector(point)\n }\n }\n- if (this.labelled) {\n- var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom);\n- var labelAttrs = {\n- value: lon,\n- label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, \"lon\", this.labelFormat) : \"\",\n- labelAlign: \"cb\",\n- xOffset: 0,\n- yOffset: 2\n- };\n- this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs))\n- }\n- var geom = new OpenLayers.Geometry.LineString(pointList);\n- lines.push(new OpenLayers.Feature.Vector(geom))\n+ this.destroyFeatures(this.features, {\n+ silent: true\n+ });\n+ this.addFeatures(features, {\n+ silent: true\n+ })\n }\n- for (var j = 0; j < centerLonPoints.length; ++j) {\n- lat = centerLonPoints[j].y;\n- if (lat < -90 || lat > 90) {\n- continue\n- }\n- var pointList = [];\n- var lonStart = centerLatPoints[0].x;\n- var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;\n- var lonDelta = (lonEnd - lonStart) / this.numPoints;\n- var lon = lonStart;\n- var labelPoint = null;\n- for (var i = 0; i <= this.numPoints; ++i) {\n- var gridPoint = new OpenLayers.Geometry.Point(lon, lat);\n- gridPoint.transform(llProj, mapProj);\n- pointList.push(gridPoint);\n- lon += lonDelta;\n- if (gridPoint.x < mapBounds.right) {\n- labelPoint = gridPoint\n- }\n+ },\n+ invalidBounds: function() {\n+ return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds())\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.PointGrid\"\n+});\n+OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ dataFrom: null,\n+ styleFrom: null,\n+ addNodes: function(pointFeatures, options) {\n+ if (pointFeatures.length < 2) {\n+ throw new Error(\"At least two point features have to be added to \" + \"create a line from\")\n+ }\n+ var lines = new Array(pointFeatures.length - 1);\n+ var pointFeature, startPoint, endPoint;\n+ for (var i = 0, len = pointFeatures.length; i < len; i++) {\n+ pointFeature = pointFeatures[i];\n+ endPoint = pointFeature.geometry;\n+ if (!endPoint) {\n+ var lonlat = pointFeature.lonlat;\n+ endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)\n+ } else if (endPoint.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n+ throw new TypeError(\"Only features with point geometries are supported.\")\n }\n- if (this.labelled) {\n- var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);\n- var labelAttrs = {\n- value: lat,\n- label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, \"lat\", this.labelFormat) : \"\",\n- labelAlign: \"rb\",\n- xOffset: -2,\n- yOffset: 2\n- };\n- this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs))\n+ if (i > 0) {\n+ var attributes = this.dataFrom != null ? pointFeatures[i + this.dataFrom].data || pointFeatures[i + this.dataFrom].attributes : null;\n+ var style = this.styleFrom != null ? pointFeatures[i + this.styleFrom].style : null;\n+ var line = new OpenLayers.Geometry.LineString([startPoint, endPoint]);\n+ lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, style)\n }\n- var geom = new OpenLayers.Geometry.LineString(pointList);\n- lines.push(new OpenLayers.Feature.Vector(geom))\n+ startPoint = endPoint\n }\n- this.gratLayer.addFeatures(lines)\n+ this.addFeatures(lines, options)\n },\n- CLASS_NAME: \"OpenLayers.Control.Graticule\"\n+ CLASS_NAME: \"OpenLayers.Layer.PointTrack\"\n });\n-OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {\n- callbacks: null,\n- displaySystem: \"metric\",\n- geodesic: false,\n- displaySystemUnits: {\n- geographic: [\"dd\"],\n- english: [\"mi\", \"ft\", \"in\"],\n- metric: [\"km\", \"m\"]\n- },\n- partialDelay: 300,\n- delayedTrigger: null,\n- persist: false,\n- immediate: false,\n- initialize: function(handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- var callbacks = {\n- done: this.measureComplete,\n- point: this.measurePartial\n- };\n- if (this.immediate) {\n- callbacks.modify = this.measureImmediate\n+OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;\n+OpenLayers.Layer.PointTrack.TARGET_NODE = 0;\n+OpenLayers.Layer.PointTrack.dataFrom = {\n+ SOURCE_NODE: -1,\n+ TARGET_NODE: 0\n+};\n+OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {\n+ MIN_ZOOM_LEVEL: 0,\n+ MAX_ZOOM_LEVEL: 21,\n+ RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7],\n+ type: null,\n+ wrapDateLine: true,\n+ sphericalMercator: false,\n+ version: null,\n+ initialize: function(name, options) {\n+ options = options || {};\n+ if (!options.version) {\n+ options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\"\n }\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlerOptions = OpenLayers.Util.extend({\n- persist: this.persist\n- }, this.handlerOptions);\n- this.handler = new handler(this, this.callbacks, this.handlerOptions)\n- },\n- deactivate: function() {\n- this.cancelDelay();\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n- },\n- cancel: function() {\n- this.cancelDelay();\n- this.handler.cancel()\n- },\n- setImmediate: function(immediate) {\n- this.immediate = immediate;\n- if (this.immediate) {\n- this.callbacks.modify = this.measureImmediate\n+ var mixin = OpenLayers.Layer.Google[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (mixin) {\n+ OpenLayers.Util.applyDefaults(options, mixin)\n } else {\n- delete this.callbacks.modify\n+ throw \"Unsupported Google Maps API version: \" + options.version\n }\n- },\n- updateHandler: function(handler, options) {\n- var active = this.active;\n- if (active) {\n- this.deactivate()\n+ OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n+ if (options.maxExtent) {\n+ options.maxExtent = options.maxExtent.clone()\n }\n- this.handler = new handler(this, this.callbacks, options);\n- if (active) {\n- this.activate()\n+ OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]);\n+ OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]);\n+ if (this.sphericalMercator) {\n+ OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n+ this.initMercatorParameters()\n }\n },\n- measureComplete: function(geometry) {\n- this.cancelDelay();\n- this.measure(geometry, \"measure\")\n+ clone: function() {\n+ return new OpenLayers.Layer.Google(this.name, this.getOptions())\n },\n- measurePartial: function(point, geometry) {\n- this.cancelDelay();\n- geometry = geometry.clone();\n- if (this.handler.freehandMode(this.handler.evt)) {\n- this.measure(geometry, \"measurepartial\")\n- } else {\n- this.delayedTrigger = window.setTimeout(OpenLayers.Function.bind(function() {\n- this.delayedTrigger = null;\n- this.measure(geometry, \"measurepartial\")\n- }, this), this.partialDelay)\n- }\n+ setVisibility: function(visible) {\n+ var opacity = this.opacity == null ? 1 : this.opacity;\n+ OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n+ this.setOpacity(opacity)\n },\n- measureImmediate: function(point, feature, drawing) {\n- if (drawing && !this.handler.freehandMode(this.handler.evt)) {\n- this.cancelDelay();\n- this.measure(feature.geometry, \"measurepartial\")\n+ display: function(visible) {\n+ if (!this._dragging) {\n+ this.setGMapVisibility(visible)\n }\n+ OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments)\n },\n- cancelDelay: function() {\n- if (this.delayedTrigger !== null) {\n- window.clearTimeout(this.delayedTrigger);\n- this.delayedTrigger = null\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ this._dragging = dragging;\n+ OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n+ delete this._dragging\n+ },\n+ setOpacity: function(opacity) {\n+ if (opacity !== this.opacity) {\n+ if (this.map != null) {\n+ this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"opacity\"\n+ })\n+ }\n+ this.opacity = opacity\n+ }\n+ if (this.getVisibility()) {\n+ var container = this.getMapContainer();\n+ OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity)\n }\n },\n- measure: function(geometry, eventType) {\n- var stat, order;\n- if (geometry.CLASS_NAME.indexOf(\"LineString\") > -1) {\n- stat = this.getBestLength(geometry);\n- order = 1\n- } else {\n- stat = this.getBestArea(geometry);\n- order = 2\n+ destroy: function() {\n+ if (this.map) {\n+ this.setGMapVisibility(false);\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache && cache.count <= 1) {\n+ this.removeGMapElements()\n+ }\n }\n- this.events.triggerEvent(eventType, {\n- measure: stat[0],\n- units: stat[1],\n- order: order,\n- geometry: geometry\n- })\n+ OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments)\n },\n- getBestArea: function(geometry) {\n- var units = this.displaySystemUnits[this.displaySystem];\n- var unit, area;\n- for (var i = 0, len = units.length; i < len; ++i) {\n- unit = units[i];\n- area = this.getArea(geometry, unit);\n- if (area > 1) {\n- break\n+ removeGMapElements: function() {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject && this.getMapContainer();\n+ if (container && container.parentNode) {\n+ container.parentNode.removeChild(container)\n+ }\n+ var termsOfUse = cache.termsOfUse;\n+ if (termsOfUse && termsOfUse.parentNode) {\n+ termsOfUse.parentNode.removeChild(termsOfUse)\n+ }\n+ var poweredBy = cache.poweredBy;\n+ if (poweredBy && poweredBy.parentNode) {\n+ poweredBy.parentNode.removeChild(poweredBy)\n+ }\n+ if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) {\n+ google.maps.event.clearListeners(this.mapObject, \"tilesloaded\")\n }\n }\n- return [area, unit]\n },\n- getArea: function(geometry, units) {\n- var area, geomUnits;\n- if (this.geodesic) {\n- area = geometry.getGeodesicArea(this.map.getProjectionObject());\n- geomUnits = \"m\"\n- } else {\n- area = geometry.getArea();\n- geomUnits = this.map.getUnits()\n+ removeMap: function(map) {\n+ if (this.visibility && this.mapObject) {\n+ this.setGMapVisibility(false)\n }\n- var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n- if (inPerDisplayUnit) {\n- var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n- area *= Math.pow(inPerMapUnit / inPerDisplayUnit, 2)\n+ var cache = OpenLayers.Layer.Google.cache[map.id];\n+ if (cache) {\n+ if (cache.count <= 1) {\n+ this.removeGMapElements();\n+ delete OpenLayers.Layer.Google.cache[map.id]\n+ } else {\n+ --cache.count\n+ }\n }\n- return area\n+ delete this.termsOfUse;\n+ delete this.poweredBy;\n+ delete this.mapObject;\n+ delete this.dragObject;\n+ OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments)\n },\n- getBestLength: function(geometry) {\n- var units = this.displaySystemUnits[this.displaySystem];\n- var unit, length;\n- for (var i = 0, len = units.length; i < len; ++i) {\n- unit = units[i];\n- length = this.getLength(geometry, unit);\n- if (length > 1) {\n- break\n+ getOLBoundsFromMapObjectBounds: function(moBounds) {\n+ var olBounds = null;\n+ if (moBounds != null) {\n+ var sw = moBounds.getSouthWest();\n+ var ne = moBounds.getNorthEast();\n+ if (this.sphericalMercator) {\n+ sw = this.forwardMercator(sw.lng(), sw.lat());\n+ ne = this.forwardMercator(ne.lng(), ne.lat())\n+ } else {\n+ sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n+ ne = new OpenLayers.LonLat(ne.lng(), ne.lat())\n }\n+ olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat)\n }\n- return [length, unit]\n+ return olBounds\n },\n- getLength: function(geometry, units) {\n- var length, geomUnits;\n- if (this.geodesic) {\n- length = geometry.getGeodesicLength(this.map.getProjectionObject());\n- geomUnits = \"m\"\n- } else {\n- length = geometry.getLength();\n- geomUnits = this.map.getUnits()\n- }\n- var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];\n- if (inPerDisplayUnit) {\n- var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];\n- length *= inPerMapUnit / inPerDisplayUnit\n- }\n- return length\n+ getWarningHTML: function() {\n+ return OpenLayers.i18n(\"googleWarning\")\n },\n- CLASS_NAME: \"OpenLayers.Control.Measure\"\n-});\n-OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {\n- element: null,\n- geodesic: false,\n- initialize: function(element, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.element = OpenLayers.Util.getElement(element)\n+ getMapObjectCenter: function() {\n+ return this.mapObject.getCenter()\n },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (!this.element) {\n- this.element = document.createElement(\"div\");\n- this.div.appendChild(this.element)\n- }\n- this.map.events.register(\"moveend\", this, this.updateScale);\n- this.updateScale();\n- return this.div\n+ getMapObjectZoom: function() {\n+ return this.mapObject.getZoom()\n },\n- updateScale: function() {\n- var scale;\n- if (this.geodesic === true) {\n- var units = this.map.getUnits();\n- if (!units) {\n- return\n- }\n- var inches = OpenLayers.INCHES_PER_UNIT;\n- scale = (this.map.getGeodesicPixelSize().w || 1e-6) * inches[\"km\"] * OpenLayers.DOTS_PER_INCH\n- } else {\n- scale = this.map.getScale()\n- }\n- if (!scale) {\n- return\n- }\n- if (scale >= 9500 && scale <= 95e4) {\n- scale = Math.round(scale / 1e3) + \"K\"\n- } else if (scale >= 95e4) {\n- scale = Math.round(scale / 1e6) + \"M\"\n- } else {\n- scale = Math.round(scale)\n- }\n- this.element.innerHTML = OpenLayers.i18n(\"Scale = 1 : ${scaleDenom}\", {\n- scaleDenom: scale\n- })\n+ getLongitudeFromMapObjectLonLat: function(moLonLat) {\n+ return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng()\n },\n- CLASS_NAME: \"OpenLayers.Control.Scale\"\n+ getLatitudeFromMapObjectLonLat: function(moLonLat) {\n+ var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat();\n+ return lat\n+ },\n+ getXFromMapObjectPixel: function(moPixel) {\n+ return moPixel.x\n+ },\n+ getYFromMapObjectPixel: function(moPixel) {\n+ return moPixel.y\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Google\"\n });\n-OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {\n- clearOnDeactivate: false,\n- layers: null,\n- callbacks: null,\n- selectionSymbolizer: {\n- Polygon: {\n- fillColor: \"#FF0000\",\n- stroke: false\n- },\n- Line: {\n- strokeColor: \"#FF0000\",\n- strokeWidth: 2\n- },\n- Point: {\n- graphicName: \"square\",\n- fillColor: \"#FF0000\",\n- pointRadius: 5\n+OpenLayers.Layer.Google.cache = {};\n+OpenLayers.Layer.Google.v2 = {\n+ termsOfUse: null,\n+ poweredBy: null,\n+ dragObject: null,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = G_NORMAL_MAP\n }\n- },\n- layerOptions: null,\n- sketchStyle: null,\n- wfsCache: {},\n- layerCache: {},\n- initialize: function(handler, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.callbacks = OpenLayers.Util.extend({\n- done: this.select,\n- click: this.select\n- }, this.callbacks);\n- this.handlerOptions = this.handlerOptions || {};\n- this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {\n- displayInLayerSwitcher: false,\n- tileOptions: {\n- maxGetUrlLength: 2048\n+ var mapObject, termsOfUse, poweredBy;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ mapObject = cache.mapObject;\n+ termsOfUse = cache.termsOfUse;\n+ poweredBy = cache.poweredBy;\n+ ++cache.count\n+ } else {\n+ var container = this.map.viewPortDiv;\n+ var div = document.createElement(\"div\");\n+ div.id = this.map.id + \"_GMap2Container\";\n+ div.style.position = \"absolute\";\n+ div.style.width = \"100%\";\n+ div.style.height = \"100%\";\n+ container.appendChild(div);\n+ try {\n+ mapObject = new GMap2(div);\n+ termsOfUse = div.lastChild;\n+ container.appendChild(termsOfUse);\n+ termsOfUse.style.zIndex = \"1100\";\n+ termsOfUse.style.right = \"\";\n+ termsOfUse.style.bottom = \"\";\n+ termsOfUse.className = \"olLayerGoogleCopyright\";\n+ poweredBy = div.lastChild;\n+ container.appendChild(poweredBy);\n+ poweredBy.style.zIndex = \"1100\";\n+ poweredBy.style.right = \"\";\n+ poweredBy.style.bottom = \"\";\n+ poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\"\n+ } catch (e) {\n+ throw e\n+ }\n+ OpenLayers.Layer.Google.cache[this.map.id] = {\n+ mapObject: mapObject,\n+ termsOfUse: termsOfUse,\n+ poweredBy: poweredBy,\n+ count: 1\n }\n- });\n- if (this.sketchStyle) {\n- this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {\n- styleMap: new OpenLayers.StyleMap({\n- default: this.sketchStyle\n- })\n- })\n }\n- this.handler = new handler(this, this.callbacks, this.handlerOptions)\n- },\n- destroy: function() {\n- for (var key in this.layerCache) {\n- delete this.layerCache[key]\n+ this.mapObject = mapObject;\n+ this.termsOfUse = termsOfUse;\n+ this.poweredBy = poweredBy;\n+ if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) {\n+ this.mapObject.addMapType(this.type)\n }\n- for (var key in this.wfsCache) {\n- delete this.wfsCache[key]\n+ if (typeof mapObject.getDragObject == \"function\") {\n+ this.dragObject = mapObject.getDragObject()\n+ } else {\n+ this.dragPanMapObject = null\n+ }\n+ if (this.isBaseLayer === false) {\n+ this.setGMapVisibility(this.div.style.display !== \"none\")\n }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n- },\n- coupleLayerVisiblity: function(evt) {\n- this.setVisibility(evt.object.getVisibility())\n },\n- createSelectionLayer: function(source) {\n- var selectionLayer;\n- if (!this.layerCache[source.id]) {\n- selectionLayer = new OpenLayers.Layer.WMS(source.name, source.url, source.params, OpenLayers.Util.applyDefaults(this.layerOptions, source.getOptions()));\n- this.layerCache[source.id] = selectionLayer;\n- if (this.layerOptions.displayInLayerSwitcher === false) {\n- source.events.on({\n- visibilitychanged: this.coupleLayerVisiblity,\n- scope: selectionLayer\n+ onMapResize: function() {\n+ if (this.visibility && this.mapObject.isLoaded()) {\n+ this.mapObject.checkResize()\n+ } else {\n+ if (!this._resized) {\n+ var layer = this;\n+ var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n+ GEvent.removeListener(handle);\n+ delete layer._resized;\n+ layer.mapObject.checkResize();\n+ layer.moveTo(layer.map.getCenter(), layer.map.getZoom())\n })\n }\n- this.map.addLayer(selectionLayer)\n- } else {\n- selectionLayer = this.layerCache[source.id]\n+ this._resized = true\n }\n- return selectionLayer\n },\n- createSLD: function(layer, filters, geometryAttributes) {\n- var sld = {\n- version: \"1.0.0\",\n- namedLayers: {}\n- };\n- var layerNames = [layer.params.LAYERS].join(\",\").split(\",\");\n- for (var i = 0, len = layerNames.length; i < len; i++) {\n- var name = layerNames[i];\n- sld.namedLayers[name] = {\n- name: name,\n- userStyles: []\n- };\n- var symbolizer = this.selectionSymbolizer;\n- var geometryAttribute = geometryAttributes[i];\n- if (geometryAttribute.type.indexOf(\"Polygon\") >= 0) {\n- symbolizer = {\n- Polygon: this.selectionSymbolizer[\"Polygon\"]\n- }\n- } else if (geometryAttribute.type.indexOf(\"LineString\") >= 0) {\n- symbolizer = {\n- Line: this.selectionSymbolizer[\"Line\"]\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ var container = this.mapObject.getContainer();\n+ if (visible === true) {\n+ this.mapObject.setMapType(this.type);\n+ container.style.display = \"\";\n+ this.termsOfUse.style.left = \"\";\n+ this.termsOfUse.style.display = \"\";\n+ this.poweredBy.style.display = \"\";\n+ cache.displayed = this.id\n+ } else {\n+ if (cache.displayed === this.id) {\n+ delete cache.displayed\n }\n- } else if (geometryAttribute.type.indexOf(\"Point\") >= 0) {\n- symbolizer = {\n- Point: this.selectionSymbolizer[\"Point\"]\n+ if (!cache.displayed) {\n+ container.style.display = \"none\";\n+ this.termsOfUse.style.display = \"none\";\n+ this.termsOfUse.style.left = \"-9999px\";\n+ this.poweredBy.style.display = \"none\"\n }\n }\n- var filter = filters[i];\n- sld.namedLayers[name].userStyles.push({\n- name: \"default\",\n- rules: [new OpenLayers.Rule({\n- symbolizer: symbolizer,\n- filter: filter,\n- maxScaleDenominator: layer.options.minScale\n- })]\n- })\n }\n- return new OpenLayers.Format.SLD({\n- srsName: this.map.getProjection()\n- }).write(sld)\n },\n- parseDescribeLayer: function(request) {\n- var format = new OpenLayers.Format.WMSDescribeLayer;\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ getMapContainer: function() {\n+ return this.mapObject.getContainer()\n+ },\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon))\n }\n- var describeLayer = format.read(doc);\n- var typeNames = [];\n- var url = null;\n- for (var i = 0, len = describeLayer.length; i < len; i++) {\n- if (describeLayer[i].owsType == \"WFS\") {\n- typeNames.push(describeLayer[i].typeName);\n- url = describeLayer[i].owsURL\n- }\n+ return moBounds\n+ },\n+ setMapObjectCenter: function(center, zoom) {\n+ this.mapObject.setCenter(center, zoom)\n+ },\n+ dragPanMapObject: function(dX, dY) {\n+ this.dragObject.moveBy(new GSize(-dX, dY))\n+ },\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ return this.mapObject.fromContainerPixelToLatLng(moPixel)\n+ },\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ return this.mapObject.fromLatLngToContainerPixel(moLonLat)\n+ },\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds)\n+ },\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new GLatLng(lonlat.lat, lonlat.lon)\n+ } else {\n+ gLatLng = new GLatLng(lat, lon)\n }\n- var options = {\n- url: url,\n- params: {\n- SERVICE: \"WFS\",\n- TYPENAME: typeNames.toString(),\n- REQUEST: \"DescribeFeatureType\",\n- VERSION: \"1.0.0\"\n- },\n- callback: function(request) {\n- var format = new OpenLayers.Format.WFSDescribeFeatureType;\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n- }\n- var describeFeatureType = format.read(doc);\n- this.control.wfsCache[this.layer.id] = describeFeatureType;\n- this.control._queue && this.control.applySelection()\n- },\n- scope: this\n- };\n- OpenLayers.Request.GET(options)\n+ return gLatLng\n },\n- getGeometryAttributes: function(layer) {\n- var result = [];\n- var cache = this.wfsCache[layer.id];\n- for (var i = 0, len = cache.featureTypes.length; i < len; i++) {\n- var typeName = cache.featureTypes[i];\n- var properties = typeName.properties;\n- for (var j = 0, lenj = properties.length; j < lenj; j++) {\n- var property = properties[j];\n- var type = property.type;\n- if (type.indexOf(\"LineString\") >= 0 || type.indexOf(\"GeometryAssociationType\") >= 0 || type.indexOf(\"GeometryPropertyType\") >= 0 || type.indexOf(\"Point\") >= 0 || type.indexOf(\"Polygon\") >= 0) {\n- result.push(property)\n- }\n- }\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new GPoint(x, y)\n+ }\n+};\n+OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {\n+ isBaseLayer: true,\n+ url: null,\n+ extent: null,\n+ size: null,\n+ tile: null,\n+ aspectRatio: null,\n+ initialize: function(name, url, extent, size, options) {\n+ this.url = url;\n+ this.extent = extent;\n+ this.maxExtent = extent;\n+ this.size = size;\n+ OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n+ this.aspectRatio = this.extent.getHeight() / this.size.h / (this.extent.getWidth() / this.size.w)\n+ },\n+ destroy: function() {\n+ if (this.tile) {\n+ this.removeTileMonitoringHooks(this.tile);\n+ this.tile.destroy();\n+ this.tile = null\n }\n- return result\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n },\n- activate: function() {\n- var activated = OpenLayers.Control.prototype.activate.call(this);\n- if (activated) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer && !this.wfsCache[layer.id]) {\n- var options = {\n- url: layer.url,\n- params: {\n- SERVICE: \"WMS\",\n- VERSION: layer.params.VERSION,\n- LAYERS: layer.params.LAYERS,\n- REQUEST: \"DescribeLayer\"\n- },\n- callback: this.parseDescribeLayer,\n- scope: {\n- layer: layer,\n- control: this\n- }\n- };\n- OpenLayers.Request.GET(options)\n- }\n- }\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Image(this.name, this.url, this.extent, this.size, this.getOptions())\n }\n- return activated\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- deactivate: function() {\n- var deactivated = OpenLayers.Control.prototype.deactivate.call(this);\n- if (deactivated) {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- if (layer && this.clearOnDeactivate === true) {\n- var layerCache = this.layerCache;\n- var selectionLayer = layerCache[layer.id];\n- if (selectionLayer) {\n- layer.events.un({\n- visibilitychanged: this.coupleLayerVisiblity,\n- scope: selectionLayer\n- });\n- selectionLayer.destroy();\n- delete layerCache[layer.id]\n- }\n- }\n+ setMap: function(map) {\n+ if (this.options.maxResolution == null) {\n+ this.options.maxResolution = this.aspectRatio * this.extent.getWidth() / this.size.w\n+ }\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments)\n+ },\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ var firstRendering = this.tile == null;\n+ if (zoomChanged || firstRendering) {\n+ this.setTileSize();\n+ var ulPx = this.map.getLayerPxFromLonLat({\n+ lon: this.extent.left,\n+ lat: this.extent.top\n+ });\n+ if (firstRendering) {\n+ this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize);\n+ this.addTileMonitoringHooks(this.tile)\n+ } else {\n+ this.tile.size = this.tileSize.clone();\n+ this.tile.position = ulPx.clone()\n }\n+ this.tile.draw()\n }\n- return deactivated\n },\n- setLayers: function(layers) {\n- if (this.active) {\n- this.deactivate();\n- this.layers = layers;\n- this.activate()\n+ setTileSize: function() {\n+ var tileWidth = this.extent.getWidth() / this.map.getResolution();\n+ var tileHeight = this.extent.getHeight() / this.map.getResolution();\n+ this.tileSize = new OpenLayers.Size(tileWidth, tileHeight)\n+ },\n+ addTileMonitoringHooks: function(tile) {\n+ tile.onLoadStart = function() {\n+ this.events.triggerEvent(\"loadstart\")\n+ };\n+ tile.events.register(\"loadstart\", this, tile.onLoadStart);\n+ tile.onLoadEnd = function() {\n+ this.events.triggerEvent(\"loadend\")\n+ };\n+ tile.events.register(\"loadend\", this, tile.onLoadEnd);\n+ tile.events.register(\"unload\", this, tile.onLoadEnd)\n+ },\n+ removeTileMonitoringHooks: function(tile) {\n+ tile.unload();\n+ tile.events.un({\n+ loadstart: tile.onLoadStart,\n+ loadend: tile.onLoadEnd,\n+ unload: tile.onLoadEnd,\n+ scope: this\n+ })\n+ },\n+ setUrl: function(newUrl) {\n+ this.url = newUrl;\n+ this.tile.draw()\n+ },\n+ getURL: function(bounds) {\n+ return this.url\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Image\"\n+});\n+OpenLayers.Layer.Google.v3 = {\n+ DEFAULTS: {\n+ sphericalMercator: true,\n+ projection: \"EPSG:900913\"\n+ },\n+ animationEnabled: true,\n+ loadMapObject: function() {\n+ if (!this.type) {\n+ this.type = google.maps.MapTypeId.ROADMAP\n+ }\n+ var mapObject;\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ if (cache) {\n+ mapObject = cache.mapObject;\n+ ++cache.count\n } else {\n- this.layers = layers\n+ var center = this.map.getCenter();\n+ var container = document.createElement(\"div\");\n+ container.className = \"olForeignContainer\";\n+ container.style.width = \"100%\";\n+ container.style.height = \"100%\";\n+ mapObject = new google.maps.Map(container, {\n+ center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n+ zoom: this.map.getZoom() || 0,\n+ mapTypeId: this.type,\n+ disableDefaultUI: true,\n+ keyboardShortcuts: false,\n+ draggable: false,\n+ disableDoubleClickZoom: true,\n+ scrollwheel: false,\n+ streetViewControl: false\n+ });\n+ var googleControl = document.createElement(\"div\");\n+ googleControl.style.width = \"100%\";\n+ googleControl.style.height = \"100%\";\n+ mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n+ cache = {\n+ googleControl: googleControl,\n+ mapObject: mapObject,\n+ count: 1\n+ };\n+ OpenLayers.Layer.Google.cache[this.map.id] = cache\n }\n+ this.mapObject = mapObject;\n+ this.setGMapVisibility(this.visibility)\n },\n- createFilter: function(geometryAttribute, geometry) {\n- var filter = null;\n- if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {\n- if (this.handler.irregular === true) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- property: geometryAttribute.name,\n- value: geometry.getBounds()\n- })\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- })\n- }\n- } else if (this.handler instanceof OpenLayers.Handler.Polygon) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- })\n- } else if (this.handler instanceof OpenLayers.Handler.Path) {\n- if (geometryAttribute.type.indexOf(\"Point\") >= 0) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.DWITHIN,\n- property: geometryAttribute.name,\n- distance: this.map.getExtent().getWidth() * .01,\n- distanceUnits: this.map.getUnits(),\n- value: geometry\n- })\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- })\n- }\n- } else if (this.handler instanceof OpenLayers.Handler.Click) {\n- if (geometryAttribute.type.indexOf(\"Polygon\") >= 0) {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: geometryAttribute.name,\n- value: geometry\n- })\n- } else {\n- filter = new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.DWITHIN,\n- property: geometryAttribute.name,\n- distance: this.map.getExtent().getWidth() * .01,\n- distanceUnits: this.map.getUnits(),\n- value: geometry\n- })\n- }\n+ onMapResize: function() {\n+ if (this.visibility) {\n+ google.maps.event.trigger(this.mapObject, \"resize\")\n }\n- return filter\n },\n- select: function(geometry) {\n- this._queue = function() {\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- var layer = this.layers[i];\n- var geometryAttributes = this.getGeometryAttributes(layer);\n- var filters = [];\n- for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) {\n- var geometryAttribute = geometryAttributes[j];\n- if (geometryAttribute !== null) {\n- if (!(geometry instanceof OpenLayers.Geometry)) {\n- var point = this.map.getLonLatFromPixel(geometry.xy);\n- geometry = new OpenLayers.Geometry.Point(point.lon, point.lat)\n- }\n- var filter = this.createFilter(geometryAttribute, geometry);\n- if (filter !== null) {\n- filters.push(filter)\n- }\n+ setGMapVisibility: function(visible) {\n+ var cache = OpenLayers.Layer.Google.cache[this.map.id];\n+ var map = this.map;\n+ if (cache) {\n+ var type = this.type;\n+ var layers = map.layers;\n+ var layer;\n+ for (var i = layers.length - 1; i >= 0; --i) {\n+ layer = layers[i];\n+ if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) {\n+ type = layer.type;\n+ visible = true;\n+ break\n+ }\n+ }\n+ var container = this.mapObject.getDiv();\n+ if (visible === true) {\n+ if (container.parentNode !== map.div) {\n+ if (!cache.rendered) {\n+ var me = this;\n+ google.maps.event.addListenerOnce(this.mapObject, \"tilesloaded\", function() {\n+ cache.rendered = true;\n+ me.setGMapVisibility(me.getVisibility());\n+ me.moveTo(me.map.getCenter())\n+ })\n+ } else {\n+ map.div.appendChild(container);\n+ cache.googleControl.appendChild(map.viewPortDiv);\n+ google.maps.event.trigger(this.mapObject, \"resize\")\n }\n }\n- var selectionLayer = this.createSelectionLayer(layer);\n- this.events.triggerEvent(\"selected\", {\n- layer: layer,\n- filters: filters\n- });\n- var sld = this.createSLD(layer, filters, geometryAttributes);\n- selectionLayer.mergeNewParams({\n- SLD_BODY: sld\n- });\n- delete this._queue\n+ this.mapObject.setMapTypeId(type)\n+ } else if (cache.googleControl.hasChildNodes()) {\n+ map.div.appendChild(map.viewPortDiv);\n+ map.div.removeChild(container)\n }\n- };\n- this.applySelection()\n+ }\n },\n- applySelection: function() {\n- var canApply = true;\n- for (var i = 0, len = this.layers.length; i < len; i++) {\n- if (!this.wfsCache[this.layers[i].id]) {\n- canApply = false;\n- break\n- }\n+ getMapContainer: function() {\n+ return this.mapObject.getDiv()\n+ },\n+ getMapObjectBoundsFromOLBounds: function(olBounds) {\n+ var moBounds = null;\n+ if (olBounds != null) {\n+ var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n+ var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);\n+ moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon))\n }\n- canApply && this._queue.call(this)\n+ return moBounds\n },\n- CLASS_NAME: \"OpenLayers.Control.SLDSelect\"\n-});\n-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n- hitDetection: true,\n- hitOverflow: 0,\n- canvas: null,\n- features: null,\n- pendingRedraw: false,\n- cachedSymbolBounds: {},\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n- this.root = document.createElement(\"canvas\");\n- this.container.appendChild(this.root);\n- this.canvas = this.root.getContext(\"2d\");\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitCanvas = document.createElement(\"canvas\");\n- this.hitContext = this.hitCanvas.getContext(\"2d\")\n+ getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n+ var size = this.map.getSize();\n+ var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n+ var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n+ var res = this.map.getResolution();\n+ var delta_x = moPixel.x - size.w / 2;\n+ var delta_y = moPixel.y - size.h / 2;\n+ var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res);\n+ if (this.wrapDateLine) {\n+ lonlat = lonlat.wrapDateLine(this.maxExtent)\n }\n+ return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat)\n },\n- setExtent: function() {\n- OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- return false\n+ getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n+ var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n+ var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n+ var res = this.map.getResolution();\n+ var extent = this.map.getExtent();\n+ return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat))\n },\n- eraseGeometry: function(geometry, featureId) {\n- this.eraseFeatures(this.features[featureId][0])\n+ setMapObjectCenter: function(center, zoom) {\n+ if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n+ var mapContainer = this.getMapContainer();\n+ google.maps.event.addListenerOnce(this.mapObject, \"idle\", function() {\n+ mapContainer.style.visibility = \"\"\n+ });\n+ mapContainer.style.visibility = \"hidden\"\n+ }\n+ this.mapObject.setOptions({\n+ center: center,\n+ zoom: zoom\n+ })\n },\n- supported: function() {\n- return OpenLayers.CANVAS_SUPPORTED\n+ getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n+ return this.mapObject.getBoundsZoomLevel(moBounds)\n },\n- setSize: function(size) {\n- this.size = size.clone();\n- var root = this.root;\n- root.style.width = size.w + \"px\";\n- root.style.height = size.h + \"px\";\n- root.width = size.w;\n- root.height = size.h;\n- this.resolution = null;\n- if (this.hitDetection) {\n- var hitCanvas = this.hitCanvas;\n- hitCanvas.style.width = size.w + \"px\";\n- hitCanvas.style.height = size.h + \"px\";\n- hitCanvas.width = size.w;\n- hitCanvas.height = size.h\n+ getMapObjectLonLatFromLonLat: function(lon, lat) {\n+ var gLatLng;\n+ if (this.sphericalMercator) {\n+ var lonlat = this.inverseMercator(lon, lat);\n+ gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon)\n+ } else {\n+ gLatLng = new google.maps.LatLng(lat, lon)\n }\n+ return gLatLng\n },\n- drawFeature: function(feature, style) {\n- var rendered;\n- if (feature.geometry) {\n- style = this.applyDefaultSymbolizer(style || feature.style);\n- var bounds = feature.geometry.getBounds();\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent()\n- }\n- var intersects = bounds && bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n+ getMapObjectPixelFromXY: function(x, y) {\n+ return new google.maps.Point(x, y)\n+ }\n+};\n+OpenLayers.Protocol.CSW = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.CSW.DEFAULTS);\n+ var cls = OpenLayers.Protocol.CSW[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSW version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Protocol.CSW.DEFAULTS = {\n+ version: \"2.0.2\"\n+};\n+OpenLayers.Protocol.SOS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.SOS.DEFAULTS);\n+ var cls = OpenLayers.Protocol.SOS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported SOS version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Protocol.SOS.DEFAULTS = {\n+ version: \"1.0.0\"\n+};\n+OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n+ url: null,\n+ headers: null,\n+ params: null,\n+ callback: null,\n+ scope: null,\n+ readWithPOST: false,\n+ updateWithPOST: false,\n+ deleteWithPOST: false,\n+ wildcarded: false,\n+ srsInBBOX: false,\n+ initialize: function(options) {\n+ options = options || {};\n+ this.params = {};\n+ this.headers = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ wildcarded: this.wildcarded,\n+ srsInBBOX: this.srsInBBOX\n });\n- rendered = style.display !== \"none\" && !!bounds && intersects;\n- if (rendered) {\n- this.features[feature.id] = [feature, style]\n- } else {\n- delete this.features[feature.id]\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params)\n }\n- this.pendingRedraw = true\n- }\n- if (this.pendingRedraw && !this.locked) {\n- this.redraw();\n- this.pendingRedraw = false\n }\n- return rendered\n },\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n- for (var i = 0; i < geometry.components.length; i++) {\n- this.drawGeometry(geometry.components[i], style, featureId)\n- }\n- return\n+ destroy: function() {\n+ this.params = null;\n+ this.headers = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n+ },\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = options || {};\n+ options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(options.filter, options.params)\n }\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- this.drawPoint(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- this.drawLineString(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- this.drawLinearRing(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- this.drawPolygon(geometry, style, featureId);\n- break;\n- default:\n- break\n+ var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST;\n+ var resp = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ if (readWithPOST) {\n+ var headers = options.headers || {};\n+ headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ data: OpenLayers.Util.getParameterString(options.params),\n+ headers: headers\n+ })\n+ } else {\n+ resp.priv = OpenLayers.Request.GET({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, resp, options),\n+ params: options.params,\n+ headers: options.headers\n+ })\n }\n+ return resp\n },\n- drawExternalGraphic: function(geometry, style, featureId) {\n- var img = new Image;\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- img.title = title\n+ handleRead: function(resp, options) {\n+ this.handleResponse(resp, options)\n+ },\n+ create: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: features,\n+ requestType: \"create\"\n+ });\n+ resp.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleCreate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(features)\n+ });\n+ return resp\n+ },\n+ handleCreate: function(resp, options) {\n+ this.handleResponse(resp, options)\n+ },\n+ update: function(feature, options) {\n+ options = options || {};\n+ var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"update\"\n+ });\n+ var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n+ resp.priv = OpenLayers.Request[method]({\n+ url: url,\n+ callback: this.createCallback(this.handleUpdate, resp, options),\n+ headers: options.headers,\n+ data: this.format.write(feature)\n+ });\n+ return resp\n+ },\n+ handleUpdate: function(resp, options) {\n+ this.handleResponse(resp, options)\n+ },\n+ delete: function(feature, options) {\n+ options = options || {};\n+ var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = new OpenLayers.Protocol.Response({\n+ reqFeatures: feature,\n+ requestType: \"delete\"\n+ });\n+ var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n+ var requestOptions = {\n+ url: url,\n+ callback: this.createCallback(this.handleDelete, resp, options),\n+ headers: options.headers\n+ };\n+ if (this.deleteWithPOST) {\n+ requestOptions.data = this.format.write(feature)\n }\n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n- var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n- var opacity = style.graphicOpacity || style.fillOpacity;\n- var onLoad = function() {\n- if (!this.features[featureId]) {\n- return\n- }\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var x = p0 + xOffset | 0;\n- var y = p1 + yOffset | 0;\n- var canvas = this.canvas;\n- canvas.globalAlpha = opacity;\n- var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || (OpenLayers.Renderer.Canvas.drawImageScaleFactor = /android 2.1/.test(navigator.userAgent.toLowerCase()) ? 320 / window.screen.width : 1);\n- canvas.drawImage(img, x * factor, y * factor, width * factor, height * factor);\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId);\n- this.hitContext.fillRect(x, y, width, height)\n+ resp.priv = OpenLayers.Request[method](requestOptions);\n+ return resp\n+ },\n+ handleDelete: function(resp, options) {\n+ this.handleResponse(resp, options)\n+ },\n+ handleResponse: function(resp, options) {\n+ var request = resp.priv;\n+ if (options.callback) {\n+ if (request.status >= 200 && request.status < 300) {\n+ if (resp.requestType != \"delete\") {\n+ resp.features = this.parseFeatures(request)\n }\n+ resp.code = OpenLayers.Protocol.Response.SUCCESS\n+ } else {\n+ resp.code = OpenLayers.Protocol.Response.FAILURE\n }\n- };\n- img.onload = OpenLayers.Function.bind(onLoad, this);\n- img.src = style.externalGraphic\n+ options.callback.call(options.scope, resp)\n+ }\n },\n- drawNamedSymbol: function(geometry, style, featureId) {\n- var x, y, cx, cy, i, symbolBounds, scaling, angle;\n- var unscaledStrokeWidth;\n- var deg2rad = Math.PI / 180;\n- var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n- if (!symbol) {\n- throw new Error(style.graphicName + \" is not a valid symbol name\")\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- if (!symbol.length || symbol.length < 2) return;\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (isNaN(p0) || isNaN(p1)) return;\n- this.canvas.lineCap = \"round\";\n- this.canvas.lineJoin = \"round\";\n- if (this.hitDetection) {\n- this.hitContext.lineCap = \"round\";\n- this.hitContext.lineJoin = \"round\"\n+ if (!doc || doc.length <= 0) {\n+ return null\n }\n- if (style.graphicName in this.cachedSymbolBounds) {\n- symbolBounds = this.cachedSymbolBounds[style.graphicName]\n- } else {\n- symbolBounds = new OpenLayers.Bounds;\n- for (i = 0; i < symbol.length; i += 2) {\n- symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]))\n+ return this.format.read(doc)\n+ },\n+ commit: function(features, options) {\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ var resp = [],\n+ nResponses = 0;\n+ var types = {};\n+ types[OpenLayers.State.INSERT] = [];\n+ types[OpenLayers.State.UPDATE] = [];\n+ types[OpenLayers.State.DELETE] = [];\n+ var feature, list, requestFeatures = [];\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ list = types[feature.state];\n+ if (list) {\n+ list.push(feature);\n+ requestFeatures.push(feature)\n }\n- this.cachedSymbolBounds[style.graphicName] = symbolBounds\n- }\n- this.canvas.save();\n- if (this.hitDetection) {\n- this.hitContext.save()\n }\n- this.canvas.translate(p0, p1);\n- if (this.hitDetection) {\n- this.hitContext.translate(p0, p1)\n+ var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length;\n+ var success = true;\n+ var finalResponse = new OpenLayers.Protocol.Response({\n+ reqFeatures: requestFeatures\n+ });\n+\n+ function insertCallback(response) {\n+ var len = response.features ? response.features.length : 0;\n+ var fids = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ fids[i] = response.features[i].fid\n+ }\n+ finalResponse.insertIds = fids;\n+ callback.apply(this, [response])\n }\n- angle = deg2rad * style.rotation;\n- if (!isNaN(angle)) {\n- this.canvas.rotate(angle);\n- if (this.hitDetection) {\n- this.hitContext.rotate(angle)\n+\n+ function callback(response) {\n+ this.callUserCallback(response, options);\n+ success = success && response.success();\n+ nResponses++;\n+ if (nResponses >= nRequests) {\n+ if (options.callback) {\n+ finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE;\n+ options.callback.apply(options.scope, [finalResponse])\n+ }\n }\n }\n- scaling = 2 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n- this.canvas.scale(scaling, scaling);\n- if (this.hitDetection) {\n- this.hitContext.scale(scaling, scaling)\n+ var queue = types[OpenLayers.State.INSERT];\n+ if (queue.length > 0) {\n+ resp.push(this.create(queue, OpenLayers.Util.applyDefaults({\n+ callback: insertCallback,\n+ scope: this\n+ }, options.create)))\n }\n- cx = symbolBounds.getCenterLonLat().lon;\n- cy = symbolBounds.getCenterLonLat().lat;\n- this.canvas.translate(-cx, -cy);\n- if (this.hitDetection) {\n- this.hitContext.translate(-cx, -cy)\n+ queue = types[OpenLayers.State.UPDATE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options.update)))\n }\n- unscaledStrokeWidth = style.strokeWidth;\n- style.strokeWidth = unscaledStrokeWidth / scaling;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y)\n- }\n- this.canvas.closePath();\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.hitContext.lineTo(x, y)\n- }\n- this.hitContext.closePath();\n- this.hitContext.fill()\n- }\n+ queue = types[OpenLayers.State.DELETE];\n+ for (var i = queue.length - 1; i >= 0; --i) {\n+ resp.push(this[\"delete\"](queue[i], OpenLayers.Util.applyDefaults({\n+ callback: callback,\n+ scope: this\n+ }, options[\"delete\"])))\n }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y)\n- }\n- this.canvas.closePath();\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.hitContext.moveTo(x, y);\n- this.hitContext.lineTo(x, y)\n- }\n- this.hitContext.closePath();\n- this.hitContext.stroke()\n- }\n+ return resp\n+ },\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort()\n }\n- style.strokeWidth = unscaledStrokeWidth;\n- this.canvas.restore();\n- if (this.hitDetection) {\n- this.hitContext.restore()\n+ },\n+ callUserCallback: function(resp, options) {\n+ var opt = options[resp.requestType];\n+ if (opt && opt.callback) {\n+ opt.callback.call(opt.scope, resp)\n }\n- this.setCanvasStyle(\"reset\")\n },\n- setCanvasStyle: function(type, style) {\n- if (type === \"fill\") {\n- this.canvas.globalAlpha = style[\"fillOpacity\"];\n- this.canvas.fillStyle = style[\"fillColor\"]\n- } else if (type === \"stroke\") {\n- this.canvas.globalAlpha = style[\"strokeOpacity\"];\n- this.canvas.strokeStyle = style[\"strokeColor\"];\n- this.canvas.lineWidth = style[\"strokeWidth\"]\n- } else {\n- this.canvas.globalAlpha = 0;\n- this.canvas.lineWidth = 1\n+ CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n+});\n+OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {\n+ url: null,\n+ params: null,\n+ callback: null,\n+ callbackTemplate: \"OpenLayers.Protocol.Script.registry.${id}\",\n+ callbackKey: \"callback\",\n+ callbackPrefix: \"\",\n+ scope: null,\n+ format: null,\n+ pendingRequests: null,\n+ srsInBBOX: false,\n+ initialize: function(options) {\n+ options = options || {};\n+ this.params = {};\n+ this.pendingRequests = {};\n+ OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n+ if (!this.format) {\n+ this.format = new OpenLayers.Format.GeoJSON\n+ }\n+ if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n+ var format = new OpenLayers.Format.QueryStringFilter({\n+ srsInBBOX: this.srsInBBOX\n+ });\n+ this.filterToParams = function(filter, params) {\n+ return format.write(filter, params)\n+ }\n }\n },\n- featureIdToHex: function(featureId) {\n- var id = Number(featureId.split(\"_\").pop()) + 1;\n- if (id >= 16777216) {\n- this.hitOverflow = id - 16777215;\n- id = id % 16777216 + 1\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = OpenLayers.Util.applyDefaults(options, this.options);\n+ options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n+ if (options.filter && this.filterToParams) {\n+ options.params = this.filterToParams(options.filter, options.params)\n }\n- var hex = \"000000\" + id.toString(16);\n- var len = hex.length;\n- hex = \"#\" + hex.substring(len - 6, len);\n- return hex\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var request = this.createRequest(options.url, options.params, OpenLayers.Function.bind(function(data) {\n+ response.data = data;\n+ this.handleRead(response, options)\n+ }, this));\n+ response.priv = request;\n+ return response\n },\n- setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n- var hex = this.featureIdToHex(featureId);\n- if (type == \"fill\") {\n- this.hitContext.globalAlpha = 1;\n- this.hitContext.fillStyle = hex\n- } else if (type == \"stroke\") {\n- this.hitContext.globalAlpha = 1;\n- this.hitContext.strokeStyle = hex;\n- if (typeof strokeScaling === \"undefined\") {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2\n+ createRequest: function(url, params, callback) {\n+ var id = OpenLayers.Protocol.Script.register(callback);\n+ var name = OpenLayers.String.format(this.callbackTemplate, {\n+ id: id\n+ });\n+ params = OpenLayers.Util.extend({}, params);\n+ params[this.callbackKey] = this.callbackPrefix + name;\n+ url = OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(params));\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = \"OpenLayers_Protocol_Script_\" + id;\n+ this.pendingRequests[script.id] = script;\n+ var head = document.getElementsByTagName(\"head\")[0];\n+ head.appendChild(script);\n+ return script\n+ },\n+ destroyRequest: function(script) {\n+ OpenLayers.Protocol.Script.unregister(script.id.split(\"_\").pop());\n+ delete this.pendingRequests[script.id];\n+ if (script.parentNode) {\n+ script.parentNode.removeChild(script)\n+ }\n+ },\n+ handleRead: function(response, options) {\n+ this.handleResponse(response, options)\n+ },\n+ handleResponse: function(response, options) {\n+ if (options.callback) {\n+ if (response.data) {\n+ response.features = this.parseFeatures(response.data);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS\n } else {\n- if (!isNaN(strokeScaling)) {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2 / strokeScaling\n- }\n+ response.code = OpenLayers.Protocol.Response.FAILURE\n }\n+ this.destroyRequest(response.priv);\n+ options.callback.call(options.scope, response)\n+ }\n+ },\n+ parseFeatures: function(data) {\n+ return this.format.read(data)\n+ },\n+ abort: function(response) {\n+ if (response) {\n+ this.destroyRequest(response.priv)\n } else {\n- this.hitContext.globalAlpha = 0;\n- this.hitContext.lineWidth = 1\n+ for (var key in this.pendingRequests) {\n+ this.destroyRequest(this.pendingRequests[key])\n+ }\n }\n },\n- drawPoint: function(geometry, style, featureId) {\n- if (style.graphic !== false) {\n- if (style.externalGraphic) {\n- this.drawExternalGraphic(geometry, style, featureId)\n- } else if (style.graphicName && style.graphicName != \"circle\") {\n- this.drawNamedSymbol(geometry, style, featureId)\n- } else {\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var twoPi = Math.PI * 2;\n- var radius = style.pointRadius;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.fill()\n- }\n- }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.stroke()\n- }\n- this.setCanvasStyle(\"reset\")\n+ destroy: function() {\n+ this.abort();\n+ delete this.params;\n+ delete this.format;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n+ },\n+ CLASS_NAME: \"OpenLayers.Protocol.Script\"\n+});\n+(function() {\n+ var o = OpenLayers.Protocol.Script;\n+ var counter = 0;\n+ o.registry = {};\n+ o.register = function(callback) {\n+ var id = \"c\" + ++counter;\n+ o.registry[id] = function() {\n+ callback.apply(this, arguments)\n+ };\n+ return id\n+ };\n+ o.unregister = function(id) {\n+ delete o.registry[id]\n+ }\n+})();\n+OpenLayers.Protocol.WFS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS);\n+ var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported WFS version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n+ var typeName, featurePrefix;\n+ var param = layer.params[\"LAYERS\"];\n+ var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n+ if (parts.length > 1) {\n+ featurePrefix = parts[0]\n+ }\n+ typeName = parts.pop();\n+ var protocolOptions = {\n+ url: layer.url,\n+ featureType: typeName,\n+ featurePrefix: featurePrefix,\n+ srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(),\n+ version: \"1.1.0\"\n+ };\n+ return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions))\n+};\n+OpenLayers.Protocol.WFS.DEFAULTS = {\n+ version: \"1.0.0\"\n+};\n+OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {\n+ version: null,\n+ srsName: \"EPSG:4326\",\n+ featureType: null,\n+ featureNS: null,\n+ geometryName: \"the_geom\",\n+ schema: null,\n+ featurePrefix: \"feature\",\n+ formatOptions: null,\n+ readFormat: null,\n+ readOptions: null,\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({\n+ version: this.version,\n+ featureType: this.featureType,\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ geometryName: this.geometryName,\n+ srsName: this.srsName,\n+ schema: this.schema\n+ }, this.formatOptions))\n+ }\n+ if (!options.geometryName && parseFloat(this.format.version) > 1) {\n+ this.setGeometryName(null)\n+ }\n+ },\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy()\n+ }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n+ },\n+ read: function(options) {\n+ OpenLayers.Protocol.prototype.read.apply(this, arguments);\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [this.format.writeNode(\"wfs:GetFeature\", options)]);\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ params: options.params,\n+ headers: options.headers,\n+ data: data\n+ });\n+ return response\n+ },\n+ setFeatureType: function(featureType) {\n+ this.featureType = featureType;\n+ this.format.featureType = featureType\n+ },\n+ setGeometryName: function(geometryName) {\n+ this.geometryName = geometryName;\n+ this.format.geometryName = geometryName\n+ },\n+ handleRead: function(response, options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ var result = this.parseResponse(request, options.readOptions);\n+ if (result && result.success !== false) {\n+ if (options.readOptions && options.readOptions.output == \"object\") {\n+ OpenLayers.Util.extend(response, result)\n+ } else {\n+ response.features = result\n }\n+ response.code = OpenLayers.Protocol.Response.SUCCESS\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n+ response.error = result\n }\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE\n }\n+ options.callback.call(options.scope, response)\n }\n },\n- drawLineString: function(geometry, style, featureId) {\n- style = OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style);\n- this.drawLinearRing(geometry, style, featureId)\n- },\n- drawLinearRing: function(geometry, style, featureId) {\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"fill\")\n- }\n+ parseResponse: function(request, options) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\")\n+ if (!doc || doc.length <= 0) {\n+ return null\n+ }\n+ var result = this.readFormat !== null ? this.readFormat.read(doc) : this.format.read(doc, options);\n+ if (!this.featureNS) {\n+ var format = this.readFormat || this.format;\n+ this.featureNS = format.featureNS;\n+ format.autoConfig = false;\n+ if (!this.geometryName) {\n+ this.setGeometryName(format.geometryName)\n }\n }\n- this.setCanvasStyle(\"reset\")\n+ return result\n },\n- renderPath: function(context, geometry, style, featureId, type) {\n- var components = geometry.components;\n- var len = components.length;\n- context.beginPath();\n- var start = this.getLocalXY(components[0]);\n- var x = start[0];\n- var y = start[1];\n- if (!isNaN(x) && !isNaN(y)) {\n- context.moveTo(start[0], start[1]);\n- for (var i = 1; i < len; ++i) {\n- var pt = this.getLocalXY(components[i]);\n- context.lineTo(pt[0], pt[1])\n+ commit: function(features, options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"commit\",\n+ reqFeatures: features\n+ });\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ headers: options.headers,\n+ data: this.format.write(features, options),\n+ callback: this.createCallback(this.handleCommit, response, options)\n+ });\n+ return response\n+ },\n+ handleCommit: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n+ var data = request.responseXML;\n+ if (!data || !data.documentElement) {\n+ data = request.responseText\n }\n- if (type === \"fill\") {\n- context.fill()\n+ var obj = this.format.read(data) || {};\n+ response.insertIds = obj.insertIds || [];\n+ if (obj.success) {\n+ response.code = OpenLayers.Protocol.Response.SUCCESS\n } else {\n- context.stroke()\n+ response.code = OpenLayers.Protocol.Response.FAILURE;\n+ response.error = obj\n }\n+ options.callback.call(options.scope, response)\n }\n },\n- drawPolygon: function(geometry, style, featureId) {\n- var components = geometry.components;\n- var len = components.length;\n- this.drawLinearRing(components[0], style, featureId);\n- for (var i = 1; i < len; ++i) {\n- this.canvas.globalCompositeOperation = \"destination-out\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"destination-out\"\n+ filterDelete: function(filter, options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options);\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"commit\"\n+ });\n+ var root = this.format.createElementNSPlus(\"wfs:Transaction\", {\n+ attributes: {\n+ service: \"WFS\",\n+ version: this.version\n }\n- this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n- stroke: false,\n- fillOpacity: 1\n- }, style), featureId);\n- this.canvas.globalCompositeOperation = \"source-over\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"source-over\"\n+ });\n+ var deleteNode = this.format.createElementNSPlus(\"wfs:Delete\", {\n+ attributes: {\n+ typeName: (options.featureNS ? this.featurePrefix + \":\" : \"\") + options.featureType\n }\n- this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style), featureId)\n+ });\n+ if (options.featureNS) {\n+ deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, options.featureNS)\n }\n+ var filterNode = this.format.writeNode(\"ogc:Filter\", filter);\n+ deleteNode.appendChild(filterNode);\n+ root.appendChild(deleteNode);\n+ var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [root]);\n+ return OpenLayers.Request.POST({\n+ url: this.url,\n+ callback: options.callback || function() {},\n+ data: data\n+ })\n },\n- drawText: function(location, style) {\n- var pt = this.getLocalXY(location);\n- this.setCanvasStyle(\"reset\");\n- this.canvas.fillStyle = style.fontColor;\n- this.canvas.globalAlpha = style.fontOpacity || 1;\n- var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\", \"normal\", style.fontWeight ? style.fontWeight : \"normal\", style.fontSize ? style.fontSize : \"1em\", style.fontFamily ? style.fontFamily : \"sans-serif\"].join(\" \");\n- var labelRows = style.label.split(\"\\n\");\n- var numRows = labelRows.length;\n- if (this.canvas.fillText) {\n- this.canvas.font = fontStyle;\n- this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || \"center\";\n- this.canvas.textBaseline = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || \"middle\";\n- var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5\n- }\n- var lineHeight = this.canvas.measureText(\"Mg\").height || this.canvas.measureText(\"xx\").width;\n- pt[1] += lineHeight * vfactor * (numRows - 1);\n- for (var i = 0; i < numRows; i++) {\n- if (style.labelOutlineWidth) {\n- this.canvas.save();\n- this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1;\n- this.canvas.strokeStyle = style.labelOutlineColor;\n- this.canvas.lineWidth = style.labelOutlineWidth;\n- this.canvas.strokeText(labelRows[i], pt[0], pt[1] + lineHeight * i + 1);\n- this.canvas.restore()\n- }\n- this.canvas.fillText(labelRows[i], pt[0], pt[1] + lineHeight * i)\n- }\n- } else if (this.canvas.mozDrawText) {\n- this.canvas.mozTextStyle = fontStyle;\n- var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n- if (hfactor == null) {\n- hfactor = -.5\n- }\n- var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5\n- }\n- var lineHeight = this.canvas.mozMeasureText(\"xx\");\n- pt[1] += lineHeight * (1 + vfactor * numRows);\n- for (var i = 0; i < numRows; i++) {\n- var x = pt[0] + hfactor * this.canvas.mozMeasureText(labelRows[i]);\n- var y = pt[1] + i * lineHeight;\n- this.canvas.translate(x, y);\n- this.canvas.mozDrawText(labelRows[i]);\n- this.canvas.translate(-x, -y)\n+ abort: function(response) {\n+ if (response) {\n+ response.priv.abort()\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1\"\n+});\n+OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n+ version: \"1.0.0\",\n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n+});\n+OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n+ version: \"1.1.0\",\n+ initialize: function(options) {\n+ OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);\n+ if (this.outputFormat && !this.readFormat) {\n+ if (this.outputFormat.toLowerCase() == \"gml2\") {\n+ this.readFormat = new OpenLayers.Format.GML.v2({\n+ featureType: this.featureType,\n+ featureNS: this.featureNS,\n+ geometryName: this.geometryName\n+ })\n+ } else if (this.outputFormat.toLowerCase() == \"json\") {\n+ this.readFormat = new OpenLayers.Format.GeoJSON\n }\n }\n- this.setCanvasStyle(\"reset\")\n },\n- getLocalXY: function(point) {\n- var resolution = this.getResolution();\n- var extent = this.extent;\n- var x = (point.x - this.featureDx) / resolution + -extent.left / resolution;\n- var y = extent.top / resolution - point.y / resolution;\n- return [x, y]\n+ CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_1_0\"\n+});\n+OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, {\n+ formatOptions: null,\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions))\n+ }\n },\n- clear: function() {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height)\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy()\n }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n },\n- getFeatureIdFromEvent: function(evt) {\n- var featureId, feature;\n- if (this.hitDetection && this.root.style.display !== \"none\") {\n- if (!this.map.dragging) {\n- var xy = evt.xy;\n- var x = xy.x | 0;\n- var y = xy.y | 0;\n- var data = this.hitContext.getImageData(x, y, 1, 1).data;\n- if (data[3] === 255) {\n- var id = data[2] + 256 * (data[1] + 256 * data[0]);\n- if (id) {\n- featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n- try {\n- feature = this.features[featureId][0]\n- } catch (err) {}\n- }\n- }\n+ read: function(options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var data = this.format.write(options.params || options);\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ params: options.params,\n+ headers: options.headers,\n+ data: data\n+ });\n+ return response\n+ },\n+ handleRead: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ response.data = this.parseData(request);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE\n }\n+ options.callback.call(options.scope, response)\n }\n- return feature\n },\n- eraseFeatures: function(features) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n+ parseData: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n }\n- for (var i = 0; i < features.length; ++i) {\n- delete this.features[features[i].id]\n+ if (!doc || doc.length <= 0) {\n+ return null\n }\n- this.redraw()\n+ return this.format.read(doc)\n },\n- redraw: function() {\n- if (!this.locked) {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height)\n- }\n- var labelMap = [];\n- var feature, geometry, style;\n- var worldBounds = this.map.baseLayer && this.map.baseLayer.wrapDateLine && this.map.getMaxExtent();\n- for (var id in this.features) {\n- if (!this.features.hasOwnProperty(id)) {\n- continue\n- }\n- feature = this.features[id][0];\n- geometry = feature.geometry;\n- this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n- style = this.features[id][1];\n- this.drawGeometry(geometry, style, feature.id);\n- if (style.label) {\n- labelMap.push([feature, style])\n- }\n- }\n- var item;\n- for (var i = 0, len = labelMap.length; i < len; ++i) {\n- item = labelMap[i];\n- this.drawText(item[0].geometry.getCentroid(), item[1])\n+ CLASS_NAME: \"OpenLayers.Protocol.CSW.v2_0_2\"\n+});\n+OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {\n+ fois: null,\n+ formatOptions: null,\n+ initialize: function(options) {\n+ OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n+ if (!options.format) {\n+ this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(this.formatOptions)\n+ }\n+ },\n+ destroy: function() {\n+ if (this.options && !this.options.format) {\n+ this.format.destroy()\n+ }\n+ this.format = null;\n+ OpenLayers.Protocol.prototype.destroy.apply(this)\n+ },\n+ read: function(options) {\n+ options = OpenLayers.Util.extend({}, options);\n+ OpenLayers.Util.applyDefaults(options, this.options || {});\n+ var response = new OpenLayers.Protocol.Response({\n+ requestType: \"read\"\n+ });\n+ var format = this.format;\n+ var data = OpenLayers.Format.XML.prototype.write.apply(format, [format.writeNode(\"sos:GetFeatureOfInterest\", {\n+ fois: this.fois\n+ })]);\n+ response.priv = OpenLayers.Request.POST({\n+ url: options.url,\n+ callback: this.createCallback(this.handleRead, response, options),\n+ data: data\n+ });\n+ return response\n+ },\n+ handleRead: function(response, options) {\n+ if (options.callback) {\n+ var request = response.priv;\n+ if (request.status >= 200 && request.status < 300) {\n+ response.features = this.parseFeatures(request);\n+ response.code = OpenLayers.Protocol.Response.SUCCESS\n+ } else {\n+ response.code = OpenLayers.Protocol.Response.FAILURE\n }\n+ options.callback.call(options.scope, response)\n }\n },\n- CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+ parseFeatures: function(request) {\n+ var doc = request.responseXML;\n+ if (!doc || !doc.documentElement) {\n+ doc = request.responseText\n+ }\n+ if (!doc || doc.length <= 0) {\n+ return null\n+ }\n+ return this.format.read(doc)\n+ },\n+ CLASS_NAME: \"OpenLayers.Protocol.SOS.v1_0_0\"\n });\n-OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n- l: \"left\",\n- r: \"right\",\n- t: \"top\",\n- b: \"bottom\"\n-};\n-OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n- l: 0,\n- r: -1,\n- t: 0,\n- b: -1\n-};\n-OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n OpenLayers.ElementsIndexer = OpenLayers.Class({\n maxZIndex: null,\n order: null,\n indices: null,\n compare: null,\n initialize: function(yOrdering) {\n this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n@@ -36868,830 +36283,1415 @@\n OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n t: 0,\n b: -1\n };\n OpenLayers.Renderer.SVG.preventDefault = function(e) {\n OpenLayers.Event.preventDefault(e)\n };\n-OpenLayers.Tile.Image.IFrame = {\n- useIFrame: null,\n- blankImageUrl: \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7\",\n- draw: function() {\n- var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);\n- if (draw) {\n- var url = this.layer.getURL(this.bounds);\n- var usedIFrame = this.useIFrame;\n- this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && url.length > this.maxGetUrlLength;\n- var fromIFrame = usedIFrame && !this.useIFrame;\n- var toIFrame = !usedIFrame && this.useIFrame;\n- if (fromIFrame || toIFrame) {\n- if (this.imgDiv && this.imgDiv.parentNode === this.frame) {\n- this.frame.removeChild(this.imgDiv)\n+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n+ hitDetection: true,\n+ hitOverflow: 0,\n+ canvas: null,\n+ features: null,\n+ pendingRedraw: false,\n+ cachedSymbolBounds: {},\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.root = document.createElement(\"canvas\");\n+ this.container.appendChild(this.root);\n+ this.canvas = this.root.getContext(\"2d\");\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitCanvas = document.createElement(\"canvas\");\n+ this.hitContext = this.hitCanvas.getContext(\"2d\")\n+ }\n+ },\n+ setExtent: function() {\n+ OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ return false\n+ },\n+ eraseGeometry: function(geometry, featureId) {\n+ this.eraseFeatures(this.features[featureId][0])\n+ },\n+ supported: function() {\n+ return OpenLayers.CANVAS_SUPPORTED\n+ },\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ var root = this.root;\n+ root.style.width = size.w + \"px\";\n+ root.style.height = size.h + \"px\";\n+ root.width = size.w;\n+ root.height = size.h;\n+ this.resolution = null;\n+ if (this.hitDetection) {\n+ var hitCanvas = this.hitCanvas;\n+ hitCanvas.style.width = size.w + \"px\";\n+ hitCanvas.style.height = size.h + \"px\";\n+ hitCanvas.width = size.w;\n+ hitCanvas.height = size.h\n+ }\n+ },\n+ drawFeature: function(feature, style) {\n+ var rendered;\n+ if (feature.geometry) {\n+ style = this.applyDefaultSymbolizer(style || feature.style);\n+ var bounds = feature.geometry.getBounds();\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent()\n+ }\n+ var intersects = bounds && bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ });\n+ rendered = style.display !== \"none\" && !!bounds && intersects;\n+ if (rendered) {\n+ this.features[feature.id] = [feature, style]\n+ } else {\n+ delete this.features[feature.id]\n+ }\n+ this.pendingRedraw = true\n+ }\n+ if (this.pendingRedraw && !this.locked) {\n+ this.redraw();\n+ this.pendingRedraw = false\n+ }\n+ return rendered\n+ },\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ this.drawGeometry(geometry.components[i], style, featureId)\n+ }\n+ return\n+ }\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ this.drawPoint(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ this.drawLineString(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ this.drawLinearRing(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ this.drawPolygon(geometry, style, featureId);\n+ break;\n+ default:\n+ break\n+ }\n+ },\n+ drawExternalGraphic: function(geometry, style, featureId) {\n+ var img = new Image;\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ img.title = title\n+ }\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n+ var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+ var onLoad = function() {\n+ if (!this.features[featureId]) {\n+ return\n+ }\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var x = p0 + xOffset | 0;\n+ var y = p1 + yOffset | 0;\n+ var canvas = this.canvas;\n+ canvas.globalAlpha = opacity;\n+ var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || (OpenLayers.Renderer.Canvas.drawImageScaleFactor = /android 2.1/.test(navigator.userAgent.toLowerCase()) ? 320 / window.screen.width : 1);\n+ canvas.drawImage(img, x * factor, y * factor, width * factor, height * factor);\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId);\n+ this.hitContext.fillRect(x, y, width, height)\n }\n- this.imgDiv = null;\n- if (fromIFrame) {\n- this.frame.removeChild(this.frame.firstChild)\n+ }\n+ };\n+ img.onload = OpenLayers.Function.bind(onLoad, this);\n+ img.src = style.externalGraphic\n+ },\n+ drawNamedSymbol: function(geometry, style, featureId) {\n+ var x, y, cx, cy, i, symbolBounds, scaling, angle;\n+ var unscaledStrokeWidth;\n+ var deg2rad = Math.PI / 180;\n+ var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n+ if (!symbol) {\n+ throw new Error(style.graphicName + \" is not a valid symbol name\")\n+ }\n+ if (!symbol.length || symbol.length < 2) return;\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (isNaN(p0) || isNaN(p1)) return;\n+ this.canvas.lineCap = \"round\";\n+ this.canvas.lineJoin = \"round\";\n+ if (this.hitDetection) {\n+ this.hitContext.lineCap = \"round\";\n+ this.hitContext.lineJoin = \"round\"\n+ }\n+ if (style.graphicName in this.cachedSymbolBounds) {\n+ symbolBounds = this.cachedSymbolBounds[style.graphicName]\n+ } else {\n+ symbolBounds = new OpenLayers.Bounds;\n+ for (i = 0; i < symbol.length; i += 2) {\n+ symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]))\n+ }\n+ this.cachedSymbolBounds[style.graphicName] = symbolBounds\n+ }\n+ this.canvas.save();\n+ if (this.hitDetection) {\n+ this.hitContext.save()\n+ }\n+ this.canvas.translate(p0, p1);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(p0, p1)\n+ }\n+ angle = deg2rad * style.rotation;\n+ if (!isNaN(angle)) {\n+ this.canvas.rotate(angle);\n+ if (this.hitDetection) {\n+ this.hitContext.rotate(angle)\n+ }\n+ }\n+ scaling = 2 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n+ this.canvas.scale(scaling, scaling);\n+ if (this.hitDetection) {\n+ this.hitContext.scale(scaling, scaling)\n+ }\n+ cx = symbolBounds.getCenterLonLat().lon;\n+ cy = symbolBounds.getCenterLonLat().lat;\n+ this.canvas.translate(-cx, -cy);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(-cx, -cy)\n+ }\n+ unscaledStrokeWidth = style.strokeWidth;\n+ style.strokeWidth = unscaledStrokeWidth / scaling;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y)\n+ }\n+ this.canvas.closePath();\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.hitContext.lineTo(x, y)\n }\n+ this.hitContext.closePath();\n+ this.hitContext.fill()\n }\n }\n- return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments)\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y)\n+ }\n+ this.canvas.closePath();\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.hitContext.moveTo(x, y);\n+ this.hitContext.lineTo(x, y)\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.stroke()\n+ }\n+ }\n+ style.strokeWidth = unscaledStrokeWidth;\n+ this.canvas.restore();\n+ if (this.hitDetection) {\n+ this.hitContext.restore()\n+ }\n+ this.setCanvasStyle(\"reset\")\n },\n- getImage: function() {\n- if (this.useIFrame === true) {\n- if (!this.frame.childNodes.length) {\n- var eventPane = document.createElement(\"div\"),\n- style = eventPane.style;\n- style.position = \"absolute\";\n- style.width = \"100%\";\n- style.height = \"100%\";\n- style.zIndex = 1;\n- style.backgroundImage = \"url(\" + this.blankImageUrl + \")\";\n- this.frame.appendChild(eventPane)\n+ setCanvasStyle: function(type, style) {\n+ if (type === \"fill\") {\n+ this.canvas.globalAlpha = style[\"fillOpacity\"];\n+ this.canvas.fillStyle = style[\"fillColor\"]\n+ } else if (type === \"stroke\") {\n+ this.canvas.globalAlpha = style[\"strokeOpacity\"];\n+ this.canvas.strokeStyle = style[\"strokeColor\"];\n+ this.canvas.lineWidth = style[\"strokeWidth\"]\n+ } else {\n+ this.canvas.globalAlpha = 0;\n+ this.canvas.lineWidth = 1\n+ }\n+ },\n+ featureIdToHex: function(featureId) {\n+ var id = Number(featureId.split(\"_\").pop()) + 1;\n+ if (id >= 16777216) {\n+ this.hitOverflow = id - 16777215;\n+ id = id % 16777216 + 1\n+ }\n+ var hex = \"000000\" + id.toString(16);\n+ var len = hex.length;\n+ hex = \"#\" + hex.substring(len - 6, len);\n+ return hex\n+ },\n+ setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n+ var hex = this.featureIdToHex(featureId);\n+ if (type == \"fill\") {\n+ this.hitContext.globalAlpha = 1;\n+ this.hitContext.fillStyle = hex\n+ } else if (type == \"stroke\") {\n+ this.hitContext.globalAlpha = 1;\n+ this.hitContext.strokeStyle = hex;\n+ if (typeof strokeScaling === \"undefined\") {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2\n+ } else {\n+ if (!isNaN(strokeScaling)) {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2 / strokeScaling\n+ }\n }\n- var id = this.id + \"_iFrame\",\n- iframe;\n- if (parseFloat(navigator.appVersion.split(\"MSIE\")[1]) < 9) {\n- iframe = document.createElement('<iframe name=\"' + id + '\">');\n- iframe.style.backgroundColor = \"#FFFFFF\";\n- iframe.style.filter = \"chroma(color=#FFFFFF)\"\n+ } else {\n+ this.hitContext.globalAlpha = 0;\n+ this.hitContext.lineWidth = 1\n+ }\n+ },\n+ drawPoint: function(geometry, style, featureId) {\n+ if (style.graphic !== false) {\n+ if (style.externalGraphic) {\n+ this.drawExternalGraphic(geometry, style, featureId)\n+ } else if (style.graphicName && style.graphicName != \"circle\") {\n+ this.drawNamedSymbol(geometry, style, featureId)\n } else {\n- iframe = document.createElement(\"iframe\");\n- iframe.style.backgroundColor = \"transparent\";\n- iframe.name = id\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var twoPi = Math.PI * 2;\n+ var radius = style.pointRadius;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.fill()\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.stroke()\n+ }\n+ this.setCanvasStyle(\"reset\")\n+ }\n+ }\n }\n- iframe.scrolling = \"no\";\n- iframe.marginWidth = \"0px\";\n- iframe.marginHeight = \"0px\";\n- iframe.frameBorder = \"0\";\n- iframe.style.position = \"absolute\";\n- iframe.style.width = \"100%\";\n- iframe.style.height = \"100%\";\n- if (this.layer.opacity < 1) {\n- OpenLayers.Util.modifyDOMElement(iframe, null, null, null, null, null, null, this.layer.opacity)\n+ }\n+ },\n+ drawLineString: function(geometry, style, featureId) {\n+ style = OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style);\n+ this.drawLinearRing(geometry, style, featureId)\n+ },\n+ drawLinearRing: function(geometry, style, featureId) {\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"fill\")\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\")\n }\n- this.frame.appendChild(iframe);\n- this.imgDiv = iframe;\n- return iframe\n- } else {\n- return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments)\n }\n+ this.setCanvasStyle(\"reset\")\n },\n- createRequestForm: function() {\n- var form = document.createElement(\"form\");\n- form.method = \"POST\";\n- var cacheId = this.layer.params[\"_OLSALT\"];\n- cacheId = (cacheId ? cacheId + \"_\" : \"\") + this.bounds.toBBOX();\n- form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);\n- form.target = this.id + \"_iFrame\";\n- var imageSize = this.layer.getImageSize(),\n- params = OpenLayers.Util.getParameters(this.url),\n- field;\n- for (var par in params) {\n- field = document.createElement(\"input\");\n- field.type = \"hidden\";\n- field.name = par;\n- field.value = params[par];\n- form.appendChild(field)\n+ renderPath: function(context, geometry, style, featureId, type) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ context.beginPath();\n+ var start = this.getLocalXY(components[0]);\n+ var x = start[0];\n+ var y = start[1];\n+ if (!isNaN(x) && !isNaN(y)) {\n+ context.moveTo(start[0], start[1]);\n+ for (var i = 1; i < len; ++i) {\n+ var pt = this.getLocalXY(components[i]);\n+ context.lineTo(pt[0], pt[1])\n+ }\n+ if (type === \"fill\") {\n+ context.fill()\n+ } else {\n+ context.stroke()\n+ }\n }\n- return form\n },\n- setImgSrc: function(url) {\n- if (this.useIFrame === true) {\n- if (url) {\n- var form = this.createRequestForm();\n- this.frame.appendChild(form);\n- form.submit();\n- this.frame.removeChild(form)\n- } else if (this.imgDiv.parentNode === this.frame) {\n- this.frame.removeChild(this.imgDiv);\n- this.imgDiv = null\n+ drawPolygon: function(geometry, style, featureId) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ this.drawLinearRing(components[0], style, featureId);\n+ for (var i = 1; i < len; ++i) {\n+ this.canvas.globalCompositeOperation = \"destination-out\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"destination-out\"\n }\n- } else {\n- OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments)\n+ this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n+ stroke: false,\n+ fillOpacity: 1\n+ }, style), featureId);\n+ this.canvas.globalCompositeOperation = \"source-over\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"source-over\"\n+ }\n+ this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style), featureId)\n }\n },\n- onImageLoad: function() {\n- OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments);\n- if (this.useIFrame === true) {\n- this.imgDiv.style.opacity = 1;\n- this.frame.style.opacity = this.layer.opacity\n+ drawText: function(location, style) {\n+ var pt = this.getLocalXY(location);\n+ this.setCanvasStyle(\"reset\");\n+ this.canvas.fillStyle = style.fontColor;\n+ this.canvas.globalAlpha = style.fontOpacity || 1;\n+ var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\", \"normal\", style.fontWeight ? style.fontWeight : \"normal\", style.fontSize ? style.fontSize : \"1em\", style.fontFamily ? style.fontFamily : \"sans-serif\"].join(\" \");\n+ var labelRows = style.label.split(\"\\n\");\n+ var numRows = labelRows.length;\n+ if (this.canvas.fillText) {\n+ this.canvas.font = fontStyle;\n+ this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || \"center\";\n+ this.canvas.textBaseline = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || \"middle\";\n+ var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5\n+ }\n+ var lineHeight = this.canvas.measureText(\"Mg\").height || this.canvas.measureText(\"xx\").width;\n+ pt[1] += lineHeight * vfactor * (numRows - 1);\n+ for (var i = 0; i < numRows; i++) {\n+ if (style.labelOutlineWidth) {\n+ this.canvas.save();\n+ this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1;\n+ this.canvas.strokeStyle = style.labelOutlineColor;\n+ this.canvas.lineWidth = style.labelOutlineWidth;\n+ this.canvas.strokeText(labelRows[i], pt[0], pt[1] + lineHeight * i + 1);\n+ this.canvas.restore()\n+ }\n+ this.canvas.fillText(labelRows[i], pt[0], pt[1] + lineHeight * i)\n+ }\n+ } else if (this.canvas.mozDrawText) {\n+ this.canvas.mozTextStyle = fontStyle;\n+ var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n+ if (hfactor == null) {\n+ hfactor = -.5\n+ }\n+ var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5\n+ }\n+ var lineHeight = this.canvas.mozMeasureText(\"xx\");\n+ pt[1] += lineHeight * (1 + vfactor * numRows);\n+ for (var i = 0; i < numRows; i++) {\n+ var x = pt[0] + hfactor * this.canvas.mozMeasureText(labelRows[i]);\n+ var y = pt[1] + i * lineHeight;\n+ this.canvas.translate(x, y);\n+ this.canvas.mozDrawText(labelRows[i]);\n+ this.canvas.translate(-x, -y)\n+ }\n }\n+ this.setCanvasStyle(\"reset\")\n },\n- createBackBuffer: function() {\n- var backBuffer;\n- if (this.useIFrame === false) {\n- backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this)\n+ getLocalXY: function(point) {\n+ var resolution = this.getResolution();\n+ var extent = this.extent;\n+ var x = (point.x - this.featureDx) / resolution + -extent.left / resolution;\n+ var y = extent.top / resolution - point.y / resolution;\n+ return [x, y]\n+ },\n+ clear: function() {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height)\n }\n- return backBuffer\n- }\n-};\n-OpenLayers.Protocol.SOS = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.SOS.DEFAULTS);\n- var cls = OpenLayers.Protocol.SOS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported SOS version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Protocol.SOS.DEFAULTS = {\n- version: \"1.0.0\"\n-};\n-OpenLayers.Protocol.CSW = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.CSW.DEFAULTS);\n- var cls = OpenLayers.Protocol.CSW[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSW version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Protocol.CSW.DEFAULTS = {\n- version: \"2.0.2\"\n-};\n-OpenLayers.Protocol.WFS = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS);\n- var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported WFS version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n- var typeName, featurePrefix;\n- var param = layer.params[\"LAYERS\"];\n- var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n- if (parts.length > 1) {\n- featurePrefix = parts[0]\n- }\n- typeName = parts.pop();\n- var protocolOptions = {\n- url: layer.url,\n- featureType: typeName,\n- featurePrefix: featurePrefix,\n- srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(),\n- version: \"1.1.0\"\n- };\n- return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions))\n-};\n-OpenLayers.Protocol.WFS.DEFAULTS = {\n- version: \"1.0.0\"\n-};\n-OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n- url: null,\n- headers: null,\n- params: null,\n- callback: null,\n- scope: null,\n- readWithPOST: false,\n- updateWithPOST: false,\n- deleteWithPOST: false,\n- wildcarded: false,\n- srsInBBOX: false,\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.headers = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- wildcarded: this.wildcarded,\n- srsInBBOX: this.srsInBBOX\n- });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params)\n+ },\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId, feature;\n+ if (this.hitDetection && this.root.style.display !== \"none\") {\n+ if (!this.map.dragging) {\n+ var xy = evt.xy;\n+ var x = xy.x | 0;\n+ var y = xy.y | 0;\n+ var data = this.hitContext.getImageData(x, y, 1, 1).data;\n+ if (data[3] === 255) {\n+ var id = data[2] + 256 * (data[1] + 256 * data[0]);\n+ if (id) {\n+ featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n+ try {\n+ feature = this.features[featureId][0]\n+ } catch (err) {}\n+ }\n+ }\n }\n }\n+ return feature\n },\n- destroy: function() {\n- this.params = null;\n- this.headers = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n+ eraseFeatures: function(features) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ for (var i = 0; i < features.length; ++i) {\n+ delete this.features[features[i].id]\n+ }\n+ this.redraw()\n },\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = options || {};\n- options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(options.filter, options.params)\n+ redraw: function() {\n+ if (!this.locked) {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height)\n+ }\n+ var labelMap = [];\n+ var feature, geometry, style;\n+ var worldBounds = this.map.baseLayer && this.map.baseLayer.wrapDateLine && this.map.getMaxExtent();\n+ for (var id in this.features) {\n+ if (!this.features.hasOwnProperty(id)) {\n+ continue\n+ }\n+ feature = this.features[id][0];\n+ geometry = feature.geometry;\n+ this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n+ style = this.features[id][1];\n+ this.drawGeometry(geometry, style, feature.id);\n+ if (style.label) {\n+ labelMap.push([feature, style])\n+ }\n+ }\n+ var item;\n+ for (var i = 0, len = labelMap.length; i < len; ++i) {\n+ item = labelMap[i];\n+ this.drawText(item[0].geometry.getCentroid(), item[1])\n+ }\n }\n- var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST;\n- var resp = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- if (readWithPOST) {\n- var headers = options.headers || {};\n- headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- data: OpenLayers.Util.getParameterString(options.params),\n- headers: headers\n- })\n+ },\n+ CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+});\n+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n+ l: \"left\",\n+ r: \"right\",\n+ t: \"top\",\n+ b: \"bottom\"\n+};\n+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n+ l: 0,\n+ r: -1,\n+ t: 0,\n+ b: -1\n+};\n+OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n+OpenLayers.Events.featureclick = OpenLayers.Class({\n+ cache: null,\n+ map: null,\n+ provides: [\"featureclick\", \"nofeatureclick\", \"featureover\", \"featureout\"],\n+ initialize: function(target) {\n+ this.target = target;\n+ if (target.object instanceof OpenLayers.Map) {\n+ this.setMap(target.object)\n+ } else if (target.object instanceof OpenLayers.Layer.Vector) {\n+ if (target.object.map) {\n+ this.setMap(target.object.map)\n+ } else {\n+ target.object.events.register(\"added\", this, function(evt) {\n+ this.setMap(target.object.map)\n+ })\n+ }\n } else {\n- resp.priv = OpenLayers.Request.GET({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, resp, options),\n- params: options.params,\n- headers: options.headers\n- })\n+ throw \"Listeners for '\" + this.provides.join(\"', '\") + \"' events can only be registered for OpenLayers.Layer.Vector \" + \"or OpenLayers.Map instances\"\n+ }\n+ for (var i = this.provides.length - 1; i >= 0; --i) {\n+ target.extensions[this.provides[i]] = true\n }\n- return resp\n- },\n- handleRead: function(resp, options) {\n- this.handleResponse(resp, options)\n },\n- create: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: features,\n- requestType: \"create\"\n+ setMap: function(map) {\n+ this.map = map;\n+ this.cache = {};\n+ map.events.register(\"mousedown\", this, this.start, {\n+ extension: true\n });\n- resp.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleCreate, resp, options),\n- headers: options.headers,\n- data: this.format.write(features)\n+ map.events.register(\"mouseup\", this, this.onClick, {\n+ extension: true\n });\n- return resp\n- },\n- handleCreate: function(resp, options) {\n- this.handleResponse(resp, options)\n- },\n- update: function(feature, options) {\n- options = options || {};\n- var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"update\"\n+ map.events.register(\"touchstart\", this, this.start, {\n+ extension: true\n });\n- var method = this.updateWithPOST ? \"POST\" : \"PUT\";\n- resp.priv = OpenLayers.Request[method]({\n- url: url,\n- callback: this.createCallback(this.handleUpdate, resp, options),\n- headers: options.headers,\n- data: this.format.write(feature)\n+ map.events.register(\"touchmove\", this, this.cancel, {\n+ extension: true\n });\n- return resp\n+ map.events.register(\"touchend\", this, this.onClick, {\n+ extension: true\n+ });\n+ map.events.register(\"mousemove\", this, this.onMousemove, {\n+ extension: true\n+ })\n },\n- handleUpdate: function(resp, options) {\n- this.handleResponse(resp, options)\n+ start: function(evt) {\n+ this.startEvt = evt\n },\n- delete: function(feature, options) {\n- options = options || {};\n- var url = options.url || feature.url || this.options.url + \"/\" + feature.fid;\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = new OpenLayers.Protocol.Response({\n- reqFeatures: feature,\n- requestType: \"delete\"\n- });\n- var method = this.deleteWithPOST ? \"POST\" : \"DELETE\";\n- var requestOptions = {\n- url: url,\n- callback: this.createCallback(this.handleDelete, resp, options),\n- headers: options.headers\n- };\n- if (this.deleteWithPOST) {\n- requestOptions.data = this.format.write(feature)\n- }\n- resp.priv = OpenLayers.Request[method](requestOptions);\n- return resp\n+ cancel: function(evt) {\n+ delete this.startEvt\n },\n- handleDelete: function(resp, options) {\n- this.handleResponse(resp, options)\n+ onClick: function(evt) {\n+ if (!this.startEvt || evt.type !== \"touchend\" && !OpenLayers.Event.isLeftClick(evt)) {\n+ return\n+ }\n+ var features = this.getFeatures(this.startEvt);\n+ delete this.startEvt;\n+ var feature, layer, more, clicked = {};\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ layer = feature.layer;\n+ clicked[layer.id] = true;\n+ more = this.triggerEvent(\"featureclick\", {\n+ feature: feature\n+ });\n+ if (more === false) {\n+ break\n+ }\n+ }\n+ for (i = 0, len = this.map.layers.length; i < len; ++i) {\n+ layer = this.map.layers[i];\n+ if (layer instanceof OpenLayers.Layer.Vector && !clicked[layer.id]) {\n+ this.triggerEvent(\"nofeatureclick\", {\n+ layer: layer\n+ })\n+ }\n+ }\n },\n- handleResponse: function(resp, options) {\n- var request = resp.priv;\n- if (options.callback) {\n- if (request.status >= 200 && request.status < 300) {\n- if (resp.requestType != \"delete\") {\n- resp.features = this.parseFeatures(request)\n+ onMousemove: function(evt) {\n+ delete this.startEvt;\n+ var features = this.getFeatures(evt);\n+ var over = {},\n+ newly = [],\n+ feature;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ over[feature.id] = feature;\n+ if (!this.cache[feature.id]) {\n+ newly.push(feature)\n+ }\n+ }\n+ var out = [];\n+ for (var id in this.cache) {\n+ feature = this.cache[id];\n+ if (feature.layer && feature.layer.map) {\n+ if (!over[feature.id]) {\n+ out.push(feature)\n }\n- resp.code = OpenLayers.Protocol.Response.SUCCESS\n } else {\n- resp.code = OpenLayers.Protocol.Response.FAILURE\n+ delete this.cache[id]\n+ }\n+ }\n+ var more;\n+ for (i = 0, len = newly.length; i < len; ++i) {\n+ feature = newly[i];\n+ this.cache[feature.id] = feature;\n+ more = this.triggerEvent(\"featureover\", {\n+ feature: feature\n+ });\n+ if (more === false) {\n+ break\n+ }\n+ }\n+ for (i = 0, len = out.length; i < len; ++i) {\n+ feature = out[i];\n+ delete this.cache[feature.id];\n+ more = this.triggerEvent(\"featureout\", {\n+ feature: feature\n+ });\n+ if (more === false) {\n+ break\n }\n- options.callback.call(options.scope, resp)\n }\n },\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ triggerEvent: function(type, evt) {\n+ var layer = evt.feature ? evt.feature.layer : evt.layer,\n+ object = this.target.object;\n+ if (object instanceof OpenLayers.Map || object === layer) {\n+ return this.target.triggerEvent(type, evt)\n }\n- if (!doc || doc.length <= 0) {\n- return null\n+ },\n+ getFeatures: function(evt) {\n+ var x = evt.clientX,\n+ y = evt.clientY,\n+ features = [],\n+ targets = [],\n+ layers = [],\n+ layer, target, feature, i, len;\n+ for (i = this.map.layers.length - 1; i >= 0; --i) {\n+ layer = this.map.layers[i];\n+ if (layer.div.style.display !== \"none\") {\n+ if (layer.renderer instanceof OpenLayers.Renderer.Elements) {\n+ if (layer instanceof OpenLayers.Layer.Vector) {\n+ target = document.elementFromPoint(x, y);\n+ while (target && target._featureId) {\n+ feature = layer.getFeatureById(target._featureId);\n+ if (feature) {\n+ features.push(feature);\n+ target.style.display = \"none\";\n+ targets.push(target);\n+ target = document.elementFromPoint(x, y)\n+ } else {\n+ target = false\n+ }\n+ }\n+ }\n+ layers.push(layer);\n+ layer.div.style.display = \"none\"\n+ } else if (layer.renderer instanceof OpenLayers.Renderer.Canvas) {\n+ feature = layer.renderer.getFeatureIdFromEvent(evt);\n+ if (feature) {\n+ features.push(feature);\n+ layers.push(layer)\n+ }\n+ }\n+ }\n }\n- return this.format.read(doc)\n+ for (i = 0, len = targets.length; i < len; ++i) {\n+ targets[i].style.display = \"\"\n+ }\n+ for (i = layers.length - 1; i >= 0; --i) {\n+ layers[i].div.style.display = \"block\"\n+ }\n+ return features\n },\n- commit: function(features, options) {\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- var resp = [],\n- nResponses = 0;\n- var types = {};\n- types[OpenLayers.State.INSERT] = [];\n- types[OpenLayers.State.UPDATE] = [];\n- types[OpenLayers.State.DELETE] = [];\n- var feature, list, requestFeatures = [];\n- for (var i = 0, len = features.length; i < len; ++i) {\n- feature = features[i];\n- list = types[feature.state];\n- if (list) {\n- list.push(feature);\n- requestFeatures.push(feature)\n+ destroy: function() {\n+ for (var i = this.provides.length - 1; i >= 0; --i) {\n+ delete this.target.extensions[this.provides[i]]\n+ }\n+ this.map.events.un({\n+ mousemove: this.onMousemove,\n+ mousedown: this.start,\n+ mouseup: this.onClick,\n+ touchstart: this.start,\n+ touchmove: this.cancel,\n+ touchend: this.onClick,\n+ scope: this\n+ });\n+ delete this.cache;\n+ delete this.map;\n+ delete this.target\n+ }\n+});\n+OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick;\n+OpenLayers.Events.featureover = OpenLayers.Events.featureclick;\n+OpenLayers.Events.featureout = OpenLayers.Events.featureclick;\n+OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {\n+ events: null,\n+ auto: false,\n+ timer: null,\n+ initialize: function(options) {\n+ OpenLayers.Strategy.prototype.initialize.apply(this, [options]);\n+ this.events = new OpenLayers.Events(this)\n+ },\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.auto) {\n+ if (typeof this.auto === \"number\") {\n+ this.timer = window.setInterval(OpenLayers.Function.bind(this.save, this), this.auto * 1e3)\n+ } else {\n+ this.layer.events.on({\n+ featureadded: this.triggerSave,\n+ afterfeaturemodified: this.triggerSave,\n+ scope: this\n+ })\n+ }\n }\n }\n- var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length;\n- var success = true;\n- var finalResponse = new OpenLayers.Protocol.Response({\n- reqFeatures: requestFeatures\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ if (this.auto) {\n+ if (typeof this.auto === \"number\") {\n+ window.clearInterval(this.timer)\n+ } else {\n+ this.layer.events.un({\n+ featureadded: this.triggerSave,\n+ afterfeaturemodified: this.triggerSave,\n+ scope: this\n+ })\n+ }\n+ }\n+ }\n+ return deactivated\n+ },\n+ triggerSave: function(event) {\n+ var feature = event.feature;\n+ if (feature.state === OpenLayers.State.INSERT || feature.state === OpenLayers.State.UPDATE || feature.state === OpenLayers.State.DELETE) {\n+ this.save([event.feature])\n+ }\n+ },\n+ save: function(features) {\n+ if (!features) {\n+ features = this.layer.features\n+ }\n+ this.events.triggerEvent(\"start\", {\n+ features: features\n });\n-\n- function insertCallback(response) {\n- var len = response.features ? response.features.length : 0;\n- var fids = new Array(len);\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var len = features.length;\n+ var clones = new Array(len);\n+ var orig, clone;\n for (var i = 0; i < len; ++i) {\n- fids[i] = response.features[i].fid\n+ orig = features[i];\n+ clone = orig.clone();\n+ clone.fid = orig.fid;\n+ clone.state = orig.state;\n+ if (orig.url) {\n+ clone.url = orig.url\n+ }\n+ clone._original = orig;\n+ clone.geometry.transform(local, remote);\n+ clones[i] = clone\n }\n- finalResponse.insertIds = fids;\n- callback.apply(this, [response])\n+ features = clones\n }\n-\n- function callback(response) {\n- this.callUserCallback(response, options);\n- success = success && response.success();\n- nResponses++;\n- if (nResponses >= nRequests) {\n- if (options.callback) {\n- finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE;\n- options.callback.apply(options.scope, [finalResponse])\n+ this.layer.protocol.commit(features, {\n+ callback: this.onCommit,\n+ scope: this\n+ })\n+ },\n+ onCommit: function(response) {\n+ var evt = {\n+ response: response\n+ };\n+ if (response.success()) {\n+ var features = response.reqFeatures;\n+ var state, feature;\n+ var destroys = [];\n+ var insertIds = response.insertIds || [];\n+ var j = 0;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ feature = features[i];\n+ feature = feature._original || feature;\n+ state = feature.state;\n+ if (state) {\n+ if (state == OpenLayers.State.DELETE) {\n+ destroys.push(feature)\n+ } else if (state == OpenLayers.State.INSERT) {\n+ feature.fid = insertIds[j];\n+ ++j\n+ }\n+ feature.state = null\n }\n }\n+ if (destroys.length > 0) {\n+ this.layer.destroyFeatures(destroys)\n+ }\n+ this.events.triggerEvent(\"success\", evt)\n+ } else {\n+ this.events.triggerEvent(\"fail\", evt)\n }\n- var queue = types[OpenLayers.State.INSERT];\n- if (queue.length > 0) {\n- resp.push(this.create(queue, OpenLayers.Util.applyDefaults({\n- callback: insertCallback,\n+ },\n+ CLASS_NAME: \"OpenLayers.Strategy.Save\"\n+});\n+OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {\n+ filter: null,\n+ cache: null,\n+ caching: false,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.cache = [];\n+ this.layer.events.on({\n+ beforefeaturesadded: this.handleAdd,\n+ beforefeaturesremoved: this.handleRemove,\n scope: this\n- }, options.create)))\n+ })\n }\n- queue = types[OpenLayers.State.UPDATE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n+ return activated\n+ },\n+ deactivate: function() {\n+ this.cache = null;\n+ if (this.layer && this.layer.events) {\n+ this.layer.events.un({\n+ beforefeaturesadded: this.handleAdd,\n+ beforefeaturesremoved: this.handleRemove,\n scope: this\n- }, options.update)))\n+ })\n }\n- queue = types[OpenLayers.State.DELETE];\n- for (var i = queue.length - 1; i >= 0; --i) {\n- resp.push(this[\"delete\"](queue[i], OpenLayers.Util.applyDefaults({\n- callback: callback,\n- scope: this\n- }, options[\"delete\"])))\n+ return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments)\n+ },\n+ handleAdd: function(event) {\n+ if (!this.caching && this.filter) {\n+ var features = event.features;\n+ event.features = [];\n+ var feature;\n+ for (var i = 0, ii = features.length; i < ii; ++i) {\n+ feature = features[i];\n+ if (this.filter.evaluate(feature)) {\n+ event.features.push(feature)\n+ } else {\n+ this.cache.push(feature)\n+ }\n+ }\n }\n- return resp\n },\n- abort: function(response) {\n- if (response) {\n- response.priv.abort()\n+ handleRemove: function(event) {\n+ if (!this.caching) {\n+ this.cache = []\n }\n },\n- callUserCallback: function(resp, options) {\n- var opt = options[resp.requestType];\n- if (opt && opt.callback) {\n- opt.callback.call(opt.scope, resp)\n+ setFilter: function(filter) {\n+ this.filter = filter;\n+ var previousCache = this.cache;\n+ this.cache = [];\n+ this.handleAdd({\n+ features: this.layer.features\n+ });\n+ if (this.cache.length > 0) {\n+ this.caching = true;\n+ this.layer.removeFeatures(this.cache.slice());\n+ this.caching = false\n+ }\n+ if (previousCache.length > 0) {\n+ var event = {\n+ features: previousCache\n+ };\n+ this.handleAdd(event);\n+ if (event.features.length > 0) {\n+ this.caching = true;\n+ this.layer.addFeatures(event.features);\n+ this.caching = false\n+ }\n }\n },\n- CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Filter\"\n });\n-OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {\n- url: null,\n- params: null,\n- callback: null,\n- callbackTemplate: \"OpenLayers.Protocol.Script.registry.${id}\",\n- callbackKey: \"callback\",\n- callbackPrefix: \"\",\n- scope: null,\n- format: null,\n- pendingRequests: null,\n- srsInBBOX: false,\n- initialize: function(options) {\n- options = options || {};\n- this.params = {};\n- this.pendingRequests = {};\n- OpenLayers.Protocol.prototype.initialize.apply(this, arguments);\n- if (!this.format) {\n- this.format = new OpenLayers.Format.GeoJSON\n- }\n- if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {\n- var format = new OpenLayers.Format.QueryStringFilter({\n- srsInBBOX: this.srsInBBOX\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n+ preload: false,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ refresh: this.load,\n+ scope: this\n });\n- this.filterToParams = function(filter, params) {\n- return format.write(filter, params)\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load()\n+ } else {\n+ this.layer.events.on({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n }\n }\n+ return activated\n },\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = OpenLayers.Util.applyDefaults(options, this.options);\n- options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);\n- if (options.filter && this.filterToParams) {\n- options.params = this.filterToParams(options.filter, options.params)\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ refresh: this.load,\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n }\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var request = this.createRequest(options.url, options.params, OpenLayers.Function.bind(function(data) {\n- response.data = data;\n- this.handleRead(response, options)\n- }, this));\n- response.priv = request;\n- return response\n+ return deactivated\n },\n- createRequest: function(url, params, callback) {\n- var id = OpenLayers.Protocol.Script.register(callback);\n- var name = OpenLayers.String.format(this.callbackTemplate, {\n- id: id\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n });\n- params = OpenLayers.Util.extend({}, params);\n- params[this.callbackKey] = this.callbackPrefix + name;\n- url = OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(params));\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = \"OpenLayers_Protocol_Script_\" + id;\n- this.pendingRequests[script.id] = script;\n- var head = document.getElementsByTagName(\"head\")[0];\n- head.appendChild(script);\n- return script\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n },\n- destroyRequest: function(script) {\n- OpenLayers.Protocol.Script.unregister(script.id.split(\"_\").pop());\n- delete this.pendingRequests[script.id];\n- if (script.parentNode) {\n- script.parentNode.removeChild(script)\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local)\n+ }\n+ }\n+ }\n+ layer.addFeatures(features)\n }\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ })\n },\n- handleRead: function(response, options) {\n- this.handleResponse(response, options)\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n+});\n+OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {\n+ features: null,\n+ length: 10,\n+ num: null,\n+ paging: false,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ beforefeaturesadded: this.cacheFeatures,\n+ scope: this\n+ })\n+ }\n+ return activated\n },\n- handleResponse: function(response, options) {\n- if (options.callback) {\n- if (response.data) {\n- response.features = this.parseFeatures(response.data);\n- response.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE\n- }\n- this.destroyRequest(response.priv);\n- options.callback.call(options.scope, response)\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.clearCache();\n+ this.layer.events.un({\n+ beforefeaturesadded: this.cacheFeatures,\n+ scope: this\n+ })\n }\n+ return deactivated\n },\n- parseFeatures: function(data) {\n- return this.format.read(data)\n+ cacheFeatures: function(event) {\n+ if (!this.paging) {\n+ this.clearCache();\n+ this.features = event.features;\n+ this.pageNext(event)\n+ }\n },\n- abort: function(response) {\n- if (response) {\n- this.destroyRequest(response.priv)\n- } else {\n- for (var key in this.pendingRequests) {\n- this.destroyRequest(this.pendingRequests[key])\n+ clearCache: function() {\n+ if (this.features) {\n+ for (var i = 0; i < this.features.length; ++i) {\n+ this.features[i].destroy()\n }\n }\n+ this.features = null;\n+ this.num = null\n },\n- destroy: function() {\n- this.abort();\n- delete this.params;\n- delete this.format;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n+ pageCount: function() {\n+ var numFeatures = this.features ? this.features.length : 0;\n+ return Math.ceil(numFeatures / this.length)\n },\n- CLASS_NAME: \"OpenLayers.Protocol.Script\"\n-});\n-(function() {\n- var o = OpenLayers.Protocol.Script;\n- var counter = 0;\n- o.registry = {};\n- o.register = function(callback) {\n- var id = \"c\" + ++counter;\n- o.registry[id] = function() {\n- callback.apply(this, arguments)\n- };\n- return id\n- };\n- o.unregister = function(id) {\n- delete o.registry[id]\n- }\n-})();\n-OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {\n- version: null,\n- srsName: \"EPSG:4326\",\n- featureType: null,\n- featureNS: null,\n- geometryName: \"the_geom\",\n- schema: null,\n- featurePrefix: \"feature\",\n- formatOptions: null,\n- readFormat: null,\n- readOptions: null,\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({\n- version: this.version,\n- featureType: this.featureType,\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- geometryName: this.geometryName,\n- srsName: this.srsName,\n- schema: this.schema\n- }, this.formatOptions))\n- }\n- if (!options.geometryName && parseFloat(this.format.version) > 1) {\n- this.setGeometryName(null)\n- }\n+ pageNum: function() {\n+ return this.num\n },\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy()\n+ pageLength: function(newLength) {\n+ if (newLength && newLength > 0) {\n+ this.length = newLength\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n- },\n- read: function(options) {\n- OpenLayers.Protocol.prototype.read.apply(this, arguments);\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [this.format.writeNode(\"wfs:GetFeature\", options)]);\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- params: options.params,\n- headers: options.headers,\n- data: data\n- });\n- return response\n+ return this.length\n },\n- setFeatureType: function(featureType) {\n- this.featureType = featureType;\n- this.format.featureType = featureType\n+ pageNext: function(event) {\n+ var changed = false;\n+ if (this.features) {\n+ if (this.num === null) {\n+ this.num = -1\n+ }\n+ var start = (this.num + 1) * this.length;\n+ changed = this.page(start, event)\n+ }\n+ return changed\n },\n- setGeometryName: function(geometryName) {\n- this.geometryName = geometryName;\n- this.format.geometryName = geometryName\n+ pagePrevious: function() {\n+ var changed = false;\n+ if (this.features) {\n+ if (this.num === null) {\n+ this.num = this.pageCount()\n+ }\n+ var start = (this.num - 1) * this.length;\n+ changed = this.page(start)\n+ }\n+ return changed\n },\n- handleRead: function(response, options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- var result = this.parseResponse(request, options.readOptions);\n- if (result && result.success !== false) {\n- if (options.readOptions && options.readOptions.output == \"object\") {\n- OpenLayers.Util.extend(response, result)\n+ page: function(start, event) {\n+ var changed = false;\n+ if (this.features) {\n+ if (start >= 0 && start < this.features.length) {\n+ var num = Math.floor(start / this.length);\n+ if (num != this.num) {\n+ this.paging = true;\n+ var features = this.features.slice(start, start + this.length);\n+ this.layer.removeFeatures(this.layer.features);\n+ this.num = num;\n+ if (event && event.features) {\n+ event.features = features\n } else {\n- response.features = result\n+ this.layer.addFeatures(features)\n }\n- response.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- response.error = result\n+ this.paging = false;\n+ changed = true\n }\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE\n }\n- options.callback.call(options.scope, response)\n }\n+ return changed\n },\n- parseResponse: function(request, options) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ CLASS_NAME: \"OpenLayers.Strategy.Paging\"\n+});\n+OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {\n+ distance: 20,\n+ threshold: null,\n+ features: null,\n+ clusters: null,\n+ clustering: false,\n+ resolution: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ beforefeaturesadded: this.cacheFeatures,\n+ featuresremoved: this.clearCache,\n+ moveend: this.cluster,\n+ scope: this\n+ })\n }\n- if (!doc || doc.length <= 0) {\n- return null\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.clearCache();\n+ this.layer.events.un({\n+ beforefeaturesadded: this.cacheFeatures,\n+ featuresremoved: this.clearCache,\n+ moveend: this.cluster,\n+ scope: this\n+ })\n }\n- var result = this.readFormat !== null ? this.readFormat.read(doc) : this.format.read(doc, options);\n- if (!this.featureNS) {\n- var format = this.readFormat || this.format;\n- this.featureNS = format.featureNS;\n- format.autoConfig = false;\n- if (!this.geometryName) {\n- this.setGeometryName(format.geometryName)\n- }\n+ return deactivated\n+ },\n+ cacheFeatures: function(event) {\n+ var propagate = true;\n+ if (!this.clustering) {\n+ this.clearCache();\n+ this.features = event.features;\n+ this.cluster();\n+ propagate = false\n }\n- return result\n+ return propagate\n },\n- commit: function(features, options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"commit\",\n- reqFeatures: features\n- });\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- headers: options.headers,\n- data: this.format.write(features, options),\n- callback: this.createCallback(this.handleCommit, response, options)\n- });\n- return response\n+ clearCache: function() {\n+ if (!this.clustering) {\n+ this.features = null\n+ }\n },\n- handleCommit: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n- var data = request.responseXML;\n- if (!data || !data.documentElement) {\n- data = request.responseText\n- }\n- var obj = this.format.read(data) || {};\n- response.insertIds = obj.insertIds || [];\n- if (obj.success) {\n- response.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE;\n- response.error = obj\n+ cluster: function(event) {\n+ if ((!event || event.zoomChanged) && this.features) {\n+ var resolution = this.layer.map.getResolution();\n+ if (resolution != this.resolution || !this.clustersExist()) {\n+ this.resolution = resolution;\n+ var clusters = [];\n+ var feature, clustered, cluster;\n+ for (var i = 0; i < this.features.length; ++i) {\n+ feature = this.features[i];\n+ if (feature.geometry) {\n+ clustered = false;\n+ for (var j = clusters.length - 1; j >= 0; --j) {\n+ cluster = clusters[j];\n+ if (this.shouldCluster(cluster, feature)) {\n+ this.addToCluster(cluster, feature);\n+ clustered = true;\n+ break\n+ }\n+ }\n+ if (!clustered) {\n+ clusters.push(this.createCluster(this.features[i]))\n+ }\n+ }\n+ }\n+ this.clustering = true;\n+ this.layer.removeAllFeatures();\n+ this.clustering = false;\n+ if (clusters.length > 0) {\n+ if (this.threshold > 1) {\n+ var clone = clusters.slice();\n+ clusters = [];\n+ var candidate;\n+ for (var i = 0, len = clone.length; i < len; ++i) {\n+ candidate = clone[i];\n+ if (candidate.attributes.count < this.threshold) {\n+ Array.prototype.push.apply(clusters, candidate.cluster)\n+ } else {\n+ clusters.push(candidate)\n+ }\n+ }\n+ }\n+ this.clustering = true;\n+ this.layer.addFeatures(clusters);\n+ this.clustering = false\n+ }\n+ this.clusters = clusters\n }\n- options.callback.call(options.scope, response)\n }\n },\n- filterDelete: function(filter, options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options);\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"commit\"\n- });\n- var root = this.format.createElementNSPlus(\"wfs:Transaction\", {\n- attributes: {\n- service: \"WFS\",\n- version: this.version\n- }\n- });\n- var deleteNode = this.format.createElementNSPlus(\"wfs:Delete\", {\n- attributes: {\n- typeName: (options.featureNS ? this.featurePrefix + \":\" : \"\") + options.featureType\n+ clustersExist: function() {\n+ var exist = false;\n+ if (this.clusters && this.clusters.length > 0 && this.clusters.length == this.layer.features.length) {\n+ exist = true;\n+ for (var i = 0; i < this.clusters.length; ++i) {\n+ if (this.clusters[i] != this.layer.features[i]) {\n+ exist = false;\n+ break\n+ }\n }\n+ }\n+ return exist\n+ },\n+ shouldCluster: function(cluster, feature) {\n+ var cc = cluster.geometry.getBounds().getCenterLonLat();\n+ var fc = feature.geometry.getBounds().getCenterLonLat();\n+ var distance = Math.sqrt(Math.pow(cc.lon - fc.lon, 2) + Math.pow(cc.lat - fc.lat, 2)) / this.resolution;\n+ return distance <= this.distance\n+ },\n+ addToCluster: function(cluster, feature) {\n+ cluster.cluster.push(feature);\n+ cluster.attributes.count += 1\n+ },\n+ createCluster: function(feature) {\n+ var center = feature.geometry.getBounds().getCenterLonLat();\n+ var cluster = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(center.lon, center.lat), {\n+ count: 1\n });\n- if (options.featureNS) {\n- deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, options.featureNS)\n+ cluster.cluster = [feature];\n+ return cluster\n+ },\n+ CLASS_NAME: \"OpenLayers.Strategy.Cluster\"\n+});\n+OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {\n+ bounds: null,\n+ resolution: null,\n+ ratio: 2,\n+ resFactor: null,\n+ response: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ this.layer.events.on({\n+ moveend: this.update,\n+ refresh: this.update,\n+ visibilitychanged: this.update,\n+ scope: this\n+ });\n+ this.update()\n }\n- var filterNode = this.format.writeNode(\"ogc:Filter\", filter);\n- deleteNode.appendChild(filterNode);\n- root.appendChild(deleteNode);\n- var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [root]);\n- return OpenLayers.Request.POST({\n- url: this.url,\n- callback: options.callback || function() {},\n- data: data\n- })\n+ return activated\n },\n- abort: function(response) {\n- if (response) {\n- response.priv.abort()\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ moveend: this.update,\n+ refresh: this.update,\n+ visibilitychanged: this.update,\n+ scope: this\n+ })\n }\n+ return deactivated\n },\n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1\"\n-});\n-OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n- version: \"1.0.0\",\n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n-});\n-OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n- version: \"1.1.0\",\n- initialize: function(options) {\n- OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);\n- if (this.outputFormat && !this.readFormat) {\n- if (this.outputFormat.toLowerCase() == \"gml2\") {\n- this.readFormat = new OpenLayers.Format.GML.v2({\n- featureType: this.featureType,\n- featureNS: this.featureNS,\n- geometryName: this.geometryName\n- })\n- } else if (this.outputFormat.toLowerCase() == \"json\") {\n- this.readFormat = new OpenLayers.Format.GeoJSON\n- }\n+ update: function(options) {\n+ var mapBounds = this.getMapBounds();\n+ if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) {\n+ this.calculateBounds(mapBounds);\n+ this.resolution = this.layer.map.getResolution();\n+ this.triggerRead(options)\n }\n },\n- CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_1_0\"\n-});\n-OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, {\n- formatOptions: null,\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions))\n+ getMapBounds: function() {\n+ if (this.layer.map === null) {\n+ return null\n+ }\n+ var bounds = this.layer.map.getExtent();\n+ if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) {\n+ bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection)\n }\n+ return bounds\n },\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy()\n+ invalidBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds()\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n+ var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);\n+ if (!invalid && this.resFactor) {\n+ var ratio = this.resolution / this.layer.map.getResolution();\n+ invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor\n+ }\n+ return invalid\n },\n- read: function(options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var data = this.format.write(options.params || options);\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- params: options.params,\n- headers: options.headers,\n- data: data\n- });\n- return response\n+ calculateBounds: function(mapBounds) {\n+ if (!mapBounds) {\n+ mapBounds = this.getMapBounds()\n+ }\n+ var center = mapBounds.getCenterLonLat();\n+ var dataWidth = mapBounds.getWidth() * this.ratio;\n+ var dataHeight = mapBounds.getHeight() * this.ratio;\n+ this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2)\n },\n- handleRead: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- response.data = this.parseData(request);\n- response.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE\n- }\n- options.callback.call(options.scope, response)\n+ triggerRead: function(options) {\n+ if (this.response && !(options && options.noAbort === true)) {\n+ this.layer.protocol.abort(this.response);\n+ this.layer.events.triggerEvent(\"loadend\")\n }\n+ var evt = {\n+ filter: this.createFilter()\n+ };\n+ this.layer.events.triggerEvent(\"loadstart\", evt);\n+ this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ filter: evt.filter,\n+ callback: this.merge,\n+ scope: this\n+ }, options))\n },\n- parseData: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ createFilter: function() {\n+ var filter = new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ value: this.bounds,\n+ projection: this.layer.projection\n+ });\n+ if (this.layer.filter) {\n+ filter = new OpenLayers.Filter.Logical({\n+ type: OpenLayers.Filter.Logical.AND,\n+ filters: [this.layer.filter, filter]\n+ })\n }\n- if (!doc || doc.length <= 0) {\n- return null\n+ return filter\n+ },\n+ merge: function(resp) {\n+ this.layer.destroyFeatures();\n+ if (resp.success()) {\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = this.layer.projection;\n+ var local = this.layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local)\n+ }\n+ }\n+ }\n+ this.layer.addFeatures(features)\n+ }\n+ } else {\n+ this.bounds = null\n }\n- return this.format.read(doc)\n+ this.response = null;\n+ this.layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ })\n },\n- CLASS_NAME: \"OpenLayers.Protocol.CSW.v2_0_2\"\n+ CLASS_NAME: \"OpenLayers.Strategy.BBOX\"\n });\n-OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {\n- fois: null,\n- formatOptions: null,\n- initialize: function(options) {\n- OpenLayers.Protocol.prototype.initialize.apply(this, [options]);\n- if (!options.format) {\n- this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(this.formatOptions)\n+OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {\n+ force: false,\n+ interval: 0,\n+ timer: null,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.call(this);\n+ if (activated) {\n+ if (this.layer.visibility === true) {\n+ this.start()\n+ }\n+ this.layer.events.on({\n+ visibilitychanged: this.reset,\n+ scope: this\n+ })\n }\n+ return activated\n },\n- destroy: function() {\n- if (this.options && !this.options.format) {\n- this.format.destroy()\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.stop();\n+ this.layer.events.un({\n+ visibilitychanged: this.reset,\n+ scope: this\n+ })\n }\n- this.format = null;\n- OpenLayers.Protocol.prototype.destroy.apply(this)\n+ return deactivated\n },\n- read: function(options) {\n- options = OpenLayers.Util.extend({}, options);\n- OpenLayers.Util.applyDefaults(options, this.options || {});\n- var response = new OpenLayers.Protocol.Response({\n- requestType: \"read\"\n- });\n- var format = this.format;\n- var data = OpenLayers.Format.XML.prototype.write.apply(format, [format.writeNode(\"sos:GetFeatureOfInterest\", {\n- fois: this.fois\n- })]);\n- response.priv = OpenLayers.Request.POST({\n- url: options.url,\n- callback: this.createCallback(this.handleRead, response, options),\n- data: data\n- });\n- return response\n+ reset: function() {\n+ if (this.layer.visibility === true) {\n+ this.start()\n+ } else {\n+ this.stop()\n+ }\n },\n- handleRead: function(response, options) {\n- if (options.callback) {\n- var request = response.priv;\n- if (request.status >= 200 && request.status < 300) {\n- response.features = this.parseFeatures(request);\n- response.code = OpenLayers.Protocol.Response.SUCCESS\n- } else {\n- response.code = OpenLayers.Protocol.Response.FAILURE\n- }\n- options.callback.call(options.scope, response)\n+ start: function() {\n+ if (this.interval && typeof this.interval === \"number\" && this.interval > 0) {\n+ this.timer = window.setInterval(OpenLayers.Function.bind(this.refresh, this), this.interval)\n }\n },\n- parseFeatures: function(request) {\n- var doc = request.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = request.responseText\n+ refresh: function() {\n+ if (this.layer && this.layer.refresh && typeof this.layer.refresh == \"function\") {\n+ this.layer.refresh({\n+ force: this.force\n+ })\n }\n- if (!doc || doc.length <= 0) {\n- return null\n+ },\n+ stop: function() {\n+ if (this.timer !== null) {\n+ window.clearInterval(this.timer);\n+ this.timer = null\n }\n- return this.format.read(doc)\n },\n- CLASS_NAME: \"OpenLayers.Protocol.SOS.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Strategy.Refresh\"\n });\n OpenLayers.Console.warn(\"OpenLayers.Rico is deprecated\");\n OpenLayers.Rico = OpenLayers.Rico || {};\n OpenLayers.Rico.Color = OpenLayers.Class({\n initialize: function(red, green, blue) {\n this.rgb = {\n r: red,\n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.mobile.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.mobile.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -13915,1106 +13915,539 @@\n this.tileCache = null;\n this.tileCacheIndex = null;\n this._destroyed = true;\n }\n \n });\n /* ======================================================================\n- OpenLayers/Layer/XYZ.js\n+ OpenLayers/Format.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n */\n \n-/** \n- * Class: OpenLayers.Layer.XYZ\n- * The XYZ class is designed to make it easier for people who have tiles\n- * arranged by a standard XYZ grid. \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+/**\n+ * Class: OpenLayers.Format\n+ * Base class for format reading/writing a variety of formats. Subclasses\n+ * of OpenLayers.Format are expected to have read and write methods.\n */\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Format = OpenLayers.Class({\n \n /**\n- * APIProperty: isBaseLayer\n- * Default is true, as this is designed to be a base tile source. \n+ * Property: options\n+ * {Object} A reference to options passed to the constructor.\n */\n- isBaseLayer: true,\n+ options: null,\n \n /**\n- * APIProperty: sphericalMercator\n- * Whether the tile extents should be set to the defaults for \n- * spherical mercator. Useful for things like OpenStreetMap.\n- * Default is false, except for the OSM subclass.\n+ * APIProperty: externalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The externalProjection is the projection used by\n+ * the content which is passed into read or which comes out of write.\n+ * In order to reproject, a projection transformation function for the\n+ * specified projections must be available. This support may be \n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- sphericalMercator: false,\n+ externalProjection: null,\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * APIProperty: internalProjection\n+ * {<OpenLayers.Projection>} When passed a externalProjection and\n+ * internalProjection, the format will reproject the geometries it\n+ * reads or writes. The internalProjection is the projection used by\n+ * the geometries which are returned by read or which are passed into\n+ * write. In order to reproject, a projection transformation function\n+ * for the specified projections must be available. This support may be\n+ * provided via proj4js or via a custom transformation function. See\n+ * {<OpenLayers.Projection.addTransform>} for more information on\n+ * custom transformations.\n */\n- zoomOffset: 0,\n+ internalProjection: null,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * APIProperty: data\n+ * {Object} When <keepData> is true, this is the parsed string sent to\n+ * <read>.\n */\n- serverResolutions: null,\n+ data: null,\n \n /**\n- * Constructor: OpenLayers.Layer.XYZ\n- *\n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * APIProperty: keepData\n+ * {Object} Maintain a reference (<data>) to the most recently read data.\n+ * Default is false.\n */\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options);\n- }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name || this.name, url || this.url, {},\n- options\n- ]);\n- },\n+ keepData: false,\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n+ * Constructor: OpenLayers.Format\n+ * Instances of this class are not useful. See one of the subclasses.\n *\n * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n+ * options - {Object} An optional object with properties to set on the\n+ * format\n+ *\n+ * Valid options:\n+ * keepData - {Boolean} If true, upon <read>, the data property will be\n+ * set to the parsed object (e.g. the json or xml object).\n+ *\n * Returns:\n- * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n+ * An instance of OpenLayers.Format\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name,\n- this.url,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n },\n \n /**\n- * Method: getURL\n- *\n+ * APIMethod: destroy\n+ * Clean up.\n+ */\n+ destroy: function() {},\n+\n+ /**\n+ * Method: read\n+ * Read data from a string, and return an object whose type depends on the\n+ * subclass. \n+ * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * data - {string} Data to read/parse.\n *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n+ * Depends on the subclass\n */\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = '' + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url);\n- }\n-\n- return OpenLayers.String.format(url, xyz);\n+ read: function(data) {\n+ throw new Error('Read not implemented.');\n },\n \n /**\n- * Method: getXYZ\n- * Calculates x, y and z for the given bounds.\n+ * Method: write\n+ * Accept an object, and return a string. \n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * object - {Object} Object to be serialized\n *\n * Returns:\n- * {Object} - an object with x, y and z properties.\n- */\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) /\n- (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) /\n- (res * this.tileSize.h));\n- var z = this.getServerZoom();\n-\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = ((x % limit) + limit) % limit;\n- }\n-\n- return {\n- 'x': x,\n- 'y': y,\n- 'z': z\n- };\n- },\n-\n- /* APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * {String} A string representation of the object.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n- this.maxExtent.bottom);\n- }\n+ write: function(object) {\n+ throw new Error('Write not implemented.');\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+ CLASS_NAME: \"OpenLayers.Format\"\n });\n /* ======================================================================\n- OpenLayers/Layer/OSM.js\n+ OpenLayers/Format/JSON.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/XYZ.js\n+ * Note:\n+ * This work draws heavily from the public domain JSON serializer/deserializer\n+ * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n+ * basic data prototypes.\n */\n \n /**\n- * Class: OpenLayers.Layer.OSM\n- * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n- * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n- * a different layer instead, you need to provide a different\n- * URL to the constructor. Here's an example for using OpenCycleMap:\n- * \n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n+ * @requires OpenLayers/Format.js\n */\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * APIProperty: name\n- * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n- * argument to the constructor is null or undefined.\n- */\n- name: \"OpenStreetMap\",\n-\n- /**\n- * APIProperty: url\n- * {String} The tileset URL scheme. Defaults to\n- * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n- * (the official OSM tileset) if the second argument to the constructor\n- * is null or undefined. To use another tileset you can have something\n- * like this:\n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n- */\n- url: [\n- 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n- ],\n-\n- /**\n- * Property: attribution\n- * {String} The layer attribution.\n- */\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n-\n- /**\n- * Property: sphericalMercator\n- * {Boolean}\n- */\n- sphericalMercator: true,\n-\n- /**\n- * Property: wrapDateLine\n- * {Boolean}\n- */\n- wrapDateLine: true,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- *\n- * When using OSM tilesets other than the default ones, it may be\n- * necessary to set this to\n- *\n- * (code)\n- * {crossOriginKeyword: null}\n- * (end)\n- *\n- * if the server does not send Access-Control-Allow-Origin headers.\n- */\n- tileOptions: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.OSM\n- *\n- * Parameters:\n- * name - {String} The layer name.\n- * url - {String} The tileset URL scheme.\n- * options - {Object} Configuration options for the layer. Any inherited\n- * layer option can be set in this object (e.g.\n- * <OpenLayers.Layer.Grid.buffer>).\n- */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options && this.options.tileOptions);\n- },\n-\n- /**\n- * Method: clone\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(\n- this.name, this.url, this.getOptions());\n- }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Bing.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/XYZ.js\n- */\n-\n-/** \n- * Class: OpenLayers.Layer.Bing\n- * Bing layer using direct tile access as provided by Bing Maps REST Services.\n- * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n- * information. Note: Terms of Service compliant use requires the map to be\n- * configured with an <OpenLayers.Control.Attribution> control and the\n- * attribution placed on or near the map.\n- * \n+ * Class: OpenLayers.Format.JSON\n+ * A parser to read/write JSON safely. Create a new instance with the\n+ * <OpenLayers.Format.JSON> constructor.\n+ *\n * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n+ * - <OpenLayers.Format>\n */\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * Property: key\n- * {String} API key for Bing maps, get your own key \n- * at http://bingmapsportal.com/ .\n- */\n- key: null,\n+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n \n /**\n- * Property: serverResolutions\n- * {Array} the resolutions provided by the Bing servers.\n+ * APIProperty: indent\n+ * {String} For \"pretty\" printing, the indent string will be used once for\n+ * each indentation level.\n */\n- serverResolutions: [\n- 156543.03390625, 78271.516953125, 39135.7584765625,\n- 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n- 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n- 305.74811309814453, 152.87405654907226, 76.43702827453613,\n- 38.218514137268066, 19.109257068634033, 9.554628534317017,\n- 4.777314267158508, 2.388657133579254, 1.194328566789627,\n- 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n- 0.07464553542435169\n- ],\n+ indent: \" \",\n \n /**\n- * Property: attributionTemplate\n- * {String}\n+ * APIProperty: space\n+ * {String} For \"pretty\" printing, the space string will be used after\n+ * the \":\" separating a name/value pair.\n */\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n- '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n- '<img src=\"${logo}\" /></a></div>${copyrights}' +\n- '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n- 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n- 'Terms of Use</a></span>',\n+ space: \" \",\n \n /**\n- * Property: metadata\n- * {Object} Metadata for this layer, as returned by the callback script\n+ * APIProperty: newline\n+ * {String} For \"pretty\" printing, the newline string will be used at the\n+ * end of each name/value pair or array item.\n */\n- metadata: null,\n+ newline: \"\\n\",\n \n /**\n- * Property: protocolRegex\n- * {RegExp} Regular expression to match and replace http: in bing urls\n+ * Property: level\n+ * {Integer} For \"pretty\" printing, this is incremented/decremented during\n+ * serialization.\n */\n- protocolRegex: /^http:/i,\n+ level: 0,\n \n /**\n- * APIProperty: type\n- * {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used. Default is \"Road\".\n+ * Property: pretty\n+ * {Boolean} Serialize with extra whitespace for structure. This is set\n+ * by the <write> method.\n */\n- type: \"Road\",\n+ pretty: false,\n \n /**\n- * APIProperty: culture\n- * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n- * for the definition and the possible values. Default is \"en-US\".\n+ * Property: nativeJSON\n+ * {Boolean} Does the browser support native json?\n */\n- culture: \"en-US\",\n+ nativeJSON: (function() {\n+ return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n+ })(),\n \n /**\n- * APIProperty: metadataParams\n- * {Object} Optional url parameters for the Get Imagery Metadata request\n- * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n- */\n- metadataParams: null,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- */\n- tileOptions: null,\n-\n- /** APIProperty: protocol\n- * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n- * Can be 'http:' 'https:' or ''\n- *\n- * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n- * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n- * this is undocumented and the Imagery Metadata API always returns HTTP\n- * urls.\n+ * Constructor: OpenLayers.Format.JSON\n+ * Create a new parser for JSON.\n *\n- * Default is '', unless when executed from a file:/// uri, in which case\n- * it is 'http:'.\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n */\n- protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n \n /**\n- * Constructor: OpenLayers.Layer.Bing\n- * Create a new Bing layer.\n- *\n- * Example:\n- * (code)\n- * var road = new OpenLayers.Layer.Bing({\n- * name: \"My Bing Aerial Layer\",\n- * type: \"Aerial\",\n- * key: \"my-api-key-here\",\n- * });\n- * (end)\n+ * APIMethod: read\n+ * Deserialize a json string.\n *\n * Parameters:\n- * options - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * key - {String} Bing Maps API key for your application. Get one at\n- * http://bingmapsportal.com/.\n- * type - {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used.\n- *\n- * Any other documented layer properties can be provided in the config object.\n- */\n- initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n-\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options.tileOptions);\n- this.loadMetadata();\n- },\n-\n- /**\n- * Method: loadMetadata\n+ * json - {String} A JSON string\n+ * filter - {Function} A function which will be called for every key and\n+ * value at every level of the final result. Each value will be\n+ * replaced by the result of the filter function. This can be used to\n+ * reform generic objects into instances of classes, or to transform\n+ * date strings into Date objects.\n+ * \n+ * Returns:\n+ * {Object} An object, array, string, or number .\n */\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- // link the processMetadata method to the global scope and bind it\n- // to this instance\n- window[this._callbackId] = OpenLayers.Function.bind(\n- OpenLayers.Layer.Bing.processMetadata, this\n- );\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n- this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script);\n- },\n+ read: function(json, filter) {\n+ var object;\n+ if (this.nativeJSON) {\n+ object = JSON.parse(json, filter);\n+ } else try {\n+ /**\n+ * Parsing happens in three stages. In the first stage, we run the\n+ * text against a regular expression which looks for non-JSON\n+ * characters. We are especially concerned with '()' and 'new'\n+ * because they can cause invocation, and '=' because it can\n+ * cause mutation. But just to be safe, we will reject all\n+ * unexpected characters.\n+ */\n+ if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n \n- /**\n- * Method: initLayer\n- *\n- * Sets layer properties according to the metadata provided by the API\n- */\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n- }\n- this.addOptions({\n- maxResolution: Math.min(\n- this.serverResolutions[res.zoomMin],\n- this.maxResolution || Number.POSITIVE_INFINITY\n- ),\n- numZoomLevels: Math.min(\n- res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n- )\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw();\n- }\n- this.updateAttribution();\n- },\n+ /**\n+ * In the second stage we use the eval function to compile the\n+ * text into a JavaScript structure. The '{' operator is\n+ * subject to a syntactic ambiguity in JavaScript - it can\n+ * begin a block or an object literal. We wrap the text in\n+ * parens to eliminate the ambiguity.\n+ */\n+ object = eval('(' + json + ')');\n \n- /**\n- * Method: getURL\n- *\n- * Paramters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- getURL: function(bounds) {\n- if (!this.url) {\n- return;\n- }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = '0';\n- var mask = 1 << (i - 1);\n- if ((x & mask) != 0) {\n- digit++;\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++;\n+ /**\n+ * In the optional third stage, we recursively walk the new\n+ * structure, passing each name/value pair to a filter\n+ * function for possible transformation.\n+ */\n+ if (typeof filter === 'function') {\n+ function walk(k, v) {\n+ if (v && typeof v === 'object') {\n+ for (var i in v) {\n+ if (v.hasOwnProperty(i)) {\n+ v[i] = walk(i, v[i]);\n+ }\n+ }\n+ }\n+ return filter(k, v);\n+ }\n+ object = walk('', object);\n+ }\n }\n- quadDigits.push(digit);\n+ } catch (e) {\n+ // Fall through if the regexp test fails.\n }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl('' + x + y + z, this.url);\n-\n- return OpenLayers.String.format(url, {\n- 'quadkey': quadKey\n- });\n- },\n \n- /**\n- * Method: updateAttribution\n- * Updates the attribution according to the requirements outlined in\n- * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n- */\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n- return;\n- }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(\n- this.map.getProjectionObject(),\n- new OpenLayers.Projection(\"EPSG:4326\")\n- );\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n- this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- // axis order provided is Y,X\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) &&\n- zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \";\n- }\n- }\n+ if (this.keepData) {\n+ this.data = object;\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- });\n- },\n \n- /**\n- * Method: setMap\n- */\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution);\n+ return object;\n },\n \n /**\n- * APIMethod: clone\n- * \n+ * APIMethod: write\n+ * Serialize an object into a JSON string.\n+ *\n * Parameters:\n- * obj - {Object}\n- * \n+ * value - {String} The object, array, string, number, boolean or date\n+ * to be serialized.\n+ * pretty - {Boolean} Structure the output with newlines and indentation.\n+ * Default is false.\n+ *\n * Returns:\n- * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n+ * {String} The JSON string representation of the input value.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options);\n+ write: function(value, pretty) {\n+ this.pretty = !!pretty;\n+ var json = null;\n+ var type = typeof value;\n+ if (this.serialize[type]) {\n+ try {\n+ json = (!this.pretty && this.nativeJSON) ?\n+ JSON.stringify(value) :\n+ this.serialize[type].apply(this, [value]);\n+ } catch (err) {\n+ OpenLayers.Console.error(\"Trouble serializing: \" + err);\n+ }\n }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- this.map &&\n- this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n+ return json;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n-});\n-\n-/**\n- * Function: OpenLayers.Layer.Bing.processMetadata\n- * This function will be bound to an instance, linked to the global scope with\n- * an id, and called by the JSONP script returned by the API.\n- *\n- * Parameters:\n- * metadata - {Object} metadata as returned by the API\n- */\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined; // cannot delete from window in IE\n- delete this._callbackId;\n-};\n-/* ======================================================================\n- OpenLayers/Renderer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Renderer \n- * This is the base class for all renderers.\n- *\n- * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n- * It is largely composed of virtual functions that are to be implemented\n- * in technology-specific subclasses, but there is some generic code too.\n- * \n- * The functions that *are* implemented here merely deal with the maintenance\n- * of the size and extent variables, as well as the cached 'resolution' \n- * value. \n- * \n- * A note to the user that all subclasses should use getResolution() instead\n- * of directly accessing this.resolution in order to correctly use the \n- * cacheing system.\n- *\n- */\n-OpenLayers.Renderer = OpenLayers.Class({\n-\n- /** \n- * Property: container\n- * {DOMElement} \n- */\n- container: null,\n-\n- /**\n- * Property: root\n- * {DOMElement}\n- */\n- root: null,\n-\n- /** \n- * Property: extent\n- * {<OpenLayers.Bounds>}\n- */\n- extent: null,\n-\n- /**\n- * Property: locked\n- * {Boolean} If the renderer is currently in a state where many things\n- * are changing, the 'locked' property is set to true. This means \n- * that renderers can expect at least one more drawFeature event to be\n- * called with the 'locked' property set to 'true': In some renderers,\n- * this might make sense to use as a 'only update local information'\n- * flag. \n- */\n- locked: false,\n-\n- /** \n- * Property: size\n- * {<OpenLayers.Size>} \n- */\n- size: null,\n-\n- /**\n- * Property: resolution\n- * {Float} cache of current map resolution\n- */\n- resolution: null,\n-\n- /**\n- * Property: map \n- * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n- */\n- map: null,\n-\n- /**\n- * Property: featureDx\n- * {Number} Feature offset in x direction. Will be calculated for and\n- * applied to the current feature while rendering (see\n- * <calculateFeatureDx>).\n- */\n- featureDx: 0,\n-\n /**\n- * Constructor: OpenLayers.Renderer \n+ * Method: writeIndent\n+ * Output an indentation string depending on the indentation level.\n *\n- * Parameters:\n- * containerID - {<String>} \n- * options - {Object} options for this renderer. See sublcasses for\n- * supported options.\n- */\n- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.container = null;\n- this.extent = null;\n- this.size = null;\n- this.resolution = null;\n- this.map = null;\n- },\n-\n- /**\n- * APIMethod: supported\n- * This should be overridden by specific subclasses\n- * \n * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n+ * {String} An appropriate indentation string.\n */\n- supported: function() {\n- return false;\n+ writeIndent: function() {\n+ var pieces = [];\n+ if (this.pretty) {\n+ for (var i = 0; i < this.level; ++i) {\n+ pieces.push(this.indent);\n+ }\n+ }\n+ return pieces.join('');\n },\n \n /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n- *\n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n- * We nullify the resolution cache (this.resolution) if resolutionChanged\n- * is set to true - this way it will be re-computed on the next\n- * getResolution() request.\n- *\n- * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n+ * Method: writeNewline\n+ * Output a string representing a newline if in pretty printing mode.\n *\n * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * {String} A string representing a new line.\n */\n- setExtent: function(extent, resolutionChanged) {\n- this.extent = extent.clone();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio);\n- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n- }\n- if (resolutionChanged) {\n- this.resolution = null;\n- }\n- return true;\n+ writeNewline: function() {\n+ return (this.pretty) ? this.newline : '';\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n- * \n- * Resolution has probably changed, so we nullify the resolution \n- * cache (this.resolution) -- this way it will be re-computed when \n- * next it is needed.\n+ * Method: writeSpace\n+ * Output a string representing a space if in pretty printing mode.\n *\n- * Parameters:\n- * size - {<OpenLayers.Size>} \n- */\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null;\n- },\n-\n- /** \n- * Method: getResolution\n- * Uses cached copy of resolution if available to minimize computing\n- * \n * Returns:\n- * {Float} The current map's resolution\n+ * {String} A space.\n */\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution;\n+ writeSpace: function() {\n+ return (this.pretty) ? this.space : '';\n },\n \n /**\n- * Method: drawFeature\n- * Draw the feature. The optional style argument can be used\n- * to override the feature's own style. This method should only\n- * be called from layer.drawFeature().\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {<Object>}\n- * \n- * Returns:\n- * {Boolean} true if the feature has been drawn completely, false if not,\n- * undefined if the feature had no geometry\n+ * Property: serialize\n+ * Object with properties corresponding to the serializable data types.\n+ * Property values are functions that do the actual serializing.\n */\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style;\n- }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n- };\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds);\n- }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n+ serialize: {\n+ /**\n+ * Method: serialize.object\n+ * Transform an object into a JSON string.\n+ *\n+ * Parameters:\n+ * object - {Object} The object to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the object.\n+ */\n+ 'object': function(object) {\n+ // three special objects that we want to treat differently\n+ if (object == null) {\n+ return \"null\";\n+ }\n+ if (object.constructor == Date) {\n+ return this.serialize.date.apply(this, [object]);\n+ }\n+ if (object.constructor == Array) {\n+ return this.serialize.array.apply(this, [object]);\n+ }\n+ var pieces = ['{'];\n+ this.level += 1;\n+ var key, keyJSON, valueJSON;\n \n- var location = feature.geometry.getCentroid();\n- if (style.labelXOffset || style.labelYOffset) {\n- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n- var res = this.getResolution();\n- location.move(xOffset * res, yOffset * res);\n+ var addComma = false;\n+ for (key in object) {\n+ if (object.hasOwnProperty(key)) {\n+ // recursive calls need to allow for sub-classing\n+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [key, this.pretty]);\n+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [object[key], this.pretty]);\n+ if (keyJSON != null && valueJSON != null) {\n+ if (addComma) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(),\n+ keyJSON, ':', this.writeSpace(), valueJSON);\n+ addComma = true;\n }\n- this.drawText(feature.id, style, location);\n- } else {\n- this.removeText(feature.id);\n }\n- return rendered;\n }\n- }\n- },\n-\n- /**\n- * Method: calculateFeatureDx\n- * {Number} Calculates the feature offset in x direction. Looking at the\n- * center of the feature bounds and the renderer extent, we calculate how\n- * many world widths the two are away from each other. This distance is\n- * used to shift the feature as close as possible to the center of the\n- * current enderer extent, which ensures that the feature is visible in the\n- * current viewport.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n- * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n- */\n- calculateFeatureDx: function(bounds, worldBounds) {\n- this.featureDx = 0;\n- if (worldBounds) {\n- var worldWidth = worldBounds.getWidth(),\n- rendererCenterX = (this.extent.left + this.extent.right) / 2,\n- featureCenterX = (bounds.left + bounds.right) / 2,\n- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n- this.featureDx = worldsAway * worldWidth;\n- }\n- },\n-\n- /** \n- * Method: drawGeometry\n- * \n- * Draw a geometry. This should only be called from the renderer itself.\n- * Use layer.drawFeature() from outside the renderer.\n- * virtual function\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * style - {Object} \n- * featureId - {<String>} \n- */\n- drawGeometry: function(geometry, style, featureId) {},\n-\n- /**\n- * Method: drawText\n- * Function for drawing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- * style -\n- * location - {<OpenLayers.Geometry.Point>}\n- */\n- drawText: function(featureId, style, location) {},\n \n- /**\n- * Method: removeText\n- * Function for removing text labels.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * featureId - {String}\n- */\n- removeText: function(featureId) {},\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), '}');\n+ return pieces.join('');\n+ },\n \n- /**\n- * Method: clear\n- * Clear all vectors from the renderer.\n- * virtual function.\n- */\n- clear: function() {},\n+ /**\n+ * Method: serialize.array\n+ * Transform an array into a JSON string.\n+ *\n+ * Parameters:\n+ * array - {Array} The array to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the array.\n+ */\n+ 'array': function(array) {\n+ var json;\n+ var pieces = ['['];\n+ this.level += 1;\n \n- /**\n- * Method: getFeatureIdFromEvent\n- * Returns a feature id from an event on the renderer. \n- * How this happens is specific to the renderer. This should be\n- * called from layer.getFeatureFromEvent().\n- * Virtual function.\n- * \n- * Parameters:\n- * evt - {<OpenLayers.Event>} \n- *\n- * Returns:\n- * {String} A feature id or undefined.\n- */\n- getFeatureIdFromEvent: function(evt) {},\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ // recursive calls need to allow for sub-classing\n+ json = OpenLayers.Format.JSON.prototype.write.apply(this,\n+ [array[i], this.pretty]);\n+ if (json != null) {\n+ if (i > 0) {\n+ pieces.push(',');\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), json);\n+ }\n+ }\n \n- /**\n- * Method: eraseFeatures \n- * This is called by the layer to erase features\n- * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- */\n- eraseFeatures: function(features) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- for (var i = 0, len = features.length; i < len; ++i) {\n- var feature = features[i];\n- this.eraseGeometry(feature.geometry, feature.id);\n- this.removeText(feature.id);\n- }\n- },\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), ']');\n+ return pieces.join('');\n+ },\n \n- /**\n- * Method: eraseGeometry\n- * Remove a geometry from the renderer (by id).\n- * virtual function.\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * featureId - {String}\n- */\n- eraseGeometry: function(geometry, featureId) {},\n+ /**\n+ * Method: serialize.string\n+ * Transform a string into a JSON string.\n+ *\n+ * Parameters:\n+ * string - {String} The string to be serialized\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the string.\n+ */\n+ 'string': function(string) {\n+ // If the string contains no control characters, no quote characters, and no\n+ // backslash characters, then we can simply slap some quotes around it.\n+ // Otherwise we must also replace the offending characters with safe\n+ // sequences. \n+ var m = {\n+ '\\b': '\\\\b',\n+ '\\t': '\\\\t',\n+ '\\n': '\\\\n',\n+ '\\f': '\\\\f',\n+ '\\r': '\\\\r',\n+ '\"': '\\\\\"',\n+ '\\\\': '\\\\\\\\'\n+ };\n+ if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n+ return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n+ var c = m[b];\n+ if (c) {\n+ return c;\n+ }\n+ c = b.charCodeAt();\n+ return '\\\\u00' +\n+ Math.floor(c / 16).toString(16) +\n+ (c % 16).toString(16);\n+ }) + '\"';\n+ }\n+ return '\"' + string + '\"';\n+ },\n \n- /**\n- * Method: moveRoot\n- * moves this renderer's root to a (different) renderer.\n- * To be implemented by subclasses that require a common renderer root for\n- * feature selection.\n- * \n- * Parameters:\n- * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n- */\n- moveRoot: function(renderer) {},\n+ /**\n+ * Method: serialize.number\n+ * Transform a number into a JSON string.\n+ *\n+ * Parameters:\n+ * number - {Number} The number to be serialized.\n+ *\n+ * Returns:\n+ * {String} A JSON string representing the number.\n+ */\n+ 'number': function(number) {\n+ return isFinite(number) ? String(number) : \"null\";\n+ },\n \n- /**\n- * Method: getRenderLayerId\n- * Gets the layer that this renderer's output appears on. If moveRoot was\n- * used, this will be different from the id of the layer containing the\n- * features rendered by this renderer.\n- * \n- * Returns:\n- * {String} the id of the output layer.\n- */\n- getRenderLayerId: function() {\n- return this.container.id;\n- },\n+ /**\n+ * Method: serialize.boolean\n+ * Transform a boolean into a JSON string.\n+ *\n+ * Parameters:\n+ * bool - {Boolean} The boolean to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the boolean.\n+ */\n+ 'boolean': function(bool) {\n+ return String(bool);\n+ },\n \n- /**\n- * Method: applyDefaultSymbolizer\n- * \n- * Parameters:\n- * symbolizer - {Object}\n- * \n- * Returns:\n- * {Object}\n- */\n- applyDefaultSymbolizer: function(symbolizer) {\n- var result = OpenLayers.Util.extend({},\n- OpenLayers.Renderer.defaultSymbolizer);\n- if (symbolizer.stroke === false) {\n- delete result.strokeWidth;\n- delete result.strokeColor;\n- }\n- if (symbolizer.fill === false) {\n- delete result.fillColor;\n+ /**\n+ * Method: serialize.object\n+ * Transform a date into a JSON string.\n+ *\n+ * Parameters:\n+ * date - {Date} The date to be serialized.\n+ * \n+ * Returns:\n+ * {String} A JSON string representing the date.\n+ */\n+ 'date': function(date) {\n+ function format(number) {\n+ // Format integers to have at least two digits.\n+ return (number < 10) ? '0' + number : number;\n+ }\n+ return '\"' + date.getFullYear() + '-' +\n+ format(date.getMonth() + 1) + '-' +\n+ format(date.getDate()) + 'T' +\n+ format(date.getHours()) + ':' +\n+ format(date.getMinutes()) + ':' +\n+ format(date.getSeconds()) + '\"';\n }\n- OpenLayers.Util.extend(result, symbolizer);\n- return result;\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Renderer.defaultSymbolizer\n- * {Object} Properties from this symbolizer will be applied to symbolizers\n- * with missing properties. This can also be used to set a global\n- * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n- * following code before rendering any vector features:\n- * (code)\n- * OpenLayers.Renderer.defaultSymbolizer = {\n- * fillColor: \"#808080\",\n- * fillOpacity: 1,\n- * strokeColor: \"#000000\",\n- * strokeOpacity: 1,\n- * strokeWidth: 1,\n- * pointRadius: 3,\n- * graphicName: \"square\"\n- * };\n- * (end)\n- */\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: 'cm'\n-};\n-\n-\n+ CLASS_NAME: \"OpenLayers.Format.JSON\"\n \n-/**\n- * Constant: OpenLayers.Renderer.symbol\n- * Coordinate arrays for well known (named) symbols.\n- */\n-OpenLayers.Renderer.symbol = {\n- \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n- 303, 215, 231, 161, 321, 161, 350, 75\n- ],\n- \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n- 4, 0\n- ],\n- \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n- \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n- \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n-};\n+});\n /* ======================================================================\n OpenLayers/Feature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -15751,2453 +15184,14 @@\n \n },\n 'delete': {\n display: \"none\"\n }\n };\n /* ======================================================================\n- OpenLayers/Style.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Style\n- * This class represents a UserStyle obtained\n- * from a SLD, containing styling rules.\n- */\n-OpenLayers.Style = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n-\n- /**\n- * APIProperty: name\n- * {String}\n- */\n- name: null,\n-\n- /**\n- * Property: title\n- * {String} Title of this style (set if included in SLD)\n- */\n- title: null,\n-\n- /**\n- * Property: description\n- * {String} Description of this style (set if abstract is included in SLD)\n- */\n- description: null,\n-\n- /**\n- * APIProperty: layerName\n- * {<String>} name of the layer that this style belongs to, usually\n- * according to the NamedLayer attribute of an SLD document.\n- */\n- layerName: null,\n-\n- /**\n- * APIProperty: isDefault\n- * {Boolean}\n- */\n- isDefault: false,\n-\n- /** \n- * Property: rules \n- * {Array(<OpenLayers.Rule>)}\n- */\n- rules: null,\n-\n- /**\n- * APIProperty: context\n- * {Object} An optional object with properties that symbolizers' property\n- * values should be evaluated against. If no context is specified,\n- * feature.attributes will be used\n- */\n- context: null,\n-\n- /**\n- * Property: defaultStyle\n- * {Object} hash of style properties to use as default for merging\n- * rule-based style symbolizers onto. If no rules are defined,\n- * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n- * true, the defaultStyle will only be taken into account if there are\n- * rules defined.\n- */\n- defaultStyle: null,\n-\n- /**\n- * Property: defaultsPerSymbolizer\n- * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n- * of every rule. Properties of the <defaultStyle> will also be used to set\n- * missing symbolizer properties if the symbolizer has stroke, fill or\n- * graphic set to true. Default is false.\n- */\n- defaultsPerSymbolizer: false,\n-\n- /**\n- * Property: propertyStyles\n- * {Hash of Boolean} cache of style properties that need to be parsed for\n- * propertyNames. Property names are keys, values won't be used.\n- */\n- propertyStyles: null,\n-\n-\n- /** \n- * Constructor: OpenLayers.Style\n- * Creates a UserStyle.\n- *\n- * Parameters:\n- * style - {Object} Optional hash of style properties that will be\n- * used as default style for this style object. This style\n- * applies if no rules are specified. Symbolizers defined in\n- * rules will extend this default style.\n- * options - {Object} An optional object with properties to set on the\n- * style.\n- *\n- * Valid options:\n- * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n- * style.\n- * \n- * Returns:\n- * {<OpenLayers.Style>}\n- */\n- initialize: function(style, options) {\n-\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules);\n- }\n-\n- // use the default style from OpenLayers.Feature.Vector if no style\n- // was given in the constructor\n- this.setDefaultStyle(style ||\n- OpenLayers.Feature.Vector.style[\"default\"]);\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null;\n- }\n- this.rules = null;\n- this.defaultStyle = null;\n- },\n-\n- /**\n- * Method: createSymbolizer\n- * creates a style by applying all feature-dependent rules to the base\n- * style.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n- * \n- * Returns:\n- * {Object} symbolizer hash\n- */\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n- OpenLayers.Util.extend({}, this.defaultStyle), feature);\n-\n- var rules = this.rules;\n-\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- // does the rule apply?\n- var applies = rule.evaluate(feature);\n-\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule);\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature);\n- }\n- }\n- }\n-\n- // if no other rules apply, apply the rules with else filters\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature);\n- }\n- }\n-\n- // don't display if there were rules but none applied\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\";\n- }\n-\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label);\n- }\n-\n- return style;\n- },\n-\n- /**\n- * Method: applySymbolizer\n- *\n- * Parameters:\n- * rule - {<OpenLayers.Rule>}\n- * style - {Object}\n- * feature - {<OpenLayer.Feature.Vector>}\n- *\n- * Returns:\n- * {Object} A style with new symbolizer applied.\n- */\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ?\n- this.getSymbolizerPrefix(feature.geometry) :\n- OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n-\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n-\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- });\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- });\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- });\n- }\n- }\n-\n- // merge the style with the current style\n- return this.createLiterals(\n- OpenLayers.Util.extend(style, symbolizer), feature);\n- },\n-\n- /**\n- * Method: createLiterals\n- * creates literals for all style properties that have an entry in\n- * <this.propertyStyles>.\n- * \n- * Parameters:\n- * style - {Object} style to create literals for. Will be modified\n- * inline.\n- * feature - {Object}\n- * \n- * Returns:\n- * {Object} the modified style\n- */\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n-\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n- }\n- return style;\n- },\n-\n- /**\n- * Method: findPropertyStyles\n- * Looks into all rules for this style and the defaultStyle to collect\n- * all the style hash property names containing ${...} strings that have\n- * to be replaced using the createLiteral method before returning them.\n- * \n- * Returns:\n- * {Object} hash of property names that need createLiteral parsing. The\n- * name of the property is the key, and the value is true;\n- */\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n-\n- // check the default style\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n-\n- // walk through all rules to check for properties in their symbolizer\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n- this.addPropertyStyles(propertyStyles, value);\n- } else {\n- // symbolizer is a hash of style properties\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break;\n- }\n- }\n- }\n- return propertyStyles;\n- },\n-\n- /**\n- * Method: addPropertyStyles\n- * \n- * Parameters:\n- * propertyStyles - {Object} hash to add new property styles to. Will be\n- * modified inline\n- * symbolizer - {Object} search this symbolizer for property styles\n- * \n- * Returns:\n- * {Object} propertyStyles hash\n- */\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" &&\n- property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true;\n- }\n- }\n- return propertyStyles;\n- },\n-\n- /**\n- * APIMethod: addRules\n- * Adds rules to this style.\n- * \n- * Parameters:\n- * rules - {Array(<OpenLayers.Rule>)}\n- */\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles();\n- },\n-\n- /**\n- * APIMethod: setDefaultStyle\n- * Sets the default style for this style object.\n- * \n- * Parameters:\n- * style - {Object} Hash of style properties\n- */\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles();\n- },\n-\n- /**\n- * Method: getSymbolizerPrefix\n- * Returns the correct symbolizer prefix according to the\n- * geometry type of the passed geometry\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {String} key of the according symbolizer\n- */\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i];\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this style.\n- * \n- * Returns:\n- * {<OpenLayers.Style>} Clone of this style.\n- */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- // clone rules\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone());\n- }\n- }\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- //clone default style\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Style\"\n-});\n-\n-\n-/**\n- * Function: createLiteral\n- * converts a style value holding a combination of PropertyName and Literal\n- * into a Literal, taking the property values from the passed features.\n- * \n- * Parameters:\n- * value - {String} value to parse. If this string contains a construct like\n- * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n- * will be replaced by the value of the \"bar\" attribute of the passed\n- * feature.\n- * context - {Object} context to take attribute values from\n- * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n- * <OpenLayers.String.format> for evaluating functions in the\n- * context.\n- * property - {String} optional, name of the property for which the literal is\n- * being created for evaluating functions in the context.\n- * \n- * Returns:\n- * {String} the parsed value. In the example of the value parameter above, the\n- * result would be \"foo valueOfBar\", assuming that the passed feature has an\n- * attribute named \"bar\" with the value \"valueOfBar\".\n- */\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = (isNaN(value) || !value) ? value : parseFloat(value);\n- }\n- return value;\n-};\n-\n-/**\n- * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n- * {Array} prefixes of the sld symbolizers. These are the\n- * same as the main geometry types\n- */\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n- 'Raster'\n-];\n-/* ======================================================================\n- OpenLayers/StyleMap.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Style.js\n- * @requires OpenLayers/Feature/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.StyleMap\n- */\n-OpenLayers.StyleMap = OpenLayers.Class({\n-\n- /**\n- * Property: styles\n- * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known\n- * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n- */\n- styles: null,\n-\n- /**\n- * Property: extendDefault\n- * {Boolean} if true, every render intent will extend the symbolizers\n- * specified for the \"default\" intent at rendering time. Otherwise, every\n- * rendering intent will be treated as a completely independent style.\n- */\n- extendDefault: true,\n-\n- /**\n- * Constructor: OpenLayers.StyleMap\n- * \n- * Parameters:\n- * style - {Object} Optional. Either a style hash, or a style object, or\n- * a hash of style objects (style hashes) keyed by rendering\n- * intent. If just one style hash or style object is passed,\n- * this will be used for all known render intents (default,\n- * select, temporary)\n- * options - {Object} optional hash of additional options for this\n- * instance\n- */\n- initialize: function(style, options) {\n- this.styles = {\n- \"default\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"default\"]),\n- \"select\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"select\"]),\n- \"temporary\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"temporary\"]),\n- \"delete\": new OpenLayers.Style(\n- OpenLayers.Feature.Vector.style[\"delete\"])\n- };\n-\n- // take whatever the user passed as style parameter and convert it\n- // into parts of stylemap.\n- if (style instanceof OpenLayers.Style) {\n- // user passed a style object\n- this.styles[\"default\"] = style;\n- this.styles[\"select\"] = style;\n- this.styles[\"temporary\"] = style;\n- this.styles[\"delete\"] = style;\n- } else if (typeof style == \"object\") {\n- for (var key in style) {\n- if (style[key] instanceof OpenLayers.Style) {\n- // user passed a hash of style objects\n- this.styles[key] = style[key];\n- } else if (typeof style[key] == \"object\") {\n- // user passsed a hash of style hashes\n- this.styles[key] = new OpenLayers.Style(style[key]);\n- } else {\n- // user passed a style hash (i.e. symbolizer)\n- this.styles[\"default\"] = new OpenLayers.Style(style);\n- this.styles[\"select\"] = new OpenLayers.Style(style);\n- this.styles[\"temporary\"] = new OpenLayers.Style(style);\n- this.styles[\"delete\"] = new OpenLayers.Style(style);\n- break;\n- }\n- }\n- }\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy();\n- }\n- this.styles = null;\n- },\n-\n- /**\n- * Method: createSymbolizer\n- * Creates the symbolizer for a feature for a render intent.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} The feature to evaluate the rules\n- * of the intended style against.\n- * intent - {String} The intent determines the symbolizer that will be\n- * used to draw the feature. Well known intents are \"default\"\n- * (for just drawing the features), \"select\" (for selected\n- * features) and \"temporary\" (for drawing features).\n- * \n- * Returns:\n- * {Object} symbolizer hash\n- */\n- createSymbolizer: function(feature, intent) {\n- if (!feature) {\n- feature = new OpenLayers.Feature.Vector();\n- }\n- if (!this.styles[intent]) {\n- intent = \"default\";\n- }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n- }\n- return OpenLayers.Util.extend(defaultSymbolizer,\n- this.styles[intent].createSymbolizer(feature));\n- },\n-\n- /**\n- * Method: addUniqueValueRules\n- * Convenience method to create comparison rules for unique values of a\n- * property. The rules will be added to the style object for a specified\n- * rendering intent. This method is a shortcut for creating something like\n- * the \"unique value legends\" familiar from well known desktop GIS systems\n- * \n- * Parameters:\n- * renderIntent - {String} rendering intent to add the rules to\n- * property - {String} values of feature attributes to create the\n- * rules for\n- * symbolizers - {Object} Hash of symbolizers, keyed by the desired\n- * property values \n- * context - {Object} An optional object with properties that\n- * symbolizers' property values should be evaluated\n- * against. If no context is specified, feature.attributes\n- * will be used\n- */\n- addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n- var rules = [];\n- for (var value in symbolizers) {\n- rules.push(new OpenLayers.Rule({\n- symbolizer: symbolizers[value],\n- context: context,\n- filter: new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- property: property,\n- value: value\n- })\n- }));\n- }\n- this.styles[renderIntent].addRules(rules);\n- },\n-\n- CLASS_NAME: \"OpenLayers.StyleMap\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Vector.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Renderer.js\n- * @requires OpenLayers/StyleMap.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Lang.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Vector\n- * Instances of OpenLayers.Layer.Vector are used to render vector data from\n- * a variety of sources. Create a new vector layer with the\n- * <OpenLayers.Layer.Vector> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer>\n- */\n-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /**\n- * APIProperty: events\n- * {<OpenLayers.Events>}\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * layer.events.register(type, obj, listener);\n- * (end)\n- *\n- * Listeners will be called with a reference to an event object. The\n- * properties of this event depends on exactly what happened.\n- *\n- * All event objects have at least the following properties:\n- * object - {Object} A reference to layer.events.object.\n- * element - {DOMElement} A reference to layer.events.element.\n- *\n- * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n- * beforefeatureadded - Triggered before a feature is added. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be added. To stop the feature from being added, a\n- * listener should return false.\n- * beforefeaturesadded - Triggered before an array of features is added.\n- * Listeners will receive an object with a *features* property\n- * referencing the feature to be added. To stop the features from\n- * being added, a listener should return false.\n- * featureadded - Triggered after a feature is added. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the added feature.\n- * featuresadded - Triggered after features are added. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of added features.\n- * beforefeatureremoved - Triggered before a feature is removed. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be removed.\n- * beforefeaturesremoved - Triggered before multiple features are removed. \n- * Listeners will receive an object with a *features* property\n- * referencing the features to be removed.\n- * featureremoved - Triggerd after a feature is removed. The event\n- * object passed to listeners will have a *feature* property with a\n- * reference to the removed feature.\n- * featuresremoved - Triggered after features are removed. The event\n- * object passed to listeners will have a *features* property with a\n- * reference to an array of removed features.\n- * beforefeatureselected - Triggered before a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * feature to be selected. To stop the feature from being selectd, a\n- * listener should return false.\n- * featureselected - Triggered after a feature is selected. Listeners\n- * will receive an object with a *feature* property referencing the\n- * selected feature.\n- * featureunselected - Triggered after a feature is unselected.\n- * Listeners will receive an object with a *feature* property\n- * referencing the unselected feature.\n- * beforefeaturemodified - Triggered when a feature is selected to \n- * be modified. Listeners will receive an object with a *feature* \n- * property referencing the selected feature.\n- * featuremodified - Triggered when a feature has been modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * afterfeaturemodified - Triggered when a feature is finished being modified.\n- * Listeners will receive an object with a *feature* property referencing \n- * the modified feature.\n- * vertexmodified - Triggered when a vertex within any feature geometry\n- * has been modified. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * modification.\n- * vertexremoved - Triggered when a vertex within any feature geometry\n- * has been deleted. Listeners will receive an object with a\n- * *feature* property referencing the modified feature, a *vertex*\n- * property referencing the vertex modified (always a point geometry),\n- * and a *pixel* property referencing the pixel location of the\n- * removal.\n- * sketchstarted - Triggered when a feature sketch bound for this layer\n- * is started. Listeners will receive an object with a *feature*\n- * property referencing the new sketch feature and a *vertex* property\n- * referencing the creation point.\n- * sketchmodified - Triggered when a feature sketch bound for this layer\n- * is modified. Listeners will receive an object with a *vertex*\n- * property referencing the modified vertex and a *feature* property\n- * referencing the sketch feature.\n- * sketchcomplete - Triggered when a feature sketch bound for this layer\n- * is complete. Listeners will receive an object with a *feature*\n- * property referencing the sketch feature. By returning false, a\n- * listener can stop the sketch feature from being added to the layer.\n- * refresh - Triggered when something wants a strategy to ask the protocol\n- * for a new set of features.\n- */\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is false. Set this property\n- * in the layer options.\n- */\n- isBaseLayer: false,\n-\n- /** \n- * APIProperty: isFixed\n- * {Boolean} Whether the layer remains in one place while dragging the\n- * map. Note that setting this to true will move the layer to the bottom\n- * of the layer stack.\n- */\n- isFixed: false,\n-\n- /** \n- * APIProperty: features\n- * {Array(<OpenLayers.Feature.Vector>)} \n- */\n- features: null,\n-\n- /** \n- * Property: filter\n- * {<OpenLayers.Filter>} The filter set in this layer,\n- * a strategy launching read requests can combined\n- * this filter with its own filter.\n- */\n- filter: null,\n-\n- /** \n- * Property: selectedFeatures\n- * {Array(<OpenLayers.Feature.Vector>)} \n- */\n- selectedFeatures: null,\n-\n- /**\n- * Property: unrenderedFeatures\n- * {Object} hash of features, keyed by feature.id, that the renderer\n- * failed to draw\n- */\n- unrenderedFeatures: null,\n-\n- /**\n- * APIProperty: reportError\n- * {Boolean} report friendly error message when loading of renderer\n- * fails.\n- */\n- reportError: true,\n-\n- /** \n- * APIProperty: style\n- * {Object} Default style for the layer\n- */\n- style: null,\n-\n- /**\n- * Property: styleMap\n- * {<OpenLayers.StyleMap>}\n- */\n- styleMap: null,\n-\n- /**\n- * Property: strategies\n- * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n- */\n- strategies: null,\n-\n- /**\n- * Property: protocol\n- * {<OpenLayers.Protocol>} Optional protocol for the layer.\n- */\n- protocol: null,\n-\n- /**\n- * Property: renderers\n- * {Array(String)} List of supported Renderer classes. Add to this list to\n- * add support for additional renderers. This list is ordered:\n- * the first renderer which returns true for the 'supported()'\n- * method will be used, if not defined in the 'renderer' option.\n- */\n- renderers: ['SVG', 'VML', 'Canvas'],\n-\n- /** \n- * Property: renderer\n- * {<OpenLayers.Renderer>}\n- */\n- renderer: null,\n-\n- /**\n- * APIProperty: rendererOptions\n- * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n- * supported options.\n- */\n- rendererOptions: null,\n-\n- /** \n- * APIProperty: geometryType\n- * {String} geometryType allows you to limit the types of geometries this\n- * layer supports. This should be set to something like\n- * \"OpenLayers.Geometry.Point\" to limit types.\n- */\n- geometryType: null,\n-\n- /** \n- * Property: drawn\n- * {Boolean} Whether the Vector Layer features have been drawn yet.\n- */\n- drawn: false,\n-\n- /** \n- * APIProperty: ratio\n- * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n- */\n- ratio: 1,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Vector\n- * Create a new vector layer\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector>} A new vector layer\n- */\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n-\n- // allow user-set renderer, otherwise assign one\n- if (!this.renderer || !this.renderer.supported()) {\n- this.assignRenderer();\n- }\n-\n- // if no valid renderer found, display error\n- if (!this.renderer || !this.renderer.supported()) {\n- this.renderer = null;\n- this.displayError();\n- }\n-\n- if (!this.styleMap) {\n- this.styleMap = new OpenLayers.StyleMap();\n- }\n-\n- this.features = [];\n- this.selectedFeatures = [];\n- this.unrenderedFeatures = {};\n-\n- // Allow for custom layer behavior\n- if (this.strategies) {\n- for (var i = 0, len = this.strategies.length; i < len; i++) {\n- this.strategies[i].setLayer(this);\n- }\n- }\n-\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Destroy this layer\n- */\n- destroy: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoDestroy) {\n- strategy.destroy();\n- }\n- }\n- this.strategies = null;\n- }\n- if (this.protocol) {\n- if (this.protocol.autoDestroy) {\n- this.protocol.destroy();\n- }\n- this.protocol = null;\n- }\n- this.destroyFeatures();\n- this.features = null;\n- this.selectedFeatures = null;\n- this.unrenderedFeatures = null;\n- if (this.renderer) {\n- this.renderer.destroy();\n- }\n- this.renderer = null;\n- this.geometryType = null;\n- this.drawn = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer.\n- * \n- * Note: Features of the layer are also cloned.\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- var features = this.features;\n- var len = features.length;\n- var clonedFeatures = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- clonedFeatures[i] = features[i].clone();\n- }\n- obj.features = clonedFeatures;\n-\n- return obj;\n- },\n-\n- /**\n- * Method: refresh\n- * Ask the layer to request features again and redraw them. Triggers\n- * the refresh event if the layer is in range and visible.\n- *\n- * Parameters:\n- * obj - {Object} Optional object with properties for any listener of\n- * the refresh event.\n- */\n- refresh: function(obj) {\n- if (this.calculateInRange() && this.visibility) {\n- this.events.triggerEvent(\"refresh\", obj);\n- }\n- },\n-\n- /** \n- * Method: assignRenderer\n- * Iterates through the available renderer implementations and selects \n- * and assigns the first one whose \"supported()\" function returns true.\n- */\n- assignRenderer: function() {\n- for (var i = 0, len = this.renderers.length; i < len; i++) {\n- var rendererClass = this.renderers[i];\n- var renderer = (typeof rendererClass == \"function\") ?\n- rendererClass :\n- OpenLayers.Renderer[rendererClass];\n- if (renderer && renderer.prototype.supported()) {\n- this.renderer = new renderer(this.div, this.rendererOptions);\n- break;\n- }\n- }\n- },\n-\n- /** \n- * Method: displayError \n- * Let the user know their browser isn't supported.\n- */\n- displayError: function() {\n- if (this.reportError) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n- renderers: this.renderers.join('\\n')\n- }));\n- }\n- },\n-\n- /** \n- * Method: setMap\n- * The layer has been added to the map. \n- * \n- * If there is no renderer set, the layer can't be used. Remove it.\n- * Otherwise, give the renderer a reference to the map and set its size.\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n-\n- if (!this.renderer) {\n- this.map.removeLayer(this);\n- } else {\n- this.renderer.map = this.map;\n-\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n- }\n- },\n-\n- /**\n- * Method: afterAdd\n- * Called at the end of the map.addLayer sequence. At this point, the map\n- * will have a base layer. Any autoActivate strategies will be\n- * activated here.\n- */\n- afterAdd: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.activate();\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: removeMap\n- * The layer has been removed from the map.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- this.drawn = false;\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.deactivate();\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: onMapResize\n- * Notify the renderer of the change in size. \n- * \n- */\n- onMapResize: function() {\n- OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n-\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize);\n- },\n-\n- /**\n- * Method: moveTo\n- * Reset the vector layer's div so that it once again is lined up with \n- * the map. Notify the renderer of the change of extent, and in the\n- * case of a change of zoom level (resolution), have the \n- * renderer redraw features.\n- * \n- * If the layer has not yet been drawn, cycle through the layer's \n- * features and draw each one.\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * zoomChanged - {Boolean} \n- * dragging - {Boolean} \n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- var coordSysUnchanged = true;\n- if (!dragging) {\n- this.renderer.root.style.visibility = 'hidden';\n-\n- var viewSize = this.map.getSize(),\n- viewWidth = viewSize.w,\n- viewHeight = viewSize.h,\n- offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n- offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n- offsetLeft += this.map.layerContainerOriginPx.x;\n- offsetLeft = -Math.round(offsetLeft);\n- offsetTop += this.map.layerContainerOriginPx.y;\n- offsetTop = -Math.round(offsetTop);\n-\n- this.div.style.left = offsetLeft + 'px';\n- this.div.style.top = offsetTop + 'px';\n-\n- var extent = this.map.getExtent().scale(this.ratio);\n- coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n-\n- this.renderer.root.style.visibility = 'visible';\n-\n- // Force a reflow on gecko based browsers to prevent jump/flicker.\n- // This seems to happen on only certain configurations; it was originally\n- // noticed in FF 2.0 and Linux.\n- if (OpenLayers.IS_GECKO === true) {\n- this.div.scrollLeft = this.div.scrollLeft;\n- }\n-\n- if (!zoomChanged && coordSysUnchanged) {\n- for (var i in this.unrenderedFeatures) {\n- var feature = this.unrenderedFeatures[i];\n- this.drawFeature(feature);\n- }\n- }\n- }\n- if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n- this.drawn = true;\n- var feature;\n- for (var i = 0, len = this.features.length; i < len; i++) {\n- this.renderer.locked = (i !== (len - 1));\n- feature = this.features[i];\n- this.drawFeature(feature);\n- }\n- }\n- },\n-\n- /** \n- * APIMethod: display\n- * Hide or show the Layer\n- * \n- * Parameters:\n- * display - {Boolean}\n- */\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- // we need to set the display style of the root in case it is attached\n- // to a foreign layer\n- var currentDisplay = this.div.style.display;\n- if (currentDisplay != this.renderer.root.style.display) {\n- this.renderer.root.style.display = currentDisplay;\n- }\n- },\n-\n- /**\n- * APIMethod: addFeatures\n- * Add Features to the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- * options - {Object}\n- */\n- addFeatures: function(features, options) {\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n-\n- var notify = !options || !options.silent;\n- if (notify) {\n- var event = {\n- features: features\n- };\n- var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n- if (ret === false) {\n- return;\n- }\n- features = event.features;\n- }\n-\n- // Track successfully added features for featuresadded event, since\n- // beforefeatureadded can veto single features.\n- var featuresAdded = [];\n- for (var i = 0, len = features.length; i < len; i++) {\n- if (i != (features.length - 1)) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n- var feature = features[i];\n-\n- if (this.geometryType &&\n- !(feature.geometry instanceof this.geometryType)) {\n- throw new TypeError('addFeatures: component should be an ' +\n- this.geometryType.prototype.CLASS_NAME);\n- }\n-\n- //give feature reference to its layer\n- feature.layer = this;\n-\n- if (!feature.style && this.style) {\n- feature.style = OpenLayers.Util.extend({}, this.style);\n- }\n-\n- if (notify) {\n- if (this.events.triggerEvent(\"beforefeatureadded\", {\n- feature: feature\n- }) === false) {\n- continue;\n- }\n- this.preFeatureInsert(feature);\n- }\n-\n- featuresAdded.push(feature);\n- this.features.push(feature);\n- this.drawFeature(feature);\n-\n- if (notify) {\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- this.onFeatureInsert(feature);\n- }\n- }\n-\n- if (notify) {\n- this.events.triggerEvent(\"featuresadded\", {\n- features: featuresAdded\n- });\n- }\n- },\n-\n-\n- /**\n- * APIMethod: removeFeatures\n- * Remove features from the layer. This erases any drawn features and\n- * removes them from the layer's control. The beforefeatureremoved\n- * and featureremoved events will be triggered for each feature. The\n- * featuresremoved event will be triggered after all features have\n- * been removed. To supress event triggering, use the silent option.\n- * \n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n- * removed.\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n- *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n- */\n- removeFeatures: function(features, options) {\n- if (!features || features.length === 0) {\n- return;\n- }\n- if (features === this.features) {\n- return this.removeAllFeatures(options);\n- }\n- if (!(OpenLayers.Util.isArray(features))) {\n- features = [features];\n- }\n- if (features === this.selectedFeatures) {\n- features = features.slice();\n- }\n-\n- var notify = !options || !options.silent;\n-\n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n- }\n-\n- for (var i = features.length - 1; i >= 0; i--) {\n- // We remain locked so long as we're not at 0\n- // and the 'next' feature has a geometry. We do the geometry check\n- // because if all the features after the current one are 'null', we\n- // won't call eraseGeometry, so we break the 'renderer functions\n- // will always be called with locked=false *last*' rule. The end result\n- // is a possible gratiutious unlocking to save a loop through the rest \n- // of the list checking the remaining features every time. So long as\n- // null geoms are rare, this is probably okay. \n- if (i != 0 && features[i - 1].geometry) {\n- this.renderer.locked = true;\n- } else {\n- this.renderer.locked = false;\n- }\n-\n- var feature = features[i];\n- delete this.unrenderedFeatures[feature.id];\n-\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n-\n- this.features = OpenLayers.Util.removeItem(this.features, feature);\n- // feature has no layer at this point\n- feature.layer = null;\n-\n- if (feature.geometry) {\n- this.renderer.eraseFeatures(feature);\n- }\n-\n- //in the case that this feature is one of the selected features, \n- // remove it from that array as well.\n- if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n- OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n- }\n-\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n- }\n- }\n-\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n- }\n- },\n-\n- /** \n- * APIMethod: removeAllFeatures\n- * Remove all features from the layer.\n- *\n- * Parameters:\n- * options - {Object} Optional properties for changing behavior of the\n- * removal.\n- *\n- * Valid options:\n- * silent - {Boolean} Supress event triggering. Default is false.\n- */\n- removeAllFeatures: function(options) {\n- var notify = !options || !options.silent;\n- var features = this.features;\n- if (notify) {\n- this.events.triggerEvent(\n- \"beforefeaturesremoved\", {\n- features: features\n- }\n- );\n- }\n- var feature;\n- for (var i = features.length - 1; i >= 0; i--) {\n- feature = features[i];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- });\n- }\n- feature.layer = null;\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- });\n- }\n- }\n- this.renderer.clear();\n- this.features = [];\n- this.unrenderedFeatures = {};\n- this.selectedFeatures = [];\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- });\n- }\n- },\n-\n- /**\n- * APIMethod: destroyFeatures\n- * Erase and destroy features on the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n- * features to destroy. If not supplied, all features on the layer\n- * will be destroyed.\n- * options - {Object}\n- */\n- destroyFeatures: function(features, options) {\n- var all = (features == undefined); // evaluates to true if\n- // features is null\n- if (all) {\n- features = this.features;\n- }\n- if (features) {\n- this.removeFeatures(features, options);\n- for (var i = features.length - 1; i >= 0; i--) {\n- features[i].destroy();\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: drawFeature\n- * Draw (or redraw) a feature on the layer. If the optional style argument\n- * is included, this style will be used. If no style is included, the\n- * feature's style will be used. If the feature doesn't have a style,\n- * the layer's style will be used.\n- * \n- * This function is not designed to be used when adding features to \n- * the layer (use addFeatures instead). It is meant to be used when\n- * the style of a feature has changed, or in some other way needs to \n- * visually updated *after* it has already been added to a layer. You\n- * must add the feature to the layer for most layer-related events to \n- * happen.\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {String | Object} Named render intent or full symbolizer object.\n- */\n- drawFeature: function(feature, style) {\n- // don't try to draw the feature with the renderer if the layer is not \n- // drawn itself\n- if (!this.drawn) {\n- return;\n- }\n- if (typeof style != \"object\") {\n- if (!style && feature.state === OpenLayers.State.DELETE) {\n- style = \"delete\";\n- }\n- var renderIntent = style || feature.renderIntent;\n- style = feature.style || this.style;\n- if (!style) {\n- style = this.styleMap.createSymbolizer(feature, renderIntent);\n- }\n- }\n-\n- var drawn = this.renderer.drawFeature(feature, style);\n- //TODO remove the check for null when we get rid of Renderer.SVG\n- if (drawn === false || drawn === null) {\n- this.unrenderedFeatures[feature.id] = feature;\n- } else {\n- delete this.unrenderedFeatures[feature.id];\n- }\n- },\n-\n- /**\n- * Method: eraseFeatures\n- * Erase features from the layer.\n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- */\n- eraseFeatures: function(features) {\n- this.renderer.eraseFeatures(features);\n- },\n-\n- /**\n- * Method: getFeatureFromEvent\n- * Given an event, return a feature if the event occurred over one.\n- * Otherwise, return null.\n- *\n- * Parameters:\n- * evt - {Event} \n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n- */\n- getFeatureFromEvent: function(evt) {\n- if (!this.renderer) {\n- throw new Error('getFeatureFromEvent called on layer with no ' +\n- 'renderer. This usually means you destroyed a ' +\n- 'layer, but not some handler which is associated ' +\n- 'with it.');\n- }\n- var feature = null;\n- var featureId = this.renderer.getFeatureIdFromEvent(evt);\n- if (featureId) {\n- if (typeof featureId === \"string\") {\n- feature = this.getFeatureById(featureId);\n- } else {\n- feature = featureId;\n- }\n- }\n- return feature;\n- },\n-\n- /**\n- * APIMethod: getFeatureBy\n- * Given a property value, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * property - {String}\n- * value - {String}\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * property value or null if there is no such feature.\n- */\n- getFeatureBy: function(property, value) {\n- //TBD - would it be more efficient to use a hash for this.features?\n- var feature = null;\n- for (var i = 0, len = this.features.length; i < len; ++i) {\n- if (this.features[i][property] == value) {\n- feature = this.features[i];\n- break;\n- }\n- }\n- return feature;\n- },\n-\n- /**\n- * APIMethod: getFeatureById\n- * Given a feature id, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * featureId - {String}\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureId or null if there is no such feature.\n- */\n- getFeatureById: function(featureId) {\n- return this.getFeatureBy('id', featureId);\n- },\n-\n- /**\n- * APIMethod: getFeatureByFid\n- * Given a feature fid, return the feature if it exists in the features array\n- *\n- * Parameters:\n- * featureFid - {String}\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n- * featureFid or null if there is no such feature.\n- */\n- getFeatureByFid: function(featureFid) {\n- return this.getFeatureBy('fid', featureFid);\n- },\n-\n- /**\n- * APIMethod: getFeaturesByAttribute\n- * Returns an array of features that have the given attribute key set to the\n- * given value. Comparison of attribute values takes care of datatypes, e.g.\n- * the string '1234' is not equal to the number 1234.\n- *\n- * Parameters:\n- * attrName - {String}\n- * attrValue - {Mixed}\n- *\n- * Returns:\n- * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n- * passed named attribute set to the given value.\n- */\n- getFeaturesByAttribute: function(attrName, attrValue) {\n- var i,\n- feature,\n- len = this.features.length,\n- foundFeatures = [];\n- for (i = 0; i < len; i++) {\n- feature = this.features[i];\n- if (feature && feature.attributes) {\n- if (feature.attributes[attrName] === attrValue) {\n- foundFeatures.push(feature);\n- }\n- }\n- }\n- return foundFeatures;\n- },\n-\n- /**\n- * Unselect the selected features\n- * i.e. clears the featureSelection array\n- * change the style back\n- clearSelection: function() {\n-\n- var vectorLayer = this.map.vectorLayer;\n- for (var i = 0; i < this.map.featureSelection.length; i++) {\n- var featureSelection = this.map.featureSelection[i];\n- vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n- }\n- this.map.featureSelection = [];\n- },\n- */\n-\n-\n- /**\n- * APIMethod: onFeatureInsert\n- * method called after a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something on feature updates.\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- onFeatureInsert: function(feature) {},\n-\n- /**\n- * APIMethod: preFeatureInsert\n- * method called before a feature is inserted.\n- * Does nothing by default. Override this if you\n- * need to do something when features are first added to the\n- * layer, but before they are drawn, such as adjust the style.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- preFeatureInsert: function(feature) {},\n-\n- /** \n- * APIMethod: getDataExtent\n- * Calculates the max extent which includes all of the features.\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} or null if the layer has no features with\n- * geometries.\n- */\n- getDataExtent: function() {\n- var maxExtent = null;\n- var features = this.features;\n- if (features && (features.length > 0)) {\n- var geometry = null;\n- for (var i = 0, len = features.length; i < len; i++) {\n- geometry = features[i].geometry;\n- if (geometry) {\n- if (maxExtent === null) {\n- maxExtent = new OpenLayers.Bounds();\n- }\n- maxExtent.extend(geometry.getBounds());\n- }\n- }\n- }\n- return maxExtent;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Vector\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/WMS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.WMS\n- * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n- * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n- */\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n- },\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Default is true for WMS layer\n- */\n- isBaseLayer: true,\n-\n- /**\n- * APIProperty: encodeBBOX\n- * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n- * but some services want it that way. Default false.\n- */\n- encodeBBOX: false,\n-\n- /** \n- * APIProperty: noMagic \n- * {Boolean} If true, the image format will not be automagicaly switched \n- * from image/jpeg to image/png or image/gif when using \n- * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n- * constructor. Default false. \n- */\n- noMagic: false,\n-\n- /**\n- * Property: yx\n- * {Object} Keys in this object are EPSG codes for which the axis order\n- * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n- * true as value. This is only relevant for WMS versions >= 1.3.0, and\n- * only if yx is not set in <OpenLayers.Projection.defaults> for the\n- * used projection.\n- */\n- yx: {},\n-\n- /**\n- * Constructor: OpenLayers.Layer.WMS\n- * Create a new WMS layer object\n- *\n- * Examples:\n- *\n- * The code below creates a simple WMS layer using the image/jpeg format.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {layers: \"modis,global_mosaic\"});\n- * (end)\n- * Note the 3rd argument (params). Properties added to this object will be\n- * added to the WMS GetMap requests used for this layer's tiles. The only\n- * mandatory parameter is \"layers\". Other common WMS params include\n- * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n- * always be ignored. Instead, it will be derived from the baseLayer's or\n- * map's projection.\n- *\n- * The code below creates a transparent WMS layer with additional options.\n- * (code)\n- * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n- * \"http://wms.jpl.nasa.gov/wms.cgi\", \n- * {\n- * layers: \"modis,global_mosaic\",\n- * transparent: true\n- * }, {\n- * opacity: 0.5,\n- * singleTile: true\n- * });\n- * (end)\n- * Note that by default, a WMS layer is configured as baseLayer. Setting\n- * the \"transparent\" param to true will apply some magic (see <noMagic>).\n- * The default image format changes from image/jpeg to image/png, and the\n- * layer is not configured as baseLayer.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the WMS\n- * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n- * params - {Object} An object with key/value pairs representing the\n- * GetMap query string parameters and parameter values.\n- * options - {Object} Hashtable of extra options to tag onto the layer.\n- * These options include all properties listed above, plus the ones\n- * inherited from superclasses.\n- */\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- //uppercase params\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\";\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n- );\n-\n-\n- //layer is transparent \n- if (!this.noMagic && this.params.TRANSPARENT &&\n- this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n-\n- // unless explicitly set in options, make layer an overlay\n- if ((options == null) || (!options.isBaseLayer)) {\n- this.isBaseLayer = false;\n- }\n-\n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n- \"image/png\";\n- }\n- }\n-\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n- /**\n- * APIMethod: reverseAxisOrder\n- * Returns true if the axis order is reversed for the WMS version and\n- * projection of the layer.\n- * \n- * Returns:\n- * {Boolean} true if the axis order is reversed, false otherwise.\n- */\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 &&\n- !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n- OpenLayers.Projection.defaults[projCode].yx));\n- },\n-\n- /**\n- * Method: getURL\n- * Return a GetMap query string for this layer\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as \n- * parameters.\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n-\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- // WMS 1.3 introduced axis order\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ?\n- bounds.toBBOX(null, reverseAxisOrder) :\n- bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString;\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * Catch changeParams and uppercase the new params to be merged in\n- * before calling changeParams on the super class.\n- * \n- * Once params have been changed, the tiles will be reloaded with\n- * the new parameters.\n- * \n- * Parameters:\n- * newParams - {Object} Hashtable of new params to use\n- */\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n- newArguments);\n- },\n-\n- /** \n- * APIMethod: getFullRequestString\n- * Combine the layer's url with its params and these newParams. \n- * \n- * Add the SRS parameter from projection -- this is probably\n- * more eloquently done via a setProjection() method, but this \n- * works for now and always.\n- *\n- * Parameters:\n- * newParams - {Object}\n- * altUrl - {String} Use this as the url instead of the layer's url\n- * \n- * Returns:\n- * {String} \n- */\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n- this.projection.getCode() :\n- mapProjection.getCode();\n- var value = (projectionCode == \"none\") ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value;\n- } else {\n- this.params.SRS = value;\n- }\n-\n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n- }\n-\n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n- this, arguments);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n-});\n-/* ======================================================================\n- OpenLayers/Format.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format\n- * Base class for format reading/writing a variety of formats. Subclasses\n- * of OpenLayers.Format are expected to have read and write methods.\n- */\n-OpenLayers.Format = OpenLayers.Class({\n-\n- /**\n- * Property: options\n- * {Object} A reference to options passed to the constructor.\n- */\n- options: null,\n-\n- /**\n- * APIProperty: externalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The externalProjection is the projection used by\n- * the content which is passed into read or which comes out of write.\n- * In order to reproject, a projection transformation function for the\n- * specified projections must be available. This support may be \n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- externalProjection: null,\n-\n- /**\n- * APIProperty: internalProjection\n- * {<OpenLayers.Projection>} When passed a externalProjection and\n- * internalProjection, the format will reproject the geometries it\n- * reads or writes. The internalProjection is the projection used by\n- * the geometries which are returned by read or which are passed into\n- * write. In order to reproject, a projection transformation function\n- * for the specified projections must be available. This support may be\n- * provided via proj4js or via a custom transformation function. See\n- * {<OpenLayers.Projection.addTransform>} for more information on\n- * custom transformations.\n- */\n- internalProjection: null,\n-\n- /**\n- * APIProperty: data\n- * {Object} When <keepData> is true, this is the parsed string sent to\n- * <read>.\n- */\n- data: null,\n-\n- /**\n- * APIProperty: keepData\n- * {Object} Maintain a reference (<data>) to the most recently read data.\n- * Default is false.\n- */\n- keepData: false,\n-\n- /**\n- * Constructor: OpenLayers.Format\n- * Instances of this class are not useful. See one of the subclasses.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * format\n- *\n- * Valid options:\n- * keepData - {Boolean} If true, upon <read>, the data property will be\n- * set to the parsed object (e.g. the json or xml object).\n- *\n- * Returns:\n- * An instance of OpenLayers.Format\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up.\n- */\n- destroy: function() {},\n-\n- /**\n- * Method: read\n- * Read data from a string, and return an object whose type depends on the\n- * subclass. \n- * \n- * Parameters:\n- * data - {string} Data to read/parse.\n- *\n- * Returns:\n- * Depends on the subclass\n- */\n- read: function(data) {\n- throw new Error('Read not implemented.');\n- },\n-\n- /**\n- * Method: write\n- * Accept an object, and return a string. \n- *\n- * Parameters:\n- * object - {Object} Object to be serialized\n- *\n- * Returns:\n- * {String} A string representation of the object.\n- */\n- write: function(object) {\n- throw new Error('Write not implemented.');\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/JSON.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * Note:\n- * This work draws heavily from the public domain JSON serializer/deserializer\n- * at http://www.json.org/json.js. Rewritten so that it doesn't modify\n- * basic data prototypes.\n- */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.JSON\n- * A parser to read/write JSON safely. Create a new instance with the\n- * <OpenLayers.Format.JSON> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * APIProperty: indent\n- * {String} For \"pretty\" printing, the indent string will be used once for\n- * each indentation level.\n- */\n- indent: \" \",\n-\n- /**\n- * APIProperty: space\n- * {String} For \"pretty\" printing, the space string will be used after\n- * the \":\" separating a name/value pair.\n- */\n- space: \" \",\n-\n- /**\n- * APIProperty: newline\n- * {String} For \"pretty\" printing, the newline string will be used at the\n- * end of each name/value pair or array item.\n- */\n- newline: \"\\n\",\n-\n- /**\n- * Property: level\n- * {Integer} For \"pretty\" printing, this is incremented/decremented during\n- * serialization.\n- */\n- level: 0,\n-\n- /**\n- * Property: pretty\n- * {Boolean} Serialize with extra whitespace for structure. This is set\n- * by the <write> method.\n- */\n- pretty: false,\n-\n- /**\n- * Property: nativeJSON\n- * {Boolean} Does the browser support native json?\n- */\n- nativeJSON: (function() {\n- return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\");\n- })(),\n-\n- /**\n- * Constructor: OpenLayers.Format.JSON\n- * Create a new parser for JSON.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Deserialize a json string.\n- *\n- * Parameters:\n- * json - {String} A JSON string\n- * filter - {Function} A function which will be called for every key and\n- * value at every level of the final result. Each value will be\n- * replaced by the result of the filter function. This can be used to\n- * reform generic objects into instances of classes, or to transform\n- * date strings into Date objects.\n- * \n- * Returns:\n- * {Object} An object, array, string, or number .\n- */\n- read: function(json, filter) {\n- var object;\n- if (this.nativeJSON) {\n- object = JSON.parse(json, filter);\n- } else try {\n- /**\n- * Parsing happens in three stages. In the first stage, we run the\n- * text against a regular expression which looks for non-JSON\n- * characters. We are especially concerned with '()' and 'new'\n- * because they can cause invocation, and '=' because it can\n- * cause mutation. But just to be safe, we will reject all\n- * unexpected characters.\n- */\n- if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, '@').replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n-\n- /**\n- * In the second stage we use the eval function to compile the\n- * text into a JavaScript structure. The '{' operator is\n- * subject to a syntactic ambiguity in JavaScript - it can\n- * begin a block or an object literal. We wrap the text in\n- * parens to eliminate the ambiguity.\n- */\n- object = eval('(' + json + ')');\n-\n- /**\n- * In the optional third stage, we recursively walk the new\n- * structure, passing each name/value pair to a filter\n- * function for possible transformation.\n- */\n- if (typeof filter === 'function') {\n- function walk(k, v) {\n- if (v && typeof v === 'object') {\n- for (var i in v) {\n- if (v.hasOwnProperty(i)) {\n- v[i] = walk(i, v[i]);\n- }\n- }\n- }\n- return filter(k, v);\n- }\n- object = walk('', object);\n- }\n- }\n- } catch (e) {\n- // Fall through if the regexp test fails.\n- }\n-\n- if (this.keepData) {\n- this.data = object;\n- }\n-\n- return object;\n- },\n-\n- /**\n- * APIMethod: write\n- * Serialize an object into a JSON string.\n- *\n- * Parameters:\n- * value - {String} The object, array, string, number, boolean or date\n- * to be serialized.\n- * pretty - {Boolean} Structure the output with newlines and indentation.\n- * Default is false.\n- *\n- * Returns:\n- * {String} The JSON string representation of the input value.\n- */\n- write: function(value, pretty) {\n- this.pretty = !!pretty;\n- var json = null;\n- var type = typeof value;\n- if (this.serialize[type]) {\n- try {\n- json = (!this.pretty && this.nativeJSON) ?\n- JSON.stringify(value) :\n- this.serialize[type].apply(this, [value]);\n- } catch (err) {\n- OpenLayers.Console.error(\"Trouble serializing: \" + err);\n- }\n- }\n- return json;\n- },\n-\n- /**\n- * Method: writeIndent\n- * Output an indentation string depending on the indentation level.\n- *\n- * Returns:\n- * {String} An appropriate indentation string.\n- */\n- writeIndent: function() {\n- var pieces = [];\n- if (this.pretty) {\n- for (var i = 0; i < this.level; ++i) {\n- pieces.push(this.indent);\n- }\n- }\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: writeNewline\n- * Output a string representing a newline if in pretty printing mode.\n- *\n- * Returns:\n- * {String} A string representing a new line.\n- */\n- writeNewline: function() {\n- return (this.pretty) ? this.newline : '';\n- },\n-\n- /**\n- * Method: writeSpace\n- * Output a string representing a space if in pretty printing mode.\n- *\n- * Returns:\n- * {String} A space.\n- */\n- writeSpace: function() {\n- return (this.pretty) ? this.space : '';\n- },\n-\n- /**\n- * Property: serialize\n- * Object with properties corresponding to the serializable data types.\n- * Property values are functions that do the actual serializing.\n- */\n- serialize: {\n- /**\n- * Method: serialize.object\n- * Transform an object into a JSON string.\n- *\n- * Parameters:\n- * object - {Object} The object to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the object.\n- */\n- 'object': function(object) {\n- // three special objects that we want to treat differently\n- if (object == null) {\n- return \"null\";\n- }\n- if (object.constructor == Date) {\n- return this.serialize.date.apply(this, [object]);\n- }\n- if (object.constructor == Array) {\n- return this.serialize.array.apply(this, [object]);\n- }\n- var pieces = ['{'];\n- this.level += 1;\n- var key, keyJSON, valueJSON;\n-\n- var addComma = false;\n- for (key in object) {\n- if (object.hasOwnProperty(key)) {\n- // recursive calls need to allow for sub-classing\n- keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [key, this.pretty]);\n- valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [object[key], this.pretty]);\n- if (keyJSON != null && valueJSON != null) {\n- if (addComma) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(),\n- keyJSON, ':', this.writeSpace(), valueJSON);\n- addComma = true;\n- }\n- }\n- }\n-\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), '}');\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: serialize.array\n- * Transform an array into a JSON string.\n- *\n- * Parameters:\n- * array - {Array} The array to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the array.\n- */\n- 'array': function(array) {\n- var json;\n- var pieces = ['['];\n- this.level += 1;\n-\n- for (var i = 0, len = array.length; i < len; ++i) {\n- // recursive calls need to allow for sub-classing\n- json = OpenLayers.Format.JSON.prototype.write.apply(this,\n- [array[i], this.pretty]);\n- if (json != null) {\n- if (i > 0) {\n- pieces.push(',');\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), json);\n- }\n- }\n-\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), ']');\n- return pieces.join('');\n- },\n-\n- /**\n- * Method: serialize.string\n- * Transform a string into a JSON string.\n- *\n- * Parameters:\n- * string - {String} The string to be serialized\n- * \n- * Returns:\n- * {String} A JSON string representing the string.\n- */\n- 'string': function(string) {\n- // If the string contains no control characters, no quote characters, and no\n- // backslash characters, then we can simply slap some quotes around it.\n- // Otherwise we must also replace the offending characters with safe\n- // sequences. \n- var m = {\n- '\\b': '\\\\b',\n- '\\t': '\\\\t',\n- '\\n': '\\\\n',\n- '\\f': '\\\\f',\n- '\\r': '\\\\r',\n- '\"': '\\\\\"',\n- '\\\\': '\\\\\\\\'\n- };\n- if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n- return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n- var c = m[b];\n- if (c) {\n- return c;\n- }\n- c = b.charCodeAt();\n- return '\\\\u00' +\n- Math.floor(c / 16).toString(16) +\n- (c % 16).toString(16);\n- }) + '\"';\n- }\n- return '\"' + string + '\"';\n- },\n-\n- /**\n- * Method: serialize.number\n- * Transform a number into a JSON string.\n- *\n- * Parameters:\n- * number - {Number} The number to be serialized.\n- *\n- * Returns:\n- * {String} A JSON string representing the number.\n- */\n- 'number': function(number) {\n- return isFinite(number) ? String(number) : \"null\";\n- },\n-\n- /**\n- * Method: serialize.boolean\n- * Transform a boolean into a JSON string.\n- *\n- * Parameters:\n- * bool - {Boolean} The boolean to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the boolean.\n- */\n- 'boolean': function(bool) {\n- return String(bool);\n- },\n-\n- /**\n- * Method: serialize.object\n- * Transform a date into a JSON string.\n- *\n- * Parameters:\n- * date - {Date} The date to be serialized.\n- * \n- * Returns:\n- * {String} A JSON string representing the date.\n- */\n- 'date': function(date) {\n- function format(number) {\n- // Format integers to have at least two digits.\n- return (number < 10) ? '0' + number : number;\n- }\n- return '\"' + date.getFullYear() + '-' +\n- format(date.getMonth() + 1) + '-' +\n- format(date.getDate()) + 'T' +\n- format(date.getHours()) + ':' +\n- format(date.getMinutes()) + ':' +\n- format(date.getSeconds()) + '\"';\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.JSON\"\n-\n-});\n-/* ======================================================================\n OpenLayers/Geometry.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -27367,282 +24361,14 @@\n }\n return geometry;\n },\n \n CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n });\n /* ======================================================================\n- OpenLayers/Strategy.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Strategy\n- * Abstract vector layer strategy class. Not to be instantiated directly. Use\n- * one of the strategy subclasses instead.\n- */\n-OpenLayers.Strategy = OpenLayers.Class({\n-\n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n- */\n- layer: null,\n-\n- /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n- */\n- options: null,\n-\n- /** \n- * Property: active \n- * {Boolean} The control is active.\n- */\n- active: null,\n-\n- /**\n- * Property: autoActivate\n- * {Boolean} The creator of the strategy can set autoActivate to false\n- * to fully control when the protocol is activated and deactivated.\n- * Defaults to true.\n- */\n- autoActivate: true,\n-\n- /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the strategy can set autoDestroy to false\n- * to fully control when the strategy is destroyed. Defaults to\n- * true.\n- */\n- autoDestroy: true,\n-\n- /**\n- * Constructor: OpenLayers.Strategy\n- * Abstract class for vector strategies. Create instances of a subclass.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- // set the active property here, so that user cannot override it\n- this.active = false;\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n- */\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n- },\n-\n- /**\n- * Method: setLayer\n- * Called to set the <layer> property.\n- *\n- * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>}\n- */\n- setLayer: function(layer) {\n- this.layer = layer;\n- },\n-\n- /**\n- * Method: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n- */\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n- }\n- return false;\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n- */\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true;\n- }\n- return false;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Strategy\"\n-});\n-/* ======================================================================\n- OpenLayers/Strategy/Fixed.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Strategy.js\n- */\n-\n-/**\n- * Class: OpenLayers.Strategy.Fixed\n- * A simple strategy that requests features once and never requests new data.\n- *\n- * Inherits from:\n- * - <OpenLayers.Strategy>\n- */\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n-\n- /**\n- * APIProperty: preload\n- * {Boolean} Load data before layer made visible. Enabling this may result\n- * in considerable overhead if your application loads many data layers\n- * that are not visible by default. Default is false.\n- */\n- preload: false,\n-\n- /**\n- * Constructor: OpenLayers.Strategy.Fixed\n- * Create a new Fixed strategy.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n-\n- /**\n- * Method: activate\n- * Activate the strategy: load data or add listener to load when visible\n- *\n- * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n- */\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- \"refresh\": this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load();\n- } else {\n- this.layer.events.on({\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- }\n- }\n- return activated;\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivate the strategy. Undo what is done in <activate>.\n- * \n- * Returns:\n- * {Boolean} The strategy was successfully deactivated.\n- */\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- \"refresh\": this.load,\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- }\n- return deactivated;\n- },\n-\n- /**\n- * Method: load\n- * Tells protocol to load data and unhooks the visibilitychanged event\n- *\n- * Parameters:\n- * options - {Object} options to pass to protocol read.\n- */\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- \"visibilitychanged\": this.load,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: merge\n- * Add all features to the layer.\n- * If the layer projection differs from the map projection, features\n- * will be transformed from the layer projection to the map projection.\n- *\n- * Parameters:\n- * resp - {<OpenLayers.Protocol.Response>} The response object passed\n- * by the protocol.\n- */\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local);\n- }\n- }\n- }\n- layer.addFeatures(features);\n- }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- });\n- },\n-\n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n-});\n-/* ======================================================================\n OpenLayers/Control.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -28010,68 +24736,715 @@\n OpenLayers.Control.TYPE_TOGGLE = 2;\n \n /**\n * Constant: OpenLayers.Control.TYPE_TOOL\n */\n OpenLayers.Control.TYPE_TOOL = 3;\n /* ======================================================================\n- OpenLayers/Handler/Drag.js\n+ OpenLayers/Events/buttonclick.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Handler.js\n+ * @requires OpenLayers/Events.js\n */\n \n /**\n- * Class: OpenLayers.Handler.Drag\n- * The drag handler is used to deal with sequences of browser events related\n- * to dragging. The handler is used by controls that want to know when\n- * a drag sequence begins, when a drag is happening, and when it has\n- * finished.\n- *\n- * Controls that use the drag handler typically construct it with callbacks\n- * for 'down', 'move', and 'done'. Callbacks for these keys are called\n- * when the drag begins, with each move, and when the drag is done. In\n- * addition, controls can have callbacks keyed to 'up' and 'out' if they\n- * care to differentiate between the types of events that correspond with\n- * the end of a drag sequence. If no drag actually occurs (no mouse move)\n- * the 'down' and 'up' callbacks will be called, but not the 'done'\n- * callback.\n+ * Class: OpenLayers.Events.buttonclick\n+ * Extension event type for handling buttons on top of a dom element. This\n+ * event type fires \"buttonclick\" on its <target> when a button was\n+ * clicked. Buttons are detected by the \"olButton\" class.\n *\n- * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n+ * This event type makes sure that button clicks do not interfere with other\n+ * events that are registered on the same <element>.\n *\n- * Inherits from:\n- * - <OpenLayers.Handler>\n+ * Event types provided by this extension:\n+ * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n+ * object with a *buttonElement* property referencing the dom element of\n+ * the clicked button, and an *buttonXY* property with the click position\n+ * relative to the button.\n */\n-OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n \n- /** \n- * Property: started\n- * {Boolean} When a mousedown or touchstart event is received, we want to\n- * record it, but not set 'dragging' until the mouse moves after starting.\n+ /**\n+ * Property: target\n+ * {<OpenLayers.Events>} The events instance that the buttonclick event will\n+ * be triggered on.\n */\n- started: false,\n+ target: null,\n \n /**\n- * Property: stopDown\n- * {Boolean} Stop propagation of mousedown events from getting to listeners\n- * on the same element. Default is true.\n+ * Property: events\n+ * {Array} Events to observe and conditionally stop from propagating when\n+ * an element with the olButton class (or its olAlphaImg child) is\n+ * clicked.\n */\n- stopDown: true,\n+ events: [\n+ 'mousedown', 'mouseup', 'click', 'dblclick',\n+ 'touchstart', 'touchmove', 'touchend', 'keydown'\n+ ],\n \n- /** \n- * Property: dragging \n- * {Boolean} \n+ /**\n+ * Property: startRegEx\n+ * {RegExp} Regular expression to test Event.type for events that start\n+ * a buttonclick sequence.\n */\n- dragging: false,\n+ startRegEx: /^mousedown|touchstart$/,\n+\n+ /**\n+ * Property: cancelRegEx\n+ * {RegExp} Regular expression to test Event.type for events that cancel\n+ * a buttonclick sequence.\n+ */\n+ cancelRegEx: /^touchmove$/,\n+\n+ /**\n+ * Property: completeRegEx\n+ * {RegExp} Regular expression to test Event.type for events that complete\n+ * a buttonclick sequence.\n+ */\n+ completeRegEx: /^mouseup|touchend$/,\n+\n+ /**\n+ * Property: startEvt\n+ * {Event} The event that started the click sequence\n+ */\n+\n+ /**\n+ * Constructor: OpenLayers.Events.buttonclick\n+ * Construct a buttonclick event type. Applications are not supposed to\n+ * create instances of this class - they are created on demand by\n+ * <OpenLayers.Events> instances.\n+ *\n+ * Parameters:\n+ * target - {<OpenLayers.Events>} The events instance that the buttonclick\n+ * event will be triggered on.\n+ */\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick);\n+ }\n+ delete this.target;\n+ },\n+\n+ /**\n+ * Method: getPressedButton\n+ * Get the pressed button, if any. Returns undefined if no button\n+ * was pressed.\n+ *\n+ * Arguments:\n+ * element - {DOMElement} The event target.\n+ *\n+ * Returns:\n+ * {DOMElement} The button element, or undefined.\n+ */\n+ getPressedButton: function(element) {\n+ var depth = 3, // limit the search depth\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ // hit!\n+ button = element;\n+ break;\n+ }\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return button;\n+ },\n+\n+ /**\n+ * Method: ignore\n+ * Check for event target elements that should be ignored by OpenLayers.\n+ *\n+ * Parameters:\n+ * element - {DOMElement} The event target.\n+ */\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === 'a') {\n+ ignore = true;\n+ break;\n+ }\n+ element = element.parentNode;\n+ } while (--depth > 0 && element);\n+ return ignore;\n+ },\n+\n+ /**\n+ * Method: buttonClick\n+ * Check if a button was clicked, and fire the buttonclick event\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ // was a button pressed?\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break;\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ });\n+ }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt;\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ }\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt;\n+ }\n+ }\n+ return propagate;\n+ }\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Control/Panel.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Events/buttonclick.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Panel\n+ * The Panel control is a container for other controls. With it toolbars\n+ * may be composed.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n+ /**\n+ * Property: controls\n+ * {Array(<OpenLayers.Control>)}\n+ */\n+ controls: null,\n+\n+ /**\n+ * APIProperty: autoActivate\n+ * {Boolean} Activate the control when it is added to a map. Default is\n+ * true.\n+ */\n+ autoActivate: true,\n+\n+ /** \n+ * APIProperty: defaultControl\n+ * {<OpenLayers.Control>} The control which is activated when the control is\n+ * activated (turned on), which also happens at instantiation.\n+ * If <saveState> is true, <defaultControl> will be nullified after the\n+ * first activation of the panel.\n+ */\n+ defaultControl: null,\n+\n+ /**\n+ * APIProperty: saveState\n+ * {Boolean} If set to true, the active state of this panel's controls will\n+ * be stored on panel deactivation, and restored on reactivation. Default\n+ * is false.\n+ */\n+ saveState: false,\n+\n+ /**\n+ * APIProperty: allowDepress\n+ * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n+ * be deactivated by clicking the icon that represents them. Default \n+ * is false.\n+ */\n+ allowDepress: false,\n+\n+ /**\n+ * Property: activeState\n+ * {Object} stores the active state of this panel's controls.\n+ */\n+ activeState: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Panel\n+ * Create a new control panel.\n+ *\n+ * Each control in the panel is represented by an icon. When clicking \n+ * on an icon, the <activateControl> method is called.\n+ *\n+ * Specific properties for controls on a panel:\n+ * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n+ * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n+ * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n+ * title - {string} Text displayed when mouse is over the icon that \n+ * represents the control. \n+ *\n+ * The <OpenLayers.Control.type> of a control determines the behavior when\n+ * clicking its icon:\n+ * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n+ * controls of this type in the same panel are deactivated. This is\n+ * the default type.\n+ * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n+ * toggled.\n+ * <OpenLayers.Control.TYPE_BUTTON> - The\n+ * <OpenLayers.Control.Button.trigger> method of the control is called,\n+ * but its active state is not changed.\n+ *\n+ * If a control is <OpenLayers.Control.active>, it will be drawn with the\n+ * olControl[Name]ItemActive class, otherwise with the\n+ * olControl[Name]ItemInactive class.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be used\n+ * to extend the control.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.controls = [];\n+ this.activeState = {};\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n+ ctl = this.controls[i];\n+ if (ctl.events) {\n+ ctl.events.un({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n+ }\n+ ctl.panel_div = null;\n+ }\n+ this.activeState = null;\n+ },\n+\n+ /**\n+ * APIMethod: activate\n+ */\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ if (control === this.defaultControl ||\n+ (this.saveState && this.activeState[control.id])) {\n+ control.activate();\n+ }\n+ }\n+ if (this.saveState === true) {\n+ this.defaultControl = null;\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: deactivate\n+ */\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ this.activeState[control.id] = control.deactivate();\n+ }\n+ this.redraw();\n+ return true;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: draw\n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick);\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n+ }\n+ this.addControlsToMap(this.controls);\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: redraw\n+ */\n+ redraw: function() {\n+ for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n+ this.div.removeChild(this.div.childNodes[i]);\n+ }\n+ this.div.innerHTML = \"\";\n+ if (this.active) {\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.div.appendChild(this.controls[i].panel_div);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: activateControl\n+ * This method is called when the user click on the icon representing a \n+ * control in the panel.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>}\n+ */\n+ activateControl: function(control) {\n+ if (!this.active) {\n+ return false;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n+ control.trigger();\n+ return;\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n+ if (control.active) {\n+ control.deactivate();\n+ } else {\n+ control.activate();\n+ }\n+ return;\n+ }\n+ if (this.allowDepress && control.active) {\n+ control.deactivate();\n+ } else {\n+ var c;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ c = this.controls[i];\n+ if (c != control &&\n+ (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n+ c.deactivate();\n+ }\n+ }\n+ control.activate();\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: addControls\n+ * To build a toolbar, you add a set of controls to it. addControls\n+ * lets you add a single control or a list of controls to the \n+ * Control Panel.\n+ *\n+ * Parameters:\n+ * controls - {<OpenLayers.Control>} Controls to add in the panel.\n+ */\n+ addControls: function(controls) {\n+ if (!(OpenLayers.Util.isArray(controls))) {\n+ controls = [controls];\n+ }\n+ this.controls = this.controls.concat(controls);\n+\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var control = controls[i],\n+ element = this.createControlMarkup(control);\n+ OpenLayers.Element.addClass(element,\n+ control.displayClass + \"ItemInactive\");\n+ OpenLayers.Element.addClass(element, \"olButton\");\n+ if (control.title != \"\" && !element.title) {\n+ element.title = control.title;\n+ }\n+ control.panel_div = element;\n+ }\n+\n+ if (this.map) { // map.addControl() has already been called on the panel\n+ this.addControlsToMap(controls);\n+ this.redraw();\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: createControlMarkup\n+ * This function just creates a div for the control. If specific HTML\n+ * markup is needed this function can be overridden in specific classes,\n+ * or at panel instantiation time:\n+ *\n+ * Example:\n+ * (code)\n+ * var panel = new OpenLayers.Control.Panel({\n+ * defaultControl: control,\n+ * // ovverride createControlMarkup to create actual buttons\n+ * // including texts wrapped into span elements.\n+ * createControlMarkup: function(control) {\n+ * var button = document.createElement('button'),\n+ * span = document.createElement('span');\n+ * if (control.text) {\n+ * span.innerHTML = control.text;\n+ * }\n+ * return button;\n+ * }\n+ * });\n+ * (end)\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control to create the HTML\n+ * markup for.\n+ *\n+ * Returns:\n+ * {DOMElement} The markup.\n+ */\n+ createControlMarkup: function(control) {\n+ return document.createElement(\"div\");\n+ },\n+\n+ /**\n+ * Method: addControlsToMap\n+ * Only for internal use in draw() and addControls() methods.\n+ *\n+ * Parameters:\n+ * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n+ */\n+ addControlsToMap: function(controls) {\n+ var control;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ control = controls[i];\n+ if (control.autoActivate === true) {\n+ control.autoActivate = false;\n+ this.map.addControl(control);\n+ control.autoActivate = true;\n+ } else {\n+ this.map.addControl(control);\n+ control.deactivate();\n+ }\n+ control.events.on({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ });\n+ }\n+ },\n+\n+ /**\n+ * Method: iconOn\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOn: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n+ d.className = d.className.replace(re, \"$1Active\");\n+ },\n+\n+ /**\n+ * Method: iconOff\n+ * Internal use, for use only with \"controls[i].events.on/un\".\n+ */\n+ iconOff: function() {\n+ var d = this.panel_div; // \"this\" refers to a control on panel!\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n+ d.className = d.className.replace(re, \"$1Inactive\");\n+ },\n+\n+ /**\n+ * Method: onButtonClick\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ onButtonClick: function(evt) {\n+ var controls = this.controls,\n+ button = evt.buttonElement;\n+ for (var i = controls.length - 1; i >= 0; --i) {\n+ if (controls[i].panel_div === button) {\n+ this.activateControl(controls[i]);\n+ break;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: getControlsBy\n+ * Get a list of controls with properties matching the given criteria.\n+ *\n+ * Parameters:\n+ * property - {String} A control property to be matched.\n+ * match - {String | Object} A string to match. Can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * match.test(control[property]) evaluates to true, the control will be\n+ * included in the array returned. If no controls are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsBy: function(property, match) {\n+ var test = (typeof match.test == \"function\");\n+ var found = OpenLayers.Array.filter(this.controls, function(item) {\n+ return item[property] == match || (test && match.test(item[property]));\n+ });\n+ return found;\n+ },\n+\n+ /**\n+ * APIMethod: getControlsByName\n+ * Get a list of contorls with names matching the given name.\n+ *\n+ * Parameters:\n+ * match - {String | Object} A control name. The name can also be a regular\n+ * expression literal or object. In addition, it can be any object\n+ * with a method named test. For reqular expressions or other, if\n+ * name.test(control.name) evaluates to true, the control will be included\n+ * in the list of controls returned. If no controls are found, an empty\n+ * array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsByName: function(match) {\n+ return this.getControlsBy(\"name\", match);\n+ },\n+\n+ /**\n+ * APIMethod: getControlsByClass\n+ * Get a list of controls of a given type (CLASS_NAME).\n+ *\n+ * Parameters:\n+ * match - {String | Object} A control class name. The type can also be a\n+ * regular expression literal or object. In addition, it can be any\n+ * object with a method named test. For reqular expressions or other,\n+ * if type.test(control.CLASS_NAME) evaluates to true, the control will\n+ * be included in the list of controls returned. If no controls are\n+ * found, an empty array is returned.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n+ * An empty array is returned if no matches are found.\n+ */\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Panel\"\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Handler/Drag.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Handler.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler.Drag\n+ * The drag handler is used to deal with sequences of browser events related\n+ * to dragging. The handler is used by controls that want to know when\n+ * a drag sequence begins, when a drag is happening, and when it has\n+ * finished.\n+ *\n+ * Controls that use the drag handler typically construct it with callbacks\n+ * for 'down', 'move', and 'done'. Callbacks for these keys are called\n+ * when the drag begins, with each move, and when the drag is done. In\n+ * addition, controls can have callbacks keyed to 'up' and 'out' if they\n+ * care to differentiate between the types of events that correspond with\n+ * the end of a drag sequence. If no drag actually occurs (no mouse move)\n+ * the 'down' and 'up' callbacks will be called, but not the 'done'\n+ * callback.\n+ *\n+ * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n+ */\n+OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n+\n+ /** \n+ * Property: started\n+ * {Boolean} When a mousedown or touchstart event is received, we want to\n+ * record it, but not set 'dragging' until the mouse moves after starting.\n+ */\n+ started: false,\n+\n+ /**\n+ * Property: stopDown\n+ * {Boolean} Stop propagation of mousedown events from getting to listeners\n+ * on the same element. Default is true.\n+ */\n+ stopDown: true,\n+\n+ /** \n+ * Property: dragging \n+ * {Boolean} \n+ */\n+ dragging: false,\n \n /** \n * Property: last\n * {<OpenLayers.Pixel>} The last pixel location of the drag.\n */\n last: null,\n \n@@ -29564,14 +26937,122 @@\n OpenLayers.Control.ModifyFeature.ROTATE = 4;\n /**\n * Constant: DRAG\n * {Integer} Constant used to make the control work in drag mode\n */\n OpenLayers.Control.ModifyFeature.DRAG = 8;\n /* ======================================================================\n+ OpenLayers/Control/Attribution.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Control.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Control.Attribution\n+ * The attribution control adds attribution from layers to the map display. \n+ * It uses 'attribution' property of each layer.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n+ */\n+OpenLayers.Control.Attribution =\n+ OpenLayers.Class(OpenLayers.Control, {\n+\n+ /**\n+ * APIProperty: separator\n+ * {String} String used to separate layers.\n+ */\n+ separator: \", \",\n+\n+ /**\n+ * APIProperty: template\n+ * {String} Template for the attribution. This has to include the substring\n+ * \"${layers}\", which will be replaced by the layer specific\n+ * attributions, separated by <separator>. The default is \"${layers}\".\n+ */\n+ template: \"${layers}\",\n+\n+ /**\n+ * Constructor: OpenLayers.Control.Attribution \n+ * \n+ * Parameters:\n+ * options - {Object} Options for control.\n+ */\n+\n+ /** \n+ * Method: destroy\n+ * Destroy control.\n+ */\n+ destroy: function() {\n+ this.map.events.un({\n+ \"removelayer\": this.updateAttribution,\n+ \"addlayer\": this.updateAttribution,\n+ \"changelayer\": this.updateAttribution,\n+ \"changebaselayer\": this.updateAttribution,\n+ scope: this\n+ });\n+\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: draw\n+ * Initialize control.\n+ * \n+ * Returns: \n+ * {DOMElement} A reference to the DIV DOMElement containing the control\n+ */\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+\n+ this.map.events.on({\n+ 'changebaselayer': this.updateAttribution,\n+ 'changelayer': this.updateAttribution,\n+ 'addlayer': this.updateAttribution,\n+ 'removelayer': this.updateAttribution,\n+ scope: this\n+ });\n+ this.updateAttribution();\n+\n+ return this.div;\n+ },\n+\n+ /**\n+ * Method: updateAttribution\n+ * Update attribution string.\n+ */\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ // add attribution only if attribution text is unique\n+ if (OpenLayers.Util.indexOf(\n+ attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution);\n+ }\n+ }\n+ }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ });\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+ });\n+/* ======================================================================\n OpenLayers/Control/DrawFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -30008,769 +27489,14 @@\n error: error\n });\n },\n \n CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n });\n /* ======================================================================\n- OpenLayers/Events/buttonclick.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Events.js\n- */\n-\n-/**\n- * Class: OpenLayers.Events.buttonclick\n- * Extension event type for handling buttons on top of a dom element. This\n- * event type fires \"buttonclick\" on its <target> when a button was\n- * clicked. Buttons are detected by the \"olButton\" class.\n- *\n- * This event type makes sure that button clicks do not interfere with other\n- * events that are registered on the same <element>.\n- *\n- * Event types provided by this extension:\n- * - *buttonclick* Triggered when a button is clicked. Listeners receive an\n- * object with a *buttonElement* property referencing the dom element of\n- * the clicked button, and an *buttonXY* property with the click position\n- * relative to the button.\n- */\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n-\n- /**\n- * Property: target\n- * {<OpenLayers.Events>} The events instance that the buttonclick event will\n- * be triggered on.\n- */\n- target: null,\n-\n- /**\n- * Property: events\n- * {Array} Events to observe and conditionally stop from propagating when\n- * an element with the olButton class (or its olAlphaImg child) is\n- * clicked.\n- */\n- events: [\n- 'mousedown', 'mouseup', 'click', 'dblclick',\n- 'touchstart', 'touchmove', 'touchend', 'keydown'\n- ],\n-\n- /**\n- * Property: startRegEx\n- * {RegExp} Regular expression to test Event.type for events that start\n- * a buttonclick sequence.\n- */\n- startRegEx: /^mousedown|touchstart$/,\n-\n- /**\n- * Property: cancelRegEx\n- * {RegExp} Regular expression to test Event.type for events that cancel\n- * a buttonclick sequence.\n- */\n- cancelRegEx: /^touchmove$/,\n-\n- /**\n- * Property: completeRegEx\n- * {RegExp} Regular expression to test Event.type for events that complete\n- * a buttonclick sequence.\n- */\n- completeRegEx: /^mouseup|touchend$/,\n-\n- /**\n- * Property: startEvt\n- * {Event} The event that started the click sequence\n- */\n-\n- /**\n- * Constructor: OpenLayers.Events.buttonclick\n- * Construct a buttonclick event type. Applications are not supposed to\n- * create instances of this class - they are created on demand by\n- * <OpenLayers.Events> instances.\n- *\n- * Parameters:\n- * target - {<OpenLayers.Events>} The events instance that the buttonclick\n- * event will be triggered on.\n- */\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- });\n- }\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick);\n- }\n- delete this.target;\n- },\n-\n- /**\n- * Method: getPressedButton\n- * Get the pressed button, if any. Returns undefined if no button\n- * was pressed.\n- *\n- * Arguments:\n- * element - {DOMElement} The event target.\n- *\n- * Returns:\n- * {DOMElement} The button element, or undefined.\n- */\n- getPressedButton: function(element) {\n- var depth = 3, // limit the search depth\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- // hit!\n- button = element;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return button;\n- },\n-\n- /**\n- * Method: ignore\n- * Check for event target elements that should be ignored by OpenLayers.\n- *\n- * Parameters:\n- * element - {DOMElement} The event target.\n- */\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === 'a') {\n- ignore = true;\n- break;\n- }\n- element = element.parentNode;\n- } while (--depth > 0 && element);\n- return ignore;\n- },\n-\n- /**\n- * Method: buttonClick\n- * Check if a button was clicked, and fire the buttonclick event\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- // was a button pressed?\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- break;\n- }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n-\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- });\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt;\n- }\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt;\n- }\n- }\n- return propagate;\n- }\n-\n-});\n-/* ======================================================================\n- OpenLayers/Control/Panel.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Events/buttonclick.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control.Panel\n- * The Panel control is a container for other controls. With it toolbars\n- * may be composed.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n- /**\n- * Property: controls\n- * {Array(<OpenLayers.Control>)}\n- */\n- controls: null,\n-\n- /**\n- * APIProperty: autoActivate\n- * {Boolean} Activate the control when it is added to a map. Default is\n- * true.\n- */\n- autoActivate: true,\n-\n- /** \n- * APIProperty: defaultControl\n- * {<OpenLayers.Control>} The control which is activated when the control is\n- * activated (turned on), which also happens at instantiation.\n- * If <saveState> is true, <defaultControl> will be nullified after the\n- * first activation of the panel.\n- */\n- defaultControl: null,\n-\n- /**\n- * APIProperty: saveState\n- * {Boolean} If set to true, the active state of this panel's controls will\n- * be stored on panel deactivation, and restored on reactivation. Default\n- * is false.\n- */\n- saveState: false,\n-\n- /**\n- * APIProperty: allowDepress\n- * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can \n- * be deactivated by clicking the icon that represents them. Default \n- * is false.\n- */\n- allowDepress: false,\n-\n- /**\n- * Property: activeState\n- * {Object} stores the active state of this panel's controls.\n- */\n- activeState: null,\n-\n- /**\n- * Constructor: OpenLayers.Control.Panel\n- * Create a new control panel.\n- *\n- * Each control in the panel is represented by an icon. When clicking \n- * on an icon, the <activateControl> method is called.\n- *\n- * Specific properties for controls on a panel:\n- * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>,\n- * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>.\n- * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed.\n- * title - {string} Text displayed when mouse is over the icon that \n- * represents the control. \n- *\n- * The <OpenLayers.Control.type> of a control determines the behavior when\n- * clicking its icon:\n- * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other\n- * controls of this type in the same panel are deactivated. This is\n- * the default type.\n- * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is\n- * toggled.\n- * <OpenLayers.Control.TYPE_BUTTON> - The\n- * <OpenLayers.Control.Button.trigger> method of the control is called,\n- * but its active state is not changed.\n- *\n- * If a control is <OpenLayers.Control.active>, it will be drawn with the\n- * olControl[Name]ItemActive class, otherwise with the\n- * olControl[Name]ItemInactive class.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be used\n- * to extend the control.\n- */\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.controls = [];\n- this.activeState = {};\n- },\n-\n- /**\n- * APIMethod: destroy\n- */\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n- ctl = this.controls[i];\n- if (ctl.events) {\n- ctl.events.un({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n- }\n- ctl.panel_div = null;\n- }\n- this.activeState = null;\n- },\n-\n- /**\n- * APIMethod: activate\n- */\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- if (control === this.defaultControl ||\n- (this.saveState && this.activeState[control.id])) {\n- control.activate();\n- }\n- }\n- if (this.saveState === true) {\n- this.defaultControl = null;\n- }\n- this.redraw();\n- return true;\n- } else {\n- return false;\n- }\n- },\n-\n- /**\n- * APIMethod: deactivate\n- */\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- this.activeState[control.id] = control.deactivate();\n- }\n- this.redraw();\n- return true;\n- } else {\n- return false;\n- }\n- },\n-\n- /**\n- * Method: draw\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick);\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick);\n- }\n- this.addControlsToMap(this.controls);\n- return this.div;\n- },\n-\n- /**\n- * Method: redraw\n- */\n- redraw: function() {\n- for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n- this.div.removeChild(this.div.childNodes[i]);\n- }\n- this.div.innerHTML = \"\";\n- if (this.active) {\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.div.appendChild(this.controls[i].panel_div);\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: activateControl\n- * This method is called when the user click on the icon representing a \n- * control in the panel.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>}\n- */\n- activateControl: function(control) {\n- if (!this.active) {\n- return false;\n- }\n- if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n- control.trigger();\n- return;\n- }\n- if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n- if (control.active) {\n- control.deactivate();\n- } else {\n- control.activate();\n- }\n- return;\n- }\n- if (this.allowDepress && control.active) {\n- control.deactivate();\n- } else {\n- var c;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- c = this.controls[i];\n- if (c != control &&\n- (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n- c.deactivate();\n- }\n- }\n- control.activate();\n- }\n- },\n-\n- /**\n- * APIMethod: addControls\n- * To build a toolbar, you add a set of controls to it. addControls\n- * lets you add a single control or a list of controls to the \n- * Control Panel.\n- *\n- * Parameters:\n- * controls - {<OpenLayers.Control>} Controls to add in the panel.\n- */\n- addControls: function(controls) {\n- if (!(OpenLayers.Util.isArray(controls))) {\n- controls = [controls];\n- }\n- this.controls = this.controls.concat(controls);\n-\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var control = controls[i],\n- element = this.createControlMarkup(control);\n- OpenLayers.Element.addClass(element,\n- control.displayClass + \"ItemInactive\");\n- OpenLayers.Element.addClass(element, \"olButton\");\n- if (control.title != \"\" && !element.title) {\n- element.title = control.title;\n- }\n- control.panel_div = element;\n- }\n-\n- if (this.map) { // map.addControl() has already been called on the panel\n- this.addControlsToMap(controls);\n- this.redraw();\n- }\n- },\n-\n- /**\n- * APIMethod: createControlMarkup\n- * This function just creates a div for the control. If specific HTML\n- * markup is needed this function can be overridden in specific classes,\n- * or at panel instantiation time:\n- *\n- * Example:\n- * (code)\n- * var panel = new OpenLayers.Control.Panel({\n- * defaultControl: control,\n- * // ovverride createControlMarkup to create actual buttons\n- * // including texts wrapped into span elements.\n- * createControlMarkup: function(control) {\n- * var button = document.createElement('button'),\n- * span = document.createElement('span');\n- * if (control.text) {\n- * span.innerHTML = control.text;\n- * }\n- * return button;\n- * }\n- * });\n- * (end)\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control to create the HTML\n- * markup for.\n- *\n- * Returns:\n- * {DOMElement} The markup.\n- */\n- createControlMarkup: function(control) {\n- return document.createElement(\"div\");\n- },\n-\n- /**\n- * Method: addControlsToMap\n- * Only for internal use in draw() and addControls() methods.\n- *\n- * Parameters:\n- * controls - {Array(<OpenLayers.Control>)} Controls to add into map.\n- */\n- addControlsToMap: function(controls) {\n- var control;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- control = controls[i];\n- if (control.autoActivate === true) {\n- control.autoActivate = false;\n- this.map.addControl(control);\n- control.autoActivate = true;\n- } else {\n- this.map.addControl(control);\n- control.deactivate();\n- }\n- control.events.on({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- });\n- }\n- },\n-\n- /**\n- * Method: iconOn\n- * Internal use, for use only with \"controls[i].events.on/un\".\n- */\n- iconOn: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n- d.className = d.className.replace(re, \"$1Active\");\n- },\n-\n- /**\n- * Method: iconOff\n- * Internal use, for use only with \"controls[i].events.on/un\".\n- */\n- iconOff: function() {\n- var d = this.panel_div; // \"this\" refers to a control on panel!\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n- d.className = d.className.replace(re, \"$1Inactive\");\n- },\n-\n- /**\n- * Method: onButtonClick\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- onButtonClick: function(evt) {\n- var controls = this.controls,\n- button = evt.buttonElement;\n- for (var i = controls.length - 1; i >= 0; --i) {\n- if (controls[i].panel_div === button) {\n- this.activateControl(controls[i]);\n- break;\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: getControlsBy\n- * Get a list of controls with properties matching the given criteria.\n- *\n- * Parameters:\n- * property - {String} A control property to be matched.\n- * match - {String | Object} A string to match. Can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * match.test(control[property]) evaluates to true, the control will be\n- * included in the array returned. If no controls are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria.\n- * An empty array is returned if no matches are found.\n- */\n- getControlsBy: function(property, match) {\n- var test = (typeof match.test == \"function\");\n- var found = OpenLayers.Array.filter(this.controls, function(item) {\n- return item[property] == match || (test && match.test(item[property]));\n- });\n- return found;\n- },\n-\n- /**\n- * APIMethod: getControlsByName\n- * Get a list of contorls with names matching the given name.\n- *\n- * Parameters:\n- * match - {String | Object} A control name. The name can also be a regular\n- * expression literal or object. In addition, it can be any object\n- * with a method named test. For reqular expressions or other, if\n- * name.test(control.name) evaluates to true, the control will be included\n- * in the list of controls returned. If no controls are found, an empty\n- * array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given name.\n- * An empty array is returned if no matches are found.\n- */\n- getControlsByName: function(match) {\n- return this.getControlsBy(\"name\", match);\n- },\n-\n- /**\n- * APIMethod: getControlsByClass\n- * Get a list of controls of a given type (CLASS_NAME).\n- *\n- * Parameters:\n- * match - {String | Object} A control class name. The type can also be a\n- * regular expression literal or object. In addition, it can be any\n- * object with a method named test. For reqular expressions or other,\n- * if type.test(control.CLASS_NAME) evaluates to true, the control will\n- * be included in the list of controls returned. If no controls are\n- * found, an empty array is returned.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Control>)} A list of controls matching the given type.\n- * An empty array is returned if no matches are found.\n- */\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control.Panel\"\n-});\n-\n-/* ======================================================================\n- OpenLayers/Control/Attribution.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Control.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control.Attribution\n- * The attribution control adds attribution from layers to the map display. \n- * It uses 'attribution' property of each layer.\n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.Attribution =\n- OpenLayers.Class(OpenLayers.Control, {\n-\n- /**\n- * APIProperty: separator\n- * {String} String used to separate layers.\n- */\n- separator: \", \",\n-\n- /**\n- * APIProperty: template\n- * {String} Template for the attribution. This has to include the substring\n- * \"${layers}\", which will be replaced by the layer specific\n- * attributions, separated by <separator>. The default is \"${layers}\".\n- */\n- template: \"${layers}\",\n-\n- /**\n- * Constructor: OpenLayers.Control.Attribution \n- * \n- * Parameters:\n- * options - {Object} Options for control.\n- */\n-\n- /** \n- * Method: destroy\n- * Destroy control.\n- */\n- destroy: function() {\n- this.map.events.un({\n- \"removelayer\": this.updateAttribution,\n- \"addlayer\": this.updateAttribution,\n- \"changelayer\": this.updateAttribution,\n- \"changebaselayer\": this.updateAttribution,\n- scope: this\n- });\n-\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: draw\n- * Initialize control.\n- * \n- * Returns: \n- * {DOMElement} A reference to the DIV DOMElement containing the control\n- */\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n-\n- this.map.events.on({\n- 'changebaselayer': this.updateAttribution,\n- 'changelayer': this.updateAttribution,\n- 'addlayer': this.updateAttribution,\n- 'removelayer': this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n-\n- return this.div;\n- },\n-\n- /**\n- * Method: updateAttribution\n- * Update attribution string.\n- */\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- // add attribution only if attribution text is unique\n- if (OpenLayers.Util.indexOf(\n- attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution);\n- }\n- }\n- }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- });\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n- });\n-/* ======================================================================\n OpenLayers/Control/Zoom.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -30906,1295 +27632,14 @@\n delete this.zoomOutLink;\n OpenLayers.Control.prototype.destroy.apply(this);\n },\n \n CLASS_NAME: \"OpenLayers.Control.Zoom\"\n });\n /* ======================================================================\n- OpenLayers/Handler/Feature.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Handler.js\n- */\n-\n-/**\n- * Class: OpenLayers.Handler.Feature \n- * Handler to respond to mouse events related to a drawn feature. Callbacks\n- * with the following keys will be notified of the following events\n- * associated with features: click, clickout, over, out, and dblclick.\n- *\n- * This handler stops event propagation for mousedown and mouseup if those\n- * browser events target features that can be selected.\n- *\n- * Inherits from:\n- * - <OpenLayers.Handler>\n- */\n-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n-\n- /**\n- * Property: EVENTMAP\n- * {Object} A object mapping the browser events to objects with callback\n- * keys for in and out.\n- */\n- EVENTMAP: {\n- 'click': {\n- 'in': 'click',\n- 'out': 'clickout'\n- },\n- 'mousemove': {\n- 'in': 'over',\n- 'out': 'out'\n- },\n- 'dblclick': {\n- 'in': 'dblclick',\n- 'out': null\n- },\n- 'mousedown': {\n- 'in': null,\n- 'out': null\n- },\n- 'mouseup': {\n- 'in': null,\n- 'out': null\n- },\n- 'touchstart': {\n- 'in': 'click',\n- 'out': 'clickout'\n- }\n- },\n-\n- /**\n- * Property: feature\n- * {<OpenLayers.Feature.Vector>} The last feature that was hovered.\n- */\n- feature: null,\n-\n- /**\n- * Property: lastFeature\n- * {<OpenLayers.Feature.Vector>} The last feature that was handled.\n- */\n- lastFeature: null,\n-\n- /**\n- * Property: down\n- * {<OpenLayers.Pixel>} The location of the last mousedown.\n- */\n- down: null,\n-\n- /**\n- * Property: up\n- * {<OpenLayers.Pixel>} The location of the last mouseup.\n- */\n- up: null,\n-\n- /**\n- * Property: clickTolerance\n- * {Number} The number of pixels the mouse can move between mousedown\n- * and mouseup for the event to still be considered a click.\n- * Dragging the map should not trigger the click and clickout callbacks\n- * unless the map is moved by less than this tolerance. Defaults to 4.\n- */\n- clickTolerance: 4,\n-\n- /**\n- * Property: geometryTypes\n- * To restrict dragging to a limited set of geometry types, send a list\n- * of strings corresponding to the geometry class names.\n- * \n- * @type Array(String)\n- */\n- geometryTypes: null,\n-\n- /**\n- * Property: stopClick\n- * {Boolean} If stopClick is set to true, handled clicks do not\n- * propagate to other click listeners. Otherwise, handled clicks\n- * do propagate. Unhandled clicks always propagate, whatever the\n- * value of stopClick. Defaults to true.\n- */\n- stopClick: true,\n-\n- /**\n- * Property: stopDown\n- * {Boolean} If stopDown is set to true, handled mousedowns do not\n- * propagate to other mousedown listeners. Otherwise, handled\n- * mousedowns do propagate. Unhandled mousedowns always propagate,\n- * whatever the value of stopDown. Defaults to true.\n- */\n- stopDown: true,\n-\n- /**\n- * Property: stopUp\n- * {Boolean} If stopUp is set to true, handled mouseups do not\n- * propagate to other mouseup listeners. Otherwise, handled mouseups\n- * do propagate. Unhandled mouseups always propagate, whatever the\n- * value of stopUp. Defaults to false.\n- */\n- stopUp: false,\n-\n- /**\n- * Constructor: OpenLayers.Handler.Feature\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} \n- * layer - {<OpenLayers.Layer.Vector>}\n- * callbacks - {Object} An object with a 'over' property whos value is\n- * a function to be called when the mouse is over a feature. The \n- * callback should expect to recieve a single argument, the feature.\n- * options - {Object} \n- */\n- initialize: function(control, layer, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.layer = layer;\n- },\n-\n- /**\n- * Method: touchstart\n- * Handle touchstart events\n- *\n- * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} Let the event propagate.\n- */\n- touchstart: function(evt) {\n- this.startTouch();\n- return OpenLayers.Event.isMultiTouch(evt) ?\n- true : this.mousedown(evt);\n- },\n-\n- /**\n- * Method: touchmove\n- * Handle touchmove events. We just prevent the browser default behavior,\n- * for Android Webkit not to select text when moving the finger after\n- * selecting a feature.\n- *\n- * Parameters:\n- * evt - {Event}\n- */\n- touchmove: function(evt) {\n- OpenLayers.Event.preventDefault(evt);\n- },\n-\n- /**\n- * Method: mousedown\n- * Handle mouse down. Stop propagation if a feature is targeted by this\n- * event (stops map dragging during feature selection).\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- mousedown: function(evt) {\n- // Feature selection is only done with a left click. Other handlers may stop the\n- // propagation of left-click mousedown events but not right-click mousedown events.\n- // This mismatch causes problems when comparing the location of the down and up\n- // events in the click function so it is important ignore right-clicks.\n- if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n- this.down = evt.xy;\n- }\n- return this.handle(evt) ? !this.stopDown : true;\n- },\n-\n- /**\n- * Method: mouseup\n- * Handle mouse up. Stop propagation if a feature is targeted by this\n- * event.\n- * \n- * Parameters:\n- * evt - {Event} \n- */\n- mouseup: function(evt) {\n- this.up = evt.xy;\n- return this.handle(evt) ? !this.stopUp : true;\n- },\n-\n- /**\n- * Method: click\n- * Handle click. Call the \"click\" callback if click on a feature,\n- * or the \"clickout\" callback if click outside any feature.\n- * \n- * Parameters:\n- * evt - {Event} \n- *\n- * Returns:\n- * {Boolean}\n- */\n- click: function(evt) {\n- return this.handle(evt) ? !this.stopClick : true;\n- },\n-\n- /**\n- * Method: mousemove\n- * Handle mouse moves. Call the \"over\" callback if moving in to a feature,\n- * or the \"out\" callback if moving out of a feature.\n- * \n- * Parameters:\n- * evt - {Event} \n- *\n- * Returns:\n- * {Boolean}\n- */\n- mousemove: function(evt) {\n- if (!this.callbacks['over'] && !this.callbacks['out']) {\n- return true;\n- }\n- this.handle(evt);\n- return true;\n- },\n-\n- /**\n- * Method: dblclick\n- * Handle dblclick. Call the \"dblclick\" callback if dblclick on a feature.\n- *\n- * Parameters:\n- * evt - {Event} \n- *\n- * Returns:\n- * {Boolean}\n- */\n- dblclick: function(evt) {\n- return !this.handle(evt);\n- },\n-\n- /**\n- * Method: geometryTypeMatches\n- * Return true if the geometry type of the passed feature matches\n- * one of the geometry types in the geometryTypes array.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Vector.Feature>}\n- *\n- * Returns:\n- * {Boolean}\n- */\n- geometryTypeMatches: function(feature) {\n- return this.geometryTypes == null ||\n- OpenLayers.Util.indexOf(this.geometryTypes,\n- feature.geometry.CLASS_NAME) > -1;\n- },\n-\n- /**\n- * Method: handle\n- *\n- * Parameters:\n- * evt - {Event}\n- *\n- * Returns:\n- * {Boolean} The event occurred over a relevant feature.\n- */\n- handle: function(evt) {\n- if (this.feature && !this.feature.layer) {\n- // feature has been destroyed\n- this.feature = null;\n- }\n- var type = evt.type;\n- var handled = false;\n- var previouslyIn = !!(this.feature); // previously in a feature\n- var click = (type == \"click\" || type == \"dblclick\" || type == \"touchstart\");\n- this.feature = this.layer.getFeatureFromEvent(evt);\n- if (this.feature && !this.feature.layer) {\n- // feature has been destroyed\n- this.feature = null;\n- }\n- if (this.lastFeature && !this.lastFeature.layer) {\n- // last feature has been destroyed\n- this.lastFeature = null;\n- }\n- if (this.feature) {\n- if (type === \"touchstart\") {\n- // stop the event to prevent Android Webkit from\n- // \"flashing\" the map div\n- OpenLayers.Event.preventDefault(evt);\n- }\n- var inNew = (this.feature != this.lastFeature);\n- if (this.geometryTypeMatches(this.feature)) {\n- // in to a feature\n- if (previouslyIn && inNew) {\n- // out of last feature and in to another\n- if (this.lastFeature) {\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- this.triggerCallback(type, 'in', [this.feature]);\n- } else if (!previouslyIn || click) {\n- // in feature for the first time\n- this.triggerCallback(type, 'in', [this.feature]);\n- }\n- this.lastFeature = this.feature;\n- handled = true;\n- } else {\n- // not in to a feature\n- if (this.lastFeature && (previouslyIn && inNew || click)) {\n- // out of last feature for the first time\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- // next time the mouse goes in a feature whose geometry type\n- // doesn't match we don't want to call the 'out' callback\n- // again, so let's set this.feature to null so that\n- // previouslyIn will evaluate to false the next time\n- // we enter handle. Yes, a bit hackish...\n- this.feature = null;\n- }\n- } else if (this.lastFeature && (previouslyIn || click)) {\n- this.triggerCallback(type, 'out', [this.lastFeature]);\n- }\n- return handled;\n- },\n-\n- /**\n- * Method: triggerCallback\n- * Call the callback keyed in the event map with the supplied arguments.\n- * For click and clickout, the <clickTolerance> is checked first.\n- *\n- * Parameters:\n- * type - {String}\n- */\n- triggerCallback: function(type, mode, args) {\n- var key = this.EVENTMAP[type][mode];\n- if (key) {\n- if (type == 'click' && this.up && this.down) {\n- // for click/clickout, only trigger callback if tolerance is met\n- var dpx = Math.sqrt(\n- Math.pow(this.up.x - this.down.x, 2) +\n- Math.pow(this.up.y - this.down.y, 2)\n- );\n- if (dpx <= this.clickTolerance) {\n- this.callback(key, args);\n- }\n- // we're done with this set of events now: clear the cached\n- // positions so we can't trip over them later (this can occur\n- // if one of the up/down events gets eaten before it gets to us\n- // but we still get the click)\n- this.up = this.down = null;\n- } else {\n- this.callback(key, args);\n- }\n- }\n- },\n-\n- /**\n- * Method: activate \n- * Turn on the handler. Returns false if the handler was already active.\n- *\n- * Returns:\n- * {Boolean}\n- */\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.moveLayerToTop();\n- this.map.events.on({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n- });\n- activated = true;\n- }\n- return activated;\n- },\n-\n- /**\n- * Method: deactivate \n- * Turn off the handler. Returns false if the handler was already active.\n- *\n- * Returns: \n- * {Boolean}\n- */\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.feature = null;\n- this.lastFeature = null;\n- this.down = null;\n- this.up = null;\n- this.map.events.un({\n- \"removelayer\": this.handleMapEvents,\n- \"changelayer\": this.handleMapEvents,\n- scope: this\n- });\n- deactivated = true;\n- }\n- return deactivated;\n- },\n-\n- /**\n- * Method: handleMapEvents\n- * \n- * Parameters:\n- * evt - {Object}\n- */\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop();\n- }\n- },\n-\n- /**\n- * Method: moveLayerToTop\n- * Moves the layer for this handler to the top, so mouse events can reach\n- * it.\n- */\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n- this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index);\n-\n- },\n-\n- /**\n- * Method: moveLayerBack\n- * Moves the layer back to the position determined by the map's layers\n- * array.\n- */\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE['Feature']) {\n- this.layer.setZIndex(index);\n- } else {\n- this.map.setLayerZIndex(this.layer,\n- this.map.getLayerIndex(this.layer));\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Handler.Feature\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Vector/RootContainer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Vector.RootContainer\n- * A special layer type to combine multiple vector layers inside a single\n- * renderer root container. This class is not supposed to be instantiated\n- * from user space, it is a helper class for controls that require event\n- * processing for multiple vector layers.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n- */\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * Property: displayInLayerSwitcher\n- * Set to false for this layer type\n- */\n- displayInLayerSwitcher: false,\n-\n- /**\n- * APIProperty: layers\n- * Layers that are attached to this container. Required config option.\n- */\n- layers: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Vector.RootContainer\n- * Create a new root container for multiple vector layer. This constructor\n- * is not supposed to be used from user space, it is only to be used by\n- * controls that need feature selection across multiple vector layers.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- * \n- * Required options properties:\n- * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n- * container\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n- * container\n- */\n-\n- /**\n- * Method: display\n- */\n- display: function() {},\n-\n- /**\n- * Method: getFeatureFromEvent\n- * walk through the layers to find the feature returned by the event\n- * \n- * Parameters:\n- * evt - {Object} event object with a feature property\n- * \n- * Returns:\n- * {<OpenLayers.Feature.Vector>}\n- */\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature;\n- }\n- }\n- },\n-\n- /**\n- * Method: setMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer);\n- },\n-\n- /**\n- * Method: removeMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n- },\n-\n- /**\n- * Method: collectRoots\n- * Collects the root nodes of all layers this control is configured with\n- * and moveswien the nodes to this control's layer\n- */\n- collectRoots: function() {\n- var layer;\n- // walk through all map layers, because we want to keep the order\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer);\n- }\n- }\n- },\n-\n- /**\n- * Method: resetRoots\n- * Resets the root nodes back into the layers they belong to.\n- */\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer);\n- }\n- }\n- },\n-\n- /**\n- * Method: handleChangeLayer\n- * Event handler for the map's changelayer event. We need to rebuild\n- * this container's layer dom if order of one of its layers changes.\n- * This handler is added with the setMap method, and removed with the\n- * removeMap method.\n- * \n- * Parameters:\n- * evt - {Object}\n- */\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" &&\n- OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots();\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n-});\n-/* ======================================================================\n- OpenLayers/Control/SelectFeature.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Control.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Handler/Feature.js\n- * @requires OpenLayers/Layer/Vector/RootContainer.js\n- */\n-\n-/**\n- * Class: OpenLayers.Control.SelectFeature\n- * The SelectFeature control selects vector features from a given layer on \n- * click or hover. \n- *\n- * Inherits from:\n- * - <OpenLayers.Control>\n- */\n-OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n-\n- /** \n- * APIProperty: events\n- * {<OpenLayers.Events>} Events instance for listeners and triggering\n- * control specific events.\n- *\n- * Register a listener for a particular event with the following syntax:\n- * (code)\n- * control.events.register(type, obj, listener);\n- * (end)\n- *\n- * Supported event types (in addition to those from <OpenLayers.Control.events>):\n- * beforefeaturehighlighted - Triggered before a feature is highlighted\n- * featurehighlighted - Triggered when a feature is highlighted\n- * featureunhighlighted - Triggered when a feature is unhighlighted\n- * boxselectionstart - Triggered before box selection starts\n- * boxselectionend - Triggered after box selection ends\n- */\n-\n- /**\n- * Property: multipleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <multiple> property to true. Default is null.\n- */\n- multipleKey: null,\n-\n- /**\n- * Property: toggleKey\n- * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n- * the <toggle> property to true. Default is null.\n- */\n- toggleKey: null,\n-\n- /**\n- * APIProperty: multiple\n- * {Boolean} Allow selection of multiple geometries. Default is false.\n- */\n- multiple: false,\n-\n- /**\n- * APIProperty: clickout\n- * {Boolean} Unselect features when clicking outside any feature.\n- * Default is true.\n- */\n- clickout: true,\n-\n- /**\n- * APIProperty: toggle\n- * {Boolean} Unselect a selected feature on click. Default is false. Only\n- * has meaning if hover is false.\n- */\n- toggle: false,\n-\n- /**\n- * APIProperty: hover\n- * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n- * ignores clicks and only listens to mouse moves.\n- */\n- hover: false,\n-\n- /**\n- * APIProperty: highlightOnly\n- * {Boolean} If true do not actually select features (that is place them in \n- * the layer's selected features array), just highlight them. This property\n- * has no effect if hover is false. Defaults to false.\n- */\n- highlightOnly: false,\n-\n- /**\n- * APIProperty: box\n- * {Boolean} Allow feature selection by drawing a box.\n- */\n- box: false,\n-\n- /**\n- * Property: onBeforeSelect \n- * {Function} Optional function to be called before a feature is selected.\n- * The function should expect to be called with a feature.\n- */\n- onBeforeSelect: function() {},\n-\n- /**\n- * APIProperty: onSelect \n- * {Function} Optional function to be called when a feature is selected.\n- * The function should expect to be called with a feature.\n- */\n- onSelect: function() {},\n-\n- /**\n- * APIProperty: onUnselect\n- * {Function} Optional function to be called when a feature is unselected.\n- * The function should expect to be called with a feature.\n- */\n- onUnselect: function() {},\n-\n- /**\n- * Property: scope\n- * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n- * callbacks. If null the scope will be this control.\n- */\n- scope: null,\n-\n- /**\n- * APIProperty: geometryTypes\n- * {Array(String)} To restrict selecting to a limited set of geometry types,\n- * send a list of strings corresponding to the geometry class names.\n- */\n- geometryTypes: null,\n-\n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n- * root for all layers this control is configured with (if an array of\n- * layers was passed to the constructor), or the vector layer the control\n- * was configured with (if a single layer was passed to the constructor).\n- */\n- layer: null,\n-\n- /**\n- * Property: layers\n- * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n- * or null if the control was configured with a single layer\n- */\n- layers: null,\n-\n- /**\n- * APIProperty: callbacks\n- * {Object} The functions that are sent to the handlers.feature for callback\n- */\n- callbacks: null,\n-\n- /**\n- * APIProperty: selectStyle \n- * {Object} Hash of styles\n- */\n- selectStyle: null,\n-\n- /**\n- * Property: renderIntent\n- * {String} key used to retrieve the select style from the layer's\n- * style map.\n- */\n- renderIntent: \"select\",\n-\n- /**\n- * Property: handlers\n- * {Object} Object with references to multiple <OpenLayers.Handler>\n- * instances.\n- */\n- handlers: null,\n-\n- /**\n- * Constructor: OpenLayers.Control.SelectFeature\n- * Create a new control for selecting features.\n- *\n- * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n- * layer(s) this control will select features from.\n- * options - {Object} \n- */\n- initialize: function(layers, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n-\n- if (this.scope === null) {\n- this.scope = this;\n- }\n- this.initLayer(layers);\n- var callbacks = {\n- click: this.clickFeature,\n- clickout: this.clickoutFeature\n- };\n- if (this.hover) {\n- callbacks.over = this.overFeature;\n- callbacks.out = this.outFeature;\n- }\n-\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlers = {\n- feature: new OpenLayers.Handler.Feature(\n- this, this.layer, this.callbacks, {\n- geometryTypes: this.geometryTypes\n- }\n- )\n- };\n-\n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(\n- this, {\n- done: this.selectBox\n- }, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- }\n- );\n- }\n- },\n-\n- /**\n- * Method: initLayer\n- * Assign the layer property. If layers is an array, we need to use\n- * a RootContainer.\n- *\n- * Parameters:\n- * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n- */\n- initLayer: function(layers) {\n- if (OpenLayers.Util.isArray(layers)) {\n- this.layers = layers;\n- this.layer = new OpenLayers.Layer.Vector.RootContainer(\n- this.id + \"_container\", {\n- layers: layers\n- }\n- );\n- } else {\n- this.layer = layers;\n- }\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- if (this.active && this.layers) {\n- this.map.removeLayer(this.layer);\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.layers) {\n- this.layer.destroy();\n- }\n- },\n-\n- /**\n- * Method: activate\n- * Activates the control.\n- * \n- * Returns:\n- * {Boolean} The control was effectively activated.\n- */\n- activate: function() {\n- if (!this.active) {\n- if (this.layers) {\n- this.map.addLayer(this.layer);\n- }\n- this.handlers.feature.activate();\n- if (this.box && this.handlers.box) {\n- this.handlers.box.activate();\n- }\n- }\n- return OpenLayers.Control.prototype.activate.apply(\n- this, arguments\n- );\n- },\n-\n- /**\n- * Method: deactivate\n- * Deactivates the control.\n- * \n- * Returns:\n- * {Boolean} The control was effectively deactivated.\n- */\n- deactivate: function() {\n- if (this.active) {\n- this.handlers.feature.deactivate();\n- if (this.handlers.box) {\n- this.handlers.box.deactivate();\n- }\n- if (this.layers) {\n- this.map.removeLayer(this.layer);\n- }\n- }\n- return OpenLayers.Control.prototype.deactivate.apply(\n- this, arguments\n- );\n- },\n-\n- /**\n- * Method: unselectAll\n- * Unselect all selected features. To unselect all except for a single\n- * feature, set the options.except property to the feature.\n- *\n- * Parameters:\n- * options - {Object} Optional configuration object.\n- */\n- unselectAll: function(options) {\n- // we'll want an option to supress notification here\n- var layers = this.layers || [this.layer],\n- layer, feature, l, numExcept;\n- for (l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- numExcept = 0;\n- //layer.selectedFeatures is null when layer is destroyed and \n- //one of it's preremovelayer listener calls setLayer \n- //with another layer on this control\n- if (layer.selectedFeatures != null) {\n- while (layer.selectedFeatures.length > numExcept) {\n- feature = layer.selectedFeatures[numExcept];\n- if (!options || options.except != feature) {\n- this.unselect(feature);\n- } else {\n- ++numExcept;\n- }\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: clickFeature\n- * Called on click in a feature\n- * Only responds if this.hover is false.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- clickFeature: function(feature) {\n- if (!this.hover) {\n- var selected = (OpenLayers.Util.indexOf(\n- feature.layer.selectedFeatures, feature) > -1);\n- if (selected) {\n- if (this.toggleSelect()) {\n- this.unselect(feature);\n- } else if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- });\n- }\n- } else {\n- if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- });\n- }\n- this.select(feature);\n- }\n- }\n- },\n-\n- /**\n- * Method: multipleSelect\n- * Allow for multiple selected features based on <multiple> property and\n- * <multipleKey> event modifier.\n- *\n- * Returns:\n- * {Boolean} Allow for multiple selected features.\n- */\n- multipleSelect: function() {\n- return this.multiple || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.multipleKey]);\n- },\n-\n- /**\n- * Method: toggleSelect\n- * Event should toggle the selected state of a feature based on <toggle>\n- * property and <toggleKey> event modifier.\n- *\n- * Returns:\n- * {Boolean} Toggle the selected state of a feature.\n- */\n- toggleSelect: function() {\n- return this.toggle || (this.handlers.feature.evt &&\n- this.handlers.feature.evt[this.toggleKey]);\n- },\n-\n- /**\n- * Method: clickoutFeature\n- * Called on click outside a previously clicked (selected) feature.\n- * Only responds if this.hover is false.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Vector.Feature>} \n- */\n- clickoutFeature: function(feature) {\n- if (!this.hover && this.clickout) {\n- this.unselectAll();\n- }\n- },\n-\n- /**\n- * Method: overFeature\n- * Called on over a feature.\n- * Only responds if this.hover is true.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- overFeature: function(feature) {\n- var layer = feature.layer;\n- if (this.hover) {\n- if (this.highlightOnly) {\n- this.highlight(feature);\n- } else if (OpenLayers.Util.indexOf(\n- layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n- }\n- }\n- },\n-\n- /**\n- * Method: outFeature\n- * Called on out of a selected feature.\n- * Only responds if this.hover is true.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- outFeature: function(feature) {\n- if (this.hover) {\n- if (this.highlightOnly) {\n- // we do nothing if we're not the last highlighter of the\n- // feature\n- if (feature._lastHighlighter == this.id) {\n- // if another select control had highlighted the feature before\n- // we did it ourself then we use that control to highlight the\n- // feature as it was before we highlighted it, else we just\n- // unhighlight it\n- if (feature._prevHighlighter &&\n- feature._prevHighlighter != this.id) {\n- delete feature._lastHighlighter;\n- var control = this.map.getControl(\n- feature._prevHighlighter);\n- if (control) {\n- control.highlight(feature);\n- }\n- } else {\n- this.unhighlight(feature);\n- }\n- }\n- } else {\n- this.unselect(feature);\n- }\n- }\n- },\n-\n- /**\n- * Method: highlight\n- * Redraw feature with the select style.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- highlight: function(feature) {\n- var layer = feature.layer;\n- var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- feature._prevHighlighter = feature._lastHighlighter;\n- feature._lastHighlighter = this.id;\n- var style = this.selectStyle || this.renderIntent;\n- layer.drawFeature(feature, style);\n- this.events.triggerEvent(\"featurehighlighted\", {\n- feature: feature\n- });\n- }\n- },\n-\n- /**\n- * Method: unhighlight\n- * Redraw feature with the \"default\" style\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- unhighlight: function(feature) {\n- var layer = feature.layer;\n- // three cases:\n- // 1. there's no other highlighter, in that case _prev is undefined,\n- // and we just need to undef _last\n- // 2. another control highlighted the feature after we did it, in\n- // that case _last references this other control, and we just\n- // need to undef _prev\n- // 3. another control highlighted the feature before we did it, in\n- // that case _prev references this other control, and we need to\n- // set _last to _prev and undef _prev\n- if (feature._prevHighlighter == undefined) {\n- delete feature._lastHighlighter;\n- } else if (feature._prevHighlighter == this.id) {\n- delete feature._prevHighlighter;\n- } else {\n- feature._lastHighlighter = feature._prevHighlighter;\n- delete feature._prevHighlighter;\n- }\n- layer.drawFeature(feature, feature.style || feature.layer.style ||\n- \"default\");\n- this.events.triggerEvent(\"featureunhighlighted\", {\n- feature: feature\n- });\n- },\n-\n- /**\n- * Method: select\n- * Add feature to the layer's selectedFeature array, render the feature as\n- * selected, and call the onSelect function.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- select: function(feature) {\n- var cont = this.onBeforeSelect.call(this.scope, feature);\n- var layer = feature.layer;\n- if (cont !== false) {\n- cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- layer.selectedFeatures.push(feature);\n- this.highlight(feature);\n- // if the feature handler isn't involved in the feature\n- // selection (because the box handler is used or the\n- // feature is selected programatically) we fake the\n- // feature handler to allow unselecting on click\n- if (!this.handlers.feature.lastFeature) {\n- this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n- }\n- layer.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- });\n- this.onSelect.call(this.scope, feature);\n- }\n- }\n- },\n-\n- /**\n- * Method: unselect\n- * Remove feature from the layer's selectedFeature array, render the feature as\n- * normal, and call the onUnselect function.\n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- */\n- unselect: function(feature) {\n- var layer = feature.layer;\n- // Store feature style for restoration later\n- this.unhighlight(feature);\n- OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n- layer.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n- });\n- this.onUnselect.call(this.scope, feature);\n- },\n-\n- /**\n- * Method: selectBox\n- * Callback from the handlers.box set up when <box> selection is true\n- * on.\n- *\n- * Parameters:\n- * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n- */\n- selectBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- var bounds = new OpenLayers.Bounds(\n- minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n- );\n-\n- // if multiple is false, first deselect currently selected features\n- if (!this.multipleSelect()) {\n- this.unselectAll();\n- }\n-\n- // because we're using a box, we consider we want multiple selection\n- var prevMultiple = this.multiple;\n- this.multiple = true;\n- var layers = this.layers || [this.layer];\n- this.events.triggerEvent(\"boxselectionstart\", {\n- layers: layers\n- });\n- var layer;\n- for (var l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- for (var i = 0, len = layer.features.length; i < len; ++i) {\n- var feature = layer.features[i];\n- // check if the feature is displayed\n- if (!feature.getVisibility()) {\n- continue;\n- }\n-\n- if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n- this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n- if (bounds.toGeometry().intersects(feature.geometry)) {\n- if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature);\n- }\n- }\n- }\n- }\n- }\n- this.multiple = prevMultiple;\n- this.events.triggerEvent(\"boxselectionend\", {\n- layers: layers\n- });\n- }\n- },\n-\n- /** \n- * Method: setMap\n- * Set the map property for the control. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- this.handlers.feature.setMap(map);\n- if (this.box) {\n- this.handlers.box.setMap(map);\n- }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: setLayer\n- * Attach a new layer to the control, overriding any existing layers.\n- *\n- * Parameters:\n- * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n- * {<OpenLayers.Layer.Vector>}\n- */\n- setLayer: function(layers) {\n- var isActive = this.active;\n- this.unselectAll();\n- this.deactivate();\n- if (this.layers) {\n- this.layer.destroy();\n- this.layers = null;\n- }\n- this.initLayer(layers);\n- this.handlers.feature.layer = this.layer;\n- if (isActive) {\n- this.activate();\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n-});\n-/* ======================================================================\n OpenLayers/Control/DragPan.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -33456,3025 +28901,4299 @@\n defaultDblClick: function(evt) {\n this.map.zoomTo(this.map.zoom + 1, evt.xy);\n },\n \n CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n });\n /* ======================================================================\n- OpenLayers/Renderer/Canvas.js\n+ OpenLayers/Handler/Feature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/Handler.js\n */\n \n /**\n- * Class: OpenLayers.Renderer.Canvas \n- * A renderer based on the 2D 'canvas' drawing element.\n- * \n- * Inherits:\n- * - <OpenLayers.Renderer>\n+ * Class: OpenLayers.Handler.Feature \n+ * Handler to respond to mouse events related to a drawn feature. Callbacks\n+ * with the following keys will be notified of the following events\n+ * associated with features: click, clickout, over, out, and dblclick.\n+ *\n+ * This handler stops event propagation for mousedown and mouseup if those\n+ * browser events target features that can be selected.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Handler>\n */\n-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n \n /**\n- * APIProperty: hitDetection\n- * {Boolean} Allow for hit detection of features. Default is true.\n+ * Property: EVENTMAP\n+ * {Object} A object mapping the browser events to objects with callback\n+ * keys for in and out.\n */\n- hitDetection: true,\n+ EVENTMAP: {\n+ 'click': {\n+ 'in': 'click',\n+ 'out': 'clickout'\n+ },\n+ 'mousemove': {\n+ 'in': 'over',\n+ 'out': 'out'\n+ },\n+ 'dblclick': {\n+ 'in': 'dblclick',\n+ 'out': null\n+ },\n+ 'mousedown': {\n+ 'in': null,\n+ 'out': null\n+ },\n+ 'mouseup': {\n+ 'in': null,\n+ 'out': null\n+ },\n+ 'touchstart': {\n+ 'in': 'click',\n+ 'out': 'clickout'\n+ }\n+ },\n \n /**\n- * Property: hitOverflow\n- * {Number} The method for converting feature identifiers to color values\n- * supports 16777215 sequential values. Two features cannot be \n- * predictably detected if their identifiers differ by more than this\n- * value. The hitOverflow allows for bigger numbers (but the \n- * difference in values is still limited).\n+ * Property: feature\n+ * {<OpenLayers.Feature.Vector>} The last feature that was hovered.\n */\n- hitOverflow: 0,\n+ feature: null,\n \n /**\n- * Property: canvas\n- * {Canvas} The canvas context object.\n+ * Property: lastFeature\n+ * {<OpenLayers.Feature.Vector>} The last feature that was handled.\n */\n- canvas: null,\n+ lastFeature: null,\n \n /**\n- * Property: features\n- * {Object} Internal object of feature/style pairs for use in redrawing the layer.\n+ * Property: down\n+ * {<OpenLayers.Pixel>} The location of the last mousedown.\n */\n- features: null,\n+ down: null,\n \n /**\n- * Property: pendingRedraw\n- * {Boolean} The renderer needs a redraw call to render features added while\n- * the renderer was locked.\n+ * Property: up\n+ * {<OpenLayers.Pixel>} The location of the last mouseup.\n */\n- pendingRedraw: false,\n+ up: null,\n \n /**\n- * Property: cachedSymbolBounds\n- * {Object} Internal cache of calculated symbol extents.\n+ * Property: clickTolerance\n+ * {Number} The number of pixels the mouse can move between mousedown\n+ * and mouseup for the event to still be considered a click.\n+ * Dragging the map should not trigger the click and clickout callbacks\n+ * unless the map is moved by less than this tolerance. Defaults to 4.\n */\n- cachedSymbolBounds: {},\n+ clickTolerance: 4,\n \n /**\n- * Constructor: OpenLayers.Renderer.Canvas\n+ * Property: geometryTypes\n+ * To restrict dragging to a limited set of geometry types, send a list\n+ * of strings corresponding to the geometry class names.\n+ * \n+ * @type Array(String)\n+ */\n+ geometryTypes: null,\n+\n+ /**\n+ * Property: stopClick\n+ * {Boolean} If stopClick is set to true, handled clicks do not\n+ * propagate to other click listeners. Otherwise, handled clicks\n+ * do propagate. Unhandled clicks always propagate, whatever the\n+ * value of stopClick. Defaults to true.\n+ */\n+ stopClick: true,\n+\n+ /**\n+ * Property: stopDown\n+ * {Boolean} If stopDown is set to true, handled mousedowns do not\n+ * propagate to other mousedown listeners. Otherwise, handled\n+ * mousedowns do propagate. Unhandled mousedowns always propagate,\n+ * whatever the value of stopDown. Defaults to true.\n+ */\n+ stopDown: true,\n+\n+ /**\n+ * Property: stopUp\n+ * {Boolean} If stopUp is set to true, handled mouseups do not\n+ * propagate to other mouseup listeners. Otherwise, handled mouseups\n+ * do propagate. Unhandled mouseups always propagate, whatever the\n+ * value of stopUp. Defaults to false.\n+ */\n+ stopUp: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Handler.Feature\n *\n * Parameters:\n- * containerID - {<String>}\n- * options - {Object} Optional properties to be set on the renderer.\n+ * control - {<OpenLayers.Control>} \n+ * layer - {<OpenLayers.Layer.Vector>}\n+ * callbacks - {Object} An object with a 'over' property whos value is\n+ * a function to be called when the mouse is over a feature. The \n+ * callback should expect to recieve a single argument, the feature.\n+ * options - {Object} \n */\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n- this.root = document.createElement(\"canvas\");\n- this.container.appendChild(this.root);\n- this.canvas = this.root.getContext(\"2d\");\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitCanvas = document.createElement(\"canvas\");\n- this.hitContext = this.hitCanvas.getContext(\"2d\");\n- }\n+ initialize: function(control, layer, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.layer = layer;\n },\n \n /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n+ * Method: touchstart\n+ * Handle touchstart events\n *\n * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n+ * evt - {Event}\n *\n * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * {Boolean} Let the event propagate.\n */\n- setExtent: function() {\n- OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- // always redraw features\n- return false;\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return OpenLayers.Event.isMultiTouch(evt) ?\n+ true : this.mousedown(evt);\n },\n \n- /** \n- * Method: eraseGeometry\n- * Erase a geometry from the renderer. Because the Canvas renderer has\n- * 'memory' of the features that it has drawn, we have to remove the\n- * feature so it doesn't redraw. \n+ /**\n+ * Method: touchmove\n+ * Handle touchmove events. We just prevent the browser default behavior,\n+ * for Android Webkit not to select text when moving the finger after\n+ * selecting a feature.\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ */\n+ touchmove: function(evt) {\n+ OpenLayers.Event.preventDefault(evt);\n+ },\n+\n+ /**\n+ * Method: mousedown\n+ * Handle mouse down. Stop propagation if a feature is targeted by this\n+ * event (stops map dragging during feature selection).\n * \n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * featureId - {String}\n+ * evt - {Event} \n */\n- eraseGeometry: function(geometry, featureId) {\n- this.eraseFeatures(this.features[featureId][0]);\n+ mousedown: function(evt) {\n+ // Feature selection is only done with a left click. Other handlers may stop the\n+ // propagation of left-click mousedown events but not right-click mousedown events.\n+ // This mismatch causes problems when comparing the location of the down and up\n+ // events in the click function so it is important ignore right-clicks.\n+ if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n+ this.down = evt.xy;\n+ }\n+ return this.handle(evt) ? !this.stopDown : true;\n },\n \n /**\n- * APIMethod: supported\n+ * Method: mouseup\n+ * Handle mouse up. Stop propagation if a feature is targeted by this\n+ * event.\n * \n+ * Parameters:\n+ * evt - {Event} \n+ */\n+ mouseup: function(evt) {\n+ this.up = evt.xy;\n+ return this.handle(evt) ? !this.stopUp : true;\n+ },\n+\n+ /**\n+ * Method: click\n+ * Handle click. Call the \"click\" callback if click on a feature,\n+ * or the \"clickout\" callback if click outside any feature.\n+ * \n+ * Parameters:\n+ * evt - {Event} \n+ *\n * Returns:\n- * {Boolean} Whether or not the browser supports the renderer class\n+ * {Boolean}\n */\n- supported: function() {\n- return OpenLayers.CANVAS_SUPPORTED;\n+ click: function(evt) {\n+ return this.handle(evt) ? !this.stopClick : true;\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n+ * Method: mousemove\n+ * Handle mouse moves. Call the \"over\" callback if moving in to a feature,\n+ * or the \"out\" callback if moving out of a feature.\n+ * \n+ * Parameters:\n+ * evt - {Event} \n *\n- * Once the size is updated, redraw the canvas.\n+ * Returns:\n+ * {Boolean}\n+ */\n+ mousemove: function(evt) {\n+ if (!this.callbacks['over'] && !this.callbacks['out']) {\n+ return true;\n+ }\n+ this.handle(evt);\n+ return true;\n+ },\n+\n+ /**\n+ * Method: dblclick\n+ * Handle dblclick. Call the \"dblclick\" callback if dblclick on a feature.\n *\n * Parameters:\n- * size - {<OpenLayers.Size>} \n+ * evt - {Event} \n+ *\n+ * Returns:\n+ * {Boolean}\n */\n- setSize: function(size) {\n- this.size = size.clone();\n- var root = this.root;\n- root.style.width = size.w + \"px\";\n- root.style.height = size.h + \"px\";\n- root.width = size.w;\n- root.height = size.h;\n- this.resolution = null;\n- if (this.hitDetection) {\n- var hitCanvas = this.hitCanvas;\n- hitCanvas.style.width = size.w + \"px\";\n- hitCanvas.style.height = size.h + \"px\";\n- hitCanvas.width = size.w;\n- hitCanvas.height = size.h;\n- }\n+ dblclick: function(evt) {\n+ return !this.handle(evt);\n },\n \n /**\n- * Method: drawFeature\n- * Draw the feature. Stores the feature in the features list,\n- * then redraws the layer. \n+ * Method: geometryTypeMatches\n+ * Return true if the geometry type of the passed feature matches\n+ * one of the geometry types in the geometryTypes array.\n *\n * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- * style - {<Object>} \n+ * feature - {<OpenLayers.Vector.Feature>}\n *\n * Returns:\n- * {Boolean} The feature has been drawn completely. If the feature has no\n- * geometry, undefined will be returned. If the feature is not rendered\n- * for other reasons, false will be returned.\n+ * {Boolean}\n */\n- drawFeature: function(feature, style) {\n- var rendered;\n- if (feature.geometry) {\n- style = this.applyDefaultSymbolizer(style || feature.style);\n- // don't render if display none or feature outside extent\n- var bounds = feature.geometry.getBounds();\n+ geometryTypeMatches: function(feature) {\n+ return this.geometryTypes == null ||\n+ OpenLayers.Util.indexOf(this.geometryTypes,\n+ feature.geometry.CLASS_NAME) > -1;\n+ },\n \n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent();\n+ /**\n+ * Method: handle\n+ *\n+ * Parameters:\n+ * evt - {Event}\n+ *\n+ * Returns:\n+ * {Boolean} The event occurred over a relevant feature.\n+ */\n+ handle: function(evt) {\n+ if (this.feature && !this.feature.layer) {\n+ // feature has been destroyed\n+ this.feature = null;\n+ }\n+ var type = evt.type;\n+ var handled = false;\n+ var previouslyIn = !!(this.feature); // previously in a feature\n+ var click = (type == \"click\" || type == \"dblclick\" || type == \"touchstart\");\n+ this.feature = this.layer.getFeatureFromEvent(evt);\n+ if (this.feature && !this.feature.layer) {\n+ // feature has been destroyed\n+ this.feature = null;\n+ }\n+ if (this.lastFeature && !this.lastFeature.layer) {\n+ // last feature has been destroyed\n+ this.lastFeature = null;\n+ }\n+ if (this.feature) {\n+ if (type === \"touchstart\") {\n+ // stop the event to prevent Android Webkit from\n+ // \"flashing\" the map div\n+ OpenLayers.Event.preventDefault(evt);\n }\n-\n- var intersects = bounds && bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- });\n-\n- rendered = (style.display !== \"none\") && !!bounds && intersects;\n- if (rendered) {\n- // keep track of what we have rendered for redraw\n- this.features[feature.id] = [feature, style];\n+ var inNew = (this.feature != this.lastFeature);\n+ if (this.geometryTypeMatches(this.feature)) {\n+ // in to a feature\n+ if (previouslyIn && inNew) {\n+ // out of last feature and in to another\n+ if (this.lastFeature) {\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n+ }\n+ this.triggerCallback(type, 'in', [this.feature]);\n+ } else if (!previouslyIn || click) {\n+ // in feature for the first time\n+ this.triggerCallback(type, 'in', [this.feature]);\n+ }\n+ this.lastFeature = this.feature;\n+ handled = true;\n } else {\n- // remove from features tracked for redraw\n- delete(this.features[feature.id]);\n+ // not in to a feature\n+ if (this.lastFeature && (previouslyIn && inNew || click)) {\n+ // out of last feature for the first time\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n+ }\n+ // next time the mouse goes in a feature whose geometry type\n+ // doesn't match we don't want to call the 'out' callback\n+ // again, so let's set this.feature to null so that\n+ // previouslyIn will evaluate to false the next time\n+ // we enter handle. Yes, a bit hackish...\n+ this.feature = null;\n }\n- this.pendingRedraw = true;\n- }\n- if (this.pendingRedraw && !this.locked) {\n- this.redraw();\n- this.pendingRedraw = false;\n+ } else if (this.lastFeature && (previouslyIn || click)) {\n+ this.triggerCallback(type, 'out', [this.lastFeature]);\n }\n- return rendered;\n+ return handled;\n },\n \n- /** \n- * Method: drawGeometry\n- * Used when looping (in redraw) over the features; draws\n- * the canvas. \n+ /**\n+ * Method: triggerCallback\n+ * Call the callback keyed in the event map with the supplied arguments.\n+ * For click and clickout, the <clickTolerance> is checked first.\n *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>} \n- * style - {Object} \n+ * type - {String}\n */\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- if ((className == \"OpenLayers.Geometry.Collection\") ||\n- (className == \"OpenLayers.Geometry.MultiPoint\") ||\n- (className == \"OpenLayers.Geometry.MultiLineString\") ||\n- (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n- for (var i = 0; i < geometry.components.length; i++) {\n- this.drawGeometry(geometry.components[i], style, featureId);\n+ triggerCallback: function(type, mode, args) {\n+ var key = this.EVENTMAP[type][mode];\n+ if (key) {\n+ if (type == 'click' && this.up && this.down) {\n+ // for click/clickout, only trigger callback if tolerance is met\n+ var dpx = Math.sqrt(\n+ Math.pow(this.up.x - this.down.x, 2) +\n+ Math.pow(this.up.y - this.down.y, 2)\n+ );\n+ if (dpx <= this.clickTolerance) {\n+ this.callback(key, args);\n+ }\n+ // we're done with this set of events now: clear the cached\n+ // positions so we can't trip over them later (this can occur\n+ // if one of the up/down events gets eaten before it gets to us\n+ // but we still get the click)\n+ this.up = this.down = null;\n+ } else {\n+ this.callback(key, args);\n }\n- return;\n- }\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- this.drawPoint(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- this.drawLineString(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- this.drawLinearRing(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- this.drawPolygon(geometry, style, featureId);\n- break;\n- default:\n- break;\n }\n },\n \n /**\n- * Method: drawExternalGraphic\n- * Called to draw External graphics. \n- * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Method: activate \n+ * Turn on the handler. Returns false if the handler was already active.\n+ *\n+ * Returns:\n+ * {Boolean}\n */\n- drawExternalGraphic: function(geometry, style, featureId) {\n- var img = new Image();\n-\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- img.title = title;\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ activated = true;\n }\n+ return activated;\n+ },\n \n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = (style.graphicXOffset != undefined) ?\n- style.graphicXOffset : -(0.5 * width);\n- var yOffset = (style.graphicYOffset != undefined) ?\n- style.graphicYOffset : -(0.5 * height);\n-\n- var opacity = style.graphicOpacity || style.fillOpacity;\n-\n- var onLoad = function() {\n- if (!this.features[featureId]) {\n- return;\n- }\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var x = (p0 + xOffset) | 0;\n- var y = (p1 + yOffset) | 0;\n- var canvas = this.canvas;\n- canvas.globalAlpha = opacity;\n- var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||\n- (OpenLayers.Renderer.Canvas.drawImageScaleFactor =\n- /android 2.1/.test(navigator.userAgent.toLowerCase()) ?\n- // 320 is the screen width of the G1 phone, for\n- // which drawImage works out of the box.\n- 320 / window.screen.width : 1\n- );\n- canvas.drawImage(\n- img, x * factor, y * factor, width * factor, height * factor\n- );\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId);\n- this.hitContext.fillRect(x, y, width, height);\n- }\n- }\n- };\n-\n- img.onload = OpenLayers.Function.bind(onLoad, this);\n- img.src = style.externalGraphic;\n+ /**\n+ * Method: deactivate \n+ * Turn off the handler. Returns false if the handler was already active.\n+ *\n+ * Returns: \n+ * {Boolean}\n+ */\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.feature = null;\n+ this.lastFeature = null;\n+ this.down = null;\n+ this.up = null;\n+ this.map.events.un({\n+ \"removelayer\": this.handleMapEvents,\n+ \"changelayer\": this.handleMapEvents,\n+ scope: this\n+ });\n+ deactivated = true;\n+ }\n+ return deactivated;\n },\n \n /**\n- * Method: drawNamedSymbol\n- * Called to draw Well Known Graphic Symbol Name. \n- * This method is only called by the renderer itself.\n+ * Method: handleMapEvents\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Parameters:\n+ * evt - {Object}\n */\n- drawNamedSymbol: function(geometry, style, featureId) {\n- var x, y, cx, cy, i, symbolBounds, scaling, angle;\n- var unscaledStrokeWidth;\n- var deg2rad = Math.PI / 180.0;\n-\n- var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n-\n- if (!symbol) {\n- throw new Error(style.graphicName + ' is not a valid symbol name');\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop();\n }\n+ },\n \n- if (!symbol.length || symbol.length < 2) return;\n-\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n-\n- if (isNaN(p0) || isNaN(p1)) return;\n-\n- // Use rounded line caps\n- this.canvas.lineCap = \"round\";\n- this.canvas.lineJoin = \"round\";\n+ /**\n+ * Method: moveLayerToTop\n+ * Moves the layer for this handler to the top, so mouse events can reach\n+ * it.\n+ */\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,\n+ this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index);\n \n- if (this.hitDetection) {\n- this.hitContext.lineCap = \"round\";\n- this.hitContext.lineJoin = \"round\";\n- }\n+ },\n \n- // Scale and rotate symbols, using precalculated bounds whenever possible.\n- if (style.graphicName in this.cachedSymbolBounds) {\n- symbolBounds = this.cachedSymbolBounds[style.graphicName];\n+ /**\n+ * Method: moveLayerBack\n+ * Moves the layer back to the position determined by the map's layers\n+ * array.\n+ */\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE['Feature']) {\n+ this.layer.setZIndex(index);\n } else {\n- symbolBounds = new OpenLayers.Bounds();\n- for (i = 0; i < symbol.length; i += 2) {\n- symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]));\n- }\n- this.cachedSymbolBounds[style.graphicName] = symbolBounds;\n- }\n-\n- // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.\n- // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)\n- this.canvas.save();\n- if (this.hitDetection) {\n- this.hitContext.save();\n+ this.map.setLayerZIndex(this.layer,\n+ this.map.getLayerIndex(this.layer));\n }\n+ },\n \n- // Step 3: place symbol at the desired location\n- this.canvas.translate(p0, p1);\n- if (this.hitDetection) {\n- this.hitContext.translate(p0, p1);\n- }\n+ CLASS_NAME: \"OpenLayers.Handler.Feature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Renderer.js\n+ ====================================================================== */\n \n- // Step 2a. rotate the symbol if necessary\n- angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.\n- if (!isNaN(angle)) {\n- this.canvas.rotate(angle);\n- if (this.hitDetection) {\n- this.hitContext.rotate(angle);\n- }\n- }\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.\n- scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n- this.canvas.scale(scaling, scaling);\n- if (this.hitDetection) {\n- this.hitContext.scale(scaling, scaling);\n- }\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n \n- // Step 1: center the symbol at the origin \n- cx = symbolBounds.getCenterLonLat().lon;\n- cy = symbolBounds.getCenterLonLat().lat;\n- this.canvas.translate(-cx, -cy);\n- if (this.hitDetection) {\n- this.hitContext.translate(-cx, -cy);\n- }\n+/**\n+ * Class: OpenLayers.Renderer \n+ * This is the base class for all renderers.\n+ *\n+ * This is based on a merger code written by Paul Spencer and Bertil Chapuis.\n+ * It is largely composed of virtual functions that are to be implemented\n+ * in technology-specific subclasses, but there is some generic code too.\n+ * \n+ * The functions that *are* implemented here merely deal with the maintenance\n+ * of the size and extent variables, as well as the cached 'resolution' \n+ * value. \n+ * \n+ * A note to the user that all subclasses should use getResolution() instead\n+ * of directly accessing this.resolution in order to correctly use the \n+ * cacheing system.\n+ *\n+ */\n+OpenLayers.Renderer = OpenLayers.Class({\n \n- // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)\n- // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.\n- unscaledStrokeWidth = style.strokeWidth;\n- style.strokeWidth = unscaledStrokeWidth / scaling;\n+ /** \n+ * Property: container\n+ * {DOMElement} \n+ */\n+ container: null,\n \n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y);\n- }\n- this.canvas.closePath();\n- this.canvas.fill();\n+ /**\n+ * Property: root\n+ * {DOMElement}\n+ */\n+ root: null,\n \n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.hitContext.lineTo(x, y);\n- }\n- this.hitContext.closePath();\n- this.hitContext.fill();\n- }\n- }\n+ /** \n+ * Property: extent\n+ * {<OpenLayers.Bounds>}\n+ */\n+ extent: null,\n \n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y);\n- }\n- this.canvas.closePath();\n- this.canvas.stroke();\n+ /**\n+ * Property: locked\n+ * {Boolean} If the renderer is currently in a state where many things\n+ * are changing, the 'locked' property is set to true. This means \n+ * that renderers can expect at least one more drawFeature event to be\n+ * called with the 'locked' property set to 'true': In some renderers,\n+ * this might make sense to use as a 'only update local information'\n+ * flag. \n+ */\n+ locked: false,\n \n+ /** \n+ * Property: size\n+ * {<OpenLayers.Size>} \n+ */\n+ size: null,\n \n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.hitContext.moveTo(x, y);\n- this.hitContext.lineTo(x, y);\n- }\n- this.hitContext.closePath();\n- this.hitContext.stroke();\n- }\n+ /**\n+ * Property: resolution\n+ * {Float} cache of current map resolution\n+ */\n+ resolution: null,\n \n- }\n+ /**\n+ * Property: map \n+ * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()\n+ */\n+ map: null,\n \n- style.strokeWidth = unscaledStrokeWidth;\n- this.canvas.restore();\n- if (this.hitDetection) {\n- this.hitContext.restore();\n- }\n- this.setCanvasStyle(\"reset\");\n- },\n+ /**\n+ * Property: featureDx\n+ * {Number} Feature offset in x direction. Will be calculated for and\n+ * applied to the current feature while rendering (see\n+ * <calculateFeatureDx>).\n+ */\n+ featureDx: 0,\n \n /**\n- * Method: setCanvasStyle\n- * Prepare the canvas for drawing by setting various global settings.\n+ * Constructor: OpenLayers.Renderer \n *\n * Parameters:\n- * type - {String} one of 'stroke', 'fill', or 'reset'\n- * style - {Object} Symbolizer hash\n+ * containerID - {<String>} \n+ * options - {Object} options for this renderer. See sublcasses for\n+ * supported options.\n */\n- setCanvasStyle: function(type, style) {\n- if (type === \"fill\") {\n- this.canvas.globalAlpha = style['fillOpacity'];\n- this.canvas.fillStyle = style['fillColor'];\n- } else if (type === \"stroke\") {\n- this.canvas.globalAlpha = style['strokeOpacity'];\n- this.canvas.strokeStyle = style['strokeColor'];\n- this.canvas.lineWidth = style['strokeWidth'];\n- } else {\n- this.canvas.globalAlpha = 0;\n- this.canvas.lineWidth = 1;\n- }\n+ initialize: function(containerID, options) {\n+ this.container = OpenLayers.Util.getElement(containerID);\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * Method: featureIdToHex\n- * Convert a feature ID string into an RGB hex string.\n- *\n- * Parameters:\n- * featureId - {String} Feature id\n- *\n+ * APIMethod: destroy\n+ */\n+ destroy: function() {\n+ this.container = null;\n+ this.extent = null;\n+ this.size = null;\n+ this.resolution = null;\n+ this.map = null;\n+ },\n+\n+ /**\n+ * APIMethod: supported\n+ * This should be overridden by specific subclasses\n+ * \n * Returns:\n- * {String} RGB hex string.\n+ * {Boolean} Whether or not the browser supports the renderer class\n */\n- featureIdToHex: function(featureId) {\n- var id = Number(featureId.split(\"_\").pop()) + 1; // zero for no feature\n- if (id >= 16777216) {\n- this.hitOverflow = id - 16777215;\n- id = id % 16777216 + 1;\n- }\n- var hex = \"000000\" + id.toString(16);\n- var len = hex.length;\n- hex = \"#\" + hex.substring(len - 6, len);\n- return hex;\n+ supported: function() {\n+ return false;\n },\n \n /**\n- * Method: setHitContextStyle\n- * Prepare the hit canvas for drawing by setting various global settings.\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ * We nullify the resolution cache (this.resolution) if resolutionChanged\n+ * is set to true - this way it will be re-computed on the next\n+ * getResolution() request.\n *\n * Parameters:\n- * type - {String} one of 'stroke', 'fill', or 'reset'\n- * featureId - {String} The feature id.\n- * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n+ *\n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n */\n- setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n- var hex = this.featureIdToHex(featureId);\n- if (type == \"fill\") {\n- this.hitContext.globalAlpha = 1.0;\n- this.hitContext.fillStyle = hex;\n- } else if (type == \"stroke\") {\n- this.hitContext.globalAlpha = 1.0;\n- this.hitContext.strokeStyle = hex;\n- // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol \n- // on a transformed canvas, so the antialias width bump has to scale as well.\n- if (typeof strokeScaling === \"undefined\") {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2;\n- } else {\n- if (!isNaN(strokeScaling)) {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling;\n- }\n- }\n- } else {\n- this.hitContext.globalAlpha = 0;\n- this.hitContext.lineWidth = 1;\n+ setExtent: function(extent, resolutionChanged) {\n+ this.extent = extent.clone();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio);\n+ this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);\n+ }\n+ if (resolutionChanged) {\n+ this.resolution = null;\n }\n+ return true;\n },\n \n /**\n- * Method: drawPoint\n- * This method is only called by the renderer itself.\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Resolution has probably changed, so we nullify the resolution \n+ * cache (this.resolution) -- this way it will be re-computed when \n+ * next it is needed.\n+ *\n+ * Parameters:\n+ * size - {<OpenLayers.Size>} \n */\n- drawPoint: function(geometry, style, featureId) {\n- if (style.graphic !== false) {\n- if (style.externalGraphic) {\n- this.drawExternalGraphic(geometry, style, featureId);\n- } else if (style.graphicName && (style.graphicName != \"circle\")) {\n- this.drawNamedSymbol(geometry, style, featureId);\n- } else {\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var twoPi = Math.PI * 2;\n- var radius = style.pointRadius;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.fill();\n- }\n- }\n-\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.stroke();\n- }\n- this.setCanvasStyle(\"reset\");\n- }\n- }\n- }\n- }\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ this.resolution = null;\n },\n \n- /**\n- * Method: drawLineString\n- * This method is only called by the renderer itself.\n+ /** \n+ * Method: getResolution\n+ * Uses cached copy of resolution if available to minimize computing\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Returns:\n+ * {Float} The current map's resolution\n */\n- drawLineString: function(geometry, style, featureId) {\n- style = OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style);\n- this.drawLinearRing(geometry, style, featureId);\n+ getResolution: function() {\n+ this.resolution = this.resolution || this.map.getResolution();\n+ return this.resolution;\n },\n \n /**\n- * Method: drawLinearRing\n- * This method is only called by the renderer itself.\n+ * Method: drawFeature\n+ * Draw the feature. The optional style argument can be used\n+ * to override the feature's own style. This method should only\n+ * be called from layer.drawFeature().\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {<Object>}\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Returns:\n+ * {Boolean} true if the feature has been drawn completely, false if not,\n+ * undefined if the feature had no geometry\n */\n- drawLinearRing: function(geometry, style, featureId) {\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"fill\");\n- }\n+ drawFeature: function(feature, style) {\n+ if (style == null) {\n+ style = feature.style;\n }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\");\n+ if (feature.geometry) {\n+ var bounds = feature.geometry.getBounds();\n+ if (bounds) {\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n+ if (!bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ })) {\n+ style = {\n+ display: \"none\"\n+ };\n+ } else {\n+ this.calculateFeatureDx(bounds, worldBounds);\n+ }\n+ var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n+ if (style.display != \"none\" && style.label && rendered !== false) {\n+\n+ var location = feature.geometry.getCentroid();\n+ if (style.labelXOffset || style.labelYOffset) {\n+ var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n+ var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n+ var res = this.getResolution();\n+ location.move(xOffset * res, yOffset * res);\n+ }\n+ this.drawText(feature.id, style, location);\n+ } else {\n+ this.removeText(feature.id);\n+ }\n+ return rendered;\n }\n }\n- this.setCanvasStyle(\"reset\");\n },\n \n /**\n- * Method: renderPath\n- * Render a path with stroke and optional fill.\n+ * Method: calculateFeatureDx\n+ * {Number} Calculates the feature offset in x direction. Looking at the\n+ * center of the feature bounds and the renderer extent, we calculate how\n+ * many world widths the two are away from each other. This distance is\n+ * used to shift the feature as close as possible to the center of the\n+ * current enderer extent, which ensures that the feature is visible in the\n+ * current viewport.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} Bounds of the feature\n+ * worldBounds - {<OpenLayers.Bounds>} Bounds of the world\n */\n- renderPath: function(context, geometry, style, featureId, type) {\n- var components = geometry.components;\n- var len = components.length;\n- context.beginPath();\n- var start = this.getLocalXY(components[0]);\n- var x = start[0];\n- var y = start[1];\n- if (!isNaN(x) && !isNaN(y)) {\n- context.moveTo(start[0], start[1]);\n- for (var i = 1; i < len; ++i) {\n- var pt = this.getLocalXY(components[i]);\n- context.lineTo(pt[0], pt[1]);\n- }\n- if (type === \"fill\") {\n- context.fill();\n- } else {\n- context.stroke();\n- }\n+ calculateFeatureDx: function(bounds, worldBounds) {\n+ this.featureDx = 0;\n+ if (worldBounds) {\n+ var worldWidth = worldBounds.getWidth(),\n+ rendererCenterX = (this.extent.left + this.extent.right) / 2,\n+ featureCenterX = (bounds.left + bounds.right) / 2,\n+ worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n+ this.featureDx = worldsAway * worldWidth;\n }\n },\n \n- /**\n- * Method: drawPolygon\n- * This method is only called by the renderer itself.\n+ /** \n+ * Method: drawGeometry\n * \n- * Parameters: \n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ * Draw a geometry. This should only be called from the renderer itself.\n+ * Use layer.drawFeature() from outside the renderer.\n+ * virtual function\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * style - {Object} \n+ * featureId - {<String>} \n */\n- drawPolygon: function(geometry, style, featureId) {\n- var components = geometry.components;\n- var len = components.length;\n- this.drawLinearRing(components[0], style, featureId);\n- // erase inner rings\n- for (var i = 1; i < len; ++i) {\n- /** \n- * Note that this is overly agressive. Here we punch holes through \n- * all previously rendered features on the same canvas. A better \n- * solution for polygons with interior rings would be to draw the \n- * polygon on a sketch canvas first. We could erase all holes \n- * there and then copy the drawing to the layer canvas. \n- * TODO: http://trac.osgeo.org/openlayers/ticket/3130 \n- */\n- this.canvas.globalCompositeOperation = \"destination-out\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"destination-out\";\n- }\n- this.drawLinearRing(\n- components[i],\n- OpenLayers.Util.applyDefaults({\n- stroke: false,\n- fillOpacity: 1.0\n- }, style),\n- featureId\n- );\n- this.canvas.globalCompositeOperation = \"source-over\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"source-over\";\n- }\n- this.drawLinearRing(\n- components[i],\n- OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style),\n- featureId\n- );\n- }\n- },\n+ drawGeometry: function(geometry, style, featureId) {},\n \n /**\n * Method: drawText\n+ * Function for drawing text labels.\n * This method is only called by the renderer itself.\n- *\n- * Parameters:\n- * location - {<OpenLayers.Point>}\n- * style - {Object}\n+ * \n+ * Parameters: \n+ * featureId - {String}\n+ * style -\n+ * location - {<OpenLayers.Geometry.Point>}\n */\n- drawText: function(location, style) {\n- var pt = this.getLocalXY(location);\n-\n- this.setCanvasStyle(\"reset\");\n- this.canvas.fillStyle = style.fontColor;\n- this.canvas.globalAlpha = style.fontOpacity || 1.0;\n- var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\",\n- \"normal\", // \"font-variant\" not supported\n- style.fontWeight ? style.fontWeight : \"normal\",\n- style.fontSize ? style.fontSize : \"1em\",\n- style.fontFamily ? style.fontFamily : \"sans-serif\"\n- ].join(\" \");\n- var labelRows = style.label.split('\\n');\n- var numRows = labelRows.length;\n- if (this.canvas.fillText) {\n- // HTML5\n- this.canvas.font = fontStyle;\n- this.canvas.textAlign =\n- OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||\n- \"center\";\n- this.canvas.textBaseline =\n- OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||\n- \"middle\";\n- var vfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5;\n- }\n- var lineHeight =\n- this.canvas.measureText('Mg').height ||\n- this.canvas.measureText('xx').width;\n- pt[1] += lineHeight * vfactor * (numRows - 1);\n- for (var i = 0; i < numRows; i++) {\n- if (style.labelOutlineWidth) {\n- this.canvas.save();\n- this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;\n- this.canvas.strokeStyle = style.labelOutlineColor;\n- this.canvas.lineWidth = style.labelOutlineWidth;\n- this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1);\n- this.canvas.restore();\n- }\n- this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));\n- }\n- } else if (this.canvas.mozDrawText) {\n- // Mozilla pre-Gecko1.9.1 (<FF3.1)\n- this.canvas.mozTextStyle = fontStyle;\n- // No built-in text alignment, so we measure and adjust the position\n- var hfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n- if (hfactor == null) {\n- hfactor = -.5;\n- }\n- var vfactor =\n- OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5;\n- }\n- var lineHeight = this.canvas.mozMeasureText('xx');\n- pt[1] += lineHeight * (1 + (vfactor * numRows));\n- for (var i = 0; i < numRows; i++) {\n- var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i]));\n- var y = pt[1] + (i * lineHeight);\n- this.canvas.translate(x, y);\n- this.canvas.mozDrawText(labelRows[i]);\n- this.canvas.translate(-x, -y);\n- }\n- }\n- this.setCanvasStyle(\"reset\");\n- },\n+ drawText: function(featureId, style, location) {},\n \n /**\n- * Method: getLocalXY\n- * transform geographic xy into pixel xy\n- *\n+ * Method: removeText\n+ * Function for removing text labels.\n+ * This method is only called by the renderer itself.\n+ * \n * Parameters: \n- * point - {<OpenLayers.Geometry.Point>}\n+ * featureId - {String}\n */\n- getLocalXY: function(point) {\n- var resolution = this.getResolution();\n- var extent = this.extent;\n- var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));\n- var y = ((extent.top / resolution) - point.y / resolution);\n- return [x, y];\n- },\n+ removeText: function(featureId) {},\n \n /**\n * Method: clear\n * Clear all vectors from the renderer.\n+ * virtual function.\n */\n- clear: function() {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height);\n- }\n- },\n+ clear: function() {},\n \n /**\n * Method: getFeatureIdFromEvent\n * Returns a feature id from an event on the renderer. \n+ * How this happens is specific to the renderer. This should be\n+ * called from layer.getFeatureFromEvent().\n+ * Virtual function.\n * \n * Parameters:\n * evt - {<OpenLayers.Event>} \n *\n * Returns:\n- * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a \n- * feature instead of a feature id to avoid an unnecessary lookup on the\n- * layer.\n+ * {String} A feature id or undefined.\n */\n- getFeatureIdFromEvent: function(evt) {\n- var featureId, feature;\n-\n- if (this.hitDetection && this.root.style.display !== \"none\") {\n- // this dragging check should go in the feature handler\n- if (!this.map.dragging) {\n- var xy = evt.xy;\n- var x = xy.x | 0;\n- var y = xy.y | 0;\n- var data = this.hitContext.getImageData(x, y, 1, 1).data;\n- if (data[3] === 255) { // antialiased\n- var id = data[2] + (256 * (data[1] + (256 * data[0])));\n- if (id) {\n- featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n- try {\n- feature = this.features[featureId][0];\n- } catch (err) {\n- // Because of antialiasing on the canvas, when the hit location is at a point where the edge of\n- // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.\n- // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.\n- }\n- }\n- }\n- }\n- }\n- return feature;\n- },\n+ getFeatureIdFromEvent: function(evt) {},\n \n /**\n * Method: eraseFeatures \n- * This is called by the layer to erase features; removes the feature from\n- * the list, then redraws the layer.\n+ * This is called by the layer to erase features\n * \n * Parameters:\n * features - {Array(<OpenLayers.Feature.Vector>)} \n */\n eraseFeatures: function(features) {\n if (!(OpenLayers.Util.isArray(features))) {\n features = [features];\n }\n- for (var i = 0; i < features.length; ++i) {\n- delete this.features[features[i].id];\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ var feature = features[i];\n+ this.eraseGeometry(feature.geometry, feature.id);\n+ this.removeText(feature.id);\n }\n- this.redraw();\n },\n \n /**\n- * Method: redraw\n- * The real 'meat' of the function: any time things have changed,\n- * redraw() can be called to loop over all the data and (you guessed\n- * it) redraw it. Unlike Elements-based Renderers, we can't interact\n- * with things once they're drawn, to remove them, for example, so\n- * instead we have to just clear everything and draw from scratch.\n+ * Method: eraseGeometry\n+ * Remove a geometry from the renderer (by id).\n+ * virtual function.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * featureId - {String}\n */\n- redraw: function() {\n- if (!this.locked) {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height);\n- }\n- var labelMap = [];\n- var feature, geometry, style;\n- var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();\n- for (var id in this.features) {\n- if (!this.features.hasOwnProperty(id)) {\n- continue;\n- }\n- feature = this.features[id][0];\n- geometry = feature.geometry;\n- this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n- style = this.features[id][1];\n- this.drawGeometry(geometry, style, feature.id);\n- if (style.label) {\n- labelMap.push([feature, style]);\n- }\n- }\n- var item;\n- for (var i = 0, len = labelMap.length; i < len; ++i) {\n- item = labelMap[i];\n- this.drawText(item[0].geometry.getCentroid(), item[1]);\n- }\n+ eraseGeometry: function(geometry, featureId) {},\n+\n+ /**\n+ * Method: moveRoot\n+ * moves this renderer's root to a (different) renderer.\n+ * To be implemented by subclasses that require a common renderer root for\n+ * feature selection.\n+ * \n+ * Parameters:\n+ * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ */\n+ moveRoot: function(renderer) {},\n+\n+ /**\n+ * Method: getRenderLayerId\n+ * Gets the layer that this renderer's output appears on. If moveRoot was\n+ * used, this will be different from the id of the layer containing the\n+ * features rendered by this renderer.\n+ * \n+ * Returns:\n+ * {String} the id of the output layer.\n+ */\n+ getRenderLayerId: function() {\n+ return this.container.id;\n+ },\n+\n+ /**\n+ * Method: applyDefaultSymbolizer\n+ * \n+ * Parameters:\n+ * symbolizer - {Object}\n+ * \n+ * Returns:\n+ * {Object}\n+ */\n+ applyDefaultSymbolizer: function(symbolizer) {\n+ var result = OpenLayers.Util.extend({},\n+ OpenLayers.Renderer.defaultSymbolizer);\n+ if (symbolizer.stroke === false) {\n+ delete result.strokeWidth;\n+ delete result.strokeColor;\n+ }\n+ if (symbolizer.fill === false) {\n+ delete result.fillColor;\n }\n+ OpenLayers.Util.extend(result, symbolizer);\n+ return result;\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+ CLASS_NAME: \"OpenLayers.Renderer\"\n });\n \n /**\n- * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN\n- * {Object}\n+ * Constant: OpenLayers.Renderer.defaultSymbolizer\n+ * {Object} Properties from this symbolizer will be applied to symbolizers\n+ * with missing properties. This can also be used to set a global\n+ * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the\n+ * following code before rendering any vector features:\n+ * (code)\n+ * OpenLayers.Renderer.defaultSymbolizer = {\n+ * fillColor: \"#808080\",\n+ * fillOpacity: 1,\n+ * strokeColor: \"#000000\",\n+ * strokeOpacity: 1,\n+ * strokeWidth: 1,\n+ * pointRadius: 3,\n+ * graphicName: \"square\"\n+ * };\n+ * (end)\n */\n-OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n- \"l\": \"left\",\n- \"r\": \"right\",\n- \"t\": \"top\",\n- \"b\": \"bottom\"\n+OpenLayers.Renderer.defaultSymbolizer = {\n+ fillColor: \"#000000\",\n+ strokeColor: \"#000000\",\n+ strokeWidth: 2,\n+ fillOpacity: 1,\n+ strokeOpacity: 1,\n+ pointRadius: 0,\n+ labelAlign: 'cm'\n };\n \n-/**\n- * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR\n- * {Object}\n- */\n-OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n- \"l\": 0,\n- \"r\": -1,\n- \"t\": 0,\n- \"b\": -1\n-};\n+\n \n /**\n- * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor\n- * {Number} Scale factor to apply to the canvas drawImage arguments. This\n- * is always 1 except for Android 2.1 devices, to work around\n- * http://code.google.com/p/android/issues/detail?id=5141.\n+ * Constant: OpenLayers.Renderer.symbol\n+ * Coordinate arrays for well known (named) symbols.\n */\n-OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n+OpenLayers.Renderer.symbol = {\n+ \"star\": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301,\n+ 303, 215, 231, 161, 321, 161, 350, 75\n+ ],\n+ \"cross\": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4,\n+ 4, 0\n+ ],\n+ \"x\": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n+ \"square\": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n+ \"triangle\": [0, 10, 10, 10, 5, 0, 0, 10]\n+};\n /* ======================================================================\n- OpenLayers/Renderer/Elements.js\n+ OpenLayers/Style.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Feature/Vector.js\n */\n \n /**\n- * Class: OpenLayers.ElementsIndexer\n- * This class takes care of figuring out which order elements should be\n- * placed in the DOM based on given indexing methods. \n+ * Class: OpenLayers.Style\n+ * This class represents a UserStyle obtained\n+ * from a SLD, containing styling rules.\n */\n-OpenLayers.ElementsIndexer = OpenLayers.Class({\n+OpenLayers.Style = OpenLayers.Class({\n \n /**\n- * Property: maxZIndex\n- * {Integer} This is the largest-most z-index value for a node\n- * contained within the indexer.\n+ * Property: id\n+ * {String} A unique id for this session.\n */\n- maxZIndex: null,\n+ id: null,\n \n /**\n- * Property: order\n- * {Array<String>} This is an array of node id's stored in the\n- * order that they should show up on screen. Id's higher up in the\n- * array (higher array index) represent nodes with higher z-indeces.\n+ * APIProperty: name\n+ * {String}\n */\n- order: null,\n+ name: null,\n \n /**\n- * Property: indices\n- * {Object} This is a hash that maps node ids to their z-index value\n- * stored in the indexer. This is done to make finding a nodes z-index \n- * value O(1).\n+ * Property: title\n+ * {String} Title of this style (set if included in SLD)\n */\n- indices: null,\n+ title: null,\n \n /**\n- * Property: compare\n- * {Function} This is the function used to determine placement of\n- * of a new node within the indexer. If null, this defaults to to\n- * the Z_ORDER_DRAWING_ORDER comparison method.\n+ * Property: description\n+ * {String} Description of this style (set if abstract is included in SLD)\n */\n- compare: null,\n+ description: null,\n \n /**\n- * APIMethod: initialize\n- * Create a new indexer with \n- * \n- * Parameters:\n- * yOrdering - {Boolean} Whether to use y-ordering.\n+ * APIProperty: layerName\n+ * {<String>} name of the layer that this style belongs to, usually\n+ * according to the NamedLayer attribute of an SLD document.\n */\n- initialize: function(yOrdering) {\n+ layerName: null,\n \n- this.compare = yOrdering ?\n- OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :\n- OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n+ /**\n+ * APIProperty: isDefault\n+ * {Boolean}\n+ */\n+ isDefault: false,\n \n- this.clear();\n- },\n+ /** \n+ * Property: rules \n+ * {Array(<OpenLayers.Rule>)}\n+ */\n+ rules: null,\n \n /**\n- * APIMethod: insert\n- * Insert a new node into the indexer. In order to find the correct \n- * positioning for the node to be inserted, this method uses a binary \n- * search. This makes inserting O(log(n)). \n- * \n+ * APIProperty: context\n+ * {Object} An optional object with properties that symbolizers' property\n+ * values should be evaluated against. If no context is specified,\n+ * feature.attributes will be used\n+ */\n+ context: null,\n+\n+ /**\n+ * Property: defaultStyle\n+ * {Object} hash of style properties to use as default for merging\n+ * rule-based style symbolizers onto. If no rules are defined,\n+ * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to\n+ * true, the defaultStyle will only be taken into account if there are\n+ * rules defined.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * Property: defaultsPerSymbolizer\n+ * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer\n+ * of every rule. Properties of the <defaultStyle> will also be used to set\n+ * missing symbolizer properties if the symbolizer has stroke, fill or\n+ * graphic set to true. Default is false.\n+ */\n+ defaultsPerSymbolizer: false,\n+\n+ /**\n+ * Property: propertyStyles\n+ * {Hash of Boolean} cache of style properties that need to be parsed for\n+ * propertyNames. Property names are keys, values won't be used.\n+ */\n+ propertyStyles: null,\n+\n+\n+ /** \n+ * Constructor: OpenLayers.Style\n+ * Creates a UserStyle.\n+ *\n * Parameters:\n- * newNode - {DOMElement} The new node to be inserted.\n+ * style - {Object} Optional hash of style properties that will be\n+ * used as default style for this style object. This style\n+ * applies if no rules are specified. Symbolizers defined in\n+ * rules will extend this default style.\n+ * options - {Object} An optional object with properties to set on the\n+ * style.\n+ *\n+ * Valid options:\n+ * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the\n+ * style.\n * \n- * Returns\n- * {DOMElement} the node before which we should insert our newNode, or\n- * null if newNode can just be appended.\n+ * Returns:\n+ * {<OpenLayers.Style>}\n */\n- insert: function(newNode) {\n- // If the node is known to the indexer, remove it so we can\n- // recalculate where it should go.\n- if (this.exists(newNode)) {\n- this.remove(newNode);\n+ initialize: function(style, options) {\n+\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules);\n }\n \n- var nodeId = newNode.id;\n+ // use the default style from OpenLayers.Feature.Vector if no style\n+ // was given in the constructor\n+ this.setDefaultStyle(style ||\n+ OpenLayers.Feature.Vector.style[\"default\"]);\n \n- this.determineZIndex(newNode);\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n \n- var leftIndex = -1;\n- var rightIndex = this.order.length;\n- var middle;\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n+ */\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null;\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null;\n+ },\n \n- while (rightIndex - leftIndex > 1) {\n- middle = parseInt((leftIndex + rightIndex) / 2);\n+ /**\n+ * Method: createSymbolizer\n+ * creates a style by applying all feature-dependent rules to the base\n+ * style.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature>} feature to evaluate rules for\n+ * \n+ * Returns:\n+ * {Object} symbolizer hash\n+ */\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(\n+ OpenLayers.Util.extend({}, this.defaultStyle), feature);\n \n- var placement = this.compare(this, newNode,\n- OpenLayers.Util.getElement(this.order[middle]));\n+ var rules = this.rules;\n \n- if (placement > 0) {\n- leftIndex = middle;\n- } else {\n- rightIndex = middle;\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ // does the rule apply?\n+ var applies = rule.evaluate(feature);\n+\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule);\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature);\n+ }\n }\n }\n \n- this.order.splice(rightIndex, 0, nodeId);\n- this.indices[nodeId] = this.getZIndex(newNode);\n+ // if no other rules apply, apply the rules with else filters\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature);\n+ }\n+ }\n \n- // If the new node should be before another in the index\n- // order, return the node before which we have to insert the new one;\n- // else, return null to indicate that the new node can be appended.\n- return this.getNextElement(rightIndex);\n+ // don't display if there were rules but none applied\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\";\n+ }\n+\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label);\n+ }\n+\n+ return style;\n },\n \n /**\n- * APIMethod: remove\n- * \n+ * Method: applySymbolizer\n+ *\n * Parameters:\n- * node - {DOMElement} The node to be removed.\n+ * rule - {<OpenLayers.Rule>}\n+ * style - {Object}\n+ * feature - {<OpenLayer.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {Object} A style with new symbolizer applied.\n */\n- remove: function(node) {\n- var nodeId = node.id;\n- var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);\n- if (arrayIndex >= 0) {\n- // Remove it from the order array, as well as deleting the node\n- // from the indeces hash.\n- this.order.splice(arrayIndex, 1);\n- delete this.indices[nodeId];\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ?\n+ this.getSymbolizerPrefix(feature.geometry) :\n+ OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n \n- // Reset the maxium z-index based on the last item in the \n- // order array.\n- if (this.order.length > 0) {\n- var lastId = this.order[this.order.length - 1];\n- this.maxZIndex = this.indices[lastId];\n- } else {\n- this.maxZIndex = 0;\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ });\n+ }\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ });\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ });\n }\n }\n+\n+ // merge the style with the current style\n+ return this.createLiterals(\n+ OpenLayers.Util.extend(style, symbolizer), feature);\n },\n \n /**\n- * APIMethod: clear\n+ * Method: createLiterals\n+ * creates literals for all style properties that have an entry in\n+ * <this.propertyStyles>.\n+ * \n+ * Parameters:\n+ * style - {Object} style to create literals for. Will be modified\n+ * inline.\n+ * feature - {Object}\n+ * \n+ * Returns:\n+ * {Object} the modified style\n */\n- clear: function() {\n- this.order = [];\n- this.indices = {};\n- this.maxZIndex = 0;\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);\n+ }\n+ return style;\n },\n \n /**\n- * APIMethod: exists\n- *\n- * Parameters:\n- * node - {DOMElement} The node to test for existence.\n- *\n+ * Method: findPropertyStyles\n+ * Looks into all rules for this style and the defaultStyle to collect\n+ * all the style hash property names containing ${...} strings that have\n+ * to be replaced using the createLiteral method before returning them.\n+ * \n * Returns:\n- * {Boolean} Whether or not the node exists in the indexer?\n+ * {Object} hash of property names that need createLiteral parsing. The\n+ * name of the property is the key, and the value is true;\n */\n- exists: function(node) {\n- return (this.indices[node.id] != null);\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+\n+ // check the default style\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+\n+ // walk through all rules to check for properties in their symbolizer\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ // symbolizer key is \"Point\", \"Line\" or \"Polygon\"\n+ this.addPropertyStyles(propertyStyles, value);\n+ } else {\n+ // symbolizer is a hash of style properties\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break;\n+ }\n+ }\n+ }\n+ return propertyStyles;\n },\n \n /**\n- * APIMethod: getZIndex\n- * Get the z-index value for the current node from the node data itself.\n+ * Method: addPropertyStyles\n * \n * Parameters:\n- * node - {DOMElement} The node whose z-index to get.\n+ * propertyStyles - {Object} hash to add new property styles to. Will be\n+ * modified inline\n+ * symbolizer - {Object} search this symbolizer for property styles\n * \n * Returns:\n- * {Integer} The z-index value for the specified node (from the node \n- * data itself).\n+ * {Object} propertyStyles hash\n */\n- getZIndex: function(node) {\n- return node._style.graphicZIndex;\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" &&\n+ property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true;\n+ }\n+ }\n+ return propertyStyles;\n },\n \n /**\n- * Method: determineZIndex\n- * Determine the z-index for the current node if there isn't one, \n- * and set the maximum value if we've found a new maximum.\n+ * APIMethod: addRules\n+ * Adds rules to this style.\n * \n * Parameters:\n- * node - {DOMElement} \n+ * rules - {Array(<OpenLayers.Rule>)}\n */\n- determineZIndex: function(node) {\n- var zIndex = node._style.graphicZIndex;\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles();\n+ },\n \n- // Everything must have a zIndex. If none is specified,\n- // this means the user *must* (hint: assumption) want this\n- // node to succomb to drawing order. To enforce drawing order\n- // over all indexing methods, we'll create a new z-index that's\n- // greater than any currently in the indexer.\n- if (zIndex == null) {\n- zIndex = this.maxZIndex;\n- node._style.graphicZIndex = zIndex;\n- } else if (zIndex > this.maxZIndex) {\n- this.maxZIndex = zIndex;\n- }\n+ /**\n+ * APIMethod: setDefaultStyle\n+ * Sets the default style for this style object.\n+ * \n+ * Parameters:\n+ * style - {Object} Hash of style properties\n+ */\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles();\n },\n \n /**\n- * APIMethod: getNextElement\n- * Get the next element in the order stack.\n+ * Method: getSymbolizerPrefix\n+ * Returns the correct symbolizer prefix according to the\n+ * geometry type of the passed geometry\n * \n * Parameters:\n- * index - {Integer} The index of the current node in this.order.\n+ * geometry - {<OpenLayers.Geometry>}\n * \n * Returns:\n- * {DOMElement} the node following the index passed in, or\n- * null.\n+ * {String} key of the according symbolizer\n */\n- getNextElement: function(index) {\n- var nextIndex = index + 1;\n- if (nextIndex < this.order.length) {\n- var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);\n- if (nextElement == undefined) {\n- nextElement = this.getNextElement(nextIndex);\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i];\n }\n- return nextElement;\n- } else {\n- return null;\n }\n },\n \n- CLASS_NAME: \"OpenLayers.ElementsIndexer\"\n+ /**\n+ * APIMethod: clone\n+ * Clones this style.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Style>} Clone of this style.\n+ */\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ // clone rules\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone());\n+ }\n+ }\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ //clone default style\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Style\"\n });\n \n+\n /**\n- * Namespace: OpenLayers.ElementsIndexer.IndexingMethods\n- * These are the compare methods for figuring out where a new node should be \n- * placed within the indexer. These methods are very similar to general \n- * sorting methods in that they return -1, 0, and 1 to specify the \n- * direction in which new nodes fall in the ordering.\n+ * Function: createLiteral\n+ * converts a style value holding a combination of PropertyName and Literal\n+ * into a Literal, taking the property values from the passed features.\n+ * \n+ * Parameters:\n+ * value - {String} value to parse. If this string contains a construct like\n+ * \"foo ${bar}\", then \"foo \" will be taken as literal, and \"${bar}\"\n+ * will be replaced by the value of the \"bar\" attribute of the passed\n+ * feature.\n+ * context - {Object} context to take attribute values from\n+ * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to\n+ * <OpenLayers.String.format> for evaluating functions in the\n+ * context.\n+ * property - {String} optional, name of the property for which the literal is\n+ * being created for evaluating functions in the context.\n+ * \n+ * Returns:\n+ * {String} the parsed value. In the example of the value parameter above, the\n+ * result would be \"foo valueOfBar\", assuming that the passed feature has an\n+ * attribute named \"bar\" with the value \"valueOfBar\".\n */\n-OpenLayers.ElementsIndexer.IndexingMethods = {\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = (isNaN(value) || !value) ? value : parseFloat(value);\n+ }\n+ return value;\n+};\n+\n+/**\n+ * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES\n+ * {Array} prefixes of the sld symbolizers. These are the\n+ * same as the main geometry types\n+ */\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',\n+ 'Raster'\n+];\n+/* ======================================================================\n+ OpenLayers/StyleMap.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Style.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.StyleMap\n+ */\n+OpenLayers.StyleMap = OpenLayers.Class({\n \n /**\n- * Method: Z_ORDER\n- * This compare method is used by other comparison methods.\n- * It can be used individually for ordering, but is not recommended,\n- * because it doesn't subscribe to drawing order.\n+ * Property: styles\n+ * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known\n+ * rendering intents (e.g. \"default\", \"temporary\", \"select\", \"delete\").\n+ */\n+ styles: null,\n+\n+ /**\n+ * Property: extendDefault\n+ * {Boolean} if true, every render intent will extend the symbolizers\n+ * specified for the \"default\" intent at rendering time. Otherwise, every\n+ * rendering intent will be treated as a completely independent style.\n+ */\n+ extendDefault: true,\n+\n+ /**\n+ * Constructor: OpenLayers.StyleMap\n * \n * Parameters:\n- * indexer - {<OpenLayers.ElementsIndexer>}\n- * newNode - {DOMElement}\n- * nextNode - {DOMElement}\n- * \n- * Returns:\n- * {Integer}\n+ * style - {Object} Optional. Either a style hash, or a style object, or\n+ * a hash of style objects (style hashes) keyed by rendering\n+ * intent. If just one style hash or style object is passed,\n+ * this will be used for all known render intents (default,\n+ * select, temporary)\n+ * options - {Object} optional hash of additional options for this\n+ * instance\n */\n- Z_ORDER: function(indexer, newNode, nextNode) {\n- var newZIndex = indexer.getZIndex(newNode);\n+ initialize: function(style, options) {\n+ this.styles = {\n+ \"default\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"default\"]),\n+ \"select\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"select\"]),\n+ \"temporary\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"temporary\"]),\n+ \"delete\": new OpenLayers.Style(\n+ OpenLayers.Feature.Vector.style[\"delete\"])\n+ };\n \n- var returnVal = 0;\n- if (nextNode) {\n- var nextZIndex = indexer.getZIndex(nextNode);\n- returnVal = newZIndex - nextZIndex;\n+ // take whatever the user passed as style parameter and convert it\n+ // into parts of stylemap.\n+ if (style instanceof OpenLayers.Style) {\n+ // user passed a style object\n+ this.styles[\"default\"] = style;\n+ this.styles[\"select\"] = style;\n+ this.styles[\"temporary\"] = style;\n+ this.styles[\"delete\"] = style;\n+ } else if (typeof style == \"object\") {\n+ for (var key in style) {\n+ if (style[key] instanceof OpenLayers.Style) {\n+ // user passed a hash of style objects\n+ this.styles[key] = style[key];\n+ } else if (typeof style[key] == \"object\") {\n+ // user passsed a hash of style hashes\n+ this.styles[key] = new OpenLayers.Style(style[key]);\n+ } else {\n+ // user passed a style hash (i.e. symbolizer)\n+ this.styles[\"default\"] = new OpenLayers.Style(style);\n+ this.styles[\"select\"] = new OpenLayers.Style(style);\n+ this.styles[\"temporary\"] = new OpenLayers.Style(style);\n+ this.styles[\"delete\"] = new OpenLayers.Style(style);\n+ break;\n+ }\n+ }\n }\n+ OpenLayers.Util.extend(this, options);\n+ },\n \n- return returnVal;\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var key in this.styles) {\n+ this.styles[key].destroy();\n+ }\n+ this.styles = null;\n },\n \n /**\n- * APIMethod: Z_ORDER_DRAWING_ORDER\n- * This method orders nodes by their z-index, but does so in a way\n- * that, if there are other nodes with the same z-index, the newest \n- * drawn will be the front most within that z-index. This is the \n- * default indexing method.\n+ * Method: createSymbolizer\n+ * Creates the symbolizer for a feature for a render intent.\n * \n * Parameters:\n- * indexer - {<OpenLayers.ElementsIndexer>}\n- * newNode - {DOMElement}\n- * nextNode - {DOMElement}\n+ * feature - {<OpenLayers.Feature>} The feature to evaluate the rules\n+ * of the intended style against.\n+ * intent - {String} The intent determines the symbolizer that will be\n+ * used to draw the feature. Well known intents are \"default\"\n+ * (for just drawing the features), \"select\" (for selected\n+ * features) and \"temporary\" (for drawing features).\n * \n * Returns:\n- * {Integer}\n+ * {Object} symbolizer hash\n */\n- Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {\n- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n- indexer,\n- newNode,\n- nextNode\n- );\n-\n- // Make Z_ORDER subscribe to drawing order by pushing it above\n- // all of the other nodes with the same z-index.\n- if (nextNode && returnVal == 0) {\n- returnVal = 1;\n+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector();\n }\n-\n- return returnVal;\n+ if (!this.styles[intent]) {\n+ intent = \"default\";\n+ }\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature);\n+ }\n+ return OpenLayers.Util.extend(defaultSymbolizer,\n+ this.styles[intent].createSymbolizer(feature));\n },\n \n /**\n- * APIMethod: Z_ORDER_Y_ORDER\n- * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it\n- * best describes which ordering methods have precedence (though, the \n- * name would be too long). This method orders nodes by their z-index, \n- * but does so in a way that, if there are other nodes with the same \n- * z-index, the nodes with the lower y position will be \"closer\" than \n- * those with a higher y position. If two nodes have the exact same y \n- * position, however, then this method will revert to using drawing \n- * order to decide placement.\n+ * Method: addUniqueValueRules\n+ * Convenience method to create comparison rules for unique values of a\n+ * property. The rules will be added to the style object for a specified\n+ * rendering intent. This method is a shortcut for creating something like\n+ * the \"unique value legends\" familiar from well known desktop GIS systems\n * \n * Parameters:\n- * indexer - {<OpenLayers.ElementsIndexer>}\n- * newNode - {DOMElement}\n- * nextNode - {DOMElement}\n- * \n- * Returns:\n- * {Integer}\n+ * renderIntent - {String} rendering intent to add the rules to\n+ * property - {String} values of feature attributes to create the\n+ * rules for\n+ * symbolizers - {Object} Hash of symbolizers, keyed by the desired\n+ * property values \n+ * context - {Object} An optional object with properties that\n+ * symbolizers' property values should be evaluated\n+ * against. If no context is specified, feature.attributes\n+ * will be used\n */\n- Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {\n- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n- indexer,\n- newNode,\n- nextNode\n- );\n-\n- if (nextNode && returnVal === 0) {\n- var result = nextNode._boundsBottom - newNode._boundsBottom;\n- returnVal = (result === 0) ? 1 : result;\n+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n+ var rules = [];\n+ for (var value in symbolizers) {\n+ rules.push(new OpenLayers.Rule({\n+ symbolizer: symbolizers[value],\n+ context: context,\n+ filter: new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ property: property,\n+ value: value\n+ })\n+ }));\n }\n+ this.styles[renderIntent].addRules(rules);\n+ },\n \n- return returnVal;\n- }\n-};\n+ CLASS_NAME: \"OpenLayers.StyleMap\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Vector.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n /**\n- * Class: OpenLayers.Renderer.Elements\n- * This is another virtual class in that it should never be instantiated by \n- * itself as a Renderer. It exists because there is *tons* of shared \n- * functionality between different vector libraries which use nodes/elements\n- * as a base for rendering vectors. \n- * \n- * The highlevel bits of code that are implemented here are the adding and \n- * removing of geometries, which is essentially the same for any \n- * element-based renderer. The details of creating each node and drawing the\n- * paths are of course different, but the machinery is the same. \n- * \n- * Inherits:\n- * - <OpenLayers.Renderer>\n+ * @requires OpenLayers/Layer.js\n+ * @requires OpenLayers/Renderer.js\n+ * @requires OpenLayers/StyleMap.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Lang.js\n */\n-OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector\n+ * Instances of OpenLayers.Layer.Vector are used to render vector data from\n+ * a variety of sources. Create a new vector layer with the\n+ * <OpenLayers.Layer.Vector> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer>\n+ */\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n \n /**\n- * Property: rendererRoot\n- * {DOMElement}\n+ * APIProperty: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * layer.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Listeners will be called with a reference to an event object. The\n+ * properties of this event depends on exactly what happened.\n+ *\n+ * All event objects have at least the following properties:\n+ * object - {Object} A reference to layer.events.object.\n+ * element - {DOMElement} A reference to layer.events.element.\n+ *\n+ * Supported map event types (in addition to those from <OpenLayers.Layer.events>):\n+ * beforefeatureadded - Triggered before a feature is added. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be added. To stop the feature from being added, a\n+ * listener should return false.\n+ * beforefeaturesadded - Triggered before an array of features is added.\n+ * Listeners will receive an object with a *features* property\n+ * referencing the feature to be added. To stop the features from\n+ * being added, a listener should return false.\n+ * featureadded - Triggered after a feature is added. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the added feature.\n+ * featuresadded - Triggered after features are added. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of added features.\n+ * beforefeatureremoved - Triggered before a feature is removed. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be removed.\n+ * beforefeaturesremoved - Triggered before multiple features are removed. \n+ * Listeners will receive an object with a *features* property\n+ * referencing the features to be removed.\n+ * featureremoved - Triggerd after a feature is removed. The event\n+ * object passed to listeners will have a *feature* property with a\n+ * reference to the removed feature.\n+ * featuresremoved - Triggered after features are removed. The event\n+ * object passed to listeners will have a *features* property with a\n+ * reference to an array of removed features.\n+ * beforefeatureselected - Triggered before a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * feature to be selected. To stop the feature from being selectd, a\n+ * listener should return false.\n+ * featureselected - Triggered after a feature is selected. Listeners\n+ * will receive an object with a *feature* property referencing the\n+ * selected feature.\n+ * featureunselected - Triggered after a feature is unselected.\n+ * Listeners will receive an object with a *feature* property\n+ * referencing the unselected feature.\n+ * beforefeaturemodified - Triggered when a feature is selected to \n+ * be modified. Listeners will receive an object with a *feature* \n+ * property referencing the selected feature.\n+ * featuremodified - Triggered when a feature has been modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * afterfeaturemodified - Triggered when a feature is finished being modified.\n+ * Listeners will receive an object with a *feature* property referencing \n+ * the modified feature.\n+ * vertexmodified - Triggered when a vertex within any feature geometry\n+ * has been modified. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * modification.\n+ * vertexremoved - Triggered when a vertex within any feature geometry\n+ * has been deleted. Listeners will receive an object with a\n+ * *feature* property referencing the modified feature, a *vertex*\n+ * property referencing the vertex modified (always a point geometry),\n+ * and a *pixel* property referencing the pixel location of the\n+ * removal.\n+ * sketchstarted - Triggered when a feature sketch bound for this layer\n+ * is started. Listeners will receive an object with a *feature*\n+ * property referencing the new sketch feature and a *vertex* property\n+ * referencing the creation point.\n+ * sketchmodified - Triggered when a feature sketch bound for this layer\n+ * is modified. Listeners will receive an object with a *vertex*\n+ * property referencing the modified vertex and a *feature* property\n+ * referencing the sketch feature.\n+ * sketchcomplete - Triggered when a feature sketch bound for this layer\n+ * is complete. Listeners will receive an object with a *feature*\n+ * property referencing the sketch feature. By returning false, a\n+ * listener can stop the sketch feature from being added to the layer.\n+ * refresh - Triggered when something wants a strategy to ask the protocol\n+ * for a new set of features.\n */\n- rendererRoot: null,\n \n /**\n- * Property: root\n- * {DOMElement}\n+ * APIProperty: isBaseLayer\n+ * {Boolean} The layer is a base layer. Default is false. Set this property\n+ * in the layer options.\n */\n- root: null,\n+ isBaseLayer: false,\n \n- /**\n- * Property: vectorRoot\n- * {DOMElement}\n+ /** \n+ * APIProperty: isFixed\n+ * {Boolean} Whether the layer remains in one place while dragging the\n+ * map. Note that setting this to true will move the layer to the bottom\n+ * of the layer stack.\n */\n- vectorRoot: null,\n+ isFixed: false,\n \n- /**\n- * Property: textRoot\n- * {DOMElement}\n+ /** \n+ * APIProperty: features\n+ * {Array(<OpenLayers.Feature.Vector>)} \n */\n- textRoot: null,\n+ features: null,\n+\n+ /** \n+ * Property: filter\n+ * {<OpenLayers.Filter>} The filter set in this layer,\n+ * a strategy launching read requests can combined\n+ * this filter with its own filter.\n+ */\n+ filter: null,\n+\n+ /** \n+ * Property: selectedFeatures\n+ * {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ selectedFeatures: null,\n \n /**\n- * Property: xmlns\n- * {String}\n+ * Property: unrenderedFeatures\n+ * {Object} hash of features, keyed by feature.id, that the renderer\n+ * failed to draw\n */\n- xmlns: null,\n+ unrenderedFeatures: null,\n \n /**\n- * Property: xOffset\n- * {Number} Offset to apply to the renderer viewport translation in x\n- * direction. If the renderer extent's center is on the right of the\n- * dateline (i.e. exceeds the world bounds), we shift the viewport to the\n- * left by one world width. This avoids that features disappear from the\n- * map viewport. Because our dateline handling logic in other places\n- * ensures that extents crossing the dateline always have a center\n- * exceeding the world bounds on the left, we need this offset to make sure\n- * that the same is true for the renderer extent in pixel space as well.\n+ * APIProperty: reportError\n+ * {Boolean} report friendly error message when loading of renderer\n+ * fails.\n */\n- xOffset: 0,\n+ reportError: true,\n+\n+ /** \n+ * APIProperty: style\n+ * {Object} Default style for the layer\n+ */\n+ style: null,\n \n /**\n- * Property: rightOfDateLine\n- * {Boolean} Keeps track of the location of the map extent relative to the\n- * date line. The <setExtent> method compares this value (which is the one\n- * from the previous <setExtent> call) with the current position of the map\n- * extent relative to the date line and updates the xOffset when the extent\n- * has moved from one side of the date line to the other.\n+ * Property: styleMap\n+ * {<OpenLayers.StyleMap>}\n */\n+ styleMap: null,\n \n /**\n- * Property: Indexer\n- * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer \n- * created upon initialization if the zIndexing or yOrdering options\n- * passed to this renderer's constructor are set to true.\n+ * Property: strategies\n+ * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.\n */\n- indexer: null,\n+ strategies: null,\n \n /**\n- * Constant: BACKGROUND_ID_SUFFIX\n- * {String}\n+ * Property: protocol\n+ * {<OpenLayers.Protocol>} Optional protocol for the layer.\n */\n- BACKGROUND_ID_SUFFIX: \"_background\",\n+ protocol: null,\n \n /**\n- * Constant: LABEL_ID_SUFFIX\n- * {String}\n+ * Property: renderers\n+ * {Array(String)} List of supported Renderer classes. Add to this list to\n+ * add support for additional renderers. This list is ordered:\n+ * the first renderer which returns true for the 'supported()'\n+ * method will be used, if not defined in the 'renderer' option.\n */\n- LABEL_ID_SUFFIX: \"_label\",\n+ renderers: ['SVG', 'VML', 'Canvas'],\n+\n+ /** \n+ * Property: renderer\n+ * {<OpenLayers.Renderer>}\n+ */\n+ renderer: null,\n \n /**\n- * Constant: LABEL_OUTLINE_SUFFIX\n- * {String}\n+ * APIProperty: rendererOptions\n+ * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for\n+ * supported options.\n */\n- LABEL_OUTLINE_SUFFIX: \"_outline\",\n+ rendererOptions: null,\n+\n+ /** \n+ * APIProperty: geometryType\n+ * {String} geometryType allows you to limit the types of geometries this\n+ * layer supports. This should be set to something like\n+ * \"OpenLayers.Geometry.Point\" to limit types.\n+ */\n+ geometryType: null,\n+\n+ /** \n+ * Property: drawn\n+ * {Boolean} Whether the Vector Layer features have been drawn yet.\n+ */\n+ drawn: false,\n+\n+ /** \n+ * APIProperty: ratio\n+ * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.\n+ */\n+ ratio: 1,\n \n /**\n- * Constructor: OpenLayers.Renderer.Elements\n- * \n+ * Constructor: OpenLayers.Layer.Vector\n+ * Create a new vector layer\n+ *\n * Parameters:\n- * containerID - {String}\n- * options - {Object} options for this renderer. \n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n *\n- * Supported options are:\n- * yOrdering - {Boolean} Whether to use y-ordering\n- * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored\n- * if yOrdering is set to true.\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} A new vector layer\n */\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n \n- this.rendererRoot = this.createRenderRoot();\n- this.root = this.createRoot(\"_root\");\n- this.vectorRoot = this.createRoot(\"_vroot\");\n- this.textRoot = this.createRoot(\"_troot\");\n+ // allow user-set renderer, otherwise assign one\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.assignRenderer();\n+ }\n \n- this.root.appendChild(this.vectorRoot);\n- this.root.appendChild(this.textRoot);\n+ // if no valid renderer found, display error\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.renderer = null;\n+ this.displayError();\n+ }\n \n- this.rendererRoot.appendChild(this.root);\n- this.container.appendChild(this.rendererRoot);\n+ if (!this.styleMap) {\n+ this.styleMap = new OpenLayers.StyleMap();\n+ }\n \n- if (options && (options.zIndexing || options.yOrdering)) {\n- this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);\n+ this.features = [];\n+ this.selectedFeatures = [];\n+ this.unrenderedFeatures = {};\n+\n+ // Allow for custom layer behavior\n+ if (this.strategies) {\n+ for (var i = 0, len = this.strategies.length; i < len; i++) {\n+ this.strategies[i].setLayer(this);\n+ }\n }\n+\n },\n \n /**\n- * Method: destroy\n+ * APIMethod: destroy\n+ * Destroy this layer\n */\n destroy: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoDestroy) {\n+ strategy.destroy();\n+ }\n+ }\n+ this.strategies = null;\n+ }\n+ if (this.protocol) {\n+ if (this.protocol.autoDestroy) {\n+ this.protocol.destroy();\n+ }\n+ this.protocol = null;\n+ }\n+ this.destroyFeatures();\n+ this.features = null;\n+ this.selectedFeatures = null;\n+ this.unrenderedFeatures = null;\n+ if (this.renderer) {\n+ this.renderer.destroy();\n+ }\n+ this.renderer = null;\n+ this.geometryType = null;\n+ this.drawn = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n+ },\n \n- this.clear();\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer.\n+ * \n+ * Note: Features of the layer are also cloned.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n \n- this.rendererRoot = null;\n- this.root = null;\n- this.xmlns = null;\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());\n+ }\n \n- OpenLayers.Renderer.prototype.destroy.apply(this, arguments);\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+ var features = this.features;\n+ var len = features.length;\n+ var clonedFeatures = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ clonedFeatures[i] = features[i].clone();\n+ }\n+ obj.features = clonedFeatures;\n+\n+ return obj;\n },\n \n /**\n- * Method: clear\n- * Remove all the elements from the root\n+ * Method: refresh\n+ * Ask the layer to request features again and redraw them. Triggers\n+ * the refresh event if the layer is in range and visible.\n+ *\n+ * Parameters:\n+ * obj - {Object} Optional object with properties for any listener of\n+ * the refresh event.\n */\n- clear: function() {\n- var child;\n- var root = this.vectorRoot;\n- if (root) {\n- while (child = root.firstChild) {\n- root.removeChild(child);\n- }\n+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj);\n }\n- root = this.textRoot;\n- if (root) {\n- while (child = root.firstChild) {\n- root.removeChild(child);\n+ },\n+\n+ /** \n+ * Method: assignRenderer\n+ * Iterates through the available renderer implementations and selects \n+ * and assigns the first one whose \"supported()\" function returns true.\n+ */\n+ assignRenderer: function() {\n+ for (var i = 0, len = this.renderers.length; i < len; i++) {\n+ var rendererClass = this.renderers[i];\n+ var renderer = (typeof rendererClass == \"function\") ?\n+ rendererClass :\n+ OpenLayers.Renderer[rendererClass];\n+ if (renderer && renderer.prototype.supported()) {\n+ this.renderer = new renderer(this.div, this.rendererOptions);\n+ break;\n }\n }\n- if (this.indexer) {\n- this.indexer.clear();\n+ },\n+\n+ /** \n+ * Method: displayError \n+ * Let the user know their browser isn't supported.\n+ */\n+ displayError: function() {\n+ if (this.reportError) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n+ renderers: this.renderers.join('\\n')\n+ }));\n+ }\n+ },\n+\n+ /** \n+ * Method: setMap\n+ * The layer has been added to the map. \n+ * \n+ * If there is no renderer set, the layer can't be used. Remove it.\n+ * Otherwise, give the renderer a reference to the map and set its size.\n+ * \n+ * Parameters:\n+ * map - {<OpenLayers.Map>} \n+ */\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+\n+ if (!this.renderer) {\n+ this.map.removeLayer(this);\n+ } else {\n+ this.renderer.map = this.map;\n+\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n }\n },\n \n /**\n- * Method: setExtent\n- * Set the visible part of the layer.\n+ * Method: afterAdd\n+ * Called at the end of the map.addLayer sequence. At this point, the map\n+ * will have a base layer. Any autoActivate strategies will be\n+ * activated here.\n+ */\n+ afterAdd: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.activate();\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: removeMap\n+ * The layer has been removed from the map.\n *\n * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n- *\n- * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * map - {<OpenLayers.Map>}\n */\n- setExtent: function(extent, resolutionChanged) {\n- var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- var resolution = this.getResolution();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var rightOfDateLine,\n- ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio),\n- world = this.map.getMaxExtent();\n- if (world.right > extent.left && world.right < extent.right) {\n- rightOfDateLine = true;\n- } else if (world.left > extent.left && world.left < extent.right) {\n- rightOfDateLine = false;\n- }\n- if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {\n- coordSysUnchanged = false;\n- this.xOffset = rightOfDateLine === true ?\n- world.getWidth() / resolution : 0;\n+ removeMap: function(map) {\n+ this.drawn = false;\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.deactivate();\n+ }\n }\n- this.rightOfDateLine = rightOfDateLine;\n }\n- return coordSysUnchanged;\n },\n \n- /** \n- * Method: getNodeType\n- * This function is in charge of asking the specific renderer which type\n- * of node to create for the given geometry and style. All geometries\n- * in an Elements-based renderer consist of one node and some\n- * attributes. We have the nodeFactory() function which creates a node\n- * for us, but it takes a 'type' as input, and that is precisely what\n- * this function tells us. \n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n+ /**\n+ * Method: onMapResize\n+ * Notify the renderer of the change in size. \n * \n- * Returns:\n- * {String} The corresponding node type for the specified geometry\n */\n- getNodeType: function(geometry, style) {},\n+ onMapResize: function() {\n+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n \n- /** \n- * Method: drawGeometry \n- * Draw the geometry, creating new nodes, setting paths, setting style,\n- * setting featureId on the node. This method should only be called\n- * by the renderer itself.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize);\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * Reset the vector layer's div so that it once again is lined up with \n+ * the map. Notify the renderer of the change of extent, and in the\n+ * case of a change of zoom level (resolution), have the \n+ * renderer redraw features.\n * \n- * Returns:\n- * {Boolean} true if the geometry has been drawn completely; null if\n- * incomplete; false otherwise\n+ * If the layer has not yet been drawn, cycle through the layer's \n+ * features and draw each one.\n+ * \n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>} \n+ * zoomChanged - {Boolean} \n+ * dragging - {Boolean} \n */\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- var rendered = true;\n- if ((className == \"OpenLayers.Geometry.Collection\") ||\n- (className == \"OpenLayers.Geometry.MultiPoint\") ||\n- (className == \"OpenLayers.Geometry.MultiLineString\") ||\n- (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n- for (var i = 0, len = geometry.components.length; i < len; i++) {\n- rendered = this.drawGeometry(\n- geometry.components[i], style, featureId) && rendered;\n- }\n- return rendered;\n- }\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n \n- rendered = false;\n- var removeBackground = false;\n- if (style.display != \"none\") {\n- if (style.backgroundGraphic) {\n- this.redrawBackgroundNode(geometry.id, geometry, style,\n- featureId);\n- } else {\n- removeBackground = true;\n+ var coordSysUnchanged = true;\n+ if (!dragging) {\n+ this.renderer.root.style.visibility = 'hidden';\n+\n+ var viewSize = this.map.getSize(),\n+ viewWidth = viewSize.w,\n+ viewHeight = viewSize.h,\n+ offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,\n+ offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;\n+ offsetLeft += this.map.layerContainerOriginPx.x;\n+ offsetLeft = -Math.round(offsetLeft);\n+ offsetTop += this.map.layerContainerOriginPx.y;\n+ offsetTop = -Math.round(offsetTop);\n+\n+ this.div.style.left = offsetLeft + 'px';\n+ this.div.style.top = offsetTop + 'px';\n+\n+ var extent = this.map.getExtent().scale(this.ratio);\n+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+\n+ this.renderer.root.style.visibility = 'visible';\n+\n+ // Force a reflow on gecko based browsers to prevent jump/flicker.\n+ // This seems to happen on only certain configurations; it was originally\n+ // noticed in FF 2.0 and Linux.\n+ if (OpenLayers.IS_GECKO === true) {\n+ this.div.scrollLeft = this.div.scrollLeft;\n }\n- rendered = this.redrawNode(geometry.id, geometry, style,\n- featureId);\n- }\n- if (rendered == false) {\n- var node = document.getElementById(geometry.id);\n- if (node) {\n- if (node._style.backgroundGraphic) {\n- removeBackground = true;\n+\n+ if (!zoomChanged && coordSysUnchanged) {\n+ for (var i in this.unrenderedFeatures) {\n+ var feature = this.unrenderedFeatures[i];\n+ this.drawFeature(feature);\n }\n- node.parentNode.removeChild(node);\n }\n }\n- if (removeBackground) {\n- var node = document.getElementById(\n- geometry.id + this.BACKGROUND_ID_SUFFIX);\n- if (node) {\n- node.parentNode.removeChild(node);\n+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n+ this.drawn = true;\n+ var feature;\n+ for (var i = 0, len = this.features.length; i < len; i++) {\n+ this.renderer.locked = (i !== (len - 1));\n+ feature = this.features[i];\n+ this.drawFeature(feature);\n }\n }\n- return rendered;\n },\n \n- /**\n- * Method: redrawNode\n+ /** \n+ * APIMethod: display\n+ * Hide or show the Layer\n * \n * Parameters:\n- * id - {String}\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n- * \n- * Returns:\n- * {Boolean} true if the complete geometry could be drawn, null if parts of\n- * the geometry could not be drawn, false otherwise\n+ * display - {Boolean}\n */\n- redrawNode: function(id, geometry, style, featureId) {\n- style = this.applyDefaultSymbolizer(style);\n- // Get the node if it's already on the map.\n- var node = this.nodeFactory(id, this.getNodeType(geometry, style));\n-\n- // Set the data for the node, then draw it.\n- node._featureId = featureId;\n- node._boundsBottom = geometry.getBounds().bottom;\n- node._geometryClass = geometry.CLASS_NAME;\n- node._style = style;\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ // we need to set the display style of the root in case it is attached\n+ // to a foreign layer\n+ var currentDisplay = this.div.style.display;\n+ if (currentDisplay != this.renderer.root.style.display) {\n+ this.renderer.root.style.display = currentDisplay;\n+ }\n+ },\n \n- var drawResult = this.drawGeometryNode(node, geometry, style);\n- if (drawResult === false) {\n- return false;\n+ /**\n+ * APIMethod: addFeatures\n+ * Add Features to the layer.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ * options - {Object}\n+ */\n+ addFeatures: function(features, options) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n }\n \n- node = drawResult.node;\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ var event = {\n+ features: features\n+ };\n+ var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n+ if (ret === false) {\n+ return;\n+ }\n+ features = event.features;\n+ }\n \n- // Insert the node into the indexer so it can show us where to\n- // place it. Note that this operation is O(log(n)). If there's a\n- // performance problem (when dragging, for instance) this is\n- // likely where it would be.\n- if (this.indexer) {\n- var insert = this.indexer.insert(node);\n- if (insert) {\n- this.vectorRoot.insertBefore(node, insert);\n+ // Track successfully added features for featuresadded event, since\n+ // beforefeatureadded can veto single features.\n+ var featuresAdded = [];\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ if (i != (features.length - 1)) {\n+ this.renderer.locked = true;\n } else {\n- this.vectorRoot.appendChild(node);\n+ this.renderer.locked = false;\n }\n- } else {\n- // if there's no indexer, simply append the node to root,\n- // but only if the node is a new one\n- if (node.parentNode !== this.vectorRoot) {\n- this.vectorRoot.appendChild(node);\n+ var feature = features[i];\n+\n+ if (this.geometryType &&\n+ !(feature.geometry instanceof this.geometryType)) {\n+ throw new TypeError('addFeatures: component should be an ' +\n+ this.geometryType.prototype.CLASS_NAME);\n }\n- }\n \n- this.postDraw(node);\n+ //give feature reference to its layer\n+ feature.layer = this;\n \n- return drawResult.complete;\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style);\n+ }\n+\n+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue;\n+ }\n+ this.preFeatureInsert(feature);\n+ }\n+\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature);\n+ }\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ });\n+ }\n },\n \n+\n /**\n- * Method: redrawBackgroundNode\n- * Redraws the node using special 'background' style properties. Basically\n- * just calls redrawNode(), but instead of directly using the \n- * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and \n- * 'graphicZIndex' properties directly from the specified 'style' \n- * parameter, we create a new style object and set those properties \n- * from the corresponding 'background'-prefixed properties from \n- * specified 'style' parameter.\n+ * APIMethod: removeFeatures\n+ * Remove features from the layer. This erases any drawn features and\n+ * removes them from the layer's control. The beforefeatureremoved\n+ * and featureremoved events will be triggered for each feature. The\n+ * featuresremoved event will be triggered after all features have\n+ * been removed. To supress event triggering, use the silent option.\n * \n * Parameters:\n- * id - {String}\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * featureId - {String}\n- * \n- * Returns:\n- * {Boolean} true if the complete geometry could be drawn, null if parts of\n- * the geometry could not be drawn, false otherwise\n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be\n+ * removed.\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n+ *\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n */\n- redrawBackgroundNode: function(id, geometry, style, featureId) {\n- var backgroundStyle = OpenLayers.Util.extend({}, style);\n+ removeFeatures: function(features, options) {\n+ if (!features || features.length === 0) {\n+ return;\n+ }\n+ if (features === this.features) {\n+ return this.removeAllFeatures(options);\n+ }\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ if (features === this.selectedFeatures) {\n+ features = features.slice();\n+ }\n \n- // Set regular style attributes to apply to the background styles.\n- backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;\n- backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;\n- backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;\n- backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;\n- backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;\n- backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;\n+ var notify = !options || !options.silent;\n \n- // Erase background styles.\n- backgroundStyle.backgroundGraphic = null;\n- backgroundStyle.backgroundXOffset = null;\n- backgroundStyle.backgroundYOffset = null;\n- backgroundStyle.backgroundGraphicZIndex = null;\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n+ }\n+ );\n+ }\n \n- return this.redrawNode(\n- id + this.BACKGROUND_ID_SUFFIX,\n- geometry,\n- backgroundStyle,\n- null\n- );\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ // We remain locked so long as we're not at 0\n+ // and the 'next' feature has a geometry. We do the geometry check\n+ // because if all the features after the current one are 'null', we\n+ // won't call eraseGeometry, so we break the 'renderer functions\n+ // will always be called with locked=false *last*' rule. The end result\n+ // is a possible gratiutious unlocking to save a loop through the rest \n+ // of the list checking the remaining features every time. So long as\n+ // null geoms are rare, this is probably okay. \n+ if (i != 0 && features[i - 1].geometry) {\n+ this.renderer.locked = true;\n+ } else {\n+ this.renderer.locked = false;\n+ }\n+\n+ var feature = features[i];\n+ delete this.unrenderedFeatures[feature.id];\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n+ }\n+\n+ this.features = OpenLayers.Util.removeItem(this.features, feature);\n+ // feature has no layer at this point\n+ feature.layer = null;\n+\n+ if (feature.geometry) {\n+ this.renderer.eraseFeatures(feature);\n+ }\n+\n+ //in the case that this feature is one of the selected features, \n+ // remove it from that array as well.\n+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n+ OpenLayers.Util.removeItem(this.selectedFeatures, feature);\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ }\n+\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n+ }\n },\n \n- /**\n- * Method: drawGeometryNode\n- * Given a node, draw a geometry on the specified layer.\n- * node and geometry are required arguments, style is optional.\n- * This method is only called by the render itself.\n+ /** \n+ * APIMethod: removeAllFeatures\n+ * Remove all features from the layer.\n *\n * Parameters:\n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * \n- * Returns:\n- * {Object} a hash with properties \"node\" (the drawn node) and \"complete\"\n- * (null if parts of the geometry could not be drawn, false if nothing\n- * could be drawn)\n+ * options - {Object} Optional properties for changing behavior of the\n+ * removal.\n+ *\n+ * Valid options:\n+ * silent - {Boolean} Supress event triggering. Default is false.\n */\n- drawGeometryNode: function(node, geometry, style) {\n- style = style || node._style;\n-\n- var options = {\n- 'isFilled': style.fill === undefined ?\n- true : style.fill,\n- 'isStroked': style.stroke === undefined ?\n- !!style.strokeWidth : style.stroke\n- };\n- var drawn;\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- if (style.graphic === false) {\n- options.isFilled = false;\n- options.isStroked = false;\n+ removeAllFeatures: function(options) {\n+ var notify = !options || !options.silent;\n+ var features = this.features;\n+ if (notify) {\n+ this.events.triggerEvent(\n+ \"beforefeaturesremoved\", {\n+ features: features\n }\n- drawn = this.drawPoint(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- options.isFilled = false;\n- drawn = this.drawLineString(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- drawn = this.drawLinearRing(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- drawn = this.drawPolygon(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.Rectangle\":\n- drawn = this.drawRectangle(node, geometry);\n- break;\n- default:\n- break;\n+ );\n }\n-\n- node._options = options;\n-\n- //set style\n- //TBD simplify this\n- if (drawn != false) {\n- return {\n- node: this.setStyle(node, style, options, geometry),\n- complete: drawn\n- };\n- } else {\n- return false;\n+ var feature;\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ feature = features[i];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ feature.layer = null;\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ });\n+ }\n+ }\n+ this.renderer.clear();\n+ this.features = [];\n+ this.unrenderedFeatures = {};\n+ this.selectedFeatures = [];\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ });\n }\n },\n \n /**\n- * Method: postDraw\n- * Things that have do be done after the geometry node is appended\n- * to its parent node. To be overridden by subclasses.\n- * \n+ * APIMethod: destroyFeatures\n+ * Erase and destroy features on the layer.\n+ *\n * Parameters:\n- * node - {DOMElement}\n+ * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of\n+ * features to destroy. If not supplied, all features on the layer\n+ * will be destroyed.\n+ * options - {Object}\n */\n- postDraw: function(node) {},\n+ destroyFeatures: function(features, options) {\n+ var all = (features == undefined); // evaluates to true if\n+ // features is null\n+ if (all) {\n+ features = this.features;\n+ }\n+ if (features) {\n+ this.removeFeatures(features, options);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ features[i].destroy();\n+ }\n+ }\n+ },\n \n /**\n- * Method: drawPoint\n- * Virtual function for drawing Point Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n+ * APIMethod: drawFeature\n+ * Draw (or redraw) a feature on the layer. If the optional style argument\n+ * is included, this style will be used. If no style is included, the\n+ * feature's style will be used. If the feature doesn't have a style,\n+ * the layer's style will be used.\n * \n+ * This function is not designed to be used when adding features to \n+ * the layer (use addFeatures instead). It is meant to be used when\n+ * the style of a feature has changed, or in some other way needs to \n+ * visually updated *after* it has already been added to a layer. You\n+ * must add the feature to the layer for most layer-related events to \n+ * happen.\n+ *\n * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement} or false if the renderer could not draw the point\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {String | Object} Named render intent or full symbolizer object.\n */\n- drawPoint: function(node, geometry) {},\n+ drawFeature: function(feature, style) {\n+ // don't try to draw the feature with the renderer if the layer is not \n+ // drawn itself\n+ if (!this.drawn) {\n+ return;\n+ }\n+ if (typeof style != \"object\") {\n+ if (!style && feature.state === OpenLayers.State.DELETE) {\n+ style = \"delete\";\n+ }\n+ var renderIntent = style || feature.renderIntent;\n+ style = feature.style || this.style;\n+ if (!style) {\n+ style = this.styleMap.createSymbolizer(feature, renderIntent);\n+ }\n+ }\n+\n+ var drawn = this.renderer.drawFeature(feature, style);\n+ //TODO remove the check for null when we get rid of Renderer.SVG\n+ if (drawn === false || drawn === null) {\n+ this.unrenderedFeatures[feature.id] = feature;\n+ } else {\n+ delete this.unrenderedFeatures[feature.id];\n+ }\n+ },\n \n /**\n- * Method: drawLineString\n- * Virtual function for drawing LineString Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement} or null if the renderer could not draw all components of\n- * the linestring, or false if nothing could be drawn\n+ * Method: eraseFeatures\n+ * Erase features from the layer.\n+ *\n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n */\n- drawLineString: function(node, geometry) {},\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features);\n+ },\n \n /**\n- * Method: drawLinearRing\n- * Virtual function for drawing LinearRing Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * Method: getFeatureFromEvent\n+ * Given an event, return a feature if the event occurred over one.\n+ * Otherwise, return null.\n+ *\n+ * Parameters:\n+ * evt - {Event} \n+ *\n * Returns:\n- * {DOMElement} or null if the renderer could not draw all components\n- * of the linear ring, or false if nothing could be drawn\n+ * {<OpenLayers.Feature.Vector>} A feature if one was under the event.\n */\n- drawLinearRing: function(node, geometry) {},\n+ getFeatureFromEvent: function(evt) {\n+ if (!this.renderer) {\n+ throw new Error('getFeatureFromEvent called on layer with no ' +\n+ 'renderer. This usually means you destroyed a ' +\n+ 'layer, but not some handler which is associated ' +\n+ 'with it.');\n+ }\n+ var feature = null;\n+ var featureId = this.renderer.getFeatureIdFromEvent(evt);\n+ if (featureId) {\n+ if (typeof featureId === \"string\") {\n+ feature = this.getFeatureById(featureId);\n+ } else {\n+ feature = featureId;\n+ }\n+ }\n+ return feature;\n+ },\n \n /**\n- * Method: drawPolygon\n- * Virtual function for drawing Polygon Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * APIMethod: getFeatureBy\n+ * Given a property value, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * property - {String}\n+ * value - {String}\n+ *\n * Returns:\n- * {DOMElement} or null if the renderer could not draw all components\n- * of the polygon, or false if nothing could be drawn\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * property value or null if there is no such feature.\n */\n- drawPolygon: function(node, geometry) {},\n+ getFeatureBy: function(property, value) {\n+ //TBD - would it be more efficient to use a hash for this.features?\n+ var feature = null;\n+ for (var i = 0, len = this.features.length; i < len; ++i) {\n+ if (this.features[i][property] == value) {\n+ feature = this.features[i];\n+ break;\n+ }\n+ }\n+ return feature;\n+ },\n \n /**\n- * Method: drawRectangle\n- * Virtual function for drawing Rectangle Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * APIMethod: getFeatureById\n+ * Given a feature id, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * featureId - {String}\n+ *\n * Returns:\n- * {DOMElement} or false if the renderer could not draw the rectangle\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureId or null if there is no such feature.\n */\n- drawRectangle: function(node, geometry) {},\n+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy('id', featureId);\n+ },\n \n /**\n- * Method: drawCircle\n- * Virtual function for drawing Circle Geometry. \n- * Should be implemented by subclasses.\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * APIMethod: getFeatureByFid\n+ * Given a feature fid, return the feature if it exists in the features array\n+ *\n+ * Parameters:\n+ * featureFid - {String}\n+ *\n * Returns:\n- * {DOMElement} or false if the renderer could not draw the circle\n+ * {<OpenLayers.Feature.Vector>} A feature corresponding to the given\n+ * featureFid or null if there is no such feature.\n */\n- drawCircle: function(node, geometry) {},\n+ getFeatureByFid: function(featureFid) {\n+ return this.getFeatureBy('fid', featureFid);\n+ },\n \n /**\n- * Method: removeText\n- * Removes a label\n- * \n+ * APIMethod: getFeaturesByAttribute\n+ * Returns an array of features that have the given attribute key set to the\n+ * given value. Comparison of attribute values takes care of datatypes, e.g.\n+ * the string '1234' is not equal to the number 1234.\n+ *\n * Parameters:\n- * featureId - {String}\n+ * attrName - {String}\n+ * attrValue - {Mixed}\n+ *\n+ * Returns:\n+ * Array({<OpenLayers.Feature.Vector>}) An array of features that have the \n+ * passed named attribute set to the given value.\n */\n- removeText: function(featureId) {\n- var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);\n- if (label) {\n- this.textRoot.removeChild(label);\n+ getFeaturesByAttribute: function(attrName, attrValue) {\n+ var i,\n+ feature,\n+ len = this.features.length,\n+ foundFeatures = [];\n+ for (i = 0; i < len; i++) {\n+ feature = this.features[i];\n+ if (feature && feature.attributes) {\n+ if (feature.attributes[attrName] === attrValue) {\n+ foundFeatures.push(feature);\n+ }\n+ }\n }\n- var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);\n- if (outline) {\n- this.textRoot.removeChild(outline);\n+ return foundFeatures;\n+ },\n+\n+ /**\n+ * Unselect the selected features\n+ * i.e. clears the featureSelection array\n+ * change the style back\n+ clearSelection: function() {\n+\n+ var vectorLayer = this.map.vectorLayer;\n+ for (var i = 0; i < this.map.featureSelection.length; i++) {\n+ var featureSelection = this.map.featureSelection[i];\n+ vectorLayer.drawFeature(featureSelection, vectorLayer.style);\n }\n+ this.map.featureSelection = [];\n },\n+ */\n+\n \n /**\n- * Method: getFeatureIdFromEvent\n- * \n- * Parameters:\n- * evt - {Object} An <OpenLayers.Event> object\n+ * APIMethod: onFeatureInsert\n+ * method called after a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something on feature updates.\n *\n- * Returns:\n- * {String} A feature id or undefined.\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- getFeatureIdFromEvent: function(evt) {\n- var target = evt.target;\n- var useElement = target && target.correspondingUseElement;\n- var node = useElement ? useElement : (target || evt.srcElement);\n- return node._featureId;\n- },\n+ onFeatureInsert: function(feature) {},\n \n- /** \n- * Method: eraseGeometry\n- * Erase a geometry from the renderer. In the case of a multi-geometry, \n- * we cycle through and recurse on ourselves. Otherwise, we look for a \n- * node with the geometry.id, destroy its geometry, and remove it from\n- * the DOM.\n- * \n+ /**\n+ * APIMethod: preFeatureInsert\n+ * method called before a feature is inserted.\n+ * Does nothing by default. Override this if you\n+ * need to do something when features are first added to the\n+ * layer, but before they are drawn, such as adjust the style.\n+ *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * featureId - {String}\n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- eraseGeometry: function(geometry, featureId) {\n- if ((geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPoint\") ||\n- (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiLineString\") ||\n- (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPolygon\") ||\n- (geometry.CLASS_NAME == \"OpenLayers.Geometry.Collection\")) {\n- for (var i = 0, len = geometry.components.length; i < len; i++) {\n- this.eraseGeometry(geometry.components[i], featureId);\n- }\n- } else {\n- var element = OpenLayers.Util.getElement(geometry.id);\n- if (element && element.parentNode) {\n- if (element.geometry) {\n- element.geometry.destroy();\n- element.geometry = null;\n- }\n- element.parentNode.removeChild(element);\n-\n- if (this.indexer) {\n- this.indexer.remove(element);\n- }\n+ preFeatureInsert: function(feature) {},\n \n- if (element._style.backgroundGraphic) {\n- var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;\n- var bElem = OpenLayers.Util.getElement(backgroundId);\n- if (bElem && bElem.parentNode) {\n- // No need to destroy the geometry since the element and the background\n- // node share the same geometry.\n- bElem.parentNode.removeChild(bElem);\n+ /** \n+ * APIMethod: getDataExtent\n+ * Calculates the max extent which includes all of the features.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Bounds>} or null if the layer has no features with\n+ * geometries.\n+ */\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ var features = this.features;\n+ if (features && (features.length > 0)) {\n+ var geometry = null;\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ geometry = features[i].geometry;\n+ if (geometry) {\n+ if (maxExtent === null) {\n+ maxExtent = new OpenLayers.Bounds();\n }\n+ maxExtent.extend(geometry.getBounds());\n }\n }\n }\n+ return maxExtent;\n },\n \n- /** \n- * Method: nodeFactory\n- * Create new node of the specified type, with the (optional) specified id.\n+ CLASS_NAME: \"OpenLayers.Layer.Vector\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Vector/RootContainer.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.Vector.RootContainer\n+ * A special layer type to combine multiple vector layers inside a single\n+ * renderer root container. This class is not supposed to be instantiated\n+ * from user space, it is a helper class for controls that require event\n+ * processing for multiple vector layers.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.Vector>\n+ */\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+\n+ /**\n+ * Property: displayInLayerSwitcher\n+ * Set to false for this layer type\n+ */\n+ displayInLayerSwitcher: false,\n+\n+ /**\n+ * APIProperty: layers\n+ * Layers that are attached to this container. Required config option.\n+ */\n+ layers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Vector.RootContainer\n+ * Create a new root container for multiple vector layer. This constructor\n+ * is not supposed to be used from user space, it is only to be used by\n+ * controls that need feature selection across multiple vector layers.\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * options - {Object} Optional object with non-default properties to set on\n+ * the layer.\n * \n- * If node already exists with same ID and a different type, we remove it\n- * and then call ourselves again to recreate it.\n+ * Required options properties:\n+ * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n+ * container\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n+ * container\n+ */\n+\n+ /**\n+ * Method: display\n+ */\n+ display: function() {},\n+\n+ /**\n+ * Method: getFeatureFromEvent\n+ * walk through the layers to find the feature returned by the event\n * \n * Parameters:\n- * id - {String}\n- * type - {String} type Kind of node to draw.\n+ * evt - {Object} event object with a feature property\n * \n * Returns:\n- * {DOMElement} A new node of the given type and id.\n+ * {<OpenLayers.Feature.Vector>}\n */\n- nodeFactory: function(id, type) {\n- var node = OpenLayers.Util.getElement(id);\n- if (node) {\n- if (!this.nodeTypeCompare(node, type)) {\n- node.parentNode.removeChild(node);\n- node = this.nodeFactory(id, type);\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature;\n }\n- } else {\n- node = this.createNode(type, id);\n }\n- return node;\n },\n \n- /** \n- * Method: nodeTypeCompare\n+ /**\n+ * Method: setMap\n * \n * Parameters:\n- * node - {DOMElement}\n- * type - {String} Kind of node\n- * \n- * Returns:\n- * {Boolean} Whether or not the specified node is of the specified type\n- * This function must be overridden by subclasses.\n+ * map - {<OpenLayers.Map>}\n */\n- nodeTypeCompare: function(node, type) {},\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer);\n+ },\n \n- /** \n- * Method: createNode\n+ /**\n+ * Method: removeMap\n * \n * Parameters:\n- * type - {String} Kind of node to draw.\n- * id - {String} Id for node.\n- * \n- * Returns:\n- * {DOMElement} A new node of the given type and id.\n- * This function must be overridden by subclasses.\n+ * map - {<OpenLayers.Map>}\n */\n- createNode: function(type, id) {},\n+ removeMap: function(map) {\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n+ },\n \n /**\n- * Method: moveRoot\n- * moves this renderer's root to a different renderer.\n- * \n- * Parameters:\n- * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ * Method: collectRoots\n+ * Collects the root nodes of all layers this control is configured with\n+ * and moveswien the nodes to this control's layer\n */\n- moveRoot: function(renderer) {\n- var root = this.root;\n- if (renderer.root.parentNode == this.rendererRoot) {\n- root = renderer.root;\n+ collectRoots: function() {\n+ var layer;\n+ // walk through all map layers, because we want to keep the order\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer);\n+ }\n }\n- root.parentNode.removeChild(root);\n- renderer.rendererRoot.appendChild(root);\n },\n \n /**\n- * Method: getRenderLayerId\n- * Gets the layer that this renderer's output appears on. If moveRoot was\n- * used, this will be different from the id of the layer containing the\n- * features rendered by this renderer.\n- * \n- * Returns:\n- * {String} the id of the output layer.\n+ * Method: resetRoots\n+ * Resets the root nodes back into the layers they belong to.\n */\n- getRenderLayerId: function() {\n- return this.root.parentNode.parentNode.id;\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer);\n+ }\n+ }\n },\n \n /**\n- * Method: isComplexSymbol\n- * Determines if a symbol cannot be rendered using drawCircle\n+ * Method: handleChangeLayer\n+ * Event handler for the map's changelayer event. We need to rebuild\n+ * this container's layer dom if order of one of its layers changes.\n+ * This handler is added with the setMap method, and removed with the\n+ * removeMap method.\n * \n * Parameters:\n- * graphicName - {String}\n- * \n- * Returns\n- * {Boolean} true if the symbol is complex, false if not\n+ * evt - {Object}\n */\n- isComplexSymbol: function(graphicName) {\n- return (graphicName != \"circle\") && !!graphicName;\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" &&\n+ OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots();\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer.Elements\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n });\n-\n /* ======================================================================\n- OpenLayers/Renderer/SVG.js\n+ OpenLayers/Control/SelectFeature.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Renderer/Elements.js\n+ * @requires OpenLayers/Control.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Handler/Feature.js\n+ * @requires OpenLayers/Layer/Vector/RootContainer.js\n */\n \n /**\n- * Class: OpenLayers.Renderer.SVG\n- * \n- * Inherits:\n- * - <OpenLayers.Renderer.Elements>\n+ * Class: OpenLayers.Control.SelectFeature\n+ * The SelectFeature control selects vector features from a given layer on \n+ * click or hover. \n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Control>\n */\n-OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {\n+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n \n /** \n- * Property: xmlns\n- * {String}\n+ * APIProperty: events\n+ * {<OpenLayers.Events>} Events instance for listeners and triggering\n+ * control specific events.\n+ *\n+ * Register a listener for a particular event with the following syntax:\n+ * (code)\n+ * control.events.register(type, obj, listener);\n+ * (end)\n+ *\n+ * Supported event types (in addition to those from <OpenLayers.Control.events>):\n+ * beforefeaturehighlighted - Triggered before a feature is highlighted\n+ * featurehighlighted - Triggered when a feature is highlighted\n+ * featureunhighlighted - Triggered when a feature is unhighlighted\n+ * boxselectionstart - Triggered before box selection starts\n+ * boxselectionend - Triggered after box selection ends\n */\n- xmlns: \"http://www.w3.org/2000/svg\",\n \n /**\n- * Property: xlinkns\n- * {String}\n+ * Property: multipleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <multiple> property to true. Default is null.\n */\n- xlinkns: \"http://www.w3.org/1999/xlink\",\n+ multipleKey: null,\n \n /**\n- * Constant: MAX_PIXEL\n- * {Integer} Firefox has a limitation where values larger or smaller than \n- * about 15000 in an SVG document lock the browser up. This \n- * works around it.\n+ * Property: toggleKey\n+ * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets\n+ * the <toggle> property to true. Default is null.\n */\n- MAX_PIXEL: 15000,\n+ toggleKey: null,\n \n /**\n- * Property: translationParameters\n- * {Object} Hash with \"x\" and \"y\" properties\n+ * APIProperty: multiple\n+ * {Boolean} Allow selection of multiple geometries. Default is false.\n */\n- translationParameters: null,\n+ multiple: false,\n \n /**\n- * Property: symbolMetrics\n- * {Object} Cache for symbol metrics according to their svg coordinate\n- * space. This is an object keyed by the symbol's id, and values are\n- * an array of [width, centerX, centerY].\n+ * APIProperty: clickout\n+ * {Boolean} Unselect features when clicking outside any feature.\n+ * Default is true.\n */\n- symbolMetrics: null,\n+ clickout: true,\n \n /**\n- * Constructor: OpenLayers.Renderer.SVG\n- * \n- * Parameters:\n- * containerID - {String}\n+ * APIProperty: toggle\n+ * {Boolean} Unselect a selected feature on click. Default is false. Only\n+ * has meaning if hover is false.\n */\n- initialize: function(containerID) {\n- if (!this.supported()) {\n- return;\n- }\n- OpenLayers.Renderer.Elements.prototype.initialize.apply(this,\n- arguments);\n- this.translationParameters = {\n- x: 0,\n- y: 0\n- };\n+ toggle: false,\n \n- this.symbolMetrics = {};\n- },\n+ /**\n+ * APIProperty: hover\n+ * {Boolean} Select on mouse over and deselect on mouse out. If true, this\n+ * ignores clicks and only listens to mouse moves.\n+ */\n+ hover: false,\n \n /**\n- * APIMethod: supported\n- * \n- * Returns:\n- * {Boolean} Whether or not the browser supports the SVG renderer\n+ * APIProperty: highlightOnly\n+ * {Boolean} If true do not actually select features (that is place them in \n+ * the layer's selected features array), just highlight them. This property\n+ * has no effect if hover is false. Defaults to false.\n */\n- supported: function() {\n- var svgFeature = \"http://www.w3.org/TR/SVG11/feature#\";\n- return (document.implementation &&\n- (document.implementation.hasFeature(\"org.w3c.svg\", \"1.0\") ||\n- document.implementation.hasFeature(svgFeature + \"SVG\", \"1.1\") ||\n- document.implementation.hasFeature(svgFeature + \"BasicStructure\", \"1.1\")));\n- },\n+ highlightOnly: false,\n \n /**\n- * Method: inValidRange\n- * See #669 for more information\n- *\n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * xyOnly - {Boolean} whether or not to just check for x and y, which means\n- * to not take the current translation parameters into account if true.\n- * \n- * Returns:\n- * {Boolean} Whether or not the 'x' and 'y' coordinates are in the \n- * valid range.\n+ * APIProperty: box\n+ * {Boolean} Allow feature selection by drawing a box.\n */\n- inValidRange: function(x, y, xyOnly) {\n- var left = x + (xyOnly ? 0 : this.translationParameters.x);\n- var top = y + (xyOnly ? 0 : this.translationParameters.y);\n- return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&\n- top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);\n- },\n+ box: false,\n \n /**\n- * Method: setExtent\n- * \n+ * Property: onBeforeSelect \n+ * {Function} Optional function to be called before a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onBeforeSelect: function() {},\n+\n+ /**\n+ * APIProperty: onSelect \n+ * {Function} Optional function to be called when a feature is selected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onSelect: function() {},\n+\n+ /**\n+ * APIProperty: onUnselect\n+ * {Function} Optional function to be called when a feature is unselected.\n+ * The function should expect to be called with a feature.\n+ */\n+ onUnselect: function() {},\n+\n+ /**\n+ * Property: scope\n+ * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect\n+ * callbacks. If null the scope will be this control.\n+ */\n+ scope: null,\n+\n+ /**\n+ * APIProperty: geometryTypes\n+ * {Array(String)} To restrict selecting to a limited set of geometry types,\n+ * send a list of strings corresponding to the geometry class names.\n+ */\n+ geometryTypes: null,\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer\n+ * root for all layers this control is configured with (if an array of\n+ * layers was passed to the constructor), or the vector layer the control\n+ * was configured with (if a single layer was passed to the constructor).\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: layers\n+ * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,\n+ * or null if the control was configured with a single layer\n+ */\n+ layers: null,\n+\n+ /**\n+ * APIProperty: callbacks\n+ * {Object} The functions that are sent to the handlers.feature for callback\n+ */\n+ callbacks: null,\n+\n+ /**\n+ * APIProperty: selectStyle \n+ * {Object} Hash of styles\n+ */\n+ selectStyle: null,\n+\n+ /**\n+ * Property: renderIntent\n+ * {String} key used to retrieve the select style from the layer's\n+ * style map.\n+ */\n+ renderIntent: \"select\",\n+\n+ /**\n+ * Property: handlers\n+ * {Object} Object with references to multiple <OpenLayers.Handler>\n+ * instances.\n+ */\n+ handlers: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Control.SelectFeature\n+ * Create a new control for selecting features.\n+ *\n * Parameters:\n- * extent - {<OpenLayers.Bounds>}\n- * resolutionChanged - {Boolean}\n- * \n- * Returns:\n- * {Boolean} true to notify the layer that the new extent does not exceed\n- * the coordinate range, and the features will not need to be redrawn.\n- * False otherwise.\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The\n+ * layer(s) this control will select features from.\n+ * options - {Object} \n */\n- setExtent: function(extent, resolutionChanged) {\n- var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n+ initialize: function(layers, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n \n- var resolution = this.getResolution(),\n- left = -extent.left / resolution,\n- top = extent.top / resolution;\n+ if (this.scope === null) {\n+ this.scope = this;\n+ }\n+ this.initLayer(layers);\n+ var callbacks = {\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature\n+ };\n+ if (this.hover) {\n+ callbacks.over = this.overFeature;\n+ callbacks.out = this.outFeature;\n+ }\n \n- // If the resolution has changed, start over changing the corner, because\n- // the features will redraw.\n- if (resolutionChanged) {\n- this.left = left;\n- this.top = top;\n- // Set the viewbox\n- var extentString = \"0 0 \" + this.size.w + \" \" + this.size.h;\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlers = {\n+ feature: new OpenLayers.Handler.Feature(\n+ this, this.layer, this.callbacks, {\n+ geometryTypes: this.geometryTypes\n+ }\n+ )\n+ };\n \n- this.rendererRoot.setAttributeNS(null, \"viewBox\", extentString);\n- this.translate(this.xOffset, 0);\n- return true;\n- } else {\n- var inRange = this.translate(left - this.left + this.xOffset, top - this.top);\n- if (!inRange) {\n- // recenter the coordinate system\n- this.setExtent(extent, true);\n- }\n- return coordSysUnchanged && inRange;\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(\n+ this, {\n+ done: this.selectBox\n+ }, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ }\n+ );\n }\n },\n \n /**\n- * Method: translate\n- * Transforms the SVG coordinate system\n- * \n+ * Method: initLayer\n+ * Assign the layer property. If layers is an array, we need to use\n+ * a RootContainer.\n+ *\n * Parameters:\n- * x - {Float}\n- * y - {Float}\n+ * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.\n+ */\n+ initLayer: function(layers) {\n+ if (OpenLayers.Util.isArray(layers)) {\n+ this.layers = layers;\n+ this.layer = new OpenLayers.Layer.Vector.RootContainer(\n+ this.id + \"_container\", {\n+ layers: layers\n+ }\n+ );\n+ } else {\n+ this.layer = layers;\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ if (this.active && this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.layers) {\n+ this.layer.destroy();\n+ }\n+ },\n+\n+ /**\n+ * Method: activate\n+ * Activates the control.\n * \n * Returns:\n- * {Boolean} true if the translation parameters are in the valid coordinates\n- * range, false otherwise.\n+ * {Boolean} The control was effectively activated.\n */\n- translate: function(x, y) {\n- if (!this.inValidRange(x, y, true)) {\n- return false;\n- } else {\n- var transformString = \"\";\n- if (x || y) {\n- transformString = \"translate(\" + x + \",\" + y + \")\";\n+ activate: function() {\n+ if (!this.active) {\n+ if (this.layers) {\n+ this.map.addLayer(this.layer);\n+ }\n+ this.handlers.feature.activate();\n+ if (this.box && this.handlers.box) {\n+ this.handlers.box.activate();\n }\n- this.root.setAttributeNS(null, \"transform\", transformString);\n- this.translationParameters = {\n- x: x,\n- y: y\n- };\n- return true;\n }\n+ return OpenLayers.Control.prototype.activate.apply(\n+ this, arguments\n+ );\n },\n \n /**\n- * Method: setSize\n- * Sets the size of the drawing surface.\n+ * Method: deactivate\n+ * Deactivates the control.\n * \n- * Parameters:\n- * size - {<OpenLayers.Size>} The size of the drawing surface\n+ * Returns:\n+ * {Boolean} The control was effectively deactivated.\n */\n- setSize: function(size) {\n- OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n-\n- this.rendererRoot.setAttributeNS(null, \"width\", this.size.w);\n- this.rendererRoot.setAttributeNS(null, \"height\", this.size.h);\n+ deactivate: function() {\n+ if (this.active) {\n+ this.handlers.feature.deactivate();\n+ if (this.handlers.box) {\n+ this.handlers.box.deactivate();\n+ }\n+ if (this.layers) {\n+ this.map.removeLayer(this.layer);\n+ }\n+ }\n+ return OpenLayers.Control.prototype.deactivate.apply(\n+ this, arguments\n+ );\n },\n \n- /** \n- * Method: getNodeType \n- * \n+ /**\n+ * Method: unselectAll\n+ * Unselect all selected features. To unselect all except for a single\n+ * feature, set the options.except property to the feature.\n+ *\n * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- * style - {Object}\n- * \n- * Returns:\n- * {String} The corresponding node type for the specified geometry\n+ * options - {Object} Optional configuration object.\n */\n- getNodeType: function(geometry, style) {\n- var nodeType = null;\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- if (style.externalGraphic) {\n- nodeType = \"image\";\n- } else if (this.isComplexSymbol(style.graphicName)) {\n- nodeType = \"svg\";\n- } else {\n- nodeType = \"circle\";\n+ unselectAll: function(options) {\n+ // we'll want an option to supress notification here\n+ var layers = this.layers || [this.layer],\n+ layer, feature, l, numExcept;\n+ for (l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ numExcept = 0;\n+ //layer.selectedFeatures is null when layer is destroyed and \n+ //one of it's preremovelayer listener calls setLayer \n+ //with another layer on this control\n+ if (layer.selectedFeatures != null) {\n+ while (layer.selectedFeatures.length > numExcept) {\n+ feature = layer.selectedFeatures[numExcept];\n+ if (!options || options.except != feature) {\n+ this.unselect(feature);\n+ } else {\n+ ++numExcept;\n+ }\n }\n- break;\n- case \"OpenLayers.Geometry.Rectangle\":\n- nodeType = \"rect\";\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- nodeType = \"polyline\";\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- nodeType = \"polygon\";\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- case \"OpenLayers.Geometry.Curve\":\n- nodeType = \"path\";\n- break;\n- default:\n- break;\n+ }\n }\n- return nodeType;\n },\n \n- /** \n- * Method: setStyle\n- * Use to set all the style attributes to a SVG node.\n- * \n- * Takes care to adjust stroke width and point radius to be\n- * resolution-relative\n+ /**\n+ * Method: clickFeature\n+ * Called on click in a feature\n+ * Only responds if this.hover is false.\n *\n * Parameters:\n- * node - {SVGDomElement} An SVG element to decorate\n- * style - {Object}\n- * options - {Object} Currently supported options include \n- * 'isFilled' {Boolean} and\n- * 'isStroked' {Boolean}\n+ * feature - {<OpenLayers.Feature.Vector>} \n */\n- setStyle: function(node, style, options) {\n- style = style || node._style;\n- options = options || node._options;\n-\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- node.setAttributeNS(null, \"title\", title);\n- //Standards-conformant SVG\n- // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 \n- var titleNode = node.getElementsByTagName(\"title\");\n- if (titleNode.length > 0) {\n- titleNode[0].firstChild.textContent = title;\n+ clickFeature: function(feature) {\n+ if (!this.hover) {\n+ var selected = (OpenLayers.Util.indexOf(\n+ feature.layer.selectedFeatures, feature) > -1);\n+ if (selected) {\n+ if (this.toggleSelect()) {\n+ this.unselect(feature);\n+ } else if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n } else {\n- var label = this.nodeFactory(null, \"title\");\n- label.textContent = title;\n- node.appendChild(label);\n+ if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ });\n+ }\n+ this.select(feature);\n }\n }\n+ },\n \n- var r = parseFloat(node.getAttributeNS(null, \"r\"));\n- var widthFactor = 1;\n- var pos;\n- if (node._geometryClass == \"OpenLayers.Geometry.Point\" && r) {\n- node.style.visibility = \"\";\n- if (style.graphic === false) {\n- node.style.visibility = \"hidden\";\n- } else if (style.externalGraphic) {\n- pos = this.getPosition(node);\n- if (style.graphicWidth && style.graphicHeight) {\n- node.setAttributeNS(null, \"preserveAspectRatio\", \"none\");\n- }\n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = (style.graphicXOffset != undefined) ?\n- style.graphicXOffset : -(0.5 * width);\n- var yOffset = (style.graphicYOffset != undefined) ?\n- style.graphicYOffset : -(0.5 * height);\n-\n- var opacity = style.graphicOpacity || style.fillOpacity;\n-\n- node.setAttributeNS(null, \"x\", (pos.x + xOffset).toFixed());\n- node.setAttributeNS(null, \"y\", (pos.y + yOffset).toFixed());\n- node.setAttributeNS(null, \"width\", width);\n- node.setAttributeNS(null, \"height\", height);\n- node.setAttributeNS(this.xlinkns, \"xlink:href\", style.externalGraphic);\n- node.setAttributeNS(null, \"style\", \"opacity: \" + opacity);\n- node.onclick = OpenLayers.Event.preventDefault;\n- } else if (this.isComplexSymbol(style.graphicName)) {\n- // the symbol viewBox is three times as large as the symbol\n- var offset = style.pointRadius * 3;\n- var size = offset * 2;\n- var src = this.importSymbol(style.graphicName);\n- pos = this.getPosition(node);\n- widthFactor = this.symbolMetrics[src.id][0] * 3 / size;\n-\n- // remove the node from the dom before we modify it. This\n- // prevents various rendering issues in Safari and FF\n- var parent = node.parentNode;\n- var nextSibling = node.nextSibling;\n- if (parent) {\n- parent.removeChild(node);\n- }\n+ /**\n+ * Method: multipleSelect\n+ * Allow for multiple selected features based on <multiple> property and\n+ * <multipleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Allow for multiple selected features.\n+ */\n+ multipleSelect: function() {\n+ return this.multiple || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.multipleKey]);\n+ },\n \n- // The more appropriate way to implement this would be use/defs,\n- // but due to various issues in several browsers, it is safer to\n- // copy the symbols instead of referencing them. \n- // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 \n- // and this email thread\n- // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html\n- node.firstChild && node.removeChild(node.firstChild);\n- node.appendChild(src.firstChild.cloneNode(true));\n- node.setAttributeNS(null, \"viewBox\", src.getAttributeNS(null, \"viewBox\"));\n+ /**\n+ * Method: toggleSelect\n+ * Event should toggle the selected state of a feature based on <toggle>\n+ * property and <toggleKey> event modifier.\n+ *\n+ * Returns:\n+ * {Boolean} Toggle the selected state of a feature.\n+ */\n+ toggleSelect: function() {\n+ return this.toggle || (this.handlers.feature.evt &&\n+ this.handlers.feature.evt[this.toggleKey]);\n+ },\n \n- node.setAttributeNS(null, \"width\", size);\n- node.setAttributeNS(null, \"height\", size);\n- node.setAttributeNS(null, \"x\", pos.x - offset);\n- node.setAttributeNS(null, \"y\", pos.y - offset);\n+ /**\n+ * Method: clickoutFeature\n+ * Called on click outside a previously clicked (selected) feature.\n+ * Only responds if this.hover is false.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Vector.Feature>} \n+ */\n+ clickoutFeature: function(feature) {\n+ if (!this.hover && this.clickout) {\n+ this.unselectAll();\n+ }\n+ },\n \n- // now that the node has all its new properties, insert it\n- // back into the dom where it was\n- if (nextSibling) {\n- parent.insertBefore(node, nextSibling);\n- } else if (parent) {\n- parent.appendChild(node);\n- }\n- } else {\n- node.setAttributeNS(null, \"r\", style.pointRadius);\n+ /**\n+ * Method: overFeature\n+ * Called on over a feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ overFeature: function(feature) {\n+ var layer = feature.layer;\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ this.highlight(feature);\n+ } else if (OpenLayers.Util.indexOf(\n+ layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n }\n+ }\n+ },\n \n- var rotation = style.rotation;\n-\n- if ((rotation !== undefined || node._rotation !== undefined) && pos) {\n- node._rotation = rotation;\n- rotation |= 0;\n- if (node.nodeName !== \"svg\") {\n- node.setAttributeNS(null, \"transform\",\n- \"rotate(\" + rotation + \" \" + pos.x + \" \" +\n- pos.y + \")\");\n- } else {\n- var metrics = this.symbolMetrics[src.id];\n- node.firstChild.setAttributeNS(null, \"transform\", \"rotate(\" +\n- rotation + \" \" +\n- metrics[1] + \" \" +\n- metrics[2] + \")\");\n+ /**\n+ * Method: outFeature\n+ * Called on out of a selected feature.\n+ * Only responds if this.hover is true.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ outFeature: function(feature) {\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ // we do nothing if we're not the last highlighter of the\n+ // feature\n+ if (feature._lastHighlighter == this.id) {\n+ // if another select control had highlighted the feature before\n+ // we did it ourself then we use that control to highlight the\n+ // feature as it was before we highlighted it, else we just\n+ // unhighlight it\n+ if (feature._prevHighlighter &&\n+ feature._prevHighlighter != this.id) {\n+ delete feature._lastHighlighter;\n+ var control = this.map.getControl(\n+ feature._prevHighlighter);\n+ if (control) {\n+ control.highlight(feature);\n+ }\n+ } else {\n+ this.unhighlight(feature);\n+ }\n }\n+ } else {\n+ this.unselect(feature);\n }\n }\n+ },\n \n- if (options.isFilled) {\n- node.setAttributeNS(null, \"fill\", style.fillColor);\n- node.setAttributeNS(null, \"fill-opacity\", style.fillOpacity);\n- } else {\n- node.setAttributeNS(null, \"fill\", \"none\");\n+ /**\n+ * Method: highlight\n+ * Redraw feature with the select style.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ highlight: function(feature) {\n+ var layer = feature.layer;\n+ var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ feature._prevHighlighter = feature._lastHighlighter;\n+ feature._lastHighlighter = this.id;\n+ var style = this.selectStyle || this.renderIntent;\n+ layer.drawFeature(feature, style);\n+ this.events.triggerEvent(\"featurehighlighted\", {\n+ feature: feature\n+ });\n }\n+ },\n \n- if (options.isStroked) {\n- node.setAttributeNS(null, \"stroke\", style.strokeColor);\n- node.setAttributeNS(null, \"stroke-opacity\", style.strokeOpacity);\n- node.setAttributeNS(null, \"stroke-width\", style.strokeWidth * widthFactor);\n- node.setAttributeNS(null, \"stroke-linecap\", style.strokeLinecap || \"round\");\n- // Hard-coded linejoin for now, to make it look the same as in VML.\n- // There is no strokeLinejoin property yet for symbolizers.\n- node.setAttributeNS(null, \"stroke-linejoin\", \"round\");\n- style.strokeDashstyle && node.setAttributeNS(null,\n- \"stroke-dasharray\", this.dashStyle(style, widthFactor));\n+ /**\n+ * Method: unhighlight\n+ * Redraw feature with the \"default\" style\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ unhighlight: function(feature) {\n+ var layer = feature.layer;\n+ // three cases:\n+ // 1. there's no other highlighter, in that case _prev is undefined,\n+ // and we just need to undef _last\n+ // 2. another control highlighted the feature after we did it, in\n+ // that case _last references this other control, and we just\n+ // need to undef _prev\n+ // 3. another control highlighted the feature before we did it, in\n+ // that case _prev references this other control, and we need to\n+ // set _last to _prev and undef _prev\n+ if (feature._prevHighlighter == undefined) {\n+ delete feature._lastHighlighter;\n+ } else if (feature._prevHighlighter == this.id) {\n+ delete feature._prevHighlighter;\n } else {\n- node.setAttributeNS(null, \"stroke\", \"none\");\n- }\n-\n- if (style.pointerEvents) {\n- node.setAttributeNS(null, \"pointer-events\", style.pointerEvents);\n+ feature._lastHighlighter = feature._prevHighlighter;\n+ delete feature._prevHighlighter;\n }\n+ layer.drawFeature(feature, feature.style || feature.layer.style ||\n+ \"default\");\n+ this.events.triggerEvent(\"featureunhighlighted\", {\n+ feature: feature\n+ });\n+ },\n \n- if (style.cursor != null) {\n- node.setAttributeNS(null, \"cursor\", style.cursor);\n+ /**\n+ * Method: select\n+ * Add feature to the layer's selectedFeature array, render the feature as\n+ * selected, and call the onSelect function.\n+ * \n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ select: function(feature) {\n+ var cont = this.onBeforeSelect.call(this.scope, feature);\n+ var layer = feature.layer;\n+ if (cont !== false) {\n+ cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ layer.selectedFeatures.push(feature);\n+ this.highlight(feature);\n+ // if the feature handler isn't involved in the feature\n+ // selection (because the box handler is used or the\n+ // feature is selected programatically) we fake the\n+ // feature handler to allow unselecting on click\n+ if (!this.handlers.feature.lastFeature) {\n+ this.handlers.feature.lastFeature = layer.selectedFeatures[0];\n+ }\n+ layer.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ });\n+ this.onSelect.call(this.scope, feature);\n+ }\n }\n+ },\n \n- return node;\n+ /**\n+ * Method: unselect\n+ * Remove feature from the layer's selectedFeature array, render the feature as\n+ * normal, and call the onUnselect function.\n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ unselect: function(feature) {\n+ var layer = feature.layer;\n+ // Store feature style for restoration later\n+ this.unhighlight(feature);\n+ OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n+ layer.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n+ });\n+ this.onUnselect.call(this.scope, feature);\n },\n \n- /** \n- * Method: dashStyle\n- * \n+ /**\n+ * Method: selectBox\n+ * Callback from the handlers.box set up when <box> selection is true\n+ * on.\n+ *\n * Parameters:\n- * style - {Object}\n- * widthFactor - {Number}\n- * \n- * Returns:\n- * {String} A SVG compliant 'stroke-dasharray' value\n+ * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } \n */\n- dashStyle: function(style, widthFactor) {\n- var w = style.strokeWidth * widthFactor;\n- var str = style.strokeDashstyle;\n- switch (str) {\n- case 'solid':\n- return 'none';\n- case 'dot':\n- return [1, 4 * w].join();\n- case 'dash':\n- return [4 * w, 4 * w].join();\n- case 'dashdot':\n- return [4 * w, 4 * w, 1, 4 * w].join();\n- case 'longdash':\n- return [8 * w, 4 * w].join();\n- case 'longdashdot':\n- return [8 * w, 4 * w, 1, 4 * w].join();\n- default:\n- return OpenLayers.String.trim(str).replace(/\\s+/g, \",\");\n+ selectBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ var bounds = new OpenLayers.Bounds(\n+ minXY.lon, minXY.lat, maxXY.lon, maxXY.lat\n+ );\n+\n+ // if multiple is false, first deselect currently selected features\n+ if (!this.multipleSelect()) {\n+ this.unselectAll();\n+ }\n+\n+ // because we're using a box, we consider we want multiple selection\n+ var prevMultiple = this.multiple;\n+ this.multiple = true;\n+ var layers = this.layers || [this.layer];\n+ this.events.triggerEvent(\"boxselectionstart\", {\n+ layers: layers\n+ });\n+ var layer;\n+ for (var l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ for (var i = 0, len = layer.features.length; i < len; ++i) {\n+ var feature = layer.features[i];\n+ // check if the feature is displayed\n+ if (!feature.getVisibility()) {\n+ continue;\n+ }\n+\n+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(\n+ this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n+ if (bounds.toGeometry().intersects(feature.geometry)) {\n+ if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature);\n+ }\n+ }\n+ }\n+ }\n+ }\n+ this.multiple = prevMultiple;\n+ this.events.triggerEvent(\"boxselectionend\", {\n+ layers: layers\n+ });\n }\n },\n \n /** \n- * Method: createNode\n+ * Method: setMap\n+ * Set the map property for the control. \n * \n * Parameters:\n- * type - {String} Kind of node to draw\n- * id - {String} Id for node\n- * \n- * Returns:\n- * {DOMElement} A new node of the given type and id\n+ * map - {<OpenLayers.Map>} \n */\n- createNode: function(type, id) {\n- var node = document.createElementNS(this.xmlns, type);\n- if (id) {\n- node.setAttributeNS(null, \"id\", id);\n+ setMap: function(map) {\n+ this.handlers.feature.setMap(map);\n+ if (this.box) {\n+ this.handlers.box.setMap(map);\n }\n- return node;\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments);\n },\n \n- /** \n- * Method: nodeTypeCompare\n- * \n+ /**\n+ * APIMethod: setLayer\n+ * Attach a new layer to the control, overriding any existing layers.\n+ *\n * Parameters:\n- * node - {SVGDomElement} An SVG element\n- * type - {String} Kind of node\n- * \n- * Returns:\n- * {Boolean} Whether or not the specified node is of the specified type\n+ * layers - Array of {<OpenLayers.Layer.Vector>} or a single\n+ * {<OpenLayers.Layer.Vector>}\n */\n- nodeTypeCompare: function(node, type) {\n- return (type == node.nodeName);\n+ setLayer: function(layers) {\n+ var isActive = this.active;\n+ this.unselectAll();\n+ this.deactivate();\n+ if (this.layers) {\n+ this.layer.destroy();\n+ this.layers = null;\n+ }\n+ this.initLayer(layers);\n+ this.handlers.feature.layer = this.layer;\n+ if (isActive) {\n+ this.activate();\n+ }\n },\n \n+ CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/XYZ.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n+\n+/** \n+ * Class: OpenLayers.Layer.XYZ\n+ * The XYZ class is designed to make it easier for people who have tiles\n+ * arranged by a standard XYZ grid. \n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+\n /**\n- * Method: createRenderRoot\n- * \n- * Returns:\n- * {DOMElement} The specific render engine's root element\n+ * APIProperty: isBaseLayer\n+ * Default is true, as this is designed to be a base tile source. \n */\n- createRenderRoot: function() {\n- var svg = this.nodeFactory(this.container.id + \"_svgRoot\", \"svg\");\n- svg.style.display = \"block\";\n- return svg;\n- },\n+ isBaseLayer: true,\n \n /**\n- * Method: createRoot\n- * \n- * Parameters:\n- * suffix - {String} suffix to append to the id\n- * \n- * Returns:\n- * {DOMElement}\n+ * APIProperty: sphericalMercator\n+ * Whether the tile extents should be set to the defaults for \n+ * spherical mercator. Useful for things like OpenStreetMap.\n+ * Default is false, except for the OSM subclass.\n */\n- createRoot: function(suffix) {\n- return this.nodeFactory(this.container.id + suffix, \"g\");\n- },\n+ sphericalMercator: false,\n \n /**\n- * Method: createDefs\n- *\n- * Returns:\n- * {DOMElement} The element to which we'll add the symbol definitions\n+ * APIProperty: zoomOffset\n+ * {Number} If your cache has more zoom levels than you want to provide\n+ * access to with this layer, supply a zoomOffset. This zoom offset\n+ * is added to the current map zoom level to determine the level\n+ * for a requested tile. For example, if you supply a zoomOffset\n+ * of 3, when the map is at the zoom 0, tiles will be requested from\n+ * level 3 of your cache. Default is 0 (assumes cache level and map\n+ * zoom are equivalent). Using <zoomOffset> is an alternative to\n+ * setting <serverResolutions> if you only want to expose a subset\n+ * of the server resolutions.\n */\n- createDefs: function() {\n- var defs = this.nodeFactory(this.container.id + \"_defs\", \"defs\");\n- this.rendererRoot.appendChild(defs);\n- return defs;\n- },\n+ zoomOffset: 0,\n \n- /**************************************\n- * *\n- * GEOMETRY DRAWING FUNCTIONS *\n- * *\n- **************************************/\n+ /**\n+ * APIProperty: serverResolutions\n+ * {Array} A list of all resolutions available on the server. Only set this\n+ * property if the map resolutions differ from the server. This\n+ * property serves two purposes. (a) <serverResolutions> can include\n+ * resolutions that the server supports and that you don't want to\n+ * provide with this layer; you can also look at <zoomOffset>, which is\n+ * an alternative to <serverResolutions> for that specific purpose.\n+ * (b) The map can work with resolutions that aren't supported by\n+ * the server, i.e. that aren't in <serverResolutions>. When the\n+ * map is displayed in such a resolution data for the closest\n+ * server-supported resolution is loaded and the layer div is\n+ * stretched as necessary.\n+ */\n+ serverResolutions: null,\n \n /**\n- * Method: drawPoint\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement} or false if the renderer could not draw the point\n+ * Constructor: OpenLayers.Layer.XYZ\n+ *\n+ * Parameters:\n+ * name - {String}\n+ * url - {String}\n+ * options - {Object} Hashtable of extra options to tag onto the layer\n */\n- drawPoint: function(node, geometry) {\n- return this.drawCircle(node, geometry, 1);\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options);\n+ }\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n+ name || this.name, url || this.url, {},\n+ options\n+ ]);\n },\n \n /**\n- * Method: drawCircle\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * radius - {Float}\n+ * APIMethod: clone\n+ * Create a clone of this layer\n+ *\n+ * Parameters:\n+ * obj - {Object} Is this ever used?\n * \n * Returns:\n- * {DOMElement} or false if the renderer could not draw the circle\n+ * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n */\n- drawCircle: function(node, geometry, radius) {\n- var resolution = this.getResolution();\n- var x = ((geometry.x - this.featureDx) / resolution + this.left);\n- var y = (this.top - geometry.y / resolution);\n+ clone: function(obj) {\n \n- if (this.inValidRange(x, y)) {\n- node.setAttributeNS(null, \"cx\", x);\n- node.setAttributeNS(null, \"cy\", y);\n- node.setAttributeNS(null, \"r\", radius);\n- return node;\n- } else {\n- return false;\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name,\n+ this.url,\n+ this.getOptions());\n }\n \n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ return obj;\n },\n \n /**\n- * Method: drawLineString\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * Method: getURL\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n * Returns:\n- * {DOMElement} or null if the renderer could not draw all components of\n- * the linestring, or false if nothing could be drawn\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as\n+ * parameters\n */\n- drawLineString: function(node, geometry) {\n- var componentsResult = this.getComponentsString(geometry.components);\n- if (componentsResult.path) {\n- node.setAttributeNS(null, \"points\", componentsResult.path);\n- return (componentsResult.complete ? node : null);\n- } else {\n- return false;\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = '' + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url);\n }\n+\n+ return OpenLayers.String.format(url, xyz);\n },\n \n /**\n- * Method: drawLinearRing\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n+ * Method: getXYZ\n+ * Calculates x, y and z for the given bounds.\n+ *\n+ * Parameters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ *\n * Returns:\n- * {DOMElement} or null if the renderer could not draw all components\n- * of the linear ring, or false if nothing could be drawn\n+ * {Object} - an object with x, y and z properties.\n */\n- drawLinearRing: function(node, geometry) {\n- var componentsResult = this.getComponentsString(geometry.components);\n- if (componentsResult.path) {\n- node.setAttributeNS(null, \"points\", componentsResult.path);\n- return (componentsResult.complete ? node : null);\n- } else {\n- return false;\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) /\n+ (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) /\n+ (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = ((x % limit) + limit) % limit;\n }\n+\n+ return {\n+ 'x': x,\n+ 'y': y,\n+ 'z': z\n+ };\n },\n \n- /**\n- * Method: drawPolygon\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n+ /* APIMethod: setMap\n+ * When the layer is added to a map, then we can fetch our origin \n+ * (if we don't have one.) \n * \n- * Returns:\n- * {DOMElement} or null if the renderer could not draw all components\n- * of the polygon, or false if nothing could be drawn\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- drawPolygon: function(node, geometry) {\n- var d = \"\";\n- var draw = true;\n- var complete = true;\n- var linearRingResult, path;\n- for (var j = 0, len = geometry.components.length; j < len; j++) {\n- d += \" M\";\n- linearRingResult = this.getComponentsString(\n- geometry.components[j].components, \" \");\n- path = linearRingResult.path;\n- if (path) {\n- d += \" \" + path;\n- complete = linearRingResult.complete && complete;\n- } else {\n- draw = false;\n- }\n- }\n- d += \" z\";\n- if (draw) {\n- node.setAttributeNS(null, \"d\", d);\n- node.setAttributeNS(null, \"fill-rule\", \"evenodd\");\n- return complete ? node : null;\n- } else {\n- return false;\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n+ this.maxExtent.bottom);\n }\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/OSM.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/XYZ.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Layer.OSM\n+ * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n+ * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n+ * a different layer instead, you need to provide a different\n+ * URL to the constructor. Here's an example for using OpenCycleMap:\n+ * \n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Layer.XYZ>\n+ */\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+\n /**\n- * Method: drawRectangle\n- * This method is only called by the renderer itself.\n- * \n- * Parameters: \n- * node - {DOMElement}\n- * geometry - {<OpenLayers.Geometry>}\n- * \n- * Returns:\n- * {DOMElement} or false if the renderer could not draw the rectangle\n+ * APIProperty: name\n+ * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n+ * argument to the constructor is null or undefined.\n */\n- drawRectangle: function(node, geometry) {\n- var resolution = this.getResolution();\n- var x = ((geometry.x - this.featureDx) / resolution + this.left);\n- var y = (this.top - geometry.y / resolution);\n+ name: \"OpenStreetMap\",\n \n- if (this.inValidRange(x, y)) {\n- node.setAttributeNS(null, \"x\", x);\n- node.setAttributeNS(null, \"y\", y);\n- node.setAttributeNS(null, \"width\", geometry.width / resolution);\n- node.setAttributeNS(null, \"height\", geometry.height / resolution);\n- return node;\n- } else {\n- return false;\n- }\n- },\n+ /**\n+ * APIProperty: url\n+ * {String} The tileset URL scheme. Defaults to\n+ * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n+ * (the official OSM tileset) if the second argument to the constructor\n+ * is null or undefined. To use another tileset you can have something\n+ * like this:\n+ * (code)\n+ * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n+ * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n+ * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n+ * (end)\n+ */\n+ url: [\n+ 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n+ 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n+ ],\n \n /**\n- * Method: drawText\n- * This method is only called by the renderer itself.\n+ * Property: attribution\n+ * {String} The layer attribution.\n+ */\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+\n+ /**\n+ * Property: sphericalMercator\n+ * {Boolean}\n+ */\n+ sphericalMercator: true,\n+\n+ /**\n+ * Property: wrapDateLine\n+ * {Boolean}\n+ */\n+ wrapDateLine: true,\n+\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n+ *\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n+ *\n+ * When using OSM tilesets other than the default ones, it may be\n+ * necessary to set this to\n+ *\n+ * (code)\n+ * {crossOriginKeyword: null}\n+ * (end)\n+ *\n+ * if the server does not send Access-Control-Allow-Origin headers.\n+ */\n+ tileOptions: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.OSM\n *\n * Parameters:\n- * featureId - {String}\n- * style -\n- * location - {<OpenLayers.Geometry.Point>}\n+ * name - {String} The layer name.\n+ * url - {String} The tileset URL scheme.\n+ * options - {Object} Configuration options for the layer. Any inherited\n+ * layer option can be set in this object (e.g.\n+ * <OpenLayers.Layer.Grid.buffer>).\n */\n- drawText: function(featureId, style, location) {\n- var drawOutline = (!!style.labelOutlineWidth);\n- // First draw text in halo color and size and overlay the\n- // normal text afterwards\n- if (drawOutline) {\n- var outlineStyle = OpenLayers.Util.extend({}, style);\n- outlineStyle.fontColor = outlineStyle.labelOutlineColor;\n- outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;\n- outlineStyle.fontStrokeWidth = style.labelOutlineWidth;\n- if (style.labelOutlineOpacity) {\n- outlineStyle.fontOpacity = style.labelOutlineOpacity;\n- }\n- delete outlineStyle.labelOutlineWidth;\n- this.drawText(featureId, outlineStyle, location);\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options && this.options.tileOptions);\n+ },\n+\n+ /**\n+ * Method: clone\n+ */\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(\n+ this.name, this.url, this.getOptions());\n }\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj;\n+ },\n \n- var resolution = this.getResolution();\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/WMS.js\n+ ====================================================================== */\n \n- var x = ((location.x - this.featureDx) / resolution + this.left);\n- var y = (location.y / resolution - this.top);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX;\n- var label = this.nodeFactory(featureId + suffix, \"text\");\n \n- label.setAttributeNS(null, \"x\", x);\n- label.setAttributeNS(null, \"y\", -y);\n+/**\n+ * @requires OpenLayers/Layer/Grid.js\n+ */\n \n- if (style.fontColor) {\n- label.setAttributeNS(null, \"fill\", style.fontColor);\n- }\n- if (style.fontStrokeColor) {\n- label.setAttributeNS(null, \"stroke\", style.fontStrokeColor);\n- }\n- if (style.fontStrokeWidth) {\n- label.setAttributeNS(null, \"stroke-width\", style.fontStrokeWidth);\n- }\n- if (style.fontOpacity) {\n- label.setAttributeNS(null, \"opacity\", style.fontOpacity);\n- }\n- if (style.fontFamily) {\n- label.setAttributeNS(null, \"font-family\", style.fontFamily);\n- }\n- if (style.fontSize) {\n- label.setAttributeNS(null, \"font-size\", style.fontSize);\n- }\n- if (style.fontWeight) {\n- label.setAttributeNS(null, \"font-weight\", style.fontWeight);\n- }\n- if (style.fontStyle) {\n- label.setAttributeNS(null, \"font-style\", style.fontStyle);\n- }\n- if (style.labelSelect === true) {\n- label.setAttributeNS(null, \"pointer-events\", \"visible\");\n- label._featureId = featureId;\n- } else {\n- label.setAttributeNS(null, \"pointer-events\", \"none\");\n- }\n- var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;\n- label.setAttributeNS(null, \"text-anchor\",\n- OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || \"middle\");\n+/**\n+ * Class: OpenLayers.Layer.WMS\n+ * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web\n+ * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS>\n+ * constructor.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.Grid>\n+ */\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n \n- if (OpenLayers.IS_GECKO === true) {\n- label.setAttributeNS(null, \"dominant-baseline\",\n- OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || \"central\");\n- }\n+ /**\n+ * Constant: DEFAULT_PARAMS\n+ * {Object} Hashtable of default parameter key/value pairs \n+ */\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n+ },\n \n- var labelRows = style.label.split('\\n');\n- var numRows = labelRows.length;\n- while (label.childNodes.length > numRows) {\n- label.removeChild(label.lastChild);\n+ /**\n+ * APIProperty: isBaseLayer\n+ * {Boolean} Default is true for WMS layer\n+ */\n+ isBaseLayer: true,\n+\n+ /**\n+ * APIProperty: encodeBBOX\n+ * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', \n+ * but some services want it that way. Default false.\n+ */\n+ encodeBBOX: false,\n+\n+ /** \n+ * APIProperty: noMagic \n+ * {Boolean} If true, the image format will not be automagicaly switched \n+ * from image/jpeg to image/png or image/gif when using \n+ * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the \n+ * constructor. Default false. \n+ */\n+ noMagic: false,\n+\n+ /**\n+ * Property: yx\n+ * {Object} Keys in this object are EPSG codes for which the axis order\n+ * is to be reversed (yx instead of xy, LatLon instead of LonLat), with\n+ * true as value. This is only relevant for WMS versions >= 1.3.0, and\n+ * only if yx is not set in <OpenLayers.Projection.defaults> for the\n+ * used projection.\n+ */\n+ yx: {},\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.WMS\n+ * Create a new WMS layer object\n+ *\n+ * Examples:\n+ *\n+ * The code below creates a simple WMS layer using the image/jpeg format.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {layers: \"modis,global_mosaic\"});\n+ * (end)\n+ * Note the 3rd argument (params). Properties added to this object will be\n+ * added to the WMS GetMap requests used for this layer's tiles. The only\n+ * mandatory parameter is \"layers\". Other common WMS params include\n+ * \"transparent\", \"styles\" and \"format\". Note that the \"srs\" param will\n+ * always be ignored. Instead, it will be derived from the baseLayer's or\n+ * map's projection.\n+ *\n+ * The code below creates a transparent WMS layer with additional options.\n+ * (code)\n+ * var wms = new OpenLayers.Layer.WMS(\"NASA Global Mosaic\",\n+ * \"http://wms.jpl.nasa.gov/wms.cgi\", \n+ * {\n+ * layers: \"modis,global_mosaic\",\n+ * transparent: true\n+ * }, {\n+ * opacity: 0.5,\n+ * singleTile: true\n+ * });\n+ * (end)\n+ * Note that by default, a WMS layer is configured as baseLayer. Setting\n+ * the \"transparent\" param to true will apply some magic (see <noMagic>).\n+ * The default image format changes from image/jpeg to image/png, and the\n+ * layer is not configured as baseLayer.\n+ *\n+ * Parameters:\n+ * name - {String} A name for the layer\n+ * url - {String} Base url for the WMS\n+ * (e.g. http://wms.jpl.nasa.gov/wms.cgi)\n+ * params - {Object} An object with key/value pairs representing the\n+ * GetMap query string parameters and parameter values.\n+ * options - {Object} Hashtable of extra options to tag onto the layer.\n+ * These options include all properties listed above, plus the ones\n+ * inherited from superclasses.\n+ */\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ //uppercase params\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\";\n }\n- for (var i = 0; i < numRows; i++) {\n- var tspan = this.nodeFactory(featureId + suffix + \"_tspan_\" + i, \"tspan\");\n- if (style.labelSelect === true) {\n- tspan._featureId = featureId;\n- tspan._geometry = location;\n- tspan._geometryClass = location.CLASS_NAME;\n- }\n- if (OpenLayers.IS_GECKO === false) {\n- tspan.setAttributeNS(null, \"baseline-shift\",\n- OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || \"-35%\");\n- }\n- tspan.setAttribute(\"x\", x);\n- if (i == 0) {\n- var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];\n- if (vfactor == null) {\n- vfactor = -.5;\n- }\n- tspan.setAttribute(\"dy\", (vfactor * (numRows - 1)) + \"em\");\n- } else {\n- tspan.setAttribute(\"dy\", \"1em\");\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(\n+ this.params,\n+ OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n+ );\n+\n+\n+ //layer is transparent \n+ if (!this.noMagic && this.params.TRANSPARENT &&\n+ this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+\n+ // unless explicitly set in options, make layer an overlay\n+ if ((options == null) || (!options.isBaseLayer)) {\n+ this.isBaseLayer = false;\n }\n- tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];\n- if (!tspan.parentNode) {\n- label.appendChild(tspan);\n+\n+ // jpegs can never be transparent, so intelligently switch the \n+ // format, depending on the browser's capabilities\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" :\n+ \"image/png\";\n }\n }\n \n- if (!label.parentNode) {\n- this.textRoot.appendChild(label);\n+ },\n+\n+ /**\n+ * Method: clone\n+ * Create a clone of this layer\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} An exact clone of this layer\n+ */\n+ clone: function(obj) {\n+\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name,\n+ this.url,\n+ this.params,\n+ this.getOptions());\n }\n+\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+\n+ // copy/set any non-init, non-simple values here\n+\n+ return obj;\n },\n \n- /** \n- * Method: getComponentString\n+ /**\n+ * APIMethod: reverseAxisOrder\n+ * Returns true if the axis order is reversed for the WMS version and\n+ * projection of the layer.\n * \n+ * Returns:\n+ * {Boolean} true if the axis order is reversed, false otherwise.\n+ */\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 &&\n+ !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] &&\n+ OpenLayers.Projection.defaults[projCode].yx));\n+ },\n+\n+ /**\n+ * Method: getURL\n+ * Return a GetMap query string for this layer\n+ *\n * Parameters:\n- * components - {Array(<OpenLayers.Geometry.Point>)} Array of points\n- * separator - {String} character between coordinate pairs. Defaults to \",\"\n- * \n+ * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n+ * request.\n+ *\n * Returns:\n- * {Object} hash with properties \"path\" (the string created from the\n- * components and \"complete\" (false if the renderer was unable to\n- * draw all components)\n+ * {String} A string with the layer's url and parameters and also the\n+ * passed-in bounds and appropriate tile size specified as \n+ * parameters.\n */\n- getComponentsString: function(components, separator) {\n- var renderCmp = [];\n- var complete = true;\n- var len = components.length;\n- var strings = [];\n- var str, component;\n- for (var i = 0; i < len; i++) {\n- component = components[i];\n- renderCmp.push(component);\n- str = this.getShortString(component);\n- if (str) {\n- strings.push(str);\n- } else {\n- // The current component is outside the valid range. Let's\n- // see if the previous or next component is inside the range.\n- // If so, add the coordinate of the intersection with the\n- // valid range bounds.\n- if (i > 0) {\n- if (this.getShortString(components[i - 1])) {\n- strings.push(this.clipLine(components[i],\n- components[i - 1]));\n- }\n- }\n- if (i < len - 1) {\n- if (this.getShortString(components[i + 1])) {\n- strings.push(this.clipLine(components[i],\n- components[i + 1]));\n- }\n- }\n- complete = false;\n- }\n- }\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n \n- return {\n- path: strings.join(separator || \",\"),\n- complete: complete\n- };\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ // WMS 1.3 introduced axis order\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ?\n+ bounds.toBBOX(null, reverseAxisOrder) :\n+ bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString;\n },\n \n /**\n- * Method: clipLine\n- * Given two points (one inside the valid range, and one outside),\n- * clips the line betweeen the two points so that the new points are both\n- * inside the valid range.\n+ * APIMethod: mergeNewParams\n+ * Catch changeParams and uppercase the new params to be merged in\n+ * before calling changeParams on the super class.\n+ * \n+ * Once params have been changed, the tiles will be reloaded with\n+ * the new parameters.\n * \n * Parameters:\n- * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the\n- * invalid point\n- * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the\n- * valid point\n- * Returns\n- * {String} the SVG coordinate pair of the clipped point (like\n- * getShortString), or an empty string if both passed componets are at\n- * the same point.\n+ * newParams - {Object} Hashtable of new params to use\n */\n- clipLine: function(badComponent, goodComponent) {\n- if (goodComponent.equals(badComponent)) {\n- return \"\";\n- }\n- var resolution = this.getResolution();\n- var maxX = this.MAX_PIXEL - this.translationParameters.x;\n- var maxY = this.MAX_PIXEL - this.translationParameters.y;\n- var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;\n- var y1 = this.top - goodComponent.y / resolution;\n- var x2 = (badComponent.x - this.featureDx) / resolution + this.left;\n- var y2 = this.top - badComponent.y / resolution;\n- var k;\n- if (x2 < -maxX || x2 > maxX) {\n- k = (y2 - y1) / (x2 - x1);\n- x2 = x2 < 0 ? -maxX : maxX;\n- y2 = y1 + (x2 - x1) * k;\n- }\n- if (y2 < -maxY || y2 > maxY) {\n- k = (x2 - x1) / (y2 - y1);\n- y2 = y2 < 0 ? -maxY : maxY;\n- x2 = x1 + (y2 - y1) * k;\n- }\n- return x2 + \",\" + y2;\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n+ newArguments);\n },\n \n /** \n- * Method: getShortString\n- * \n+ * APIMethod: getFullRequestString\n+ * Combine the layer's url with its params and these newParams. \n+ * \n+ * Add the SRS parameter from projection -- this is probably\n+ * more eloquently done via a setProjection() method, but this \n+ * works for now and always.\n+ *\n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n+ * newParams - {Object}\n+ * altUrl - {String} Use this as the url instead of the layer's url\n * \n * Returns:\n- * {String} or false if point is outside the valid range\n+ * {String} \n */\n- getShortString: function(point) {\n- var resolution = this.getResolution();\n- var x = ((point.x - this.featureDx) / resolution + this.left);\n- var y = (this.top - point.y / resolution);\n-\n- if (this.inValidRange(x, y)) {\n- return x + \",\" + y;\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ?\n+ this.projection.getCode() :\n+ mapProjection.getCode();\n+ var value = (projectionCode == \"none\") ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value;\n } else {\n- return false;\n+ this.params.SRS = value;\n+ }\n+\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\";\n }\n+\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(\n+ this, arguments);\n },\n \n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Layer/Bing.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Layer/XYZ.js\n+ */\n+\n+/** \n+ * Class: OpenLayers.Layer.Bing\n+ * Bing layer using direct tile access as provided by Bing Maps REST Services.\n+ * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n+ * information. Note: Terms of Service compliant use requires the map to be\n+ * configured with an <OpenLayers.Control.Attribution> control and the\n+ * attribution placed on or near the map.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Layer.XYZ>\n+ */\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+\n /**\n- * Method: getPosition\n- * Finds the position of an svg node.\n- * \n+ * Property: key\n+ * {String} API key for Bing maps, get your own key \n+ * at http://bingmapsportal.com/ .\n+ */\n+ key: null,\n+\n+ /**\n+ * Property: serverResolutions\n+ * {Array} the resolutions provided by the Bing servers.\n+ */\n+ serverResolutions: [\n+ 156543.03390625, 78271.516953125, 39135.7584765625,\n+ 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n+ 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n+ 305.74811309814453, 152.87405654907226, 76.43702827453613,\n+ 38.218514137268066, 19.109257068634033, 9.554628534317017,\n+ 4.777314267158508, 2.388657133579254, 1.194328566789627,\n+ 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n+ 0.07464553542435169\n+ ],\n+\n+ /**\n+ * Property: attributionTemplate\n+ * {String}\n+ */\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n+ '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n+ '<img src=\"${logo}\" /></a></div>${copyrights}' +\n+ '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n+ 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n+ 'Terms of Use</a></span>',\n+\n+ /**\n+ * Property: metadata\n+ * {Object} Metadata for this layer, as returned by the callback script\n+ */\n+ metadata: null,\n+\n+ /**\n+ * Property: protocolRegex\n+ * {RegExp} Regular expression to match and replace http: in bing urls\n+ */\n+ protocolRegex: /^http:/i,\n+\n+ /**\n+ * APIProperty: type\n+ * {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used. Default is \"Road\".\n+ */\n+ type: \"Road\",\n+\n+ /**\n+ * APIProperty: culture\n+ * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n+ * for the definition and the possible values. Default is \"en-US\".\n+ */\n+ culture: \"en-US\",\n+\n+ /**\n+ * APIProperty: metadataParams\n+ * {Object} Optional url parameters for the Get Imagery Metadata request\n+ * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n+ */\n+ metadataParams: null,\n+\n+ /** APIProperty: tileOptions\n+ * {Object} optional configuration options for <OpenLayers.Tile> instances\n+ * created by this Layer. Default is\n+ *\n+ * (code)\n+ * {crossOriginKeyword: 'anonymous'}\n+ * (end)\n+ */\n+ tileOptions: null,\n+\n+ /** APIProperty: protocol\n+ * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n+ * Can be 'http:' 'https:' or ''\n+ *\n+ * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n+ * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n+ * this is undocumented and the Imagery Metadata API always returns HTTP\n+ * urls.\n+ *\n+ * Default is '', unless when executed from a file:/// uri, in which case\n+ * it is 'http:'.\n+ */\n+ protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n+\n+ /**\n+ * Constructor: OpenLayers.Layer.Bing\n+ * Create a new Bing layer.\n+ *\n+ * Example:\n+ * (code)\n+ * var road = new OpenLayers.Layer.Bing({\n+ * name: \"My Bing Aerial Layer\",\n+ * type: \"Aerial\",\n+ * key: \"my-api-key-here\",\n+ * });\n+ * (end)\n+ *\n * Parameters:\n- * node - {DOMElement}\n- * \n- * Returns:\n- * {Object} hash with x and y properties, representing the coordinates\n- * within the svg coordinate system\n+ * options - {Object} Configuration properties for the layer.\n+ *\n+ * Required configuration properties:\n+ * key - {String} Bing Maps API key for your application. Get one at\n+ * http://bingmapsportal.com/.\n+ * type - {String} The layer identifier. Any non-birdseye imageryType\n+ * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n+ * used.\n+ *\n+ * Any other documented layer properties can be provided in the config object.\n */\n- getPosition: function(node) {\n- return ({\n- x: parseFloat(node.getAttributeNS(null, \"cx\")),\n- y: parseFloat(node.getAttributeNS(null, \"cy\"))\n- });\n+ initialize: function(options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: 'anonymous'\n+ }, this.options.tileOptions);\n+ this.loadMetadata();\n },\n \n /**\n- * Method: importSymbol\n- * add a new symbol definition from the rendererer's symbol hash\n- * \n- * Parameters:\n- * graphicName - {String} name of the symbol to import\n- * \n- * Returns:\n- * {DOMElement} - the imported symbol\n+ * Method: loadMetadata\n */\n- importSymbol: function(graphicName) {\n- if (!this.defs) {\n- // create svg defs tag\n- this.defs = this.createDefs();\n- }\n- var id = this.container.id + \"-\" + graphicName;\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ // link the processMetadata method to the global scope and bind it\n+ // to this instance\n+ window[this._callbackId] = OpenLayers.Function.bind(\n+ OpenLayers.Layer.Bing.processMetadata, this\n+ );\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n+ this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script);\n+ },\n \n- // check if symbol already exists in the defs\n- var existing = document.getElementById(id);\n- if (existing != null) {\n- return existing;\n+ /**\n+ * Method: initLayer\n+ *\n+ * Sets layer properties according to the metadata provided by the API\n+ */\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n }\n-\n- var symbol = OpenLayers.Renderer.symbol[graphicName];\n- if (!symbol) {\n- throw new Error(graphicName + ' is not a valid symbol name');\n+ this.addOptions({\n+ maxResolution: Math.min(\n+ this.serverResolutions[res.zoomMin],\n+ this.maxResolution || Number.POSITIVE_INFINITY\n+ ),\n+ numZoomLevels: Math.min(\n+ res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n+ )\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw();\n }\n+ this.updateAttribution();\n+ },\n \n- var symbolNode = this.nodeFactory(id, \"symbol\");\n- var node = this.nodeFactory(null, \"polygon\");\n- symbolNode.appendChild(node);\n- var symbolExtent = new OpenLayers.Bounds(\n- Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);\n-\n- var points = [];\n- var x, y;\n- for (var i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- symbolExtent.left = Math.min(symbolExtent.left, x);\n- symbolExtent.bottom = Math.min(symbolExtent.bottom, y);\n- symbolExtent.right = Math.max(symbolExtent.right, x);\n- symbolExtent.top = Math.max(symbolExtent.top, y);\n- points.push(x, \",\", y);\n+ /**\n+ * Method: getURL\n+ *\n+ * Paramters:\n+ * bounds - {<OpenLayers.Bounds>}\n+ */\n+ getURL: function(bounds) {\n+ if (!this.url) {\n+ return;\n+ }\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = '0';\n+ var mask = 1 << (i - 1);\n+ if ((x & mask) != 0) {\n+ digit++;\n+ }\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++;\n+ }\n+ quadDigits.push(digit);\n }\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl('' + x + y + z, this.url);\n \n- node.setAttributeNS(null, \"points\", points.join(\" \"));\n+ return OpenLayers.String.format(url, {\n+ 'quadkey': quadKey\n+ });\n+ },\n \n- var width = symbolExtent.getWidth();\n- var height = symbolExtent.getHeight();\n- // create a viewBox three times as large as the symbol itself,\n- // to allow for strokeWidth being displayed correctly at the corners.\n- var viewBox = [symbolExtent.left - width,\n- symbolExtent.bottom - height, width * 3, height * 3\n- ];\n- symbolNode.setAttributeNS(null, \"viewBox\", viewBox.join(\" \"));\n- this.symbolMetrics[id] = [\n- Math.max(width, height),\n- symbolExtent.getCenterLonLat().lon,\n- symbolExtent.getCenterLonLat().lat\n- ];\n+ /**\n+ * Method: updateAttribution\n+ * Updates the attribution according to the requirements outlined in\n+ * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n+ */\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ return;\n+ }\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(\n+ this.map.getProjectionObject(),\n+ new OpenLayers.Projection(\"EPSG:4326\")\n+ );\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n+ this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ // axis order provided is Y,X\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) &&\n+ zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \";\n+ }\n+ }\n+ }\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ });\n+ },\n \n- this.defs.appendChild(symbolNode);\n- return symbolNode;\n+ /**\n+ * Method: setMap\n+ */\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution);\n },\n \n /**\n- * Method: getFeatureIdFromEvent\n+ * APIMethod: clone\n * \n * Parameters:\n- * evt - {Object} An <OpenLayers.Event> object\n- *\n+ * obj - {Object}\n+ * \n * Returns:\n- * {String} A feature id or undefined.\n+ * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n */\n- getFeatureIdFromEvent: function(evt) {\n- var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);\n- if (!featureId) {\n- var target = evt.target;\n- featureId = target.parentNode && target != this.rendererRoot ?\n- target.parentNode._featureId : undefined;\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options);\n }\n- return featureId;\n+ //get all additions from superclasses\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ // copy/set any non-init, non-simple values here\n+ return obj;\n },\n \n- CLASS_NAME: \"OpenLayers.Renderer.SVG\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN\n- * {Object}\n- */\n-OpenLayers.Renderer.SVG.LABEL_ALIGN = {\n- \"l\": \"start\",\n- \"r\": \"end\",\n- \"b\": \"bottom\",\n- \"t\": \"hanging\"\n-};\n-\n-/**\n- * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT\n- * {Object}\n- */\n-OpenLayers.Renderer.SVG.LABEL_VSHIFT = {\n- // according to\n- // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html\n- // a baseline-shift of -70% shifts the text exactly from the\n- // bottom to the top of the baseline, so -35% moves the text to\n- // the center of the baseline.\n- \"t\": \"-70%\",\n- \"b\": \"0\"\n-};\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ this.map &&\n+ this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n+ },\n \n-/**\n- * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR\n- * {Object}\n- */\n-OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n- \"t\": 0,\n- \"b\": -1\n-};\n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n+});\n \n /**\n- * Function: OpenLayers.Renderer.SVG.preventDefault\n- * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.\n- * Used to prevent default events (especially opening images in a new tab on\n- * ctrl-click) from being executed for externalGraphic symbols\n+ * Function: OpenLayers.Layer.Bing.processMetadata\n+ * This function will be bound to an instance, linked to the global scope with\n+ * an id, and called by the JSONP script returned by the API.\n+ *\n+ * Parameters:\n+ * metadata - {Object} metadata as returned by the API\n */\n-OpenLayers.Renderer.SVG.preventDefault = function(e) {\n- OpenLayers.Event.preventDefault(e);\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined; // cannot delete from window in IE\n+ delete this._callbackId;\n };\n /* ======================================================================\n OpenLayers/Protocol.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -36759,104 +33478,14 @@\n \n CLASS_NAME: \"OpenLayers.Protocol.Response\"\n });\n \n OpenLayers.Protocol.Response.SUCCESS = 1;\n OpenLayers.Protocol.Response.FAILURE = 0;\n /* ======================================================================\n- OpenLayers/Protocol/WFS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Protocol.js\n- */\n-\n-/**\n- * Class: OpenLayers.Protocol.WFS\n- * Used to create a versioned WFS protocol. Default version is 1.0.0.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol>} A WFS protocol of the given version.\n- *\n- * Example:\n- * (code)\n- * var protocol = new OpenLayers.Protocol.WFS({\n- * version: \"1.1.0\",\n- * url: \"http://demo.opengeo.org/geoserver/wfs\",\n- * featureType: \"tasmania_roads\",\n- * featureNS: \"http://www.openplans.org/topp\",\n- * geometryName: \"the_geom\"\n- * });\n- * (end)\n- *\n- * See the protocols for specific WFS versions for more detail.\n- */\n-OpenLayers.Protocol.WFS = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Protocol.WFS.DEFAULTS\n- );\n- var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported WFS version: \" + options.version;\n- }\n- return new cls(options);\n-};\n-\n-/**\n- * Function: fromWMSLayer\n- * Convenience function to create a WFS protocol from a WMS layer. This makes\n- * the assumption that a WFS requests can be issued at the same URL as\n- * WMS requests and that a WFS featureType exists with the same name as the\n- * WMS layer.\n- * \n- * This function is designed to auto-configure <url>, <featureType>,\n- * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that\n- * srsName matching with the WMS layer will not work with WFS 1.0.0.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS\n- * FeatureType at the same server url with the same typename.\n- * options - {Object} Default properties to be set on the protocol.\n- *\n- * Returns:\n- * {<OpenLayers.Protocol.WFS>}\n- */\n-OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n- var typeName, featurePrefix;\n- var param = layer.params[\"LAYERS\"];\n- var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n- if (parts.length > 1) {\n- featurePrefix = parts[0];\n- }\n- typeName = parts.pop();\n- var protocolOptions = {\n- url: layer.url,\n- featureType: typeName,\n- featurePrefix: featurePrefix,\n- srsName: layer.projection && layer.projection.getCode() ||\n- layer.map && layer.map.getProjectionObject().getCode(),\n- version: \"1.1.0\"\n- };\n- return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(\n- options, protocolOptions\n- ));\n-};\n-\n-/**\n- * Constant: OpenLayers.Protocol.WFS.DEFAULTS\n- */\n-OpenLayers.Protocol.WFS.DEFAULTS = {\n- \"version\": \"1.0.0\"\n-};\n-/* ======================================================================\n OpenLayers/Protocol/HTTP.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -37437,14 +34066,104 @@\n opt.callback.call(opt.scope, resp);\n }\n },\n \n CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n });\n /* ======================================================================\n+ OpenLayers/Protocol/WFS.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Protocol.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Protocol.WFS\n+ * Used to create a versioned WFS protocol. Default version is 1.0.0.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol>} A WFS protocol of the given version.\n+ *\n+ * Example:\n+ * (code)\n+ * var protocol = new OpenLayers.Protocol.WFS({\n+ * version: \"1.1.0\",\n+ * url: \"http://demo.opengeo.org/geoserver/wfs\",\n+ * featureType: \"tasmania_roads\",\n+ * featureNS: \"http://www.openplans.org/topp\",\n+ * geometryName: \"the_geom\"\n+ * });\n+ * (end)\n+ *\n+ * See the protocols for specific WFS versions for more detail.\n+ */\n+OpenLayers.Protocol.WFS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Protocol.WFS.DEFAULTS\n+ );\n+ var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported WFS version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n+\n+/**\n+ * Function: fromWMSLayer\n+ * Convenience function to create a WFS protocol from a WMS layer. This makes\n+ * the assumption that a WFS requests can be issued at the same URL as\n+ * WMS requests and that a WFS featureType exists with the same name as the\n+ * WMS layer.\n+ * \n+ * This function is designed to auto-configure <url>, <featureType>,\n+ * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that\n+ * srsName matching with the WMS layer will not work with WFS 1.0.0.\n+ * \n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS\n+ * FeatureType at the same server url with the same typename.\n+ * options - {Object} Default properties to be set on the protocol.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Protocol.WFS>}\n+ */\n+OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n+ var typeName, featurePrefix;\n+ var param = layer.params[\"LAYERS\"];\n+ var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n+ if (parts.length > 1) {\n+ featurePrefix = parts[0];\n+ }\n+ typeName = parts.pop();\n+ var protocolOptions = {\n+ url: layer.url,\n+ featureType: typeName,\n+ featurePrefix: featurePrefix,\n+ srsName: layer.projection && layer.projection.getCode() ||\n+ layer.map && layer.map.getProjectionObject().getCode(),\n+ version: \"1.1.0\"\n+ };\n+ return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(\n+ options, protocolOptions\n+ ));\n+};\n+\n+/**\n+ * Constant: OpenLayers.Protocol.WFS.DEFAULTS\n+ */\n+OpenLayers.Protocol.WFS.DEFAULTS = {\n+ \"version\": \"1.0.0\"\n+};\n+/* ======================================================================\n OpenLayers/Protocol/WFS/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -42272,8 +38991,3289 @@\n * featureNS - {String} Feature namespace (optional).\n * featurePrefix - {String} Feature namespace alias (optional - only used\n * if featureNS is provided). Default is 'feature'.\n * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n */\n \n CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n+});\n+/* ======================================================================\n+ OpenLayers/Renderer/Elements.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Renderer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.ElementsIndexer\n+ * This class takes care of figuring out which order elements should be\n+ * placed in the DOM based on given indexing methods. \n+ */\n+OpenLayers.ElementsIndexer = OpenLayers.Class({\n+\n+ /**\n+ * Property: maxZIndex\n+ * {Integer} This is the largest-most z-index value for a node\n+ * contained within the indexer.\n+ */\n+ maxZIndex: null,\n+\n+ /**\n+ * Property: order\n+ * {Array<String>} This is an array of node id's stored in the\n+ * order that they should show up on screen. Id's higher up in the\n+ * array (higher array index) represent nodes with higher z-indeces.\n+ */\n+ order: null,\n+\n+ /**\n+ * Property: indices\n+ * {Object} This is a hash that maps node ids to their z-index value\n+ * stored in the indexer. This is done to make finding a nodes z-index \n+ * value O(1).\n+ */\n+ indices: null,\n+\n+ /**\n+ * Property: compare\n+ * {Function} This is the function used to determine placement of\n+ * of a new node within the indexer. If null, this defaults to to\n+ * the Z_ORDER_DRAWING_ORDER comparison method.\n+ */\n+ compare: null,\n+\n+ /**\n+ * APIMethod: initialize\n+ * Create a new indexer with \n+ * \n+ * Parameters:\n+ * yOrdering - {Boolean} Whether to use y-ordering.\n+ */\n+ initialize: function(yOrdering) {\n+\n+ this.compare = yOrdering ?\n+ OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :\n+ OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n+\n+ this.clear();\n+ },\n+\n+ /**\n+ * APIMethod: insert\n+ * Insert a new node into the indexer. In order to find the correct \n+ * positioning for the node to be inserted, this method uses a binary \n+ * search. This makes inserting O(log(n)). \n+ * \n+ * Parameters:\n+ * newNode - {DOMElement} The new node to be inserted.\n+ * \n+ * Returns\n+ * {DOMElement} the node before which we should insert our newNode, or\n+ * null if newNode can just be appended.\n+ */\n+ insert: function(newNode) {\n+ // If the node is known to the indexer, remove it so we can\n+ // recalculate where it should go.\n+ if (this.exists(newNode)) {\n+ this.remove(newNode);\n+ }\n+\n+ var nodeId = newNode.id;\n+\n+ this.determineZIndex(newNode);\n+\n+ var leftIndex = -1;\n+ var rightIndex = this.order.length;\n+ var middle;\n+\n+ while (rightIndex - leftIndex > 1) {\n+ middle = parseInt((leftIndex + rightIndex) / 2);\n+\n+ var placement = this.compare(this, newNode,\n+ OpenLayers.Util.getElement(this.order[middle]));\n+\n+ if (placement > 0) {\n+ leftIndex = middle;\n+ } else {\n+ rightIndex = middle;\n+ }\n+ }\n+\n+ this.order.splice(rightIndex, 0, nodeId);\n+ this.indices[nodeId] = this.getZIndex(newNode);\n+\n+ // If the new node should be before another in the index\n+ // order, return the node before which we have to insert the new one;\n+ // else, return null to indicate that the new node can be appended.\n+ return this.getNextElement(rightIndex);\n+ },\n+\n+ /**\n+ * APIMethod: remove\n+ * \n+ * Parameters:\n+ * node - {DOMElement} The node to be removed.\n+ */\n+ remove: function(node) {\n+ var nodeId = node.id;\n+ var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);\n+ if (arrayIndex >= 0) {\n+ // Remove it from the order array, as well as deleting the node\n+ // from the indeces hash.\n+ this.order.splice(arrayIndex, 1);\n+ delete this.indices[nodeId];\n+\n+ // Reset the maxium z-index based on the last item in the \n+ // order array.\n+ if (this.order.length > 0) {\n+ var lastId = this.order[this.order.length - 1];\n+ this.maxZIndex = this.indices[lastId];\n+ } else {\n+ this.maxZIndex = 0;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: clear\n+ */\n+ clear: function() {\n+ this.order = [];\n+ this.indices = {};\n+ this.maxZIndex = 0;\n+ },\n+\n+ /**\n+ * APIMethod: exists\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The node to test for existence.\n+ *\n+ * Returns:\n+ * {Boolean} Whether or not the node exists in the indexer?\n+ */\n+ exists: function(node) {\n+ return (this.indices[node.id] != null);\n+ },\n+\n+ /**\n+ * APIMethod: getZIndex\n+ * Get the z-index value for the current node from the node data itself.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} The node whose z-index to get.\n+ * \n+ * Returns:\n+ * {Integer} The z-index value for the specified node (from the node \n+ * data itself).\n+ */\n+ getZIndex: function(node) {\n+ return node._style.graphicZIndex;\n+ },\n+\n+ /**\n+ * Method: determineZIndex\n+ * Determine the z-index for the current node if there isn't one, \n+ * and set the maximum value if we've found a new maximum.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} \n+ */\n+ determineZIndex: function(node) {\n+ var zIndex = node._style.graphicZIndex;\n+\n+ // Everything must have a zIndex. If none is specified,\n+ // this means the user *must* (hint: assumption) want this\n+ // node to succomb to drawing order. To enforce drawing order\n+ // over all indexing methods, we'll create a new z-index that's\n+ // greater than any currently in the indexer.\n+ if (zIndex == null) {\n+ zIndex = this.maxZIndex;\n+ node._style.graphicZIndex = zIndex;\n+ } else if (zIndex > this.maxZIndex) {\n+ this.maxZIndex = zIndex;\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: getNextElement\n+ * Get the next element in the order stack.\n+ * \n+ * Parameters:\n+ * index - {Integer} The index of the current node in this.order.\n+ * \n+ * Returns:\n+ * {DOMElement} the node following the index passed in, or\n+ * null.\n+ */\n+ getNextElement: function(index) {\n+ var nextIndex = index + 1;\n+ if (nextIndex < this.order.length) {\n+ var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);\n+ if (nextElement == undefined) {\n+ nextElement = this.getNextElement(nextIndex);\n+ }\n+ return nextElement;\n+ } else {\n+ return null;\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.ElementsIndexer\"\n+});\n+\n+/**\n+ * Namespace: OpenLayers.ElementsIndexer.IndexingMethods\n+ * These are the compare methods for figuring out where a new node should be \n+ * placed within the indexer. These methods are very similar to general \n+ * sorting methods in that they return -1, 0, and 1 to specify the \n+ * direction in which new nodes fall in the ordering.\n+ */\n+OpenLayers.ElementsIndexer.IndexingMethods = {\n+\n+ /**\n+ * Method: Z_ORDER\n+ * This compare method is used by other comparison methods.\n+ * It can be used individually for ordering, but is not recommended,\n+ * because it doesn't subscribe to drawing order.\n+ * \n+ * Parameters:\n+ * indexer - {<OpenLayers.ElementsIndexer>}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER: function(indexer, newNode, nextNode) {\n+ var newZIndex = indexer.getZIndex(newNode);\n+\n+ var returnVal = 0;\n+ if (nextNode) {\n+ var nextZIndex = indexer.getZIndex(nextNode);\n+ returnVal = newZIndex - nextZIndex;\n+ }\n+\n+ return returnVal;\n+ },\n+\n+ /**\n+ * APIMethod: Z_ORDER_DRAWING_ORDER\n+ * This method orders nodes by their z-index, but does so in a way\n+ * that, if there are other nodes with the same z-index, the newest \n+ * drawn will be the front most within that z-index. This is the \n+ * default indexing method.\n+ * \n+ * Parameters:\n+ * indexer - {<OpenLayers.ElementsIndexer>}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n+ indexer,\n+ newNode,\n+ nextNode\n+ );\n+\n+ // Make Z_ORDER subscribe to drawing order by pushing it above\n+ // all of the other nodes with the same z-index.\n+ if (nextNode && returnVal == 0) {\n+ returnVal = 1;\n+ }\n+\n+ return returnVal;\n+ },\n+\n+ /**\n+ * APIMethod: Z_ORDER_Y_ORDER\n+ * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it\n+ * best describes which ordering methods have precedence (though, the \n+ * name would be too long). This method orders nodes by their z-index, \n+ * but does so in a way that, if there are other nodes with the same \n+ * z-index, the nodes with the lower y position will be \"closer\" than \n+ * those with a higher y position. If two nodes have the exact same y \n+ * position, however, then this method will revert to using drawing \n+ * order to decide placement.\n+ * \n+ * Parameters:\n+ * indexer - {<OpenLayers.ElementsIndexer>}\n+ * newNode - {DOMElement}\n+ * nextNode - {DOMElement}\n+ * \n+ * Returns:\n+ * {Integer}\n+ */\n+ Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(\n+ indexer,\n+ newNode,\n+ nextNode\n+ );\n+\n+ if (nextNode && returnVal === 0) {\n+ var result = nextNode._boundsBottom - newNode._boundsBottom;\n+ returnVal = (result === 0) ? 1 : result;\n+ }\n+\n+ return returnVal;\n+ }\n+};\n+\n+/**\n+ * Class: OpenLayers.Renderer.Elements\n+ * This is another virtual class in that it should never be instantiated by \n+ * itself as a Renderer. It exists because there is *tons* of shared \n+ * functionality between different vector libraries which use nodes/elements\n+ * as a base for rendering vectors. \n+ * \n+ * The highlevel bits of code that are implemented here are the adding and \n+ * removing of geometries, which is essentially the same for any \n+ * element-based renderer. The details of creating each node and drawing the\n+ * paths are of course different, but the machinery is the same. \n+ * \n+ * Inherits:\n+ * - <OpenLayers.Renderer>\n+ */\n+OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {\n+\n+ /**\n+ * Property: rendererRoot\n+ * {DOMElement}\n+ */\n+ rendererRoot: null,\n+\n+ /**\n+ * Property: root\n+ * {DOMElement}\n+ */\n+ root: null,\n+\n+ /**\n+ * Property: vectorRoot\n+ * {DOMElement}\n+ */\n+ vectorRoot: null,\n+\n+ /**\n+ * Property: textRoot\n+ * {DOMElement}\n+ */\n+ textRoot: null,\n+\n+ /**\n+ * Property: xmlns\n+ * {String}\n+ */\n+ xmlns: null,\n+\n+ /**\n+ * Property: xOffset\n+ * {Number} Offset to apply to the renderer viewport translation in x\n+ * direction. If the renderer extent's center is on the right of the\n+ * dateline (i.e. exceeds the world bounds), we shift the viewport to the\n+ * left by one world width. This avoids that features disappear from the\n+ * map viewport. Because our dateline handling logic in other places\n+ * ensures that extents crossing the dateline always have a center\n+ * exceeding the world bounds on the left, we need this offset to make sure\n+ * that the same is true for the renderer extent in pixel space as well.\n+ */\n+ xOffset: 0,\n+\n+ /**\n+ * Property: rightOfDateLine\n+ * {Boolean} Keeps track of the location of the map extent relative to the\n+ * date line. The <setExtent> method compares this value (which is the one\n+ * from the previous <setExtent> call) with the current position of the map\n+ * extent relative to the date line and updates the xOffset when the extent\n+ * has moved from one side of the date line to the other.\n+ */\n+\n+ /**\n+ * Property: Indexer\n+ * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer \n+ * created upon initialization if the zIndexing or yOrdering options\n+ * passed to this renderer's constructor are set to true.\n+ */\n+ indexer: null,\n+\n+ /**\n+ * Constant: BACKGROUND_ID_SUFFIX\n+ * {String}\n+ */\n+ BACKGROUND_ID_SUFFIX: \"_background\",\n+\n+ /**\n+ * Constant: LABEL_ID_SUFFIX\n+ * {String}\n+ */\n+ LABEL_ID_SUFFIX: \"_label\",\n+\n+ /**\n+ * Constant: LABEL_OUTLINE_SUFFIX\n+ * {String}\n+ */\n+ LABEL_OUTLINE_SUFFIX: \"_outline\",\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.Elements\n+ * \n+ * Parameters:\n+ * containerID - {String}\n+ * options - {Object} options for this renderer. \n+ *\n+ * Supported options are:\n+ * yOrdering - {Boolean} Whether to use y-ordering\n+ * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored\n+ * if yOrdering is set to true.\n+ */\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+\n+ this.rendererRoot = this.createRenderRoot();\n+ this.root = this.createRoot(\"_root\");\n+ this.vectorRoot = this.createRoot(\"_vroot\");\n+ this.textRoot = this.createRoot(\"_troot\");\n+\n+ this.root.appendChild(this.vectorRoot);\n+ this.root.appendChild(this.textRoot);\n+\n+ this.rendererRoot.appendChild(this.root);\n+ this.container.appendChild(this.rendererRoot);\n+\n+ if (options && (options.zIndexing || options.yOrdering)) {\n+ this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+\n+ this.clear();\n+\n+ this.rendererRoot = null;\n+ this.root = null;\n+ this.xmlns = null;\n+\n+ OpenLayers.Renderer.prototype.destroy.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Method: clear\n+ * Remove all the elements from the root\n+ */\n+ clear: function() {\n+ var child;\n+ var root = this.vectorRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child);\n+ }\n+ }\n+ root = this.textRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child);\n+ }\n+ }\n+ if (this.indexer) {\n+ this.indexer.clear();\n+ }\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n+ *\n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n+ */\n+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ var resolution = this.getResolution();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var rightOfDateLine,\n+ ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio),\n+ world = this.map.getMaxExtent();\n+ if (world.right > extent.left && world.right < extent.right) {\n+ rightOfDateLine = true;\n+ } else if (world.left > extent.left && world.left < extent.right) {\n+ rightOfDateLine = false;\n+ }\n+ if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {\n+ coordSysUnchanged = false;\n+ this.xOffset = rightOfDateLine === true ?\n+ world.getWidth() / resolution : 0;\n+ }\n+ this.rightOfDateLine = rightOfDateLine;\n+ }\n+ return coordSysUnchanged;\n+ },\n+\n+ /** \n+ * Method: getNodeType\n+ * This function is in charge of asking the specific renderer which type\n+ * of node to create for the given geometry and style. All geometries\n+ * in an Elements-based renderer consist of one node and some\n+ * attributes. We have the nodeFactory() function which creates a node\n+ * for us, but it takes a 'type' as input, and that is precisely what\n+ * this function tells us. \n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {String} The corresponding node type for the specified geometry\n+ */\n+ getNodeType: function(geometry, style) {},\n+\n+ /** \n+ * Method: drawGeometry \n+ * Draw the geometry, creating new nodes, setting paths, setting style,\n+ * setting featureId on the node. This method should only be called\n+ * by the renderer itself.\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the geometry has been drawn completely; null if\n+ * incomplete; false otherwise\n+ */\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ var rendered = true;\n+ if ((className == \"OpenLayers.Geometry.Collection\") ||\n+ (className == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (className == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ rendered = this.drawGeometry(\n+ geometry.components[i], style, featureId) && rendered;\n+ }\n+ return rendered;\n+ }\n+\n+ rendered = false;\n+ var removeBackground = false;\n+ if (style.display != \"none\") {\n+ if (style.backgroundGraphic) {\n+ this.redrawBackgroundNode(geometry.id, geometry, style,\n+ featureId);\n+ } else {\n+ removeBackground = true;\n+ }\n+ rendered = this.redrawNode(geometry.id, geometry, style,\n+ featureId);\n+ }\n+ if (rendered == false) {\n+ var node = document.getElementById(geometry.id);\n+ if (node) {\n+ if (node._style.backgroundGraphic) {\n+ removeBackground = true;\n+ }\n+ node.parentNode.removeChild(node);\n+ }\n+ }\n+ if (removeBackground) {\n+ var node = document.getElementById(\n+ geometry.id + this.BACKGROUND_ID_SUFFIX);\n+ if (node) {\n+ node.parentNode.removeChild(node);\n+ }\n+ }\n+ return rendered;\n+ },\n+\n+ /**\n+ * Method: redrawNode\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the complete geometry could be drawn, null if parts of\n+ * the geometry could not be drawn, false otherwise\n+ */\n+ redrawNode: function(id, geometry, style, featureId) {\n+ style = this.applyDefaultSymbolizer(style);\n+ // Get the node if it's already on the map.\n+ var node = this.nodeFactory(id, this.getNodeType(geometry, style));\n+\n+ // Set the data for the node, then draw it.\n+ node._featureId = featureId;\n+ node._boundsBottom = geometry.getBounds().bottom;\n+ node._geometryClass = geometry.CLASS_NAME;\n+ node._style = style;\n+\n+ var drawResult = this.drawGeometryNode(node, geometry, style);\n+ if (drawResult === false) {\n+ return false;\n+ }\n+\n+ node = drawResult.node;\n+\n+ // Insert the node into the indexer so it can show us where to\n+ // place it. Note that this operation is O(log(n)). If there's a\n+ // performance problem (when dragging, for instance) this is\n+ // likely where it would be.\n+ if (this.indexer) {\n+ var insert = this.indexer.insert(node);\n+ if (insert) {\n+ this.vectorRoot.insertBefore(node, insert);\n+ } else {\n+ this.vectorRoot.appendChild(node);\n+ }\n+ } else {\n+ // if there's no indexer, simply append the node to root,\n+ // but only if the node is a new one\n+ if (node.parentNode !== this.vectorRoot) {\n+ this.vectorRoot.appendChild(node);\n+ }\n+ }\n+\n+ this.postDraw(node);\n+\n+ return drawResult.complete;\n+ },\n+\n+ /**\n+ * Method: redrawBackgroundNode\n+ * Redraws the node using special 'background' style properties. Basically\n+ * just calls redrawNode(), but instead of directly using the \n+ * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and \n+ * 'graphicZIndex' properties directly from the specified 'style' \n+ * parameter, we create a new style object and set those properties \n+ * from the corresponding 'background'-prefixed properties from \n+ * specified 'style' parameter.\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ * \n+ * Returns:\n+ * {Boolean} true if the complete geometry could be drawn, null if parts of\n+ * the geometry could not be drawn, false otherwise\n+ */\n+ redrawBackgroundNode: function(id, geometry, style, featureId) {\n+ var backgroundStyle = OpenLayers.Util.extend({}, style);\n+\n+ // Set regular style attributes to apply to the background styles.\n+ backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;\n+ backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;\n+ backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;\n+ backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;\n+ backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;\n+ backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;\n+\n+ // Erase background styles.\n+ backgroundStyle.backgroundGraphic = null;\n+ backgroundStyle.backgroundXOffset = null;\n+ backgroundStyle.backgroundYOffset = null;\n+ backgroundStyle.backgroundGraphicZIndex = null;\n+\n+ return this.redrawNode(\n+ id + this.BACKGROUND_ID_SUFFIX,\n+ geometry,\n+ backgroundStyle,\n+ null\n+ );\n+ },\n+\n+ /**\n+ * Method: drawGeometryNode\n+ * Given a node, draw a geometry on the specified layer.\n+ * node and geometry are required arguments, style is optional.\n+ * This method is only called by the render itself.\n+ *\n+ * Parameters:\n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {Object} a hash with properties \"node\" (the drawn node) and \"complete\"\n+ * (null if parts of the geometry could not be drawn, false if nothing\n+ * could be drawn)\n+ */\n+ drawGeometryNode: function(node, geometry, style) {\n+ style = style || node._style;\n+\n+ var options = {\n+ 'isFilled': style.fill === undefined ?\n+ true : style.fill,\n+ 'isStroked': style.stroke === undefined ?\n+ !!style.strokeWidth : style.stroke\n+ };\n+ var drawn;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.graphic === false) {\n+ options.isFilled = false;\n+ options.isStroked = false;\n+ }\n+ drawn = this.drawPoint(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ options.isFilled = false;\n+ drawn = this.drawLineString(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ drawn = this.drawLinearRing(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ drawn = this.drawPolygon(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ drawn = this.drawRectangle(node, geometry);\n+ break;\n+ default:\n+ break;\n+ }\n+\n+ node._options = options;\n+\n+ //set style\n+ //TBD simplify this\n+ if (drawn != false) {\n+ return {\n+ node: this.setStyle(node, style, options, geometry),\n+ complete: drawn\n+ };\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: postDraw\n+ * Things that have do be done after the geometry node is appended\n+ * to its parent node. To be overridden by subclasses.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ */\n+ postDraw: function(node) {},\n+\n+ /**\n+ * Method: drawPoint\n+ * Virtual function for drawing Point Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the point\n+ */\n+ drawPoint: function(node, geometry) {},\n+\n+ /**\n+ * Method: drawLineString\n+ * Virtual function for drawing LineString Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components of\n+ * the linestring, or false if nothing could be drawn\n+ */\n+ drawLineString: function(node, geometry) {},\n+\n+ /**\n+ * Method: drawLinearRing\n+ * Virtual function for drawing LinearRing Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the linear ring, or false if nothing could be drawn\n+ */\n+ drawLinearRing: function(node, geometry) {},\n+\n+ /**\n+ * Method: drawPolygon\n+ * Virtual function for drawing Polygon Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the polygon, or false if nothing could be drawn\n+ */\n+ drawPolygon: function(node, geometry) {},\n+\n+ /**\n+ * Method: drawRectangle\n+ * Virtual function for drawing Rectangle Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the rectangle\n+ */\n+ drawRectangle: function(node, geometry) {},\n+\n+ /**\n+ * Method: drawCircle\n+ * Virtual function for drawing Circle Geometry. \n+ * Should be implemented by subclasses.\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the circle\n+ */\n+ drawCircle: function(node, geometry) {},\n+\n+ /**\n+ * Method: removeText\n+ * Removes a label\n+ * \n+ * Parameters:\n+ * featureId - {String}\n+ */\n+ removeText: function(featureId) {\n+ var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);\n+ if (label) {\n+ this.textRoot.removeChild(label);\n+ }\n+ var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);\n+ if (outline) {\n+ this.textRoot.removeChild(outline);\n+ }\n+ },\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * \n+ * Parameters:\n+ * evt - {Object} An <OpenLayers.Event> object\n+ *\n+ * Returns:\n+ * {String} A feature id or undefined.\n+ */\n+ getFeatureIdFromEvent: function(evt) {\n+ var target = evt.target;\n+ var useElement = target && target.correspondingUseElement;\n+ var node = useElement ? useElement : (target || evt.srcElement);\n+ return node._featureId;\n+ },\n+\n+ /** \n+ * Method: eraseGeometry\n+ * Erase a geometry from the renderer. In the case of a multi-geometry, \n+ * we cycle through and recurse on ourselves. Otherwise, we look for a \n+ * node with the geometry.id, destroy its geometry, and remove it from\n+ * the DOM.\n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * featureId - {String}\n+ */\n+ eraseGeometry: function(geometry, featureId) {\n+ if ((geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPolygon\") ||\n+ (geometry.CLASS_NAME == \"OpenLayers.Geometry.Collection\")) {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ this.eraseGeometry(geometry.components[i], featureId);\n+ }\n+ } else {\n+ var element = OpenLayers.Util.getElement(geometry.id);\n+ if (element && element.parentNode) {\n+ if (element.geometry) {\n+ element.geometry.destroy();\n+ element.geometry = null;\n+ }\n+ element.parentNode.removeChild(element);\n+\n+ if (this.indexer) {\n+ this.indexer.remove(element);\n+ }\n+\n+ if (element._style.backgroundGraphic) {\n+ var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;\n+ var bElem = OpenLayers.Util.getElement(backgroundId);\n+ if (bElem && bElem.parentNode) {\n+ // No need to destroy the geometry since the element and the background\n+ // node share the same geometry.\n+ bElem.parentNode.removeChild(bElem);\n+ }\n+ }\n+ }\n+ }\n+ },\n+\n+ /** \n+ * Method: nodeFactory\n+ * Create new node of the specified type, with the (optional) specified id.\n+ * \n+ * If node already exists with same ID and a different type, we remove it\n+ * and then call ourselves again to recreate it.\n+ * \n+ * Parameters:\n+ * id - {String}\n+ * type - {String} type Kind of node to draw.\n+ * \n+ * Returns:\n+ * {DOMElement} A new node of the given type and id.\n+ */\n+ nodeFactory: function(id, type) {\n+ var node = OpenLayers.Util.getElement(id);\n+ if (node) {\n+ if (!this.nodeTypeCompare(node, type)) {\n+ node.parentNode.removeChild(node);\n+ node = this.nodeFactory(id, type);\n+ }\n+ } else {\n+ node = this.createNode(type, id);\n+ }\n+ return node;\n+ },\n+\n+ /** \n+ * Method: nodeTypeCompare\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * type - {String} Kind of node\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the specified node is of the specified type\n+ * This function must be overridden by subclasses.\n+ */\n+ nodeTypeCompare: function(node, type) {},\n+\n+ /** \n+ * Method: createNode\n+ * \n+ * Parameters:\n+ * type - {String} Kind of node to draw.\n+ * id - {String} Id for node.\n+ * \n+ * Returns:\n+ * {DOMElement} A new node of the given type and id.\n+ * This function must be overridden by subclasses.\n+ */\n+ createNode: function(type, id) {},\n+\n+ /**\n+ * Method: moveRoot\n+ * moves this renderer's root to a different renderer.\n+ * \n+ * Parameters:\n+ * renderer - {<OpenLayers.Renderer>} target renderer for the moved root\n+ */\n+ moveRoot: function(renderer) {\n+ var root = this.root;\n+ if (renderer.root.parentNode == this.rendererRoot) {\n+ root = renderer.root;\n+ }\n+ root.parentNode.removeChild(root);\n+ renderer.rendererRoot.appendChild(root);\n+ },\n+\n+ /**\n+ * Method: getRenderLayerId\n+ * Gets the layer that this renderer's output appears on. If moveRoot was\n+ * used, this will be different from the id of the layer containing the\n+ * features rendered by this renderer.\n+ * \n+ * Returns:\n+ * {String} the id of the output layer.\n+ */\n+ getRenderLayerId: function() {\n+ return this.root.parentNode.parentNode.id;\n+ },\n+\n+ /**\n+ * Method: isComplexSymbol\n+ * Determines if a symbol cannot be rendered using drawCircle\n+ * \n+ * Parameters:\n+ * graphicName - {String}\n+ * \n+ * Returns\n+ * {Boolean} true if the symbol is complex, false if not\n+ */\n+ isComplexSymbol: function(graphicName) {\n+ return (graphicName != \"circle\") && !!graphicName;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Renderer.Elements\"\n+});\n+\n+/* ======================================================================\n+ OpenLayers/Renderer/SVG.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Renderer/Elements.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Renderer.SVG\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Renderer.Elements>\n+ */\n+OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {\n+\n+ /** \n+ * Property: xmlns\n+ * {String}\n+ */\n+ xmlns: \"http://www.w3.org/2000/svg\",\n+\n+ /**\n+ * Property: xlinkns\n+ * {String}\n+ */\n+ xlinkns: \"http://www.w3.org/1999/xlink\",\n+\n+ /**\n+ * Constant: MAX_PIXEL\n+ * {Integer} Firefox has a limitation where values larger or smaller than \n+ * about 15000 in an SVG document lock the browser up. This \n+ * works around it.\n+ */\n+ MAX_PIXEL: 15000,\n+\n+ /**\n+ * Property: translationParameters\n+ * {Object} Hash with \"x\" and \"y\" properties\n+ */\n+ translationParameters: null,\n+\n+ /**\n+ * Property: symbolMetrics\n+ * {Object} Cache for symbol metrics according to their svg coordinate\n+ * space. This is an object keyed by the symbol's id, and values are\n+ * an array of [width, centerX, centerY].\n+ */\n+ symbolMetrics: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.SVG\n+ * \n+ * Parameters:\n+ * containerID - {String}\n+ */\n+ initialize: function(containerID) {\n+ if (!this.supported()) {\n+ return;\n+ }\n+ OpenLayers.Renderer.Elements.prototype.initialize.apply(this,\n+ arguments);\n+ this.translationParameters = {\n+ x: 0,\n+ y: 0\n+ };\n+\n+ this.symbolMetrics = {};\n+ },\n+\n+ /**\n+ * APIMethod: supported\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the SVG renderer\n+ */\n+ supported: function() {\n+ var svgFeature = \"http://www.w3.org/TR/SVG11/feature#\";\n+ return (document.implementation &&\n+ (document.implementation.hasFeature(\"org.w3c.svg\", \"1.0\") ||\n+ document.implementation.hasFeature(svgFeature + \"SVG\", \"1.1\") ||\n+ document.implementation.hasFeature(svgFeature + \"BasicStructure\", \"1.1\")));\n+ },\n+\n+ /**\n+ * Method: inValidRange\n+ * See #669 for more information\n+ *\n+ * Parameters:\n+ * x - {Integer}\n+ * y - {Integer}\n+ * xyOnly - {Boolean} whether or not to just check for x and y, which means\n+ * to not take the current translation parameters into account if true.\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the 'x' and 'y' coordinates are in the \n+ * valid range.\n+ */\n+ inValidRange: function(x, y, xyOnly) {\n+ var left = x + (xyOnly ? 0 : this.translationParameters.x);\n+ var top = y + (xyOnly ? 0 : this.translationParameters.y);\n+ return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&\n+ top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * \n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n+ * \n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n+ */\n+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n+\n+ var resolution = this.getResolution(),\n+ left = -extent.left / resolution,\n+ top = extent.top / resolution;\n+\n+ // If the resolution has changed, start over changing the corner, because\n+ // the features will redraw.\n+ if (resolutionChanged) {\n+ this.left = left;\n+ this.top = top;\n+ // Set the viewbox\n+ var extentString = \"0 0 \" + this.size.w + \" \" + this.size.h;\n+\n+ this.rendererRoot.setAttributeNS(null, \"viewBox\", extentString);\n+ this.translate(this.xOffset, 0);\n+ return true;\n+ } else {\n+ var inRange = this.translate(left - this.left + this.xOffset, top - this.top);\n+ if (!inRange) {\n+ // recenter the coordinate system\n+ this.setExtent(extent, true);\n+ }\n+ return coordSysUnchanged && inRange;\n+ }\n+ },\n+\n+ /**\n+ * Method: translate\n+ * Transforms the SVG coordinate system\n+ * \n+ * Parameters:\n+ * x - {Float}\n+ * y - {Float}\n+ * \n+ * Returns:\n+ * {Boolean} true if the translation parameters are in the valid coordinates\n+ * range, false otherwise.\n+ */\n+ translate: function(x, y) {\n+ if (!this.inValidRange(x, y, true)) {\n+ return false;\n+ } else {\n+ var transformString = \"\";\n+ if (x || y) {\n+ transformString = \"translate(\" + x + \",\" + y + \")\";\n+ }\n+ this.root.setAttributeNS(null, \"transform\", transformString);\n+ this.translationParameters = {\n+ x: x,\n+ y: y\n+ };\n+ return true;\n+ }\n+ },\n+\n+ /**\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ * \n+ * Parameters:\n+ * size - {<OpenLayers.Size>} The size of the drawing surface\n+ */\n+ setSize: function(size) {\n+ OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n+\n+ this.rendererRoot.setAttributeNS(null, \"width\", this.size.w);\n+ this.rendererRoot.setAttributeNS(null, \"height\", this.size.h);\n+ },\n+\n+ /** \n+ * Method: getNodeType \n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * \n+ * Returns:\n+ * {String} The corresponding node type for the specified geometry\n+ */\n+ getNodeType: function(geometry, style) {\n+ var nodeType = null;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.externalGraphic) {\n+ nodeType = \"image\";\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ nodeType = \"svg\";\n+ } else {\n+ nodeType = \"circle\";\n+ }\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ nodeType = \"rect\";\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ nodeType = \"polyline\";\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ nodeType = \"polygon\";\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ case \"OpenLayers.Geometry.Curve\":\n+ nodeType = \"path\";\n+ break;\n+ default:\n+ break;\n+ }\n+ return nodeType;\n+ },\n+\n+ /** \n+ * Method: setStyle\n+ * Use to set all the style attributes to a SVG node.\n+ * \n+ * Takes care to adjust stroke width and point radius to be\n+ * resolution-relative\n+ *\n+ * Parameters:\n+ * node - {SVGDomElement} An SVG element to decorate\n+ * style - {Object}\n+ * options - {Object} Currently supported options include \n+ * 'isFilled' {Boolean} and\n+ * 'isStroked' {Boolean}\n+ */\n+ setStyle: function(node, style, options) {\n+ style = style || node._style;\n+ options = options || node._options;\n+\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ node.setAttributeNS(null, \"title\", title);\n+ //Standards-conformant SVG\n+ // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 \n+ var titleNode = node.getElementsByTagName(\"title\");\n+ if (titleNode.length > 0) {\n+ titleNode[0].firstChild.textContent = title;\n+ } else {\n+ var label = this.nodeFactory(null, \"title\");\n+ label.textContent = title;\n+ node.appendChild(label);\n+ }\n+ }\n+\n+ var r = parseFloat(node.getAttributeNS(null, \"r\"));\n+ var widthFactor = 1;\n+ var pos;\n+ if (node._geometryClass == \"OpenLayers.Geometry.Point\" && r) {\n+ node.style.visibility = \"\";\n+ if (style.graphic === false) {\n+ node.style.visibility = \"hidden\";\n+ } else if (style.externalGraphic) {\n+ pos = this.getPosition(node);\n+ if (style.graphicWidth && style.graphicHeight) {\n+ node.setAttributeNS(null, \"preserveAspectRatio\", \"none\");\n+ }\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n+\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+\n+ node.setAttributeNS(null, \"x\", (pos.x + xOffset).toFixed());\n+ node.setAttributeNS(null, \"y\", (pos.y + yOffset).toFixed());\n+ node.setAttributeNS(null, \"width\", width);\n+ node.setAttributeNS(null, \"height\", height);\n+ node.setAttributeNS(this.xlinkns, \"xlink:href\", style.externalGraphic);\n+ node.setAttributeNS(null, \"style\", \"opacity: \" + opacity);\n+ node.onclick = OpenLayers.Event.preventDefault;\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ // the symbol viewBox is three times as large as the symbol\n+ var offset = style.pointRadius * 3;\n+ var size = offset * 2;\n+ var src = this.importSymbol(style.graphicName);\n+ pos = this.getPosition(node);\n+ widthFactor = this.symbolMetrics[src.id][0] * 3 / size;\n+\n+ // remove the node from the dom before we modify it. This\n+ // prevents various rendering issues in Safari and FF\n+ var parent = node.parentNode;\n+ var nextSibling = node.nextSibling;\n+ if (parent) {\n+ parent.removeChild(node);\n+ }\n+\n+ // The more appropriate way to implement this would be use/defs,\n+ // but due to various issues in several browsers, it is safer to\n+ // copy the symbols instead of referencing them. \n+ // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 \n+ // and this email thread\n+ // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html\n+ node.firstChild && node.removeChild(node.firstChild);\n+ node.appendChild(src.firstChild.cloneNode(true));\n+ node.setAttributeNS(null, \"viewBox\", src.getAttributeNS(null, \"viewBox\"));\n+\n+ node.setAttributeNS(null, \"width\", size);\n+ node.setAttributeNS(null, \"height\", size);\n+ node.setAttributeNS(null, \"x\", pos.x - offset);\n+ node.setAttributeNS(null, \"y\", pos.y - offset);\n+\n+ // now that the node has all its new properties, insert it\n+ // back into the dom where it was\n+ if (nextSibling) {\n+ parent.insertBefore(node, nextSibling);\n+ } else if (parent) {\n+ parent.appendChild(node);\n+ }\n+ } else {\n+ node.setAttributeNS(null, \"r\", style.pointRadius);\n+ }\n+\n+ var rotation = style.rotation;\n+\n+ if ((rotation !== undefined || node._rotation !== undefined) && pos) {\n+ node._rotation = rotation;\n+ rotation |= 0;\n+ if (node.nodeName !== \"svg\") {\n+ node.setAttributeNS(null, \"transform\",\n+ \"rotate(\" + rotation + \" \" + pos.x + \" \" +\n+ pos.y + \")\");\n+ } else {\n+ var metrics = this.symbolMetrics[src.id];\n+ node.firstChild.setAttributeNS(null, \"transform\", \"rotate(\" +\n+ rotation + \" \" +\n+ metrics[1] + \" \" +\n+ metrics[2] + \")\");\n+ }\n+ }\n+ }\n+\n+ if (options.isFilled) {\n+ node.setAttributeNS(null, \"fill\", style.fillColor);\n+ node.setAttributeNS(null, \"fill-opacity\", style.fillOpacity);\n+ } else {\n+ node.setAttributeNS(null, \"fill\", \"none\");\n+ }\n+\n+ if (options.isStroked) {\n+ node.setAttributeNS(null, \"stroke\", style.strokeColor);\n+ node.setAttributeNS(null, \"stroke-opacity\", style.strokeOpacity);\n+ node.setAttributeNS(null, \"stroke-width\", style.strokeWidth * widthFactor);\n+ node.setAttributeNS(null, \"stroke-linecap\", style.strokeLinecap || \"round\");\n+ // Hard-coded linejoin for now, to make it look the same as in VML.\n+ // There is no strokeLinejoin property yet for symbolizers.\n+ node.setAttributeNS(null, \"stroke-linejoin\", \"round\");\n+ style.strokeDashstyle && node.setAttributeNS(null,\n+ \"stroke-dasharray\", this.dashStyle(style, widthFactor));\n+ } else {\n+ node.setAttributeNS(null, \"stroke\", \"none\");\n+ }\n+\n+ if (style.pointerEvents) {\n+ node.setAttributeNS(null, \"pointer-events\", style.pointerEvents);\n+ }\n+\n+ if (style.cursor != null) {\n+ node.setAttributeNS(null, \"cursor\", style.cursor);\n+ }\n+\n+ return node;\n+ },\n+\n+ /** \n+ * Method: dashStyle\n+ * \n+ * Parameters:\n+ * style - {Object}\n+ * widthFactor - {Number}\n+ * \n+ * Returns:\n+ * {String} A SVG compliant 'stroke-dasharray' value\n+ */\n+ dashStyle: function(style, widthFactor) {\n+ var w = style.strokeWidth * widthFactor;\n+ var str = style.strokeDashstyle;\n+ switch (str) {\n+ case 'solid':\n+ return 'none';\n+ case 'dot':\n+ return [1, 4 * w].join();\n+ case 'dash':\n+ return [4 * w, 4 * w].join();\n+ case 'dashdot':\n+ return [4 * w, 4 * w, 1, 4 * w].join();\n+ case 'longdash':\n+ return [8 * w, 4 * w].join();\n+ case 'longdashdot':\n+ return [8 * w, 4 * w, 1, 4 * w].join();\n+ default:\n+ return OpenLayers.String.trim(str).replace(/\\s+/g, \",\");\n+ }\n+ },\n+\n+ /** \n+ * Method: createNode\n+ * \n+ * Parameters:\n+ * type - {String} Kind of node to draw\n+ * id - {String} Id for node\n+ * \n+ * Returns:\n+ * {DOMElement} A new node of the given type and id\n+ */\n+ createNode: function(type, id) {\n+ var node = document.createElementNS(this.xmlns, type);\n+ if (id) {\n+ node.setAttributeNS(null, \"id\", id);\n+ }\n+ return node;\n+ },\n+\n+ /** \n+ * Method: nodeTypeCompare\n+ * \n+ * Parameters:\n+ * node - {SVGDomElement} An SVG element\n+ * type - {String} Kind of node\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the specified node is of the specified type\n+ */\n+ nodeTypeCompare: function(node, type) {\n+ return (type == node.nodeName);\n+ },\n+\n+ /**\n+ * Method: createRenderRoot\n+ * \n+ * Returns:\n+ * {DOMElement} The specific render engine's root element\n+ */\n+ createRenderRoot: function() {\n+ var svg = this.nodeFactory(this.container.id + \"_svgRoot\", \"svg\");\n+ svg.style.display = \"block\";\n+ return svg;\n+ },\n+\n+ /**\n+ * Method: createRoot\n+ * \n+ * Parameters:\n+ * suffix - {String} suffix to append to the id\n+ * \n+ * Returns:\n+ * {DOMElement}\n+ */\n+ createRoot: function(suffix) {\n+ return this.nodeFactory(this.container.id + suffix, \"g\");\n+ },\n+\n+ /**\n+ * Method: createDefs\n+ *\n+ * Returns:\n+ * {DOMElement} The element to which we'll add the symbol definitions\n+ */\n+ createDefs: function() {\n+ var defs = this.nodeFactory(this.container.id + \"_defs\", \"defs\");\n+ this.rendererRoot.appendChild(defs);\n+ return defs;\n+ },\n+\n+ /**************************************\n+ * *\n+ * GEOMETRY DRAWING FUNCTIONS *\n+ * *\n+ **************************************/\n+\n+ /**\n+ * Method: drawPoint\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the point\n+ */\n+ drawPoint: function(node, geometry) {\n+ return this.drawCircle(node, geometry, 1);\n+ },\n+\n+ /**\n+ * Method: drawCircle\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * radius - {Float}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the circle\n+ */\n+ drawCircle: function(node, geometry, radius) {\n+ var resolution = this.getResolution();\n+ var x = ((geometry.x - this.featureDx) / resolution + this.left);\n+ var y = (this.top - geometry.y / resolution);\n+\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"cx\", x);\n+ node.setAttributeNS(null, \"cy\", y);\n+ node.setAttributeNS(null, \"r\", radius);\n+ return node;\n+ } else {\n+ return false;\n+ }\n+\n+ },\n+\n+ /**\n+ * Method: drawLineString\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components of\n+ * the linestring, or false if nothing could be drawn\n+ */\n+ drawLineString: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return (componentsResult.complete ? node : null);\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawLinearRing\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the linear ring, or false if nothing could be drawn\n+ */\n+ drawLinearRing: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return (componentsResult.complete ? node : null);\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawPolygon\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or null if the renderer could not draw all components\n+ * of the polygon, or false if nothing could be drawn\n+ */\n+ drawPolygon: function(node, geometry) {\n+ var d = \"\";\n+ var draw = true;\n+ var complete = true;\n+ var linearRingResult, path;\n+ for (var j = 0, len = geometry.components.length; j < len; j++) {\n+ d += \" M\";\n+ linearRingResult = this.getComponentsString(\n+ geometry.components[j].components, \" \");\n+ path = linearRingResult.path;\n+ if (path) {\n+ d += \" \" + path;\n+ complete = linearRingResult.complete && complete;\n+ } else {\n+ draw = false;\n+ }\n+ }\n+ d += \" z\";\n+ if (draw) {\n+ node.setAttributeNS(null, \"d\", d);\n+ node.setAttributeNS(null, \"fill-rule\", \"evenodd\");\n+ return complete ? node : null;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawRectangle\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * node - {DOMElement}\n+ * geometry - {<OpenLayers.Geometry>}\n+ * \n+ * Returns:\n+ * {DOMElement} or false if the renderer could not draw the rectangle\n+ */\n+ drawRectangle: function(node, geometry) {\n+ var resolution = this.getResolution();\n+ var x = ((geometry.x - this.featureDx) / resolution + this.left);\n+ var y = (this.top - geometry.y / resolution);\n+\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"x\", x);\n+ node.setAttributeNS(null, \"y\", y);\n+ node.setAttributeNS(null, \"width\", geometry.width / resolution);\n+ node.setAttributeNS(null, \"height\", geometry.height / resolution);\n+ return node;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawText\n+ * This method is only called by the renderer itself.\n+ *\n+ * Parameters:\n+ * featureId - {String}\n+ * style -\n+ * location - {<OpenLayers.Geometry.Point>}\n+ */\n+ drawText: function(featureId, style, location) {\n+ var drawOutline = (!!style.labelOutlineWidth);\n+ // First draw text in halo color and size and overlay the\n+ // normal text afterwards\n+ if (drawOutline) {\n+ var outlineStyle = OpenLayers.Util.extend({}, style);\n+ outlineStyle.fontColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeWidth = style.labelOutlineWidth;\n+ if (style.labelOutlineOpacity) {\n+ outlineStyle.fontOpacity = style.labelOutlineOpacity;\n+ }\n+ delete outlineStyle.labelOutlineWidth;\n+ this.drawText(featureId, outlineStyle, location);\n+ }\n+\n+ var resolution = this.getResolution();\n+\n+ var x = ((location.x - this.featureDx) / resolution + this.left);\n+ var y = (location.y / resolution - this.top);\n+\n+ var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX;\n+ var label = this.nodeFactory(featureId + suffix, \"text\");\n+\n+ label.setAttributeNS(null, \"x\", x);\n+ label.setAttributeNS(null, \"y\", -y);\n+\n+ if (style.fontColor) {\n+ label.setAttributeNS(null, \"fill\", style.fontColor);\n+ }\n+ if (style.fontStrokeColor) {\n+ label.setAttributeNS(null, \"stroke\", style.fontStrokeColor);\n+ }\n+ if (style.fontStrokeWidth) {\n+ label.setAttributeNS(null, \"stroke-width\", style.fontStrokeWidth);\n+ }\n+ if (style.fontOpacity) {\n+ label.setAttributeNS(null, \"opacity\", style.fontOpacity);\n+ }\n+ if (style.fontFamily) {\n+ label.setAttributeNS(null, \"font-family\", style.fontFamily);\n+ }\n+ if (style.fontSize) {\n+ label.setAttributeNS(null, \"font-size\", style.fontSize);\n+ }\n+ if (style.fontWeight) {\n+ label.setAttributeNS(null, \"font-weight\", style.fontWeight);\n+ }\n+ if (style.fontStyle) {\n+ label.setAttributeNS(null, \"font-style\", style.fontStyle);\n+ }\n+ if (style.labelSelect === true) {\n+ label.setAttributeNS(null, \"pointer-events\", \"visible\");\n+ label._featureId = featureId;\n+ } else {\n+ label.setAttributeNS(null, \"pointer-events\", \"none\");\n+ }\n+ var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;\n+ label.setAttributeNS(null, \"text-anchor\",\n+ OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || \"middle\");\n+\n+ if (OpenLayers.IS_GECKO === true) {\n+ label.setAttributeNS(null, \"dominant-baseline\",\n+ OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || \"central\");\n+ }\n+\n+ var labelRows = style.label.split('\\n');\n+ var numRows = labelRows.length;\n+ while (label.childNodes.length > numRows) {\n+ label.removeChild(label.lastChild);\n+ }\n+ for (var i = 0; i < numRows; i++) {\n+ var tspan = this.nodeFactory(featureId + suffix + \"_tspan_\" + i, \"tspan\");\n+ if (style.labelSelect === true) {\n+ tspan._featureId = featureId;\n+ tspan._geometry = location;\n+ tspan._geometryClass = location.CLASS_NAME;\n+ }\n+ if (OpenLayers.IS_GECKO === false) {\n+ tspan.setAttributeNS(null, \"baseline-shift\",\n+ OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || \"-35%\");\n+ }\n+ tspan.setAttribute(\"x\", x);\n+ if (i == 0) {\n+ var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n+ }\n+ tspan.setAttribute(\"dy\", (vfactor * (numRows - 1)) + \"em\");\n+ } else {\n+ tspan.setAttribute(\"dy\", \"1em\");\n+ }\n+ tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];\n+ if (!tspan.parentNode) {\n+ label.appendChild(tspan);\n+ }\n+ }\n+\n+ if (!label.parentNode) {\n+ this.textRoot.appendChild(label);\n+ }\n+ },\n+\n+ /** \n+ * Method: getComponentString\n+ * \n+ * Parameters:\n+ * components - {Array(<OpenLayers.Geometry.Point>)} Array of points\n+ * separator - {String} character between coordinate pairs. Defaults to \",\"\n+ * \n+ * Returns:\n+ * {Object} hash with properties \"path\" (the string created from the\n+ * components and \"complete\" (false if the renderer was unable to\n+ * draw all components)\n+ */\n+ getComponentsString: function(components, separator) {\n+ var renderCmp = [];\n+ var complete = true;\n+ var len = components.length;\n+ var strings = [];\n+ var str, component;\n+ for (var i = 0; i < len; i++) {\n+ component = components[i];\n+ renderCmp.push(component);\n+ str = this.getShortString(component);\n+ if (str) {\n+ strings.push(str);\n+ } else {\n+ // The current component is outside the valid range. Let's\n+ // see if the previous or next component is inside the range.\n+ // If so, add the coordinate of the intersection with the\n+ // valid range bounds.\n+ if (i > 0) {\n+ if (this.getShortString(components[i - 1])) {\n+ strings.push(this.clipLine(components[i],\n+ components[i - 1]));\n+ }\n+ }\n+ if (i < len - 1) {\n+ if (this.getShortString(components[i + 1])) {\n+ strings.push(this.clipLine(components[i],\n+ components[i + 1]));\n+ }\n+ }\n+ complete = false;\n+ }\n+ }\n+\n+ return {\n+ path: strings.join(separator || \",\"),\n+ complete: complete\n+ };\n+ },\n+\n+ /**\n+ * Method: clipLine\n+ * Given two points (one inside the valid range, and one outside),\n+ * clips the line betweeen the two points so that the new points are both\n+ * inside the valid range.\n+ * \n+ * Parameters:\n+ * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the\n+ * invalid point\n+ * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the\n+ * valid point\n+ * Returns\n+ * {String} the SVG coordinate pair of the clipped point (like\n+ * getShortString), or an empty string if both passed componets are at\n+ * the same point.\n+ */\n+ clipLine: function(badComponent, goodComponent) {\n+ if (goodComponent.equals(badComponent)) {\n+ return \"\";\n+ }\n+ var resolution = this.getResolution();\n+ var maxX = this.MAX_PIXEL - this.translationParameters.x;\n+ var maxY = this.MAX_PIXEL - this.translationParameters.y;\n+ var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;\n+ var y1 = this.top - goodComponent.y / resolution;\n+ var x2 = (badComponent.x - this.featureDx) / resolution + this.left;\n+ var y2 = this.top - badComponent.y / resolution;\n+ var k;\n+ if (x2 < -maxX || x2 > maxX) {\n+ k = (y2 - y1) / (x2 - x1);\n+ x2 = x2 < 0 ? -maxX : maxX;\n+ y2 = y1 + (x2 - x1) * k;\n+ }\n+ if (y2 < -maxY || y2 > maxY) {\n+ k = (x2 - x1) / (y2 - y1);\n+ y2 = y2 < 0 ? -maxY : maxY;\n+ x2 = x1 + (y2 - y1) * k;\n+ }\n+ return x2 + \",\" + y2;\n+ },\n+\n+ /** \n+ * Method: getShortString\n+ * \n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n+ * \n+ * Returns:\n+ * {String} or false if point is outside the valid range\n+ */\n+ getShortString: function(point) {\n+ var resolution = this.getResolution();\n+ var x = ((point.x - this.featureDx) / resolution + this.left);\n+ var y = (this.top - point.y / resolution);\n+\n+ if (this.inValidRange(x, y)) {\n+ return x + \",\" + y;\n+ } else {\n+ return false;\n+ }\n+ },\n+\n+ /**\n+ * Method: getPosition\n+ * Finds the position of an svg node.\n+ * \n+ * Parameters:\n+ * node - {DOMElement}\n+ * \n+ * Returns:\n+ * {Object} hash with x and y properties, representing the coordinates\n+ * within the svg coordinate system\n+ */\n+ getPosition: function(node) {\n+ return ({\n+ x: parseFloat(node.getAttributeNS(null, \"cx\")),\n+ y: parseFloat(node.getAttributeNS(null, \"cy\"))\n+ });\n+ },\n+\n+ /**\n+ * Method: importSymbol\n+ * add a new symbol definition from the rendererer's symbol hash\n+ * \n+ * Parameters:\n+ * graphicName - {String} name of the symbol to import\n+ * \n+ * Returns:\n+ * {DOMElement} - the imported symbol\n+ */\n+ importSymbol: function(graphicName) {\n+ if (!this.defs) {\n+ // create svg defs tag\n+ this.defs = this.createDefs();\n+ }\n+ var id = this.container.id + \"-\" + graphicName;\n+\n+ // check if symbol already exists in the defs\n+ var existing = document.getElementById(id);\n+ if (existing != null) {\n+ return existing;\n+ }\n+\n+ var symbol = OpenLayers.Renderer.symbol[graphicName];\n+ if (!symbol) {\n+ throw new Error(graphicName + ' is not a valid symbol name');\n+ }\n+\n+ var symbolNode = this.nodeFactory(id, \"symbol\");\n+ var node = this.nodeFactory(null, \"polygon\");\n+ symbolNode.appendChild(node);\n+ var symbolExtent = new OpenLayers.Bounds(\n+ Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);\n+\n+ var points = [];\n+ var x, y;\n+ for (var i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ symbolExtent.left = Math.min(symbolExtent.left, x);\n+ symbolExtent.bottom = Math.min(symbolExtent.bottom, y);\n+ symbolExtent.right = Math.max(symbolExtent.right, x);\n+ symbolExtent.top = Math.max(symbolExtent.top, y);\n+ points.push(x, \",\", y);\n+ }\n+\n+ node.setAttributeNS(null, \"points\", points.join(\" \"));\n+\n+ var width = symbolExtent.getWidth();\n+ var height = symbolExtent.getHeight();\n+ // create a viewBox three times as large as the symbol itself,\n+ // to allow for strokeWidth being displayed correctly at the corners.\n+ var viewBox = [symbolExtent.left - width,\n+ symbolExtent.bottom - height, width * 3, height * 3\n+ ];\n+ symbolNode.setAttributeNS(null, \"viewBox\", viewBox.join(\" \"));\n+ this.symbolMetrics[id] = [\n+ Math.max(width, height),\n+ symbolExtent.getCenterLonLat().lon,\n+ symbolExtent.getCenterLonLat().lat\n+ ];\n+\n+ this.defs.appendChild(symbolNode);\n+ return symbolNode;\n+ },\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * \n+ * Parameters:\n+ * evt - {Object} An <OpenLayers.Event> object\n+ *\n+ * Returns:\n+ * {String} A feature id or undefined.\n+ */\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);\n+ if (!featureId) {\n+ var target = evt.target;\n+ featureId = target.parentNode && target != this.rendererRoot ?\n+ target.parentNode._featureId : undefined;\n+ }\n+ return featureId;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Renderer.SVG\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN\n+ * {Object}\n+ */\n+OpenLayers.Renderer.SVG.LABEL_ALIGN = {\n+ \"l\": \"start\",\n+ \"r\": \"end\",\n+ \"b\": \"bottom\",\n+ \"t\": \"hanging\"\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT\n+ * {Object}\n+ */\n+OpenLayers.Renderer.SVG.LABEL_VSHIFT = {\n+ // according to\n+ // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html\n+ // a baseline-shift of -70% shifts the text exactly from the\n+ // bottom to the top of the baseline, so -35% moves the text to\n+ // the center of the baseline.\n+ \"t\": \"-70%\",\n+ \"b\": \"0\"\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR\n+ * {Object}\n+ */\n+OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n+ \"t\": 0,\n+ \"b\": -1\n+};\n+\n+/**\n+ * Function: OpenLayers.Renderer.SVG.preventDefault\n+ * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead.\n+ * Used to prevent default events (especially opening images in a new tab on\n+ * ctrl-click) from being executed for externalGraphic symbols\n+ */\n+OpenLayers.Renderer.SVG.preventDefault = function(e) {\n+ OpenLayers.Event.preventDefault(e);\n+};\n+/* ======================================================================\n+ OpenLayers/Renderer/Canvas.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Renderer.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Renderer.Canvas \n+ * A renderer based on the 2D 'canvas' drawing element.\n+ * \n+ * Inherits:\n+ * - <OpenLayers.Renderer>\n+ */\n+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n+\n+ /**\n+ * APIProperty: hitDetection\n+ * {Boolean} Allow for hit detection of features. Default is true.\n+ */\n+ hitDetection: true,\n+\n+ /**\n+ * Property: hitOverflow\n+ * {Number} The method for converting feature identifiers to color values\n+ * supports 16777215 sequential values. Two features cannot be \n+ * predictably detected if their identifiers differ by more than this\n+ * value. The hitOverflow allows for bigger numbers (but the \n+ * difference in values is still limited).\n+ */\n+ hitOverflow: 0,\n+\n+ /**\n+ * Property: canvas\n+ * {Canvas} The canvas context object.\n+ */\n+ canvas: null,\n+\n+ /**\n+ * Property: features\n+ * {Object} Internal object of feature/style pairs for use in redrawing the layer.\n+ */\n+ features: null,\n+\n+ /**\n+ * Property: pendingRedraw\n+ * {Boolean} The renderer needs a redraw call to render features added while\n+ * the renderer was locked.\n+ */\n+ pendingRedraw: false,\n+\n+ /**\n+ * Property: cachedSymbolBounds\n+ * {Object} Internal cache of calculated symbol extents.\n+ */\n+ cachedSymbolBounds: {},\n+\n+ /**\n+ * Constructor: OpenLayers.Renderer.Canvas\n+ *\n+ * Parameters:\n+ * containerID - {<String>}\n+ * options - {Object} Optional properties to be set on the renderer.\n+ */\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.root = document.createElement(\"canvas\");\n+ this.container.appendChild(this.root);\n+ this.canvas = this.root.getContext(\"2d\");\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitCanvas = document.createElement(\"canvas\");\n+ this.hitContext = this.hitCanvas.getContext(\"2d\");\n+ }\n+ },\n+\n+ /**\n+ * Method: setExtent\n+ * Set the visible part of the layer.\n+ *\n+ * Parameters:\n+ * extent - {<OpenLayers.Bounds>}\n+ * resolutionChanged - {Boolean}\n+ *\n+ * Returns:\n+ * {Boolean} true to notify the layer that the new extent does not exceed\n+ * the coordinate range, and the features will not need to be redrawn.\n+ * False otherwise.\n+ */\n+ setExtent: function() {\n+ OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ // always redraw features\n+ return false;\n+ },\n+\n+ /** \n+ * Method: eraseGeometry\n+ * Erase a geometry from the renderer. Because the Canvas renderer has\n+ * 'memory' of the features that it has drawn, we have to remove the\n+ * feature so it doesn't redraw. \n+ * \n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>}\n+ * featureId - {String}\n+ */\n+ eraseGeometry: function(geometry, featureId) {\n+ this.eraseFeatures(this.features[featureId][0]);\n+ },\n+\n+ /**\n+ * APIMethod: supported\n+ * \n+ * Returns:\n+ * {Boolean} Whether or not the browser supports the renderer class\n+ */\n+ supported: function() {\n+ return OpenLayers.CANVAS_SUPPORTED;\n+ },\n+\n+ /**\n+ * Method: setSize\n+ * Sets the size of the drawing surface.\n+ *\n+ * Once the size is updated, redraw the canvas.\n+ *\n+ * Parameters:\n+ * size - {<OpenLayers.Size>} \n+ */\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ var root = this.root;\n+ root.style.width = size.w + \"px\";\n+ root.style.height = size.h + \"px\";\n+ root.width = size.w;\n+ root.height = size.h;\n+ this.resolution = null;\n+ if (this.hitDetection) {\n+ var hitCanvas = this.hitCanvas;\n+ hitCanvas.style.width = size.w + \"px\";\n+ hitCanvas.style.height = size.h + \"px\";\n+ hitCanvas.width = size.w;\n+ hitCanvas.height = size.h;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawFeature\n+ * Draw the feature. Stores the feature in the features list,\n+ * then redraws the layer. \n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ * style - {<Object>} \n+ *\n+ * Returns:\n+ * {Boolean} The feature has been drawn completely. If the feature has no\n+ * geometry, undefined will be returned. If the feature is not rendered\n+ * for other reasons, false will be returned.\n+ */\n+ drawFeature: function(feature, style) {\n+ var rendered;\n+ if (feature.geometry) {\n+ style = this.applyDefaultSymbolizer(style || feature.style);\n+ // don't render if display none or feature outside extent\n+ var bounds = feature.geometry.getBounds();\n+\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent();\n+ }\n+\n+ var intersects = bounds && bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ });\n+\n+ rendered = (style.display !== \"none\") && !!bounds && intersects;\n+ if (rendered) {\n+ // keep track of what we have rendered for redraw\n+ this.features[feature.id] = [feature, style];\n+ } else {\n+ // remove from features tracked for redraw\n+ delete(this.features[feature.id]);\n+ }\n+ this.pendingRedraw = true;\n+ }\n+ if (this.pendingRedraw && !this.locked) {\n+ this.redraw();\n+ this.pendingRedraw = false;\n+ }\n+ return rendered;\n+ },\n+\n+ /** \n+ * Method: drawGeometry\n+ * Used when looping (in redraw) over the features; draws\n+ * the canvas. \n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry>} \n+ * style - {Object} \n+ */\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ if ((className == \"OpenLayers.Geometry.Collection\") ||\n+ (className == \"OpenLayers.Geometry.MultiPoint\") ||\n+ (className == \"OpenLayers.Geometry.MultiLineString\") ||\n+ (className == \"OpenLayers.Geometry.MultiPolygon\")) {\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ this.drawGeometry(geometry.components[i], style, featureId);\n+ }\n+ return;\n+ }\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ this.drawPoint(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ this.drawLineString(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ this.drawLinearRing(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ this.drawPolygon(geometry, style, featureId);\n+ break;\n+ default:\n+ break;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawExternalGraphic\n+ * Called to draw External graphics. \n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawExternalGraphic: function(geometry, style, featureId) {\n+ var img = new Image();\n+\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ img.title = title;\n+ }\n+\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = (style.graphicXOffset != undefined) ?\n+ style.graphicXOffset : -(0.5 * width);\n+ var yOffset = (style.graphicYOffset != undefined) ?\n+ style.graphicYOffset : -(0.5 * height);\n+\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+\n+ var onLoad = function() {\n+ if (!this.features[featureId]) {\n+ return;\n+ }\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var x = (p0 + xOffset) | 0;\n+ var y = (p1 + yOffset) | 0;\n+ var canvas = this.canvas;\n+ canvas.globalAlpha = opacity;\n+ var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||\n+ (OpenLayers.Renderer.Canvas.drawImageScaleFactor =\n+ /android 2.1/.test(navigator.userAgent.toLowerCase()) ?\n+ // 320 is the screen width of the G1 phone, for\n+ // which drawImage works out of the box.\n+ 320 / window.screen.width : 1\n+ );\n+ canvas.drawImage(\n+ img, x * factor, y * factor, width * factor, height * factor\n+ );\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId);\n+ this.hitContext.fillRect(x, y, width, height);\n+ }\n+ }\n+ };\n+\n+ img.onload = OpenLayers.Function.bind(onLoad, this);\n+ img.src = style.externalGraphic;\n+ },\n+\n+ /**\n+ * Method: drawNamedSymbol\n+ * Called to draw Well Known Graphic Symbol Name. \n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawNamedSymbol: function(geometry, style, featureId) {\n+ var x, y, cx, cy, i, symbolBounds, scaling, angle;\n+ var unscaledStrokeWidth;\n+ var deg2rad = Math.PI / 180.0;\n+\n+ var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n+\n+ if (!symbol) {\n+ throw new Error(style.graphicName + ' is not a valid symbol name');\n+ }\n+\n+ if (!symbol.length || symbol.length < 2) return;\n+\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+\n+ if (isNaN(p0) || isNaN(p1)) return;\n+\n+ // Use rounded line caps\n+ this.canvas.lineCap = \"round\";\n+ this.canvas.lineJoin = \"round\";\n+\n+ if (this.hitDetection) {\n+ this.hitContext.lineCap = \"round\";\n+ this.hitContext.lineJoin = \"round\";\n+ }\n+\n+ // Scale and rotate symbols, using precalculated bounds whenever possible.\n+ if (style.graphicName in this.cachedSymbolBounds) {\n+ symbolBounds = this.cachedSymbolBounds[style.graphicName];\n+ } else {\n+ symbolBounds = new OpenLayers.Bounds();\n+ for (i = 0; i < symbol.length; i += 2) {\n+ symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]));\n+ }\n+ this.cachedSymbolBounds[style.graphicName] = symbolBounds;\n+ }\n+\n+ // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.\n+ // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)\n+ this.canvas.save();\n+ if (this.hitDetection) {\n+ this.hitContext.save();\n+ }\n+\n+ // Step 3: place symbol at the desired location\n+ this.canvas.translate(p0, p1);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(p0, p1);\n+ }\n+\n+ // Step 2a. rotate the symbol if necessary\n+ angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.\n+ if (!isNaN(angle)) {\n+ this.canvas.rotate(angle);\n+ if (this.hitDetection) {\n+ this.hitContext.rotate(angle);\n+ }\n+ }\n+\n+ // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.\n+ scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n+ this.canvas.scale(scaling, scaling);\n+ if (this.hitDetection) {\n+ this.hitContext.scale(scaling, scaling);\n+ }\n+\n+ // Step 1: center the symbol at the origin \n+ cx = symbolBounds.getCenterLonLat().lon;\n+ cy = symbolBounds.getCenterLonLat().lat;\n+ this.canvas.translate(-cx, -cy);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(-cx, -cy);\n+ }\n+\n+ // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)\n+ // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.\n+ unscaledStrokeWidth = style.strokeWidth;\n+ style.strokeWidth = unscaledStrokeWidth / scaling;\n+\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n+ }\n+ this.canvas.closePath();\n+ this.canvas.fill();\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y);\n+ }\n+ this.canvas.closePath();\n+ this.canvas.stroke();\n+\n+\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.hitContext.moveTo(x, y);\n+ this.hitContext.lineTo(x, y);\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.stroke();\n+ }\n+\n+ }\n+\n+ style.strokeWidth = unscaledStrokeWidth;\n+ this.canvas.restore();\n+ if (this.hitDetection) {\n+ this.hitContext.restore();\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n+\n+ /**\n+ * Method: setCanvasStyle\n+ * Prepare the canvas for drawing by setting various global settings.\n+ *\n+ * Parameters:\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * style - {Object} Symbolizer hash\n+ */\n+ setCanvasStyle: function(type, style) {\n+ if (type === \"fill\") {\n+ this.canvas.globalAlpha = style['fillOpacity'];\n+ this.canvas.fillStyle = style['fillColor'];\n+ } else if (type === \"stroke\") {\n+ this.canvas.globalAlpha = style['strokeOpacity'];\n+ this.canvas.strokeStyle = style['strokeColor'];\n+ this.canvas.lineWidth = style['strokeWidth'];\n+ } else {\n+ this.canvas.globalAlpha = 0;\n+ this.canvas.lineWidth = 1;\n+ }\n+ },\n+\n+ /**\n+ * Method: featureIdToHex\n+ * Convert a feature ID string into an RGB hex string.\n+ *\n+ * Parameters:\n+ * featureId - {String} Feature id\n+ *\n+ * Returns:\n+ * {String} RGB hex string.\n+ */\n+ featureIdToHex: function(featureId) {\n+ var id = Number(featureId.split(\"_\").pop()) + 1; // zero for no feature\n+ if (id >= 16777216) {\n+ this.hitOverflow = id - 16777215;\n+ id = id % 16777216 + 1;\n+ }\n+ var hex = \"000000\" + id.toString(16);\n+ var len = hex.length;\n+ hex = \"#\" + hex.substring(len - 6, len);\n+ return hex;\n+ },\n+\n+ /**\n+ * Method: setHitContextStyle\n+ * Prepare the hit canvas for drawing by setting various global settings.\n+ *\n+ * Parameters:\n+ * type - {String} one of 'stroke', 'fill', or 'reset'\n+ * featureId - {String} The feature id.\n+ * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.\n+ */\n+ setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n+ var hex = this.featureIdToHex(featureId);\n+ if (type == \"fill\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.fillStyle = hex;\n+ } else if (type == \"stroke\") {\n+ this.hitContext.globalAlpha = 1.0;\n+ this.hitContext.strokeStyle = hex;\n+ // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol \n+ // on a transformed canvas, so the antialias width bump has to scale as well.\n+ if (typeof strokeScaling === \"undefined\") {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2;\n+ } else {\n+ if (!isNaN(strokeScaling)) {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling;\n+ }\n+ }\n+ } else {\n+ this.hitContext.globalAlpha = 0;\n+ this.hitContext.lineWidth = 1;\n+ }\n+ },\n+\n+ /**\n+ * Method: drawPoint\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawPoint: function(geometry, style, featureId) {\n+ if (style.graphic !== false) {\n+ if (style.externalGraphic) {\n+ this.drawExternalGraphic(geometry, style, featureId);\n+ } else if (style.graphicName && (style.graphicName != \"circle\")) {\n+ this.drawNamedSymbol(geometry, style, featureId);\n+ } else {\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var twoPi = Math.PI * 2;\n+ var radius = style.pointRadius;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.fill();\n+ }\n+ }\n+\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.stroke();\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ }\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: drawLineString\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawLineString: function(geometry, style, featureId) {\n+ style = OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style);\n+ this.drawLinearRing(geometry, style, featureId);\n+ },\n+\n+ /**\n+ * Method: drawLinearRing\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawLinearRing: function(geometry, style, featureId) {\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"fill\");\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\");\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n+\n+ /**\n+ * Method: renderPath\n+ * Render a path with stroke and optional fill.\n+ */\n+ renderPath: function(context, geometry, style, featureId, type) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ context.beginPath();\n+ var start = this.getLocalXY(components[0]);\n+ var x = start[0];\n+ var y = start[1];\n+ if (!isNaN(x) && !isNaN(y)) {\n+ context.moveTo(start[0], start[1]);\n+ for (var i = 1; i < len; ++i) {\n+ var pt = this.getLocalXY(components[i]);\n+ context.lineTo(pt[0], pt[1]);\n+ }\n+ if (type === \"fill\") {\n+ context.fill();\n+ } else {\n+ context.stroke();\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: drawPolygon\n+ * This method is only called by the renderer itself.\n+ * \n+ * Parameters: \n+ * geometry - {<OpenLayers.Geometry>}\n+ * style - {Object}\n+ * featureId - {String}\n+ */\n+ drawPolygon: function(geometry, style, featureId) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ this.drawLinearRing(components[0], style, featureId);\n+ // erase inner rings\n+ for (var i = 1; i < len; ++i) {\n+ /** \n+ * Note that this is overly agressive. Here we punch holes through \n+ * all previously rendered features on the same canvas. A better \n+ * solution for polygons with interior rings would be to draw the \n+ * polygon on a sketch canvas first. We could erase all holes \n+ * there and then copy the drawing to the layer canvas. \n+ * TODO: http://trac.osgeo.org/openlayers/ticket/3130 \n+ */\n+ this.canvas.globalCompositeOperation = \"destination-out\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"destination-out\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ stroke: false,\n+ fillOpacity: 1.0\n+ }, style),\n+ featureId\n+ );\n+ this.canvas.globalCompositeOperation = \"source-over\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"source-over\";\n+ }\n+ this.drawLinearRing(\n+ components[i],\n+ OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style),\n+ featureId\n+ );\n+ }\n+ },\n+\n+ /**\n+ * Method: drawText\n+ * This method is only called by the renderer itself.\n+ *\n+ * Parameters:\n+ * location - {<OpenLayers.Point>}\n+ * style - {Object}\n+ */\n+ drawText: function(location, style) {\n+ var pt = this.getLocalXY(location);\n+\n+ this.setCanvasStyle(\"reset\");\n+ this.canvas.fillStyle = style.fontColor;\n+ this.canvas.globalAlpha = style.fontOpacity || 1.0;\n+ var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\",\n+ \"normal\", // \"font-variant\" not supported\n+ style.fontWeight ? style.fontWeight : \"normal\",\n+ style.fontSize ? style.fontSize : \"1em\",\n+ style.fontFamily ? style.fontFamily : \"sans-serif\"\n+ ].join(\" \");\n+ var labelRows = style.label.split('\\n');\n+ var numRows = labelRows.length;\n+ if (this.canvas.fillText) {\n+ // HTML5\n+ this.canvas.font = fontStyle;\n+ this.canvas.textAlign =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||\n+ \"center\";\n+ this.canvas.textBaseline =\n+ OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||\n+ \"middle\";\n+ var vfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n+ }\n+ var lineHeight =\n+ this.canvas.measureText('Mg').height ||\n+ this.canvas.measureText('xx').width;\n+ pt[1] += lineHeight * vfactor * (numRows - 1);\n+ for (var i = 0; i < numRows; i++) {\n+ if (style.labelOutlineWidth) {\n+ this.canvas.save();\n+ this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;\n+ this.canvas.strokeStyle = style.labelOutlineColor;\n+ this.canvas.lineWidth = style.labelOutlineWidth;\n+ this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1);\n+ this.canvas.restore();\n+ }\n+ this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));\n+ }\n+ } else if (this.canvas.mozDrawText) {\n+ // Mozilla pre-Gecko1.9.1 (<FF3.1)\n+ this.canvas.mozTextStyle = fontStyle;\n+ // No built-in text alignment, so we measure and adjust the position\n+ var hfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n+ if (hfactor == null) {\n+ hfactor = -.5;\n+ }\n+ var vfactor =\n+ OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5;\n+ }\n+ var lineHeight = this.canvas.mozMeasureText('xx');\n+ pt[1] += lineHeight * (1 + (vfactor * numRows));\n+ for (var i = 0; i < numRows; i++) {\n+ var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i]));\n+ var y = pt[1] + (i * lineHeight);\n+ this.canvas.translate(x, y);\n+ this.canvas.mozDrawText(labelRows[i]);\n+ this.canvas.translate(-x, -y);\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\");\n+ },\n+\n+ /**\n+ * Method: getLocalXY\n+ * transform geographic xy into pixel xy\n+ *\n+ * Parameters: \n+ * point - {<OpenLayers.Geometry.Point>}\n+ */\n+ getLocalXY: function(point) {\n+ var resolution = this.getResolution();\n+ var extent = this.extent;\n+ var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));\n+ var y = ((extent.top / resolution) - point.y / resolution);\n+ return [x, y];\n+ },\n+\n+ /**\n+ * Method: clear\n+ * Clear all vectors from the renderer.\n+ */\n+ clear: function() {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ },\n+\n+ /**\n+ * Method: getFeatureIdFromEvent\n+ * Returns a feature id from an event on the renderer. \n+ * \n+ * Parameters:\n+ * evt - {<OpenLayers.Event>} \n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a \n+ * feature instead of a feature id to avoid an unnecessary lookup on the\n+ * layer.\n+ */\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId, feature;\n+\n+ if (this.hitDetection && this.root.style.display !== \"none\") {\n+ // this dragging check should go in the feature handler\n+ if (!this.map.dragging) {\n+ var xy = evt.xy;\n+ var x = xy.x | 0;\n+ var y = xy.y | 0;\n+ var data = this.hitContext.getImageData(x, y, 1, 1).data;\n+ if (data[3] === 255) { // antialiased\n+ var id = data[2] + (256 * (data[1] + (256 * data[0])));\n+ if (id) {\n+ featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n+ try {\n+ feature = this.features[featureId][0];\n+ } catch (err) {\n+ // Because of antialiasing on the canvas, when the hit location is at a point where the edge of\n+ // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.\n+ // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return feature;\n+ },\n+\n+ /**\n+ * Method: eraseFeatures \n+ * This is called by the layer to erase features; removes the feature from\n+ * the list, then redraws the layer.\n+ * \n+ * Parameters:\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ eraseFeatures: function(features) {\n+ if (!(OpenLayers.Util.isArray(features))) {\n+ features = [features];\n+ }\n+ for (var i = 0; i < features.length; ++i) {\n+ delete this.features[features[i].id];\n+ }\n+ this.redraw();\n+ },\n+\n+ /**\n+ * Method: redraw\n+ * The real 'meat' of the function: any time things have changed,\n+ * redraw() can be called to loop over all the data and (you guessed\n+ * it) redraw it. Unlike Elements-based Renderers, we can't interact\n+ * with things once they're drawn, to remove them, for example, so\n+ * instead we have to just clear everything and draw from scratch.\n+ */\n+ redraw: function() {\n+ if (!this.locked) {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height);\n+ }\n+ var labelMap = [];\n+ var feature, geometry, style;\n+ var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();\n+ for (var id in this.features) {\n+ if (!this.features.hasOwnProperty(id)) {\n+ continue;\n+ }\n+ feature = this.features[id][0];\n+ geometry = feature.geometry;\n+ this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n+ style = this.features[id][1];\n+ this.drawGeometry(geometry, style, feature.id);\n+ if (style.label) {\n+ labelMap.push([feature, style]);\n+ }\n+ }\n+ var item;\n+ for (var i = 0, len = labelMap.length; i < len; ++i) {\n+ item = labelMap[i];\n+ this.drawText(item[0].geometry.getCentroid(), item[1]);\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN\n+ * {Object}\n+ */\n+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n+ \"l\": \"left\",\n+ \"r\": \"right\",\n+ \"t\": \"top\",\n+ \"b\": \"bottom\"\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR\n+ * {Object}\n+ */\n+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n+ \"l\": 0,\n+ \"r\": -1,\n+ \"t\": 0,\n+ \"b\": -1\n+};\n+\n+/**\n+ * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor\n+ * {Number} Scale factor to apply to the canvas drawImage arguments. This\n+ * is always 1 except for Android 2.1 devices, to work around\n+ * http://code.google.com/p/android/issues/detail?id=5141.\n+ */\n+OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n+/* ======================================================================\n+ OpenLayers/Strategy.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Strategy\n+ * Abstract vector layer strategy class. Not to be instantiated directly. Use\n+ * one of the strategy subclasses instead.\n+ */\n+OpenLayers.Strategy = OpenLayers.Class({\n+\n+ /**\n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n+ */\n+ layer: null,\n+\n+ /**\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n+ */\n+ options: null,\n+\n+ /** \n+ * Property: active \n+ * {Boolean} The control is active.\n+ */\n+ active: null,\n+\n+ /**\n+ * Property: autoActivate\n+ * {Boolean} The creator of the strategy can set autoActivate to false\n+ * to fully control when the protocol is activated and deactivated.\n+ * Defaults to true.\n+ */\n+ autoActivate: true,\n+\n+ /**\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the strategy can set autoDestroy to false\n+ * to fully control when the strategy is destroyed. Defaults to\n+ * true.\n+ */\n+ autoDestroy: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Strategy\n+ * Abstract class for vector strategies. Create instances of a subclass.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ // set the active property here, so that user cannot override it\n+ this.active = false;\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Clean up the strategy.\n+ */\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null;\n+ },\n+\n+ /**\n+ * Method: setLayer\n+ * Called to set the <layer> property.\n+ *\n+ * Parameters:\n+ * layer - {<OpenLayers.Layer.Vector>}\n+ */\n+ setLayer: function(layer) {\n+ this.layer = layer;\n+ },\n+\n+ /**\n+ * Method: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n+ */\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true;\n+ }\n+ return false;\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n+ */\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true;\n+ }\n+ return false;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n+});\n+/* ======================================================================\n+ OpenLayers/Strategy/Fixed.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Strategy.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Strategy.Fixed\n+ * A simple strategy that requests features once and never requests new data.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Strategy>\n+ */\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n+\n+ /**\n+ * APIProperty: preload\n+ * {Boolean} Load data before layer made visible. Enabling this may result\n+ * in considerable overhead if your application loads many data layers\n+ * that are not visible by default. Default is false.\n+ */\n+ preload: false,\n+\n+ /**\n+ * Constructor: OpenLayers.Strategy.Fixed\n+ * Create a new Fixed strategy.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ */\n+\n+ /**\n+ * Method: activate\n+ * Activate the strategy: load data or add listener to load when visible\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n+ */\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ \"refresh\": this.load,\n+ scope: this\n+ });\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load();\n+ } else {\n+ this.layer.events.on({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ }\n+ return activated;\n+ },\n+\n+ /**\n+ * Method: deactivate\n+ * Deactivate the strategy. Undo what is done in <activate>.\n+ * \n+ * Returns:\n+ * {Boolean} The strategy was successfully deactivated.\n+ */\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ \"refresh\": this.load,\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ }\n+ return deactivated;\n+ },\n+\n+ /**\n+ * Method: load\n+ * Tells protocol to load data and unhooks the visibilitychanged event\n+ *\n+ * Parameters:\n+ * options - {Object} options to pass to protocol read.\n+ */\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n+ });\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ \"visibilitychanged\": this.load,\n+ scope: this\n+ });\n+ },\n+\n+ /**\n+ * Method: merge\n+ * Add all features to the layer.\n+ * If the layer projection differs from the map projection, features\n+ * will be transformed from the layer projection to the map projection.\n+ *\n+ * Parameters:\n+ * resp - {<OpenLayers.Protocol.Response>} The response object passed\n+ * by the protocol.\n+ */\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local);\n+ }\n+ }\n+ }\n+ layer.addFeatures(features);\n+ }\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ });\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n });\n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.mobile.min.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.mobile.min.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -5452,369 +5452,183 @@\n this.tileQueue = null;\n this.tileQueueId = null;\n this.tileCache = null;\n this.tileCacheIndex = null;\n this._destroyed = true\n }\n });\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- isBaseLayer: true,\n- sphericalMercator: false,\n- zoomOffset: 0,\n- serverResolutions: null,\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options)\n- }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = \"\" + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url)\n- }\n- return OpenLayers.String.format(url, xyz)\n- },\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = (x % limit + limit) % limit\n- }\n- return {\n- x: x,\n- y: y,\n- z: z\n- }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n-});\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- name: \"OpenStreetMap\",\n- url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n- sphericalMercator: true,\n- wrapDateLine: true,\n- tileOptions: null,\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options && this.options.tileOptions)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n- }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n- key: null,\n- serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n- metadata: null,\n- protocolRegex: /^http:/i,\n- type: \"Road\",\n- culture: \"en-US\",\n- metadataParams: null,\n- tileOptions: null,\n- protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n+OpenLayers.Format = OpenLayers.Class({\n+ options: null,\n+ externalProjection: null,\n+ internalProjection: null,\n+ data: null,\n+ keepData: false,\n initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: \"anonymous\"\n- }, this.options.tileOptions);\n- this.loadMetadata()\n- },\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script)\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options\n },\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n- }\n- this.addOptions({\n- maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n- numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw()\n- }\n- this.updateAttribution()\n+ destroy: function() {},\n+ read: function(data) {\n+ throw new Error(\"Read not implemented.\")\n },\n- getURL: function(bounds) {\n- if (!this.url) {\n- return\n- }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = \"0\";\n- var mask = 1 << i - 1;\n- if ((x & mask) != 0) {\n- digit++\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++\n- }\n- quadDigits.push(digit)\n- }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl(\"\" + x + y + z, this.url);\n- return OpenLayers.String.format(url, {\n- quadkey: quadKey\n- })\n+ write: function(object) {\n+ throw new Error(\"Write not implemented.\")\n },\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n- return\n- }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \"\n+ CLASS_NAME: \"OpenLayers.Format\"\n+});\n+OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n+ indent: \" \",\n+ space: \" \",\n+ newline: \"\\n\",\n+ level: 0,\n+ pretty: false,\n+ nativeJSON: function() {\n+ return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\")\n+ }(),\n+ read: function(json, filter) {\n+ var object;\n+ if (this.nativeJSON) {\n+ object = JSON.parse(json, filter)\n+ } else try {\n+ if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, \"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, \"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g, \"\"))) {\n+ object = eval(\"(\" + json + \")\");\n+ if (typeof filter === \"function\") {\n+ function walk(k, v) {\n+ if (v && typeof v === \"object\") {\n+ for (var i in v) {\n+ if (v.hasOwnProperty(i)) {\n+ v[i] = walk(i, v[i])\n+ }\n+ }\n+ }\n+ return filter(k, v)\n+ }\n+ object = walk(\"\", object)\n }\n }\n+ } catch (e) {}\n+ if (this.keepData) {\n+ this.data = object\n }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- })\n- },\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution)\n+ return object\n },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options)\n+ write: function(value, pretty) {\n+ this.pretty = !!pretty;\n+ var json = null;\n+ var type = typeof value;\n+ if (this.serialize[type]) {\n+ try {\n+ json = !this.pretty && this.nativeJSON ? JSON.stringify(value) : this.serialize[type].apply(this, [value])\n+ } catch (err) {\n+ OpenLayers.Console.error(\"Trouble serializing: \" + err)\n+ }\n }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- destroy: function() {\n- this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n-});\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined;\n- delete this._callbackId\n-};\n-OpenLayers.Renderer = OpenLayers.Class({\n- container: null,\n- root: null,\n- extent: null,\n- locked: false,\n- size: null,\n- resolution: null,\n- map: null,\n- featureDx: 0,\n- initialize: function(containerID, options) {\n- this.container = OpenLayers.Util.getElement(containerID);\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {\n- this.container = null;\n- this.extent = null;\n- this.size = null;\n- this.resolution = null;\n- this.map = null\n- },\n- supported: function() {\n- return false\n+ return json\n },\n- setExtent: function(extent, resolutionChanged) {\n- this.extent = extent.clone();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio);\n- this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio)\n- }\n- if (resolutionChanged) {\n- this.resolution = null\n+ writeIndent: function() {\n+ var pieces = [];\n+ if (this.pretty) {\n+ for (var i = 0; i < this.level; ++i) {\n+ pieces.push(this.indent)\n+ }\n }\n- return true\n+ return pieces.join(\"\")\n },\n- setSize: function(size) {\n- this.size = size.clone();\n- this.resolution = null\n+ writeNewline: function() {\n+ return this.pretty ? this.newline : \"\"\n },\n- getResolution: function() {\n- this.resolution = this.resolution || this.map.getResolution();\n- return this.resolution\n+ writeSpace: function() {\n+ return this.pretty ? this.space : \"\"\n },\n- drawFeature: function(feature, style) {\n- if (style == null) {\n- style = feature.style\n- }\n- if (feature.geometry) {\n- var bounds = feature.geometry.getBounds();\n- if (bounds) {\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent()\n- }\n- if (!bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- })) {\n- style = {\n- display: \"none\"\n+ serialize: {\n+ object: function(object) {\n+ if (object == null) {\n+ return \"null\"\n+ }\n+ if (object.constructor == Date) {\n+ return this.serialize.date.apply(this, [object])\n+ }\n+ if (object.constructor == Array) {\n+ return this.serialize.array.apply(this, [object])\n+ }\n+ var pieces = [\"{\"];\n+ this.level += 1;\n+ var key, keyJSON, valueJSON;\n+ var addComma = false;\n+ for (key in object) {\n+ if (object.hasOwnProperty(key)) {\n+ keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [key, this.pretty]);\n+ valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [object[key], this.pretty]);\n+ if (keyJSON != null && valueJSON != null) {\n+ if (addComma) {\n+ pieces.push(\",\")\n+ }\n+ pieces.push(this.writeNewline(), this.writeIndent(), keyJSON, \":\", this.writeSpace(), valueJSON);\n+ addComma = true\n }\n- } else {\n- this.calculateFeatureDx(bounds, worldBounds)\n }\n- var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n- if (style.display != \"none\" && style.label && rendered !== false) {\n- var location = feature.geometry.getCentroid();\n- if (style.labelXOffset || style.labelYOffset) {\n- var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n- var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n- var res = this.getResolution();\n- location.move(xOffset * res, yOffset * res)\n+ }\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), \"}\");\n+ return pieces.join(\"\")\n+ },\n+ array: function(array) {\n+ var json;\n+ var pieces = [\"[\"];\n+ this.level += 1;\n+ for (var i = 0, len = array.length; i < len; ++i) {\n+ json = OpenLayers.Format.JSON.prototype.write.apply(this, [array[i], this.pretty]);\n+ if (json != null) {\n+ if (i > 0) {\n+ pieces.push(\",\")\n }\n- this.drawText(feature.id, style, location)\n- } else {\n- this.removeText(feature.id)\n+ pieces.push(this.writeNewline(), this.writeIndent(), json)\n }\n- return rendered\n }\n+ this.level -= 1;\n+ pieces.push(this.writeNewline(), this.writeIndent(), \"]\");\n+ return pieces.join(\"\")\n+ },\n+ string: function(string) {\n+ var m = {\n+ \"\\b\": \"\\\\b\",\n+ \"\\t\": \"\\\\t\",\n+ \"\\n\": \"\\\\n\",\n+ \"\\f\": \"\\\\f\",\n+ \"\\r\": \"\\\\r\",\n+ '\"': '\\\\\"',\n+ \"\\\\\": \"\\\\\\\\\"\n+ };\n+ if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n+ return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n+ var c = m[b];\n+ if (c) {\n+ return c\n+ }\n+ c = b.charCodeAt();\n+ return \"\\\\u00\" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)\n+ }) + '\"'\n+ }\n+ return '\"' + string + '\"'\n+ },\n+ number: function(number) {\n+ return isFinite(number) ? String(number) : \"null\"\n+ },\n+ boolean: function(bool) {\n+ return String(bool)\n+ },\n+ date: function(date) {\n+ function format(number) {\n+ return number < 10 ? \"0\" + number : number\n+ }\n+ return '\"' + date.getFullYear() + \"-\" + format(date.getMonth() + 1) + \"-\" + format(date.getDate()) + \"T\" + format(date.getHours()) + \":\" + format(date.getMinutes()) + \":\" + format(date.getSeconds()) + '\"'\n }\n },\n- calculateFeatureDx: function(bounds, worldBounds) {\n- this.featureDx = 0;\n- if (worldBounds) {\n- var worldWidth = worldBounds.getWidth(),\n- rendererCenterX = (this.extent.left + this.extent.right) / 2,\n- featureCenterX = (bounds.left + bounds.right) / 2,\n- worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n- this.featureDx = worldsAway * worldWidth\n- }\n- },\n- drawGeometry: function(geometry, style, featureId) {},\n- drawText: function(featureId, style, location) {},\n- removeText: function(featureId) {},\n- clear: function() {},\n- getFeatureIdFromEvent: function(evt) {},\n- eraseFeatures: function(features) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- for (var i = 0, len = features.length; i < len; ++i) {\n- var feature = features[i];\n- this.eraseGeometry(feature.geometry, feature.id);\n- this.removeText(feature.id)\n- }\n- },\n- eraseGeometry: function(geometry, featureId) {},\n- moveRoot: function(renderer) {},\n- getRenderLayerId: function() {\n- return this.container.id\n- },\n- applyDefaultSymbolizer: function(symbolizer) {\n- var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer);\n- if (symbolizer.stroke === false) {\n- delete result.strokeWidth;\n- delete result.strokeColor\n- }\n- if (symbolizer.fill === false) {\n- delete result.fillColor\n- }\n- OpenLayers.Util.extend(result, symbolizer);\n- return result\n- },\n- CLASS_NAME: \"OpenLayers.Renderer\"\n+ CLASS_NAME: \"OpenLayers.Format.JSON\"\n });\n-OpenLayers.Renderer.defaultSymbolizer = {\n- fillColor: \"#000000\",\n- strokeColor: \"#000000\",\n- strokeWidth: 2,\n- fillOpacity: 1,\n- strokeOpacity: 1,\n- pointRadius: 0,\n- labelAlign: \"cm\"\n-};\n-OpenLayers.Renderer.symbol = {\n- star: [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75],\n- cross: [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0],\n- x: [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n- square: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n- triangle: [0, 10, 10, 10, 5, 0, 0, 10]\n-};\n OpenLayers.Feature = OpenLayers.Class({\n layer: null,\n id: null,\n lonlat: null,\n data: null,\n marker: null,\n popupClass: null,\n@@ -6080,932 +5894,14 @@\n labelOutlineColor: \"white\",\n labelOutlineWidth: 3\n },\n delete: {\n display: \"none\"\n }\n };\n-OpenLayers.Style = OpenLayers.Class({\n- id: null,\n- name: null,\n- title: null,\n- description: null,\n- layerName: null,\n- isDefault: false,\n- rules: null,\n- context: null,\n- defaultStyle: null,\n- defaultsPerSymbolizer: false,\n- propertyStyles: null,\n- initialize: function(style, options) {\n- OpenLayers.Util.extend(this, options);\n- this.rules = [];\n- if (options && options.rules) {\n- this.addRules(options.rules)\n- }\n- this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n- },\n- destroy: function() {\n- for (var i = 0, len = this.rules.length; i < len; i++) {\n- this.rules[i].destroy();\n- this.rules[i] = null\n- }\n- this.rules = null;\n- this.defaultStyle = null\n- },\n- createSymbolizer: function(feature) {\n- var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n- var rules = this.rules;\n- var rule, context;\n- var elseRules = [];\n- var appliedRules = false;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- rule = rules[i];\n- var applies = rule.evaluate(feature);\n- if (applies) {\n- if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n- elseRules.push(rule)\n- } else {\n- appliedRules = true;\n- this.applySymbolizer(rule, style, feature)\n- }\n- }\n- }\n- if (appliedRules == false && elseRules.length > 0) {\n- appliedRules = true;\n- for (var i = 0, len = elseRules.length; i < len; i++) {\n- this.applySymbolizer(elseRules[i], style, feature)\n- }\n- }\n- if (rules.length > 0 && appliedRules == false) {\n- style.display = \"none\"\n- }\n- if (style.label != null && typeof style.label !== \"string\") {\n- style.label = String(style.label)\n- }\n- return style\n- },\n- applySymbolizer: function(rule, style, feature) {\n- var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n- var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n- if (this.defaultsPerSymbolizer === true) {\n- var defaults = this.defaultStyle;\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: defaults.pointRadius\n- });\n- if (symbolizer.stroke === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- strokeWidth: defaults.strokeWidth,\n- strokeColor: defaults.strokeColor,\n- strokeOpacity: defaults.strokeOpacity,\n- strokeDashstyle: defaults.strokeDashstyle,\n- strokeLinecap: defaults.strokeLinecap\n- })\n- }\n- if (symbolizer.fill === true || symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- fillColor: defaults.fillColor,\n- fillOpacity: defaults.fillOpacity\n- })\n- }\n- if (symbolizer.graphic === true) {\n- OpenLayers.Util.applyDefaults(symbolizer, {\n- pointRadius: this.defaultStyle.pointRadius,\n- externalGraphic: this.defaultStyle.externalGraphic,\n- graphicName: this.defaultStyle.graphicName,\n- graphicOpacity: this.defaultStyle.graphicOpacity,\n- graphicWidth: this.defaultStyle.graphicWidth,\n- graphicHeight: this.defaultStyle.graphicHeight,\n- graphicXOffset: this.defaultStyle.graphicXOffset,\n- graphicYOffset: this.defaultStyle.graphicYOffset\n- })\n- }\n- }\n- return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n- },\n- createLiterals: function(style, feature) {\n- var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n- OpenLayers.Util.extend(context, this.context);\n- for (var i in this.propertyStyles) {\n- style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n- }\n- return style\n- },\n- findPropertyStyles: function() {\n- var propertyStyles = {};\n- var style = this.defaultStyle;\n- this.addPropertyStyles(propertyStyles, style);\n- var rules = this.rules;\n- var symbolizer, value;\n- for (var i = 0, len = rules.length; i < len; i++) {\n- symbolizer = rules[i].symbolizer;\n- for (var key in symbolizer) {\n- value = symbolizer[key];\n- if (typeof value == \"object\") {\n- this.addPropertyStyles(propertyStyles, value)\n- } else {\n- this.addPropertyStyles(propertyStyles, symbolizer);\n- break\n- }\n- }\n- }\n- return propertyStyles\n- },\n- addPropertyStyles: function(propertyStyles, symbolizer) {\n- var property;\n- for (var key in symbolizer) {\n- property = symbolizer[key];\n- if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n- propertyStyles[key] = true\n- }\n- }\n- return propertyStyles\n- },\n- addRules: function(rules) {\n- Array.prototype.push.apply(this.rules, rules);\n- this.propertyStyles = this.findPropertyStyles()\n- },\n- setDefaultStyle: function(style) {\n- this.defaultStyle = style;\n- this.propertyStyles = this.findPropertyStyles()\n- },\n- getSymbolizerPrefix: function(geometry) {\n- var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n- for (var i = 0, len = prefixes.length; i < len; i++) {\n- if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n- return prefixes[i]\n- }\n- }\n- },\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.rules) {\n- options.rules = [];\n- for (var i = 0, len = this.rules.length; i < len; ++i) {\n- options.rules.push(this.rules[i].clone())\n- }\n- }\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n- return new OpenLayers.Style(defaultStyle, options)\n- },\n- CLASS_NAME: \"OpenLayers.Style\"\n-});\n-OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n- if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n- value = OpenLayers.String.format(value, context, [feature, property]);\n- value = isNaN(value) || !value ? value : parseFloat(value)\n- }\n- return value\n-};\n-OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n-OpenLayers.StyleMap = OpenLayers.Class({\n- styles: null,\n- extendDefault: true,\n- initialize: function(style, options) {\n- this.styles = {\n- default: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"default\"]),\n- select: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"select\"]),\n- temporary: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"temporary\"]),\n- delete: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"delete\"])\n- };\n- if (style instanceof OpenLayers.Style) {\n- this.styles[\"default\"] = style;\n- this.styles[\"select\"] = style;\n- this.styles[\"temporary\"] = style;\n- this.styles[\"delete\"] = style\n- } else if (typeof style == \"object\") {\n- for (var key in style) {\n- if (style[key] instanceof OpenLayers.Style) {\n- this.styles[key] = style[key]\n- } else if (typeof style[key] == \"object\") {\n- this.styles[key] = new OpenLayers.Style(style[key])\n- } else {\n- this.styles[\"default\"] = new OpenLayers.Style(style);\n- this.styles[\"select\"] = new OpenLayers.Style(style);\n- this.styles[\"temporary\"] = new OpenLayers.Style(style);\n- this.styles[\"delete\"] = new OpenLayers.Style(style);\n- break\n- }\n- }\n- }\n- OpenLayers.Util.extend(this, options)\n- },\n- destroy: function() {\n- for (var key in this.styles) {\n- this.styles[key].destroy()\n- }\n- this.styles = null\n- },\n- createSymbolizer: function(feature, intent) {\n- if (!feature) {\n- feature = new OpenLayers.Feature.Vector\n- }\n- if (!this.styles[intent]) {\n- intent = \"default\"\n- }\n- feature.renderIntent = intent;\n- var defaultSymbolizer = {};\n- if (this.extendDefault && intent != \"default\") {\n- defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature)\n- }\n- return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature))\n- },\n- addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n- var rules = [];\n- for (var value in symbolizers) {\n- rules.push(new OpenLayers.Rule({\n- symbolizer: symbolizers[value],\n- context: context,\n- filter: new OpenLayers.Filter.Comparison({\n- type: OpenLayers.Filter.Comparison.EQUAL_TO,\n- property: property,\n- value: value\n- })\n- }))\n- }\n- this.styles[renderIntent].addRules(rules)\n- },\n- CLASS_NAME: \"OpenLayers.StyleMap\"\n-});\n-OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n- isBaseLayer: false,\n- isFixed: false,\n- features: null,\n- filter: null,\n- selectedFeatures: null,\n- unrenderedFeatures: null,\n- reportError: true,\n- style: null,\n- styleMap: null,\n- strategies: null,\n- protocol: null,\n- renderers: [\"SVG\", \"VML\", \"Canvas\"],\n- renderer: null,\n- rendererOptions: null,\n- geometryType: null,\n- drawn: false,\n- ratio: 1,\n- initialize: function(name, options) {\n- OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n- if (!this.renderer || !this.renderer.supported()) {\n- this.assignRenderer()\n- }\n- if (!this.renderer || !this.renderer.supported()) {\n- this.renderer = null;\n- this.displayError()\n- }\n- if (!this.styleMap) {\n- this.styleMap = new OpenLayers.StyleMap\n- }\n- this.features = [];\n- this.selectedFeatures = [];\n- this.unrenderedFeatures = {};\n- if (this.strategies) {\n- for (var i = 0, len = this.strategies.length; i < len; i++) {\n- this.strategies[i].setLayer(this)\n- }\n- }\n- },\n- destroy: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoDestroy) {\n- strategy.destroy()\n- }\n- }\n- this.strategies = null\n- }\n- if (this.protocol) {\n- if (this.protocol.autoDestroy) {\n- this.protocol.destroy()\n- }\n- this.protocol = null\n- }\n- this.destroyFeatures();\n- this.features = null;\n- this.selectedFeatures = null;\n- this.unrenderedFeatures = null;\n- if (this.renderer) {\n- this.renderer.destroy()\n- }\n- this.renderer = null;\n- this.geometryType = null;\n- this.drawn = null;\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Vector(this.name, this.getOptions())\n- }\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n- var features = this.features;\n- var len = features.length;\n- var clonedFeatures = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- clonedFeatures[i] = features[i].clone()\n- }\n- obj.features = clonedFeatures;\n- return obj\n- },\n- refresh: function(obj) {\n- if (this.calculateInRange() && this.visibility) {\n- this.events.triggerEvent(\"refresh\", obj)\n- }\n- },\n- assignRenderer: function() {\n- for (var i = 0, len = this.renderers.length; i < len; i++) {\n- var rendererClass = this.renderers[i];\n- var renderer = typeof rendererClass == \"function\" ? rendererClass : OpenLayers.Renderer[rendererClass];\n- if (renderer && renderer.prototype.supported()) {\n- this.renderer = new renderer(this.div, this.rendererOptions);\n- break\n- }\n- }\n- },\n- displayError: function() {\n- if (this.reportError) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n- renderers: this.renderers.join(\"\\n\")\n- }))\n- }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n- if (!this.renderer) {\n- this.map.removeLayer(this)\n- } else {\n- this.renderer.map = this.map;\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize)\n- }\n- },\n- afterAdd: function() {\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.activate()\n- }\n- }\n- }\n- },\n- removeMap: function(map) {\n- this.drawn = false;\n- if (this.strategies) {\n- var strategy, i, len;\n- for (i = 0, len = this.strategies.length; i < len; i++) {\n- strategy = this.strategies[i];\n- if (strategy.autoActivate) {\n- strategy.deactivate()\n- }\n- }\n- }\n- },\n- onMapResize: function() {\n- OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n- var newSize = this.map.getSize();\n- newSize.w = newSize.w * this.ratio;\n- newSize.h = newSize.h * this.ratio;\n- this.renderer.setSize(newSize)\n- },\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n- var coordSysUnchanged = true;\n- if (!dragging) {\n- this.renderer.root.style.visibility = \"hidden\";\n- var viewSize = this.map.getSize(),\n- viewWidth = viewSize.w,\n- viewHeight = viewSize.h,\n- offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2,\n- offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2;\n- offsetLeft += this.map.layerContainerOriginPx.x;\n- offsetLeft = -Math.round(offsetLeft);\n- offsetTop += this.map.layerContainerOriginPx.y;\n- offsetTop = -Math.round(offsetTop);\n- this.div.style.left = offsetLeft + \"px\";\n- this.div.style.top = offsetTop + \"px\";\n- var extent = this.map.getExtent().scale(this.ratio);\n- coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n- this.renderer.root.style.visibility = \"visible\";\n- if (OpenLayers.IS_GECKO === true) {\n- this.div.scrollLeft = this.div.scrollLeft\n- }\n- if (!zoomChanged && coordSysUnchanged) {\n- for (var i in this.unrenderedFeatures) {\n- var feature = this.unrenderedFeatures[i];\n- this.drawFeature(feature)\n- }\n- }\n- }\n- if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n- this.drawn = true;\n- var feature;\n- for (var i = 0, len = this.features.length; i < len; i++) {\n- this.renderer.locked = i !== len - 1;\n- feature = this.features[i];\n- this.drawFeature(feature)\n- }\n- }\n- },\n- display: function(display) {\n- OpenLayers.Layer.prototype.display.apply(this, arguments);\n- var currentDisplay = this.div.style.display;\n- if (currentDisplay != this.renderer.root.style.display) {\n- this.renderer.root.style.display = currentDisplay\n- }\n- },\n- addFeatures: function(features, options) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- var notify = !options || !options.silent;\n- if (notify) {\n- var event = {\n- features: features\n- };\n- var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n- if (ret === false) {\n- return\n- }\n- features = event.features\n- }\n- var featuresAdded = [];\n- for (var i = 0, len = features.length; i < len; i++) {\n- if (i != features.length - 1) {\n- this.renderer.locked = true\n- } else {\n- this.renderer.locked = false\n- }\n- var feature = features[i];\n- if (this.geometryType && !(feature.geometry instanceof this.geometryType)) {\n- throw new TypeError(\"addFeatures: component should be an \" + this.geometryType.prototype.CLASS_NAME)\n- }\n- feature.layer = this;\n- if (!feature.style && this.style) {\n- feature.style = OpenLayers.Util.extend({}, this.style)\n- }\n- if (notify) {\n- if (this.events.triggerEvent(\"beforefeatureadded\", {\n- feature: feature\n- }) === false) {\n- continue\n- }\n- this.preFeatureInsert(feature)\n- }\n- featuresAdded.push(feature);\n- this.features.push(feature);\n- this.drawFeature(feature);\n- if (notify) {\n- this.events.triggerEvent(\"featureadded\", {\n- feature: feature\n- });\n- this.onFeatureInsert(feature)\n- }\n- }\n- if (notify) {\n- this.events.triggerEvent(\"featuresadded\", {\n- features: featuresAdded\n- })\n- }\n- },\n- removeFeatures: function(features, options) {\n- if (!features || features.length === 0) {\n- return\n- }\n- if (features === this.features) {\n- return this.removeAllFeatures(options)\n- }\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n- }\n- if (features === this.selectedFeatures) {\n- features = features.slice()\n- }\n- var notify = !options || !options.silent;\n- if (notify) {\n- this.events.triggerEvent(\"beforefeaturesremoved\", {\n- features: features\n- })\n- }\n- for (var i = features.length - 1; i >= 0; i--) {\n- if (i != 0 && features[i - 1].geometry) {\n- this.renderer.locked = true\n- } else {\n- this.renderer.locked = false\n- }\n- var feature = features[i];\n- delete this.unrenderedFeatures[feature.id];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- })\n- }\n- this.features = OpenLayers.Util.removeItem(this.features, feature);\n- feature.layer = null;\n- if (feature.geometry) {\n- this.renderer.eraseFeatures(feature)\n- }\n- if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n- OpenLayers.Util.removeItem(this.selectedFeatures, feature)\n- }\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- })\n- }\n- }\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- })\n- }\n- },\n- removeAllFeatures: function(options) {\n- var notify = !options || !options.silent;\n- var features = this.features;\n- if (notify) {\n- this.events.triggerEvent(\"beforefeaturesremoved\", {\n- features: features\n- })\n- }\n- var feature;\n- for (var i = features.length - 1; i >= 0; i--) {\n- feature = features[i];\n- if (notify) {\n- this.events.triggerEvent(\"beforefeatureremoved\", {\n- feature: feature\n- })\n- }\n- feature.layer = null;\n- if (notify) {\n- this.events.triggerEvent(\"featureremoved\", {\n- feature: feature\n- })\n- }\n- }\n- this.renderer.clear();\n- this.features = [];\n- this.unrenderedFeatures = {};\n- this.selectedFeatures = [];\n- if (notify) {\n- this.events.triggerEvent(\"featuresremoved\", {\n- features: features\n- })\n- }\n- },\n- destroyFeatures: function(features, options) {\n- var all = features == undefined;\n- if (all) {\n- features = this.features\n- }\n- if (features) {\n- this.removeFeatures(features, options);\n- for (var i = features.length - 1; i >= 0; i--) {\n- features[i].destroy()\n- }\n- }\n- },\n- drawFeature: function(feature, style) {\n- if (!this.drawn) {\n- return\n- }\n- if (typeof style != \"object\") {\n- if (!style && feature.state === OpenLayers.State.DELETE) {\n- style = \"delete\"\n- }\n- var renderIntent = style || feature.renderIntent;\n- style = feature.style || this.style;\n- if (!style) {\n- style = this.styleMap.createSymbolizer(feature, renderIntent)\n- }\n- }\n- var drawn = this.renderer.drawFeature(feature, style);\n- if (drawn === false || drawn === null) {\n- this.unrenderedFeatures[feature.id] = feature\n- } else {\n- delete this.unrenderedFeatures[feature.id]\n- }\n- },\n- eraseFeatures: function(features) {\n- this.renderer.eraseFeatures(features)\n- },\n- getFeatureFromEvent: function(evt) {\n- if (!this.renderer) {\n- throw new Error(\"getFeatureFromEvent called on layer with no \" + \"renderer. This usually means you destroyed a \" + \"layer, but not some handler which is associated \" + \"with it.\")\n- }\n- var feature = null;\n- var featureId = this.renderer.getFeatureIdFromEvent(evt);\n- if (featureId) {\n- if (typeof featureId === \"string\") {\n- feature = this.getFeatureById(featureId)\n- } else {\n- feature = featureId\n- }\n- }\n- return feature\n- },\n- getFeatureBy: function(property, value) {\n- var feature = null;\n- for (var i = 0, len = this.features.length; i < len; ++i) {\n- if (this.features[i][property] == value) {\n- feature = this.features[i];\n- break\n- }\n- }\n- return feature\n- },\n- getFeatureById: function(featureId) {\n- return this.getFeatureBy(\"id\", featureId)\n- },\n- getFeatureByFid: function(featureFid) {\n- return this.getFeatureBy(\"fid\", featureFid)\n- },\n- getFeaturesByAttribute: function(attrName, attrValue) {\n- var i, feature, len = this.features.length,\n- foundFeatures = [];\n- for (i = 0; i < len; i++) {\n- feature = this.features[i];\n- if (feature && feature.attributes) {\n- if (feature.attributes[attrName] === attrValue) {\n- foundFeatures.push(feature)\n- }\n- }\n- }\n- return foundFeatures\n- },\n- onFeatureInsert: function(feature) {},\n- preFeatureInsert: function(feature) {},\n- getDataExtent: function() {\n- var maxExtent = null;\n- var features = this.features;\n- if (features && features.length > 0) {\n- var geometry = null;\n- for (var i = 0, len = features.length; i < len; i++) {\n- geometry = features[i].geometry;\n- if (geometry) {\n- if (maxExtent === null) {\n- maxExtent = new OpenLayers.Bounds\n- }\n- maxExtent.extend(geometry.getBounds())\n- }\n- }\n- }\n- return maxExtent\n- },\n- CLASS_NAME: \"OpenLayers.Layer.Vector\"\n-});\n-OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n- DEFAULT_PARAMS: {\n- service: \"WMS\",\n- version: \"1.1.1\",\n- request: \"GetMap\",\n- styles: \"\",\n- format: \"image/jpeg\"\n- },\n- isBaseLayer: true,\n- encodeBBOX: false,\n- noMagic: false,\n- yx: {},\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- params = OpenLayers.Util.upperCaseObject(params);\n- if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n- params.EXCEPTIONS = \"INIMAGE\"\n- }\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n- if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n- if (options == null || !options.isBaseLayer) {\n- this.isBaseLayer = false\n- }\n- if (this.params.FORMAT == \"image/jpeg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n- }\n- }\n- },\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n- }\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- return obj\n- },\n- reverseAxisOrder: function() {\n- var projCode = this.projection.getCode();\n- return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n- },\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var imageSize = this.getImageSize();\n- var newParams = {};\n- var reverseAxisOrder = this.reverseAxisOrder();\n- newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n- newParams.WIDTH = imageSize.w;\n- newParams.HEIGHT = imageSize.h;\n- var requestString = this.getFullRequestString(newParams);\n- return requestString\n- },\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n- },\n- getFullRequestString: function(newParams, altUrl) {\n- var mapProjection = this.map.getProjectionObject();\n- var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n- var value = projectionCode == \"none\" ? null : projectionCode;\n- if (parseFloat(this.params.VERSION) >= 1.3) {\n- this.params.CRS = value\n- } else {\n- this.params.SRS = value\n- }\n- if (typeof this.params.TRANSPARENT == \"boolean\") {\n- newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n- }\n- return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n- },\n- CLASS_NAME: \"OpenLayers.Layer.WMS\"\n-});\n-OpenLayers.Format = OpenLayers.Class({\n- options: null,\n- externalProjection: null,\n- internalProjection: null,\n- data: null,\n- keepData: false,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options\n- },\n- destroy: function() {},\n- read: function(data) {\n- throw new Error(\"Read not implemented.\")\n- },\n- write: function(object) {\n- throw new Error(\"Write not implemented.\")\n- },\n- CLASS_NAME: \"OpenLayers.Format\"\n-});\n-OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {\n- indent: \" \",\n- space: \" \",\n- newline: \"\\n\",\n- level: 0,\n- pretty: false,\n- nativeJSON: function() {\n- return !!(window.JSON && typeof JSON.parse == \"function\" && typeof JSON.stringify == \"function\")\n- }(),\n- read: function(json, filter) {\n- var object;\n- if (this.nativeJSON) {\n- object = JSON.parse(json, filter)\n- } else try {\n- if (/^[\\],:{}\\s]*$/.test(json.replace(/\\\\[\"\\\\\\/bfnrtu]/g, \"@\").replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, \"]\").replace(/(?:^|:|,)(?:\\s*\\[)+/g, \"\"))) {\n- object = eval(\"(\" + json + \")\");\n- if (typeof filter === \"function\") {\n- function walk(k, v) {\n- if (v && typeof v === \"object\") {\n- for (var i in v) {\n- if (v.hasOwnProperty(i)) {\n- v[i] = walk(i, v[i])\n- }\n- }\n- }\n- return filter(k, v)\n- }\n- object = walk(\"\", object)\n- }\n- }\n- } catch (e) {}\n- if (this.keepData) {\n- this.data = object\n- }\n- return object\n- },\n- write: function(value, pretty) {\n- this.pretty = !!pretty;\n- var json = null;\n- var type = typeof value;\n- if (this.serialize[type]) {\n- try {\n- json = !this.pretty && this.nativeJSON ? JSON.stringify(value) : this.serialize[type].apply(this, [value])\n- } catch (err) {\n- OpenLayers.Console.error(\"Trouble serializing: \" + err)\n- }\n- }\n- return json\n- },\n- writeIndent: function() {\n- var pieces = [];\n- if (this.pretty) {\n- for (var i = 0; i < this.level; ++i) {\n- pieces.push(this.indent)\n- }\n- }\n- return pieces.join(\"\")\n- },\n- writeNewline: function() {\n- return this.pretty ? this.newline : \"\"\n- },\n- writeSpace: function() {\n- return this.pretty ? this.space : \"\"\n- },\n- serialize: {\n- object: function(object) {\n- if (object == null) {\n- return \"null\"\n- }\n- if (object.constructor == Date) {\n- return this.serialize.date.apply(this, [object])\n- }\n- if (object.constructor == Array) {\n- return this.serialize.array.apply(this, [object])\n- }\n- var pieces = [\"{\"];\n- this.level += 1;\n- var key, keyJSON, valueJSON;\n- var addComma = false;\n- for (key in object) {\n- if (object.hasOwnProperty(key)) {\n- keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [key, this.pretty]);\n- valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [object[key], this.pretty]);\n- if (keyJSON != null && valueJSON != null) {\n- if (addComma) {\n- pieces.push(\",\")\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), keyJSON, \":\", this.writeSpace(), valueJSON);\n- addComma = true\n- }\n- }\n- }\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), \"}\");\n- return pieces.join(\"\")\n- },\n- array: function(array) {\n- var json;\n- var pieces = [\"[\"];\n- this.level += 1;\n- for (var i = 0, len = array.length; i < len; ++i) {\n- json = OpenLayers.Format.JSON.prototype.write.apply(this, [array[i], this.pretty]);\n- if (json != null) {\n- if (i > 0) {\n- pieces.push(\",\")\n- }\n- pieces.push(this.writeNewline(), this.writeIndent(), json)\n- }\n- }\n- this.level -= 1;\n- pieces.push(this.writeNewline(), this.writeIndent(), \"]\");\n- return pieces.join(\"\")\n- },\n- string: function(string) {\n- var m = {\n- \"\\b\": \"\\\\b\",\n- \"\\t\": \"\\\\t\",\n- \"\\n\": \"\\\\n\",\n- \"\\f\": \"\\\\f\",\n- \"\\r\": \"\\\\r\",\n- '\"': '\\\\\"',\n- \"\\\\\": \"\\\\\\\\\"\n- };\n- if (/[\"\\\\\\x00-\\x1f]/.test(string)) {\n- return '\"' + string.replace(/([\\x00-\\x1f\\\\\"])/g, function(a, b) {\n- var c = m[b];\n- if (c) {\n- return c\n- }\n- c = b.charCodeAt();\n- return \"\\\\u00\" + Math.floor(c / 16).toString(16) + (c % 16).toString(16)\n- }) + '\"'\n- }\n- return '\"' + string + '\"'\n- },\n- number: function(number) {\n- return isFinite(number) ? String(number) : \"null\"\n- },\n- boolean: function(bool) {\n- return String(bool)\n- },\n- date: function(date) {\n- function format(number) {\n- return number < 10 ? \"0\" + number : number\n- }\n- return '\"' + date.getFullYear() + \"-\" + format(date.getMonth() + 1) + \"-\" + format(date.getDate()) + \"T\" + format(date.getHours()) + \":\" + format(date.getMinutes()) + \":\" + format(date.getSeconds()) + '\"'\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Format.JSON\"\n-});\n OpenLayers.Geometry = OpenLayers.Class({\n id: null,\n parent: null,\n bounds: null,\n initialize: function() {\n this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n@@ -11055,119 +9951,14 @@\n if (geometry && this.multi) {\n geometry = new OpenLayers.Geometry.MultiPolygon([geometry])\n }\n return geometry\n },\n CLASS_NAME: \"OpenLayers.Handler.Polygon\"\n });\n-OpenLayers.Strategy = OpenLayers.Class({\n- layer: null,\n- options: null,\n- active: null,\n- autoActivate: true,\n- autoDestroy: true,\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- this.active = false\n- },\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null\n- },\n- setLayer: function(layer) {\n- this.layer = layer\n- },\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true\n- }\n- return false\n- },\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true\n- }\n- return false\n- },\n- CLASS_NAME: \"OpenLayers.Strategy\"\n-});\n-OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n- preload: false,\n- activate: function() {\n- var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n- if (activated) {\n- this.layer.events.on({\n- refresh: this.load,\n- scope: this\n- });\n- if (this.layer.visibility == true || this.preload) {\n- this.load()\n- } else {\n- this.layer.events.on({\n- visibilitychanged: this.load,\n- scope: this\n- })\n- }\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n- if (deactivated) {\n- this.layer.events.un({\n- refresh: this.load,\n- visibilitychanged: this.load,\n- scope: this\n- })\n- }\n- return deactivated\n- },\n- load: function(options) {\n- var layer = this.layer;\n- layer.events.triggerEvent(\"loadstart\", {\n- filter: layer.filter\n- });\n- layer.protocol.read(OpenLayers.Util.applyDefaults({\n- callback: this.merge,\n- filter: layer.filter,\n- scope: this\n- }, options));\n- layer.events.un({\n- visibilitychanged: this.load,\n- scope: this\n- })\n- },\n- merge: function(resp) {\n- var layer = this.layer;\n- layer.destroyFeatures();\n- var features = resp.features;\n- if (features && features.length > 0) {\n- var remote = layer.projection;\n- var local = layer.map.getProjectionObject();\n- if (!local.equals(remote)) {\n- var geom;\n- for (var i = 0, len = features.length; i < len; ++i) {\n- geom = features[i].geometry;\n- if (geom) {\n- geom.transform(remote, local)\n- }\n- }\n- }\n- layer.addFeatures(features)\n- }\n- layer.events.triggerEvent(\"loadend\", {\n- response: resp\n- })\n- },\n- CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n-});\n OpenLayers.Control = OpenLayers.Class({\n id: null,\n map: null,\n div: null,\n type: null,\n allowSelection: false,\n displayClass: \"\",\n@@ -11276,14 +10067,297 @@\n return false\n },\n CLASS_NAME: \"OpenLayers.Control\"\n });\n OpenLayers.Control.TYPE_BUTTON = 1;\n OpenLayers.Control.TYPE_TOGGLE = 2;\n OpenLayers.Control.TYPE_TOOL = 3;\n+OpenLayers.Events.buttonclick = OpenLayers.Class({\n+ target: null,\n+ events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n+ startRegEx: /^mousedown|touchstart$/,\n+ cancelRegEx: /^touchmove$/,\n+ completeRegEx: /^mouseup|touchend$/,\n+ initialize: function(target) {\n+ this.target = target;\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.register(this.events[i], this, this.buttonClick, {\n+ extension: true\n+ })\n+ }\n+ },\n+ destroy: function() {\n+ for (var i = this.events.length - 1; i >= 0; --i) {\n+ this.target.unregister(this.events[i], this, this.buttonClick)\n+ }\n+ delete this.target\n+ },\n+ getPressedButton: function(element) {\n+ var depth = 3,\n+ button;\n+ do {\n+ if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n+ button = element;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return button\n+ },\n+ ignore: function(element) {\n+ var depth = 3,\n+ ignore = false;\n+ do {\n+ if (element.nodeName.toLowerCase() === \"a\") {\n+ ignore = true;\n+ break\n+ }\n+ element = element.parentNode\n+ } while (--depth > 0 && element);\n+ return ignore\n+ },\n+ buttonClick: function(evt) {\n+ var propagate = true,\n+ element = OpenLayers.Event.element(evt);\n+ if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n+ var button = this.getPressedButton(element);\n+ if (button) {\n+ if (evt.type === \"keydown\") {\n+ switch (evt.keyCode) {\n+ case OpenLayers.Event.KEY_RETURN:\n+ case OpenLayers.Event.KEY_SPACE:\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button\n+ });\n+ OpenLayers.Event.stop(evt);\n+ propagate = false;\n+ break\n+ }\n+ } else if (this.startEvt) {\n+ if (this.completeRegEx.test(evt.type)) {\n+ var pos = OpenLayers.Util.pagePosition(button);\n+ var viewportElement = OpenLayers.Util.getViewportElement();\n+ var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n+ var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n+ pos[0] = pos[0] - scrollLeft;\n+ pos[1] = pos[1] - scrollTop;\n+ this.target.triggerEvent(\"buttonclick\", {\n+ buttonElement: button,\n+ buttonXY: {\n+ x: this.startEvt.clientX - pos[0],\n+ y: this.startEvt.clientY - pos[1]\n+ }\n+ })\n+ }\n+ if (this.cancelRegEx.test(evt.type)) {\n+ delete this.startEvt\n+ }\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n+ }\n+ if (this.startRegEx.test(evt.type)) {\n+ this.startEvt = evt;\n+ OpenLayers.Event.stop(evt);\n+ propagate = false\n+ }\n+ } else {\n+ propagate = !this.ignore(OpenLayers.Event.element(evt));\n+ delete this.startEvt\n+ }\n+ }\n+ return propagate\n+ }\n+});\n+OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n+ controls: null,\n+ autoActivate: true,\n+ defaultControl: null,\n+ saveState: false,\n+ allowDepress: false,\n+ activeState: null,\n+ initialize: function(options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ this.controls = [];\n+ this.activeState = {}\n+ },\n+ destroy: function() {\n+ if (this.map) {\n+ this.map.events.unregister(\"buttonclick\", this, this.onButtonClick)\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n+ ctl = this.controls[i];\n+ if (ctl.events) {\n+ ctl.events.un({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ })\n+ }\n+ ctl.panel_div = null\n+ }\n+ this.activeState = null\n+ },\n+ activate: function() {\n+ if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ if (control === this.defaultControl || this.saveState && this.activeState[control.id]) {\n+ control.activate()\n+ }\n+ }\n+ if (this.saveState === true) {\n+ this.defaultControl = null\n+ }\n+ this.redraw();\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ deactivate: function() {\n+ if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n+ var control;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ control = this.controls[i];\n+ this.activeState[control.id] = control.deactivate()\n+ }\n+ this.redraw();\n+ return true\n+ } else {\n+ return false\n+ }\n+ },\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ if (this.outsideViewport) {\n+ this.events.attachToElement(this.div);\n+ this.events.register(\"buttonclick\", this, this.onButtonClick)\n+ } else {\n+ this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n+ }\n+ this.addControlsToMap(this.controls);\n+ return this.div\n+ },\n+ redraw: function() {\n+ for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n+ this.div.removeChild(this.div.childNodes[i])\n+ }\n+ this.div.innerHTML = \"\";\n+ if (this.active) {\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ this.div.appendChild(this.controls[i].panel_div)\n+ }\n+ }\n+ },\n+ activateControl: function(control) {\n+ if (!this.active) {\n+ return false\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n+ control.trigger();\n+ return\n+ }\n+ if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n+ if (control.active) {\n+ control.deactivate()\n+ } else {\n+ control.activate()\n+ }\n+ return\n+ }\n+ if (this.allowDepress && control.active) {\n+ control.deactivate()\n+ } else {\n+ var c;\n+ for (var i = 0, len = this.controls.length; i < len; i++) {\n+ c = this.controls[i];\n+ if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n+ c.deactivate()\n+ }\n+ }\n+ control.activate()\n+ }\n+ },\n+ addControls: function(controls) {\n+ if (!OpenLayers.Util.isArray(controls)) {\n+ controls = [controls]\n+ }\n+ this.controls = this.controls.concat(controls);\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ var control = controls[i],\n+ element = this.createControlMarkup(control);\n+ OpenLayers.Element.addClass(element, control.displayClass + \"ItemInactive\");\n+ OpenLayers.Element.addClass(element, \"olButton\");\n+ if (control.title != \"\" && !element.title) {\n+ element.title = control.title\n+ }\n+ control.panel_div = element\n+ }\n+ if (this.map) {\n+ this.addControlsToMap(controls);\n+ this.redraw()\n+ }\n+ },\n+ createControlMarkup: function(control) {\n+ return document.createElement(\"div\")\n+ },\n+ addControlsToMap: function(controls) {\n+ var control;\n+ for (var i = 0, len = controls.length; i < len; i++) {\n+ control = controls[i];\n+ if (control.autoActivate === true) {\n+ control.autoActivate = false;\n+ this.map.addControl(control);\n+ control.autoActivate = true\n+ } else {\n+ this.map.addControl(control);\n+ control.deactivate()\n+ }\n+ control.events.on({\n+ activate: this.iconOn,\n+ deactivate: this.iconOff\n+ })\n+ }\n+ },\n+ iconOn: function() {\n+ var d = this.panel_div;\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n+ d.className = d.className.replace(re, \"$1Active\")\n+ },\n+ iconOff: function() {\n+ var d = this.panel_div;\n+ var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n+ d.className = d.className.replace(re, \"$1Inactive\")\n+ },\n+ onButtonClick: function(evt) {\n+ var controls = this.controls,\n+ button = evt.buttonElement;\n+ for (var i = controls.length - 1; i >= 0; --i) {\n+ if (controls[i].panel_div === button) {\n+ this.activateControl(controls[i]);\n+ break\n+ }\n+ }\n+ },\n+ getControlsBy: function(property, match) {\n+ var test = typeof match.test == \"function\";\n+ var found = OpenLayers.Array.filter(this.controls, function(item) {\n+ return item[property] == match || test && match.test(item[property])\n+ });\n+ return found\n+ },\n+ getControlsByName: function(match) {\n+ return this.getControlsBy(\"name\", match)\n+ },\n+ getControlsByClass: function(match) {\n+ return this.getControlsBy(\"CLASS_NAME\", match)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Panel\"\n+});\n OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {\n started: false,\n stopDown: true,\n dragging: false,\n last: null,\n start: null,\n lastMoveEvt: null,\n@@ -11970,14 +11044,57 @@\n },\n CLASS_NAME: \"OpenLayers.Control.ModifyFeature\"\n });\n OpenLayers.Control.ModifyFeature.RESHAPE = 1;\n OpenLayers.Control.ModifyFeature.RESIZE = 2;\n OpenLayers.Control.ModifyFeature.ROTATE = 4;\n OpenLayers.Control.ModifyFeature.DRAG = 8;\n+OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n+ separator: \", \",\n+ template: \"${layers}\",\n+ destroy: function() {\n+ this.map.events.un({\n+ removelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ changebaselayer: this.updateAttribution,\n+ scope: this\n+ });\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments)\n+ },\n+ draw: function() {\n+ OpenLayers.Control.prototype.draw.apply(this, arguments);\n+ this.map.events.on({\n+ changebaselayer: this.updateAttribution,\n+ changelayer: this.updateAttribution,\n+ addlayer: this.updateAttribution,\n+ removelayer: this.updateAttribution,\n+ scope: this\n+ });\n+ this.updateAttribution();\n+ return this.div\n+ },\n+ updateAttribution: function() {\n+ var attributions = [];\n+ if (this.map && this.map.layers) {\n+ for (var i = 0, len = this.map.layers.length; i < len; i++) {\n+ var layer = this.map.layers[i];\n+ if (layer.attribution && layer.getVisibility()) {\n+ if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n+ attributions.push(layer.attribution)\n+ }\n+ }\n+ }\n+ this.div.innerHTML = OpenLayers.String.format(this.template, {\n+ layers: attributions.join(this.separator)\n+ })\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.Attribution\"\n+});\n OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {\n layer: null,\n callbacks: null,\n multi: false,\n featureAdded: function() {},\n initialize: function(layer, handler, options) {\n OpenLayers.Control.prototype.initialize.apply(this, [options]);\n@@ -12117,340 +11234,14 @@\n failure: function(error) {\n this.events.triggerEvent(\"locationfailed\", {\n error: error\n })\n },\n CLASS_NAME: \"OpenLayers.Control.Geolocate\"\n });\n-OpenLayers.Events.buttonclick = OpenLayers.Class({\n- target: null,\n- events: [\"mousedown\", \"mouseup\", \"click\", \"dblclick\", \"touchstart\", \"touchmove\", \"touchend\", \"keydown\"],\n- startRegEx: /^mousedown|touchstart$/,\n- cancelRegEx: /^touchmove$/,\n- completeRegEx: /^mouseup|touchend$/,\n- initialize: function(target) {\n- this.target = target;\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.register(this.events[i], this, this.buttonClick, {\n- extension: true\n- })\n- }\n- },\n- destroy: function() {\n- for (var i = this.events.length - 1; i >= 0; --i) {\n- this.target.unregister(this.events[i], this, this.buttonClick)\n- }\n- delete this.target\n- },\n- getPressedButton: function(element) {\n- var depth = 3,\n- button;\n- do {\n- if (OpenLayers.Element.hasClass(element, \"olButton\")) {\n- button = element;\n- break\n- }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return button\n- },\n- ignore: function(element) {\n- var depth = 3,\n- ignore = false;\n- do {\n- if (element.nodeName.toLowerCase() === \"a\") {\n- ignore = true;\n- break\n- }\n- element = element.parentNode\n- } while (--depth > 0 && element);\n- return ignore\n- },\n- buttonClick: function(evt) {\n- var propagate = true,\n- element = OpenLayers.Event.element(evt);\n- if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf(\"mouse\"))) {\n- var button = this.getPressedButton(element);\n- if (button) {\n- if (evt.type === \"keydown\") {\n- switch (evt.keyCode) {\n- case OpenLayers.Event.KEY_RETURN:\n- case OpenLayers.Event.KEY_SPACE:\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button\n- });\n- OpenLayers.Event.stop(evt);\n- propagate = false;\n- break\n- }\n- } else if (this.startEvt) {\n- if (this.completeRegEx.test(evt.type)) {\n- var pos = OpenLayers.Util.pagePosition(button);\n- var viewportElement = OpenLayers.Util.getViewportElement();\n- var scrollTop = window.pageYOffset || viewportElement.scrollTop;\n- var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;\n- pos[0] = pos[0] - scrollLeft;\n- pos[1] = pos[1] - scrollTop;\n- this.target.triggerEvent(\"buttonclick\", {\n- buttonElement: button,\n- buttonXY: {\n- x: this.startEvt.clientX - pos[0],\n- y: this.startEvt.clientY - pos[1]\n- }\n- })\n- }\n- if (this.cancelRegEx.test(evt.type)) {\n- delete this.startEvt\n- }\n- OpenLayers.Event.stop(evt);\n- propagate = false\n- }\n- if (this.startRegEx.test(evt.type)) {\n- this.startEvt = evt;\n- OpenLayers.Event.stop(evt);\n- propagate = false\n- }\n- } else {\n- propagate = !this.ignore(OpenLayers.Event.element(evt));\n- delete this.startEvt\n- }\n- }\n- return propagate\n- }\n-});\n-OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {\n- controls: null,\n- autoActivate: true,\n- defaultControl: null,\n- saveState: false,\n- allowDepress: false,\n- activeState: null,\n- initialize: function(options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- this.controls = [];\n- this.activeState = {}\n- },\n- destroy: function() {\n- if (this.map) {\n- this.map.events.unregister(\"buttonclick\", this, this.onButtonClick)\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- for (var ctl, i = this.controls.length - 1; i >= 0; i--) {\n- ctl = this.controls[i];\n- if (ctl.events) {\n- ctl.events.un({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- })\n- }\n- ctl.panel_div = null\n- }\n- this.activeState = null\n- },\n- activate: function() {\n- if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- if (control === this.defaultControl || this.saveState && this.activeState[control.id]) {\n- control.activate()\n- }\n- }\n- if (this.saveState === true) {\n- this.defaultControl = null\n- }\n- this.redraw();\n- return true\n- } else {\n- return false\n- }\n- },\n- deactivate: function() {\n- if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {\n- var control;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- control = this.controls[i];\n- this.activeState[control.id] = control.deactivate()\n- }\n- this.redraw();\n- return true\n- } else {\n- return false\n- }\n- },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- if (this.outsideViewport) {\n- this.events.attachToElement(this.div);\n- this.events.register(\"buttonclick\", this, this.onButtonClick)\n- } else {\n- this.map.events.register(\"buttonclick\", this, this.onButtonClick)\n- }\n- this.addControlsToMap(this.controls);\n- return this.div\n- },\n- redraw: function() {\n- for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {\n- this.div.removeChild(this.div.childNodes[i])\n- }\n- this.div.innerHTML = \"\";\n- if (this.active) {\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- this.div.appendChild(this.controls[i].panel_div)\n- }\n- }\n- },\n- activateControl: function(control) {\n- if (!this.active) {\n- return false\n- }\n- if (control.type == OpenLayers.Control.TYPE_BUTTON) {\n- control.trigger();\n- return\n- }\n- if (control.type == OpenLayers.Control.TYPE_TOGGLE) {\n- if (control.active) {\n- control.deactivate()\n- } else {\n- control.activate()\n- }\n- return\n- }\n- if (this.allowDepress && control.active) {\n- control.deactivate()\n- } else {\n- var c;\n- for (var i = 0, len = this.controls.length; i < len; i++) {\n- c = this.controls[i];\n- if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {\n- c.deactivate()\n- }\n- }\n- control.activate()\n- }\n- },\n- addControls: function(controls) {\n- if (!OpenLayers.Util.isArray(controls)) {\n- controls = [controls]\n- }\n- this.controls = this.controls.concat(controls);\n- for (var i = 0, len = controls.length; i < len; i++) {\n- var control = controls[i],\n- element = this.createControlMarkup(control);\n- OpenLayers.Element.addClass(element, control.displayClass + \"ItemInactive\");\n- OpenLayers.Element.addClass(element, \"olButton\");\n- if (control.title != \"\" && !element.title) {\n- element.title = control.title\n- }\n- control.panel_div = element\n- }\n- if (this.map) {\n- this.addControlsToMap(controls);\n- this.redraw()\n- }\n- },\n- createControlMarkup: function(control) {\n- return document.createElement(\"div\")\n- },\n- addControlsToMap: function(controls) {\n- var control;\n- for (var i = 0, len = controls.length; i < len; i++) {\n- control = controls[i];\n- if (control.autoActivate === true) {\n- control.autoActivate = false;\n- this.map.addControl(control);\n- control.autoActivate = true\n- } else {\n- this.map.addControl(control);\n- control.deactivate()\n- }\n- control.events.on({\n- activate: this.iconOn,\n- deactivate: this.iconOff\n- })\n- }\n- },\n- iconOn: function() {\n- var d = this.panel_div;\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Inactive\\\\b\");\n- d.className = d.className.replace(re, \"$1Active\")\n- },\n- iconOff: function() {\n- var d = this.panel_div;\n- var re = new RegExp(\"\\\\b(\" + this.displayClass + \"Item)Active\\\\b\");\n- d.className = d.className.replace(re, \"$1Inactive\")\n- },\n- onButtonClick: function(evt) {\n- var controls = this.controls,\n- button = evt.buttonElement;\n- for (var i = controls.length - 1; i >= 0; --i) {\n- if (controls[i].panel_div === button) {\n- this.activateControl(controls[i]);\n- break\n- }\n- }\n- },\n- getControlsBy: function(property, match) {\n- var test = typeof match.test == \"function\";\n- var found = OpenLayers.Array.filter(this.controls, function(item) {\n- return item[property] == match || test && match.test(item[property])\n- });\n- return found\n- },\n- getControlsByName: function(match) {\n- return this.getControlsBy(\"name\", match)\n- },\n- getControlsByClass: function(match) {\n- return this.getControlsBy(\"CLASS_NAME\", match)\n- },\n- CLASS_NAME: \"OpenLayers.Control.Panel\"\n-});\n-OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {\n- separator: \", \",\n- template: \"${layers}\",\n- destroy: function() {\n- this.map.events.un({\n- removelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- changebaselayer: this.updateAttribution,\n- scope: this\n- });\n- OpenLayers.Control.prototype.destroy.apply(this, arguments)\n- },\n- draw: function() {\n- OpenLayers.Control.prototype.draw.apply(this, arguments);\n- this.map.events.on({\n- changebaselayer: this.updateAttribution,\n- changelayer: this.updateAttribution,\n- addlayer: this.updateAttribution,\n- removelayer: this.updateAttribution,\n- scope: this\n- });\n- this.updateAttribution();\n- return this.div\n- },\n- updateAttribution: function() {\n- var attributions = [];\n- if (this.map && this.map.layers) {\n- for (var i = 0, len = this.map.layers.length; i < len; i++) {\n- var layer = this.map.layers[i];\n- if (layer.attribution && layer.getVisibility()) {\n- if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {\n- attributions.push(layer.attribution)\n- }\n- }\n- }\n- this.div.innerHTML = OpenLayers.String.format(this.template, {\n- layers: attributions.join(this.separator)\n- })\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Control.Attribution\"\n-});\n OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {\n zoomInText: \"+\",\n zoomInId: \"olZoomInLink\",\n zoomOutText: \"\u2212\",\n zoomOutId: \"olZoomOutLink\",\n draw: function() {\n var div = OpenLayers.Control.prototype.draw.apply(this),\n@@ -12505,538 +11296,14 @@\n }\n delete this.zoomInLink;\n delete this.zoomOutLink;\n OpenLayers.Control.prototype.destroy.apply(this)\n },\n CLASS_NAME: \"OpenLayers.Control.Zoom\"\n });\n-OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n- EVENTMAP: {\n- click: {\n- in: \"click\",\n- out: \"clickout\"\n- },\n- mousemove: {\n- in: \"over\",\n- out: \"out\"\n- },\n- dblclick: {\n- in: \"dblclick\",\n- out: null\n- },\n- mousedown: {\n- in: null,\n- out: null\n- },\n- mouseup: {\n- in: null,\n- out: null\n- },\n- touchstart: {\n- in: \"click\",\n- out: \"clickout\"\n- }\n- },\n- feature: null,\n- lastFeature: null,\n- down: null,\n- up: null,\n- clickTolerance: 4,\n- geometryTypes: null,\n- stopClick: true,\n- stopDown: true,\n- stopUp: false,\n- initialize: function(control, layer, callbacks, options) {\n- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n- this.layer = layer\n- },\n- touchstart: function(evt) {\n- this.startTouch();\n- return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n- },\n- touchmove: function(evt) {\n- OpenLayers.Event.preventDefault(evt)\n- },\n- mousedown: function(evt) {\n- if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n- this.down = evt.xy\n- }\n- return this.handle(evt) ? !this.stopDown : true\n- },\n- mouseup: function(evt) {\n- this.up = evt.xy;\n- return this.handle(evt) ? !this.stopUp : true\n- },\n- click: function(evt) {\n- return this.handle(evt) ? !this.stopClick : true\n- },\n- mousemove: function(evt) {\n- if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n- return true\n- }\n- this.handle(evt);\n- return true\n- },\n- dblclick: function(evt) {\n- return !this.handle(evt)\n- },\n- geometryTypeMatches: function(feature) {\n- return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n- },\n- handle: function(evt) {\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n- }\n- var type = evt.type;\n- var handled = false;\n- var previouslyIn = !!this.feature;\n- var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n- this.feature = this.layer.getFeatureFromEvent(evt);\n- if (this.feature && !this.feature.layer) {\n- this.feature = null\n- }\n- if (this.lastFeature && !this.lastFeature.layer) {\n- this.lastFeature = null\n- }\n- if (this.feature) {\n- if (type === \"touchstart\") {\n- OpenLayers.Event.preventDefault(evt)\n- }\n- var inNew = this.feature != this.lastFeature;\n- if (this.geometryTypeMatches(this.feature)) {\n- if (previouslyIn && inNew) {\n- if (this.lastFeature) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- this.triggerCallback(type, \"in\", [this.feature])\n- } else if (!previouslyIn || click) {\n- this.triggerCallback(type, \"in\", [this.feature])\n- }\n- this.lastFeature = this.feature;\n- handled = true\n- } else {\n- if (this.lastFeature && (previouslyIn && inNew || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- this.feature = null\n- }\n- } else if (this.lastFeature && (previouslyIn || click)) {\n- this.triggerCallback(type, \"out\", [this.lastFeature])\n- }\n- return handled\n- },\n- triggerCallback: function(type, mode, args) {\n- var key = this.EVENTMAP[type][mode];\n- if (key) {\n- if (type == \"click\" && this.up && this.down) {\n- var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n- if (dpx <= this.clickTolerance) {\n- this.callback(key, args)\n- }\n- this.up = this.down = null\n- } else {\n- this.callback(key, args)\n- }\n- }\n- },\n- activate: function() {\n- var activated = false;\n- if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n- this.moveLayerToTop();\n- this.map.events.on({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n- });\n- activated = true\n- }\n- return activated\n- },\n- deactivate: function() {\n- var deactivated = false;\n- if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n- this.moveLayerBack();\n- this.feature = null;\n- this.lastFeature = null;\n- this.down = null;\n- this.up = null;\n- this.map.events.un({\n- removelayer: this.handleMapEvents,\n- changelayer: this.handleMapEvents,\n- scope: this\n- });\n- deactivated = true\n- }\n- return deactivated\n- },\n- handleMapEvents: function(evt) {\n- if (evt.type == \"removelayer\" || evt.property == \"order\") {\n- this.moveLayerToTop()\n- }\n- },\n- moveLayerToTop: function() {\n- var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n- this.layer.setZIndex(index)\n- },\n- moveLayerBack: function() {\n- var index = this.layer.getZIndex() - 1;\n- if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n- this.layer.setZIndex(index)\n- } else {\n- this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Handler.Feature\"\n-});\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n- displayInLayerSwitcher: false,\n- layers: null,\n- display: function() {},\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature\n- }\n- }\n- },\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer)\n- },\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n- },\n- collectRoots: function() {\n- var layer;\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer)\n- }\n- }\n- },\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer)\n- }\n- }\n- },\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots()\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n-});\n-OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n- multipleKey: null,\n- toggleKey: null,\n- multiple: false,\n- clickout: true,\n- toggle: false,\n- hover: false,\n- highlightOnly: false,\n- box: false,\n- onBeforeSelect: function() {},\n- onSelect: function() {},\n- onUnselect: function() {},\n- scope: null,\n- geometryTypes: null,\n- layer: null,\n- layers: null,\n- callbacks: null,\n- selectStyle: null,\n- renderIntent: \"select\",\n- handlers: null,\n- initialize: function(layers, options) {\n- OpenLayers.Control.prototype.initialize.apply(this, [options]);\n- if (this.scope === null) {\n- this.scope = this\n- }\n- this.initLayer(layers);\n- var callbacks = {\n- click: this.clickFeature,\n- clickout: this.clickoutFeature\n- };\n- if (this.hover) {\n- callbacks.over = this.overFeature;\n- callbacks.out = this.outFeature\n- }\n- this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n- this.handlers = {\n- feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, {\n- geometryTypes: this.geometryTypes\n- })\n- };\n- if (this.box) {\n- this.handlers.box = new OpenLayers.Handler.Box(this, {\n- done: this.selectBox\n- }, {\n- boxDivClassName: \"olHandlerBoxSelectFeature\"\n- })\n- }\n- },\n- initLayer: function(layers) {\n- if (OpenLayers.Util.isArray(layers)) {\n- this.layers = layers;\n- this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + \"_container\", {\n- layers: layers\n- })\n- } else {\n- this.layer = layers\n- }\n- },\n- destroy: function() {\n- if (this.active && this.layers) {\n- this.map.removeLayer(this.layer)\n- }\n- OpenLayers.Control.prototype.destroy.apply(this, arguments);\n- if (this.layers) {\n- this.layer.destroy()\n- }\n- },\n- activate: function() {\n- if (!this.active) {\n- if (this.layers) {\n- this.map.addLayer(this.layer)\n- }\n- this.handlers.feature.activate();\n- if (this.box && this.handlers.box) {\n- this.handlers.box.activate()\n- }\n- }\n- return OpenLayers.Control.prototype.activate.apply(this, arguments)\n- },\n- deactivate: function() {\n- if (this.active) {\n- this.handlers.feature.deactivate();\n- if (this.handlers.box) {\n- this.handlers.box.deactivate()\n- }\n- if (this.layers) {\n- this.map.removeLayer(this.layer)\n- }\n- }\n- return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n- },\n- unselectAll: function(options) {\n- var layers = this.layers || [this.layer],\n- layer, feature, l, numExcept;\n- for (l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- numExcept = 0;\n- if (layer.selectedFeatures != null) {\n- while (layer.selectedFeatures.length > numExcept) {\n- feature = layer.selectedFeatures[numExcept];\n- if (!options || options.except != feature) {\n- this.unselect(feature)\n- } else {\n- ++numExcept\n- }\n- }\n- }\n- }\n- },\n- clickFeature: function(feature) {\n- if (!this.hover) {\n- var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1;\n- if (selected) {\n- if (this.toggleSelect()) {\n- this.unselect(feature)\n- } else if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- })\n- }\n- } else {\n- if (!this.multipleSelect()) {\n- this.unselectAll({\n- except: feature\n- })\n- }\n- this.select(feature)\n- }\n- }\n- },\n- multipleSelect: function() {\n- return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]\n- },\n- toggleSelect: function() {\n- return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]\n- },\n- clickoutFeature: function(feature) {\n- if (!this.hover && this.clickout) {\n- this.unselectAll()\n- }\n- },\n- overFeature: function(feature) {\n- var layer = feature.layer;\n- if (this.hover) {\n- if (this.highlightOnly) {\n- this.highlight(feature)\n- } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature)\n- }\n- }\n- },\n- outFeature: function(feature) {\n- if (this.hover) {\n- if (this.highlightOnly) {\n- if (feature._lastHighlighter == this.id) {\n- if (feature._prevHighlighter && feature._prevHighlighter != this.id) {\n- delete feature._lastHighlighter;\n- var control = this.map.getControl(feature._prevHighlighter);\n- if (control) {\n- control.highlight(feature)\n- }\n- } else {\n- this.unhighlight(feature)\n- }\n- }\n- } else {\n- this.unselect(feature)\n- }\n- }\n- },\n- highlight: function(feature) {\n- var layer = feature.layer;\n- var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- feature._prevHighlighter = feature._lastHighlighter;\n- feature._lastHighlighter = this.id;\n- var style = this.selectStyle || this.renderIntent;\n- layer.drawFeature(feature, style);\n- this.events.triggerEvent(\"featurehighlighted\", {\n- feature: feature\n- })\n- }\n- },\n- unhighlight: function(feature) {\n- var layer = feature.layer;\n- if (feature._prevHighlighter == undefined) {\n- delete feature._lastHighlighter\n- } else if (feature._prevHighlighter == this.id) {\n- delete feature._prevHighlighter\n- } else {\n- feature._lastHighlighter = feature._prevHighlighter;\n- delete feature._prevHighlighter\n- }\n- layer.drawFeature(feature, feature.style || feature.layer.style || \"default\");\n- this.events.triggerEvent(\"featureunhighlighted\", {\n- feature: feature\n- })\n- },\n- select: function(feature) {\n- var cont = this.onBeforeSelect.call(this.scope, feature);\n- var layer = feature.layer;\n- if (cont !== false) {\n- cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n- feature: feature\n- });\n- if (cont !== false) {\n- layer.selectedFeatures.push(feature);\n- this.highlight(feature);\n- if (!this.handlers.feature.lastFeature) {\n- this.handlers.feature.lastFeature = layer.selectedFeatures[0]\n- }\n- layer.events.triggerEvent(\"featureselected\", {\n- feature: feature\n- });\n- this.onSelect.call(this.scope, feature)\n- }\n- }\n- },\n- unselect: function(feature) {\n- var layer = feature.layer;\n- this.unhighlight(feature);\n- OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n- layer.events.triggerEvent(\"featureunselected\", {\n- feature: feature\n- });\n- this.onUnselect.call(this.scope, feature)\n- },\n- selectBox: function(position) {\n- if (position instanceof OpenLayers.Bounds) {\n- var minXY = this.map.getLonLatFromPixel({\n- x: position.left,\n- y: position.bottom\n- });\n- var maxXY = this.map.getLonLatFromPixel({\n- x: position.right,\n- y: position.top\n- });\n- var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);\n- if (!this.multipleSelect()) {\n- this.unselectAll()\n- }\n- var prevMultiple = this.multiple;\n- this.multiple = true;\n- var layers = this.layers || [this.layer];\n- this.events.triggerEvent(\"boxselectionstart\", {\n- layers: layers\n- });\n- var layer;\n- for (var l = 0; l < layers.length; ++l) {\n- layer = layers[l];\n- for (var i = 0, len = layer.features.length; i < len; ++i) {\n- var feature = layer.features[i];\n- if (!feature.getVisibility()) {\n- continue\n- }\n- if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n- if (bounds.toGeometry().intersects(feature.geometry)) {\n- if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n- this.select(feature)\n- }\n- }\n- }\n- }\n- }\n- this.multiple = prevMultiple;\n- this.events.triggerEvent(\"boxselectionend\", {\n- layers: layers\n- })\n- }\n- },\n- setMap: function(map) {\n- this.handlers.feature.setMap(map);\n- if (this.box) {\n- this.handlers.box.setMap(map)\n- }\n- OpenLayers.Control.prototype.setMap.apply(this, arguments)\n- },\n- setLayer: function(layers) {\n- var isActive = this.active;\n- this.unselectAll();\n- this.deactivate();\n- if (this.layers) {\n- this.layer.destroy();\n- this.layers = null\n- }\n- this.initLayer(layers);\n- this.handlers.feature.layer = this.layer;\n- if (isActive) {\n- this.activate()\n- }\n- },\n- CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n-});\n OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {\n type: OpenLayers.Control.TYPE_TOOL,\n panned: false,\n interval: 0,\n documentDrag: false,\n kinetic: null,\n enableKinetic: true,\n@@ -13476,1464 +11743,1641 @@\n }\n },\n defaultDblClick: function(evt) {\n this.map.zoomTo(this.map.zoom + 1, evt.xy)\n },\n CLASS_NAME: \"OpenLayers.Control.TouchNavigation\"\n });\n-OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n- hitDetection: true,\n- hitOverflow: 0,\n- canvas: null,\n- features: null,\n- pendingRedraw: false,\n- cachedSymbolBounds: {},\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n- this.root = document.createElement(\"canvas\");\n- this.container.appendChild(this.root);\n- this.canvas = this.root.getContext(\"2d\");\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitCanvas = document.createElement(\"canvas\");\n- this.hitContext = this.hitCanvas.getContext(\"2d\")\n+OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {\n+ EVENTMAP: {\n+ click: {\n+ in: \"click\",\n+ out: \"clickout\"\n+ },\n+ mousemove: {\n+ in: \"over\",\n+ out: \"out\"\n+ },\n+ dblclick: {\n+ in: \"dblclick\",\n+ out: null\n+ },\n+ mousedown: {\n+ in: null,\n+ out: null\n+ },\n+ mouseup: {\n+ in: null,\n+ out: null\n+ },\n+ touchstart: {\n+ in: \"click\",\n+ out: \"clickout\"\n }\n },\n- setExtent: function() {\n- OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- return false\n+ feature: null,\n+ lastFeature: null,\n+ down: null,\n+ up: null,\n+ clickTolerance: 4,\n+ geometryTypes: null,\n+ stopClick: true,\n+ stopDown: true,\n+ stopUp: false,\n+ initialize: function(control, layer, callbacks, options) {\n+ OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);\n+ this.layer = layer\n },\n- eraseGeometry: function(geometry, featureId) {\n- this.eraseFeatures(this.features[featureId][0])\n+ touchstart: function(evt) {\n+ this.startTouch();\n+ return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt)\n },\n- supported: function() {\n- return OpenLayers.CANVAS_SUPPORTED\n+ touchmove: function(evt) {\n+ OpenLayers.Event.preventDefault(evt)\n },\n- setSize: function(size) {\n- this.size = size.clone();\n- var root = this.root;\n- root.style.width = size.w + \"px\";\n- root.style.height = size.h + \"px\";\n- root.width = size.w;\n- root.height = size.h;\n- this.resolution = null;\n- if (this.hitDetection) {\n- var hitCanvas = this.hitCanvas;\n- hitCanvas.style.width = size.w + \"px\";\n- hitCanvas.style.height = size.h + \"px\";\n- hitCanvas.width = size.w;\n- hitCanvas.height = size.h\n+ mousedown: function(evt) {\n+ if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {\n+ this.down = evt.xy\n }\n+ return this.handle(evt) ? !this.stopDown : true\n },\n- drawFeature: function(feature, style) {\n- var rendered;\n- if (feature.geometry) {\n- style = this.applyDefaultSymbolizer(style || feature.style);\n- var bounds = feature.geometry.getBounds();\n- var worldBounds;\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- worldBounds = this.map.getMaxExtent()\n- }\n- var intersects = bounds && bounds.intersectsBounds(this.extent, {\n- worldBounds: worldBounds\n- });\n- rendered = style.display !== \"none\" && !!bounds && intersects;\n- if (rendered) {\n- this.features[feature.id] = [feature, style]\n- } else {\n- delete this.features[feature.id]\n- }\n- this.pendingRedraw = true\n- }\n- if (this.pendingRedraw && !this.locked) {\n- this.redraw();\n- this.pendingRedraw = false\n+ mouseup: function(evt) {\n+ this.up = evt.xy;\n+ return this.handle(evt) ? !this.stopUp : true\n+ },\n+ click: function(evt) {\n+ return this.handle(evt) ? !this.stopClick : true\n+ },\n+ mousemove: function(evt) {\n+ if (!this.callbacks[\"over\"] && !this.callbacks[\"out\"]) {\n+ return true\n }\n- return rendered\n+ this.handle(evt);\n+ return true\n },\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n- for (var i = 0; i < geometry.components.length; i++) {\n- this.drawGeometry(geometry.components[i], style, featureId)\n- }\n- return\n+ dblclick: function(evt) {\n+ return !this.handle(evt)\n+ },\n+ geometryTypeMatches: function(feature) {\n+ return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1\n+ },\n+ handle: function(evt) {\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n }\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- this.drawPoint(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- this.drawLineString(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- this.drawLinearRing(geometry, style, featureId);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- this.drawPolygon(geometry, style, featureId);\n- break;\n- default:\n- break\n+ var type = evt.type;\n+ var handled = false;\n+ var previouslyIn = !!this.feature;\n+ var click = type == \"click\" || type == \"dblclick\" || type == \"touchstart\";\n+ this.feature = this.layer.getFeatureFromEvent(evt);\n+ if (this.feature && !this.feature.layer) {\n+ this.feature = null\n }\n- },\n- drawExternalGraphic: function(geometry, style, featureId) {\n- var img = new Image;\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- img.title = title\n+ if (this.lastFeature && !this.lastFeature.layer) {\n+ this.lastFeature = null\n }\n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n- var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n- var opacity = style.graphicOpacity || style.fillOpacity;\n- var onLoad = function() {\n- if (!this.features[featureId]) {\n- return\n+ if (this.feature) {\n+ if (type === \"touchstart\") {\n+ OpenLayers.Event.preventDefault(evt)\n }\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var x = p0 + xOffset | 0;\n- var y = p1 + yOffset | 0;\n- var canvas = this.canvas;\n- canvas.globalAlpha = opacity;\n- var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || (OpenLayers.Renderer.Canvas.drawImageScaleFactor = /android 2.1/.test(navigator.userAgent.toLowerCase()) ? 320 / window.screen.width : 1);\n- canvas.drawImage(img, x * factor, y * factor, width * factor, height * factor);\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId);\n- this.hitContext.fillRect(x, y, width, height)\n+ var inNew = this.feature != this.lastFeature;\n+ if (this.geometryTypeMatches(this.feature)) {\n+ if (previouslyIn && inNew) {\n+ if (this.lastFeature) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n+ }\n+ this.triggerCallback(type, \"in\", [this.feature])\n+ } else if (!previouslyIn || click) {\n+ this.triggerCallback(type, \"in\", [this.feature])\n+ }\n+ this.lastFeature = this.feature;\n+ handled = true\n+ } else {\n+ if (this.lastFeature && (previouslyIn && inNew || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n }\n+ this.feature = null\n }\n- };\n- img.onload = OpenLayers.Function.bind(onLoad, this);\n- img.src = style.externalGraphic\n+ } else if (this.lastFeature && (previouslyIn || click)) {\n+ this.triggerCallback(type, \"out\", [this.lastFeature])\n+ }\n+ return handled\n },\n- drawNamedSymbol: function(geometry, style, featureId) {\n- var x, y, cx, cy, i, symbolBounds, scaling, angle;\n- var unscaledStrokeWidth;\n- var deg2rad = Math.PI / 180;\n- var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n- if (!symbol) {\n- throw new Error(style.graphicName + \" is not a valid symbol name\")\n+ triggerCallback: function(type, mode, args) {\n+ var key = this.EVENTMAP[type][mode];\n+ if (key) {\n+ if (type == \"click\" && this.up && this.down) {\n+ var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));\n+ if (dpx <= this.clickTolerance) {\n+ this.callback(key, args)\n+ }\n+ this.up = this.down = null\n+ } else {\n+ this.callback(key, args)\n+ }\n }\n- if (!symbol.length || symbol.length < 2) return;\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (isNaN(p0) || isNaN(p1)) return;\n- this.canvas.lineCap = \"round\";\n- this.canvas.lineJoin = \"round\";\n- if (this.hitDetection) {\n- this.hitContext.lineCap = \"round\";\n- this.hitContext.lineJoin = \"round\"\n+ },\n+ activate: function() {\n+ var activated = false;\n+ if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {\n+ this.moveLayerToTop();\n+ this.map.events.on({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n+ scope: this\n+ });\n+ activated = true\n }\n- if (style.graphicName in this.cachedSymbolBounds) {\n- symbolBounds = this.cachedSymbolBounds[style.graphicName]\n- } else {\n- symbolBounds = new OpenLayers.Bounds;\n- for (i = 0; i < symbol.length; i += 2) {\n- symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]))\n- }\n- this.cachedSymbolBounds[style.graphicName] = symbolBounds\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = false;\n+ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {\n+ this.moveLayerBack();\n+ this.feature = null;\n+ this.lastFeature = null;\n+ this.down = null;\n+ this.up = null;\n+ this.map.events.un({\n+ removelayer: this.handleMapEvents,\n+ changelayer: this.handleMapEvents,\n+ scope: this\n+ });\n+ deactivated = true\n }\n- this.canvas.save();\n- if (this.hitDetection) {\n- this.hitContext.save()\n+ return deactivated\n+ },\n+ handleMapEvents: function(evt) {\n+ if (evt.type == \"removelayer\" || evt.property == \"order\") {\n+ this.moveLayerToTop()\n }\n- this.canvas.translate(p0, p1);\n- if (this.hitDetection) {\n- this.hitContext.translate(p0, p1)\n+ },\n+ moveLayerToTop: function() {\n+ var index = Math.max(this.map.Z_INDEX_BASE[\"Feature\"] - 1, this.layer.getZIndex()) + 1;\n+ this.layer.setZIndex(index)\n+ },\n+ moveLayerBack: function() {\n+ var index = this.layer.getZIndex() - 1;\n+ if (index >= this.map.Z_INDEX_BASE[\"Feature\"]) {\n+ this.layer.setZIndex(index)\n+ } else {\n+ this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer))\n }\n- angle = deg2rad * style.rotation;\n- if (!isNaN(angle)) {\n- this.canvas.rotate(angle);\n- if (this.hitDetection) {\n- this.hitContext.rotate(angle)\n- }\n+ },\n+ CLASS_NAME: \"OpenLayers.Handler.Feature\"\n+});\n+OpenLayers.Renderer = OpenLayers.Class({\n+ container: null,\n+ root: null,\n+ extent: null,\n+ locked: false,\n+ size: null,\n+ resolution: null,\n+ map: null,\n+ featureDx: 0,\n+ initialize: function(containerID, options) {\n+ this.container = OpenLayers.Util.getElement(containerID);\n+ OpenLayers.Util.extend(this, options)\n+ },\n+ destroy: function() {\n+ this.container = null;\n+ this.extent = null;\n+ this.size = null;\n+ this.resolution = null;\n+ this.map = null\n+ },\n+ supported: function() {\n+ return false\n+ },\n+ setExtent: function(extent, resolutionChanged) {\n+ this.extent = extent.clone();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio);\n+ this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio)\n }\n- scaling = 2 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n- this.canvas.scale(scaling, scaling);\n- if (this.hitDetection) {\n- this.hitContext.scale(scaling, scaling)\n+ if (resolutionChanged) {\n+ this.resolution = null\n }\n- cx = symbolBounds.getCenterLonLat().lon;\n- cy = symbolBounds.getCenterLonLat().lat;\n- this.canvas.translate(-cx, -cy);\n- if (this.hitDetection) {\n- this.hitContext.translate(-cx, -cy)\n+ return true\n+ },\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ this.resolution = null\n+ },\n+ getResolution: function() {\n+ this.resolution = this.resolution || this.map.getResolution();\n+ return this.resolution\n+ },\n+ drawFeature: function(feature, style) {\n+ if (style == null) {\n+ style = feature.style\n }\n- unscaledStrokeWidth = style.strokeWidth;\n- style.strokeWidth = unscaledStrokeWidth / scaling;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y)\n- }\n- this.canvas.closePath();\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.hitContext.lineTo(x, y)\n+ if (feature.geometry) {\n+ var bounds = feature.geometry.getBounds();\n+ if (bounds) {\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent()\n }\n- this.hitContext.closePath();\n- this.hitContext.fill()\n- }\n- }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.canvas.moveTo(x, y);\n- this.canvas.lineTo(x, y)\n- }\n- this.canvas.closePath();\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n- this.hitContext.beginPath();\n- for (i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- if (i == 0) this.hitContext.moveTo(x, y);\n- this.hitContext.lineTo(x, y)\n+ if (!bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ })) {\n+ style = {\n+ display: \"none\"\n+ }\n+ } else {\n+ this.calculateFeatureDx(bounds, worldBounds)\n }\n- this.hitContext.closePath();\n- this.hitContext.stroke()\n+ var rendered = this.drawGeometry(feature.geometry, style, feature.id);\n+ if (style.display != \"none\" && style.label && rendered !== false) {\n+ var location = feature.geometry.getCentroid();\n+ if (style.labelXOffset || style.labelYOffset) {\n+ var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;\n+ var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;\n+ var res = this.getResolution();\n+ location.move(xOffset * res, yOffset * res)\n+ }\n+ this.drawText(feature.id, style, location)\n+ } else {\n+ this.removeText(feature.id)\n+ }\n+ return rendered\n }\n }\n- style.strokeWidth = unscaledStrokeWidth;\n- this.canvas.restore();\n- if (this.hitDetection) {\n- this.hitContext.restore()\n- }\n- this.setCanvasStyle(\"reset\")\n },\n- setCanvasStyle: function(type, style) {\n- if (type === \"fill\") {\n- this.canvas.globalAlpha = style[\"fillOpacity\"];\n- this.canvas.fillStyle = style[\"fillColor\"]\n- } else if (type === \"stroke\") {\n- this.canvas.globalAlpha = style[\"strokeOpacity\"];\n- this.canvas.strokeStyle = style[\"strokeColor\"];\n- this.canvas.lineWidth = style[\"strokeWidth\"]\n- } else {\n- this.canvas.globalAlpha = 0;\n- this.canvas.lineWidth = 1\n+ calculateFeatureDx: function(bounds, worldBounds) {\n+ this.featureDx = 0;\n+ if (worldBounds) {\n+ var worldWidth = worldBounds.getWidth(),\n+ rendererCenterX = (this.extent.left + this.extent.right) / 2,\n+ featureCenterX = (bounds.left + bounds.right) / 2,\n+ worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);\n+ this.featureDx = worldsAway * worldWidth\n }\n },\n- featureIdToHex: function(featureId) {\n- var id = Number(featureId.split(\"_\").pop()) + 1;\n- if (id >= 16777216) {\n- this.hitOverflow = id - 16777215;\n- id = id % 16777216 + 1\n+ drawGeometry: function(geometry, style, featureId) {},\n+ drawText: function(featureId, style, location) {},\n+ removeText: function(featureId) {},\n+ clear: function() {},\n+ getFeatureIdFromEvent: function(evt) {},\n+ eraseFeatures: function(features) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ var feature = features[i];\n+ this.eraseGeometry(feature.geometry, feature.id);\n+ this.removeText(feature.id)\n }\n- var hex = \"000000\" + id.toString(16);\n- var len = hex.length;\n- hex = \"#\" + hex.substring(len - 6, len);\n- return hex\n },\n- setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n- var hex = this.featureIdToHex(featureId);\n- if (type == \"fill\") {\n- this.hitContext.globalAlpha = 1;\n- this.hitContext.fillStyle = hex\n- } else if (type == \"stroke\") {\n- this.hitContext.globalAlpha = 1;\n- this.hitContext.strokeStyle = hex;\n- if (typeof strokeScaling === \"undefined\") {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2\n- } else {\n- if (!isNaN(strokeScaling)) {\n- this.hitContext.lineWidth = symbolizer.strokeWidth + 2 / strokeScaling\n- }\n- }\n- } else {\n- this.hitContext.globalAlpha = 0;\n- this.hitContext.lineWidth = 1\n+ eraseGeometry: function(geometry, featureId) {},\n+ moveRoot: function(renderer) {},\n+ getRenderLayerId: function() {\n+ return this.container.id\n+ },\n+ applyDefaultSymbolizer: function(symbolizer) {\n+ var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer);\n+ if (symbolizer.stroke === false) {\n+ delete result.strokeWidth;\n+ delete result.strokeColor\n+ }\n+ if (symbolizer.fill === false) {\n+ delete result.fillColor\n }\n+ OpenLayers.Util.extend(result, symbolizer);\n+ return result\n },\n- drawPoint: function(geometry, style, featureId) {\n- if (style.graphic !== false) {\n- if (style.externalGraphic) {\n- this.drawExternalGraphic(geometry, style, featureId)\n- } else if (style.graphicName && style.graphicName != \"circle\") {\n- this.drawNamedSymbol(geometry, style, featureId)\n- } else {\n- var pt = this.getLocalXY(geometry);\n- var p0 = pt[0];\n- var p1 = pt[1];\n- if (!isNaN(p0) && !isNaN(p1)) {\n- var twoPi = Math.PI * 2;\n- var radius = style.pointRadius;\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.fill();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.fill()\n- }\n- }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.canvas.beginPath();\n- this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n- this.canvas.stroke();\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.hitContext.beginPath();\n- this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n- this.hitContext.stroke()\n- }\n- this.setCanvasStyle(\"reset\")\n- }\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Renderer\"\n+});\n+OpenLayers.Renderer.defaultSymbolizer = {\n+ fillColor: \"#000000\",\n+ strokeColor: \"#000000\",\n+ strokeWidth: 2,\n+ fillOpacity: 1,\n+ strokeOpacity: 1,\n+ pointRadius: 0,\n+ labelAlign: \"cm\"\n+};\n+OpenLayers.Renderer.symbol = {\n+ star: [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75],\n+ cross: [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0],\n+ x: [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],\n+ square: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],\n+ triangle: [0, 10, 10, 10, 5, 0, 0, 10]\n+};\n+OpenLayers.Style = OpenLayers.Class({\n+ id: null,\n+ name: null,\n+ title: null,\n+ description: null,\n+ layerName: null,\n+ isDefault: false,\n+ rules: null,\n+ context: null,\n+ defaultStyle: null,\n+ defaultsPerSymbolizer: false,\n+ propertyStyles: null,\n+ initialize: function(style, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.rules = [];\n+ if (options && options.rules) {\n+ this.addRules(options.rules)\n }\n+ this.setDefaultStyle(style || OpenLayers.Feature.Vector.style[\"default\"]);\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\")\n },\n- drawLineString: function(geometry, style, featureId) {\n- style = OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style);\n- this.drawLinearRing(geometry, style, featureId)\n+ destroy: function() {\n+ for (var i = 0, len = this.rules.length; i < len; i++) {\n+ this.rules[i].destroy();\n+ this.rules[i] = null\n+ }\n+ this.rules = null;\n+ this.defaultStyle = null\n },\n- drawLinearRing: function(geometry, style, featureId) {\n- if (style.fill !== false) {\n- this.setCanvasStyle(\"fill\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"fill\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"fill\")\n+ createSymbolizer: function(feature) {\n+ var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);\n+ var rules = this.rules;\n+ var rule, context;\n+ var elseRules = [];\n+ var appliedRules = false;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ rule = rules[i];\n+ var applies = rule.evaluate(feature);\n+ if (applies) {\n+ if (rule instanceof OpenLayers.Rule && rule.elseFilter) {\n+ elseRules.push(rule)\n+ } else {\n+ appliedRules = true;\n+ this.applySymbolizer(rule, style, feature)\n+ }\n }\n }\n- if (style.stroke !== false) {\n- this.setCanvasStyle(\"stroke\", style);\n- this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n- if (this.hitDetection) {\n- this.setHitContextStyle(\"stroke\", featureId, style);\n- this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\")\n+ if (appliedRules == false && elseRules.length > 0) {\n+ appliedRules = true;\n+ for (var i = 0, len = elseRules.length; i < len; i++) {\n+ this.applySymbolizer(elseRules[i], style, feature)\n }\n }\n- this.setCanvasStyle(\"reset\")\n+ if (rules.length > 0 && appliedRules == false) {\n+ style.display = \"none\"\n+ }\n+ if (style.label != null && typeof style.label !== \"string\") {\n+ style.label = String(style.label)\n+ }\n+ return style\n },\n- renderPath: function(context, geometry, style, featureId, type) {\n- var components = geometry.components;\n- var len = components.length;\n- context.beginPath();\n- var start = this.getLocalXY(components[0]);\n- var x = start[0];\n- var y = start[1];\n- if (!isNaN(x) && !isNaN(y)) {\n- context.moveTo(start[0], start[1]);\n- for (var i = 1; i < len; ++i) {\n- var pt = this.getLocalXY(components[i]);\n- context.lineTo(pt[0], pt[1])\n+ applySymbolizer: function(rule, style, feature) {\n+ var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];\n+ var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;\n+ if (this.defaultsPerSymbolizer === true) {\n+ var defaults = this.defaultStyle;\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: defaults.pointRadius\n+ });\n+ if (symbolizer.stroke === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ strokeWidth: defaults.strokeWidth,\n+ strokeColor: defaults.strokeColor,\n+ strokeOpacity: defaults.strokeOpacity,\n+ strokeDashstyle: defaults.strokeDashstyle,\n+ strokeLinecap: defaults.strokeLinecap\n+ })\n }\n- if (type === \"fill\") {\n- context.fill()\n- } else {\n- context.stroke()\n+ if (symbolizer.fill === true || symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ fillColor: defaults.fillColor,\n+ fillOpacity: defaults.fillOpacity\n+ })\n+ }\n+ if (symbolizer.graphic === true) {\n+ OpenLayers.Util.applyDefaults(symbolizer, {\n+ pointRadius: this.defaultStyle.pointRadius,\n+ externalGraphic: this.defaultStyle.externalGraphic,\n+ graphicName: this.defaultStyle.graphicName,\n+ graphicOpacity: this.defaultStyle.graphicOpacity,\n+ graphicWidth: this.defaultStyle.graphicWidth,\n+ graphicHeight: this.defaultStyle.graphicHeight,\n+ graphicXOffset: this.defaultStyle.graphicXOffset,\n+ graphicYOffset: this.defaultStyle.graphicYOffset\n+ })\n }\n }\n+ return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature)\n },\n- drawPolygon: function(geometry, style, featureId) {\n- var components = geometry.components;\n- var len = components.length;\n- this.drawLinearRing(components[0], style, featureId);\n- for (var i = 1; i < len; ++i) {\n- this.canvas.globalCompositeOperation = \"destination-out\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"destination-out\"\n- }\n- this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n- stroke: false,\n- fillOpacity: 1\n- }, style), featureId);\n- this.canvas.globalCompositeOperation = \"source-over\";\n- if (this.hitDetection) {\n- this.hitContext.globalCompositeOperation = \"source-over\"\n- }\n- this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n- fill: false\n- }, style), featureId)\n+ createLiterals: function(style, feature) {\n+ var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);\n+ OpenLayers.Util.extend(context, this.context);\n+ for (var i in this.propertyStyles) {\n+ style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i)\n }\n+ return style\n },\n- drawText: function(location, style) {\n- var pt = this.getLocalXY(location);\n- this.setCanvasStyle(\"reset\");\n- this.canvas.fillStyle = style.fontColor;\n- this.canvas.globalAlpha = style.fontOpacity || 1;\n- var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\", \"normal\", style.fontWeight ? style.fontWeight : \"normal\", style.fontSize ? style.fontSize : \"1em\", style.fontFamily ? style.fontFamily : \"sans-serif\"].join(\" \");\n- var labelRows = style.label.split(\"\\n\");\n- var numRows = labelRows.length;\n- if (this.canvas.fillText) {\n- this.canvas.font = fontStyle;\n- this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || \"center\";\n- this.canvas.textBaseline = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || \"middle\";\n- var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5\n- }\n- var lineHeight = this.canvas.measureText(\"Mg\").height || this.canvas.measureText(\"xx\").width;\n- pt[1] += lineHeight * vfactor * (numRows - 1);\n- for (var i = 0; i < numRows; i++) {\n- if (style.labelOutlineWidth) {\n- this.canvas.save();\n- this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1;\n- this.canvas.strokeStyle = style.labelOutlineColor;\n- this.canvas.lineWidth = style.labelOutlineWidth;\n- this.canvas.strokeText(labelRows[i], pt[0], pt[1] + lineHeight * i + 1);\n- this.canvas.restore()\n+ findPropertyStyles: function() {\n+ var propertyStyles = {};\n+ var style = this.defaultStyle;\n+ this.addPropertyStyles(propertyStyles, style);\n+ var rules = this.rules;\n+ var symbolizer, value;\n+ for (var i = 0, len = rules.length; i < len; i++) {\n+ symbolizer = rules[i].symbolizer;\n+ for (var key in symbolizer) {\n+ value = symbolizer[key];\n+ if (typeof value == \"object\") {\n+ this.addPropertyStyles(propertyStyles, value)\n+ } else {\n+ this.addPropertyStyles(propertyStyles, symbolizer);\n+ break\n }\n- this.canvas.fillText(labelRows[i], pt[0], pt[1] + lineHeight * i)\n- }\n- } else if (this.canvas.mozDrawText) {\n- this.canvas.mozTextStyle = fontStyle;\n- var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n- if (hfactor == null) {\n- hfactor = -.5\n }\n- var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n- if (vfactor == null) {\n- vfactor = -.5\n- }\n- var lineHeight = this.canvas.mozMeasureText(\"xx\");\n- pt[1] += lineHeight * (1 + vfactor * numRows);\n- for (var i = 0; i < numRows; i++) {\n- var x = pt[0] + hfactor * this.canvas.mozMeasureText(labelRows[i]);\n- var y = pt[1] + i * lineHeight;\n- this.canvas.translate(x, y);\n- this.canvas.mozDrawText(labelRows[i]);\n- this.canvas.translate(-x, -y)\n+ }\n+ return propertyStyles\n+ },\n+ addPropertyStyles: function(propertyStyles, symbolizer) {\n+ var property;\n+ for (var key in symbolizer) {\n+ property = symbolizer[key];\n+ if (typeof property == \"string\" && property.match(/\\$\\{\\w+\\}/)) {\n+ propertyStyles[key] = true\n }\n }\n- this.setCanvasStyle(\"reset\")\n+ return propertyStyles\n },\n- getLocalXY: function(point) {\n- var resolution = this.getResolution();\n- var extent = this.extent;\n- var x = (point.x - this.featureDx) / resolution + -extent.left / resolution;\n- var y = extent.top / resolution - point.y / resolution;\n- return [x, y]\n+ addRules: function(rules) {\n+ Array.prototype.push.apply(this.rules, rules);\n+ this.propertyStyles = this.findPropertyStyles()\n },\n- clear: function() {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- this.features = {};\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height)\n+ setDefaultStyle: function(style) {\n+ this.defaultStyle = style;\n+ this.propertyStyles = this.findPropertyStyles()\n+ },\n+ getSymbolizerPrefix: function(geometry) {\n+ var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;\n+ for (var i = 0, len = prefixes.length; i < len; i++) {\n+ if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {\n+ return prefixes[i]\n+ }\n }\n },\n- getFeatureIdFromEvent: function(evt) {\n- var featureId, feature;\n- if (this.hitDetection && this.root.style.display !== \"none\") {\n- if (!this.map.dragging) {\n- var xy = evt.xy;\n- var x = xy.x | 0;\n- var y = xy.y | 0;\n- var data = this.hitContext.getImageData(x, y, 1, 1).data;\n- if (data[3] === 255) {\n- var id = data[2] + 256 * (data[1] + 256 * data[0]);\n- if (id) {\n- featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n- try {\n- feature = this.features[featureId][0]\n- } catch (err) {}\n- }\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.rules) {\n+ options.rules = [];\n+ for (var i = 0, len = this.rules.length; i < len; ++i) {\n+ options.rules.push(this.rules[i].clone())\n+ }\n+ }\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);\n+ return new OpenLayers.Style(defaultStyle, options)\n+ },\n+ CLASS_NAME: \"OpenLayers.Style\"\n+});\n+OpenLayers.Style.createLiteral = function(value, context, feature, property) {\n+ if (typeof value == \"string\" && value.indexOf(\"${\") != -1) {\n+ value = OpenLayers.String.format(value, context, [feature, property]);\n+ value = isNaN(value) || !value ? value : parseFloat(value)\n+ }\n+ return value\n+};\n+OpenLayers.Style.SYMBOLIZER_PREFIXES = [\"Point\", \"Line\", \"Polygon\", \"Text\", \"Raster\"];\n+OpenLayers.StyleMap = OpenLayers.Class({\n+ styles: null,\n+ extendDefault: true,\n+ initialize: function(style, options) {\n+ this.styles = {\n+ default: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"default\"]),\n+ select: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"select\"]),\n+ temporary: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"temporary\"]),\n+ delete: new OpenLayers.Style(OpenLayers.Feature.Vector.style[\"delete\"])\n+ };\n+ if (style instanceof OpenLayers.Style) {\n+ this.styles[\"default\"] = style;\n+ this.styles[\"select\"] = style;\n+ this.styles[\"temporary\"] = style;\n+ this.styles[\"delete\"] = style\n+ } else if (typeof style == \"object\") {\n+ for (var key in style) {\n+ if (style[key] instanceof OpenLayers.Style) {\n+ this.styles[key] = style[key]\n+ } else if (typeof style[key] == \"object\") {\n+ this.styles[key] = new OpenLayers.Style(style[key])\n+ } else {\n+ this.styles[\"default\"] = new OpenLayers.Style(style);\n+ this.styles[\"select\"] = new OpenLayers.Style(style);\n+ this.styles[\"temporary\"] = new OpenLayers.Style(style);\n+ this.styles[\"delete\"] = new OpenLayers.Style(style);\n+ break\n }\n }\n }\n- return feature\n+ OpenLayers.Util.extend(this, options)\n },\n- eraseFeatures: function(features) {\n- if (!OpenLayers.Util.isArray(features)) {\n- features = [features]\n+ destroy: function() {\n+ for (var key in this.styles) {\n+ this.styles[key].destroy()\n }\n- for (var i = 0; i < features.length; ++i) {\n- delete this.features[features[i].id]\n+ this.styles = null\n+ },\n+ createSymbolizer: function(feature, intent) {\n+ if (!feature) {\n+ feature = new OpenLayers.Feature.Vector\n }\n- this.redraw()\n+ if (!this.styles[intent]) {\n+ intent = \"default\"\n+ }\n+ feature.renderIntent = intent;\n+ var defaultSymbolizer = {};\n+ if (this.extendDefault && intent != \"default\") {\n+ defaultSymbolizer = this.styles[\"default\"].createSymbolizer(feature)\n+ }\n+ return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature))\n },\n- redraw: function() {\n- if (!this.locked) {\n- var height = this.root.height;\n- var width = this.root.width;\n- this.canvas.clearRect(0, 0, width, height);\n- if (this.hitDetection) {\n- this.hitContext.clearRect(0, 0, width, height)\n- }\n- var labelMap = [];\n- var feature, geometry, style;\n- var worldBounds = this.map.baseLayer && this.map.baseLayer.wrapDateLine && this.map.getMaxExtent();\n- for (var id in this.features) {\n- if (!this.features.hasOwnProperty(id)) {\n- continue\n- }\n- feature = this.features[id][0];\n- geometry = feature.geometry;\n- this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n- style = this.features[id][1];\n- this.drawGeometry(geometry, style, feature.id);\n- if (style.label) {\n- labelMap.push([feature, style])\n- }\n- }\n- var item;\n- for (var i = 0, len = labelMap.length; i < len; ++i) {\n- item = labelMap[i];\n- this.drawText(item[0].geometry.getCentroid(), item[1])\n- }\n+ addUniqueValueRules: function(renderIntent, property, symbolizers, context) {\n+ var rules = [];\n+ for (var value in symbolizers) {\n+ rules.push(new OpenLayers.Rule({\n+ symbolizer: symbolizers[value],\n+ context: context,\n+ filter: new OpenLayers.Filter.Comparison({\n+ type: OpenLayers.Filter.Comparison.EQUAL_TO,\n+ property: property,\n+ value: value\n+ })\n+ }))\n }\n+ this.styles[renderIntent].addRules(rules)\n },\n- CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+ CLASS_NAME: \"OpenLayers.StyleMap\"\n });\n-OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n- l: \"left\",\n- r: \"right\",\n- t: \"top\",\n- b: \"bottom\"\n-};\n-OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n- l: 0,\n- r: -1,\n- t: 0,\n- b: -1\n-};\n-OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n-OpenLayers.ElementsIndexer = OpenLayers.Class({\n- maxZIndex: null,\n- order: null,\n- indices: null,\n- compare: null,\n- initialize: function(yOrdering) {\n- this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n- this.clear()\n- },\n- insert: function(newNode) {\n- if (this.exists(newNode)) {\n- this.remove(newNode)\n+OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {\n+ isBaseLayer: false,\n+ isFixed: false,\n+ features: null,\n+ filter: null,\n+ selectedFeatures: null,\n+ unrenderedFeatures: null,\n+ reportError: true,\n+ style: null,\n+ styleMap: null,\n+ strategies: null,\n+ protocol: null,\n+ renderers: [\"SVG\", \"VML\", \"Canvas\"],\n+ renderer: null,\n+ rendererOptions: null,\n+ geometryType: null,\n+ drawn: false,\n+ ratio: 1,\n+ initialize: function(name, options) {\n+ OpenLayers.Layer.prototype.initialize.apply(this, arguments);\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.assignRenderer()\n }\n- var nodeId = newNode.id;\n- this.determineZIndex(newNode);\n- var leftIndex = -1;\n- var rightIndex = this.order.length;\n- var middle;\n- while (rightIndex - leftIndex > 1) {\n- middle = parseInt((leftIndex + rightIndex) / 2);\n- var placement = this.compare(this, newNode, OpenLayers.Util.getElement(this.order[middle]));\n- if (placement > 0) {\n- leftIndex = middle\n- } else {\n- rightIndex = middle\n+ if (!this.renderer || !this.renderer.supported()) {\n+ this.renderer = null;\n+ this.displayError()\n+ }\n+ if (!this.styleMap) {\n+ this.styleMap = new OpenLayers.StyleMap\n+ }\n+ this.features = [];\n+ this.selectedFeatures = [];\n+ this.unrenderedFeatures = {};\n+ if (this.strategies) {\n+ for (var i = 0, len = this.strategies.length; i < len; i++) {\n+ this.strategies[i].setLayer(this)\n }\n }\n- this.order.splice(rightIndex, 0, nodeId);\n- this.indices[nodeId] = this.getZIndex(newNode);\n- return this.getNextElement(rightIndex)\n },\n- remove: function(node) {\n- var nodeId = node.id;\n- var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);\n- if (arrayIndex >= 0) {\n- this.order.splice(arrayIndex, 1);\n- delete this.indices[nodeId];\n- if (this.order.length > 0) {\n- var lastId = this.order[this.order.length - 1];\n- this.maxZIndex = this.indices[lastId]\n- } else {\n- this.maxZIndex = 0\n+ destroy: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoDestroy) {\n+ strategy.destroy()\n+ }\n }\n+ this.strategies = null\n }\n+ if (this.protocol) {\n+ if (this.protocol.autoDestroy) {\n+ this.protocol.destroy()\n+ }\n+ this.protocol = null\n+ }\n+ this.destroyFeatures();\n+ this.features = null;\n+ this.selectedFeatures = null;\n+ this.unrenderedFeatures = null;\n+ if (this.renderer) {\n+ this.renderer.destroy()\n+ }\n+ this.renderer = null;\n+ this.geometryType = null;\n+ this.drawn = null;\n+ OpenLayers.Layer.prototype.destroy.apply(this, arguments)\n },\n- clear: function() {\n- this.order = [];\n- this.indices = {};\n- this.maxZIndex = 0\n- },\n- exists: function(node) {\n- return this.indices[node.id] != null\n- },\n- getZIndex: function(node) {\n- return node._style.graphicZIndex\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Vector(this.name, this.getOptions())\n+ }\n+ obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n+ var features = this.features;\n+ var len = features.length;\n+ var clonedFeatures = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ clonedFeatures[i] = features[i].clone()\n+ }\n+ obj.features = clonedFeatures;\n+ return obj\n },\n- determineZIndex: function(node) {\n- var zIndex = node._style.graphicZIndex;\n- if (zIndex == null) {\n- zIndex = this.maxZIndex;\n- node._style.graphicZIndex = zIndex\n- } else if (zIndex > this.maxZIndex) {\n- this.maxZIndex = zIndex\n+ refresh: function(obj) {\n+ if (this.calculateInRange() && this.visibility) {\n+ this.events.triggerEvent(\"refresh\", obj)\n }\n },\n- getNextElement: function(index) {\n- var nextIndex = index + 1;\n- if (nextIndex < this.order.length) {\n- var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);\n- if (nextElement == undefined) {\n- nextElement = this.getNextElement(nextIndex)\n+ assignRenderer: function() {\n+ for (var i = 0, len = this.renderers.length; i < len; i++) {\n+ var rendererClass = this.renderers[i];\n+ var renderer = typeof rendererClass == \"function\" ? rendererClass : OpenLayers.Renderer[rendererClass];\n+ if (renderer && renderer.prototype.supported()) {\n+ this.renderer = new renderer(this.div, this.rendererOptions);\n+ break\n }\n- return nextElement\n- } else {\n- return null\n }\n },\n- CLASS_NAME: \"OpenLayers.ElementsIndexer\"\n-});\n-OpenLayers.ElementsIndexer.IndexingMethods = {\n- Z_ORDER: function(indexer, newNode, nextNode) {\n- var newZIndex = indexer.getZIndex(newNode);\n- var returnVal = 0;\n- if (nextNode) {\n- var nextZIndex = indexer.getZIndex(nextNode);\n- returnVal = newZIndex - nextZIndex\n+ displayError: function() {\n+ if (this.reportError) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"browserNotSupported\", {\n+ renderers: this.renderers.join(\"\\n\")\n+ }))\n }\n- return returnVal\n },\n- Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {\n- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);\n- if (nextNode && returnVal == 0) {\n- returnVal = 1\n+ setMap: function(map) {\n+ OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n+ if (!this.renderer) {\n+ this.map.removeLayer(this)\n+ } else {\n+ this.renderer.map = this.map;\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize)\n }\n- return returnVal\n },\n- Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {\n- var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);\n- if (nextNode && returnVal === 0) {\n- var result = nextNode._boundsBottom - newNode._boundsBottom;\n- returnVal = result === 0 ? 1 : result\n+ afterAdd: function() {\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.activate()\n+ }\n+ }\n }\n- return returnVal\n- }\n-};\n-OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {\n- rendererRoot: null,\n- root: null,\n- vectorRoot: null,\n- textRoot: null,\n- xmlns: null,\n- xOffset: 0,\n- indexer: null,\n- BACKGROUND_ID_SUFFIX: \"_background\",\n- LABEL_ID_SUFFIX: \"_label\",\n- LABEL_OUTLINE_SUFFIX: \"_outline\",\n- initialize: function(containerID, options) {\n- OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n- this.rendererRoot = this.createRenderRoot();\n- this.root = this.createRoot(\"_root\");\n- this.vectorRoot = this.createRoot(\"_vroot\");\n- this.textRoot = this.createRoot(\"_troot\");\n- this.root.appendChild(this.vectorRoot);\n- this.root.appendChild(this.textRoot);\n- this.rendererRoot.appendChild(this.root);\n- this.container.appendChild(this.rendererRoot);\n- if (options && (options.zIndexing || options.yOrdering)) {\n- this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering)\n+ },\n+ removeMap: function(map) {\n+ this.drawn = false;\n+ if (this.strategies) {\n+ var strategy, i, len;\n+ for (i = 0, len = this.strategies.length; i < len; i++) {\n+ strategy = this.strategies[i];\n+ if (strategy.autoActivate) {\n+ strategy.deactivate()\n+ }\n+ }\n }\n },\n- destroy: function() {\n- this.clear();\n- this.rendererRoot = null;\n- this.root = null;\n- this.xmlns = null;\n- OpenLayers.Renderer.prototype.destroy.apply(this, arguments)\n+ onMapResize: function() {\n+ OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);\n+ var newSize = this.map.getSize();\n+ newSize.w = newSize.w * this.ratio;\n+ newSize.h = newSize.h * this.ratio;\n+ this.renderer.setSize(newSize)\n },\n- clear: function() {\n- var child;\n- var root = this.vectorRoot;\n- if (root) {\n- while (child = root.firstChild) {\n- root.removeChild(child)\n+ moveTo: function(bounds, zoomChanged, dragging) {\n+ OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n+ var coordSysUnchanged = true;\n+ if (!dragging) {\n+ this.renderer.root.style.visibility = \"hidden\";\n+ var viewSize = this.map.getSize(),\n+ viewWidth = viewSize.w,\n+ viewHeight = viewSize.h,\n+ offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2,\n+ offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2;\n+ offsetLeft += this.map.layerContainerOriginPx.x;\n+ offsetLeft = -Math.round(offsetLeft);\n+ offsetTop += this.map.layerContainerOriginPx.y;\n+ offsetTop = -Math.round(offsetTop);\n+ this.div.style.left = offsetLeft + \"px\";\n+ this.div.style.top = offsetTop + \"px\";\n+ var extent = this.map.getExtent().scale(this.ratio);\n+ coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);\n+ this.renderer.root.style.visibility = \"visible\";\n+ if (OpenLayers.IS_GECKO === true) {\n+ this.div.scrollLeft = this.div.scrollLeft\n }\n- }\n- root = this.textRoot;\n- if (root) {\n- while (child = root.firstChild) {\n- root.removeChild(child)\n+ if (!zoomChanged && coordSysUnchanged) {\n+ for (var i in this.unrenderedFeatures) {\n+ var feature = this.unrenderedFeatures[i];\n+ this.drawFeature(feature)\n+ }\n }\n }\n- if (this.indexer) {\n- this.indexer.clear()\n+ if (!this.drawn || zoomChanged || !coordSysUnchanged) {\n+ this.drawn = true;\n+ var feature;\n+ for (var i = 0, len = this.features.length; i < len; i++) {\n+ this.renderer.locked = i !== len - 1;\n+ feature = this.features[i];\n+ this.drawFeature(feature)\n+ }\n }\n },\n- setExtent: function(extent, resolutionChanged) {\n- var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n- var resolution = this.getResolution();\n- if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n- var rightOfDateLine, ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n- extent = extent.scale(1 / ratio),\n- world = this.map.getMaxExtent();\n- if (world.right > extent.left && world.right < extent.right) {\n- rightOfDateLine = true\n- } else if (world.left > extent.left && world.left < extent.right) {\n- rightOfDateLine = false\n- }\n- if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {\n- coordSysUnchanged = false;\n- this.xOffset = rightOfDateLine === true ? world.getWidth() / resolution : 0\n- }\n- this.rightOfDateLine = rightOfDateLine\n+ display: function(display) {\n+ OpenLayers.Layer.prototype.display.apply(this, arguments);\n+ var currentDisplay = this.div.style.display;\n+ if (currentDisplay != this.renderer.root.style.display) {\n+ this.renderer.root.style.display = currentDisplay\n }\n- return coordSysUnchanged\n },\n- getNodeType: function(geometry, style) {},\n- drawGeometry: function(geometry, style, featureId) {\n- var className = geometry.CLASS_NAME;\n- var rendered = true;\n- if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n- for (var i = 0, len = geometry.components.length; i < len; i++) {\n- rendered = this.drawGeometry(geometry.components[i], style, featureId) && rendered\n+ addFeatures: function(features, options) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ var event = {\n+ features: features\n+ };\n+ var ret = this.events.triggerEvent(\"beforefeaturesadded\", event);\n+ if (ret === false) {\n+ return\n }\n- return rendered\n+ features = event.features\n }\n- rendered = false;\n- var removeBackground = false;\n- if (style.display != \"none\") {\n- if (style.backgroundGraphic) {\n- this.redrawBackgroundNode(geometry.id, geometry, style, featureId)\n+ var featuresAdded = [];\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ if (i != features.length - 1) {\n+ this.renderer.locked = true\n } else {\n- removeBackground = true\n+ this.renderer.locked = false\n }\n- rendered = this.redrawNode(geometry.id, geometry, style, featureId)\n- }\n- if (rendered == false) {\n- var node = document.getElementById(geometry.id);\n- if (node) {\n- if (node._style.backgroundGraphic) {\n- removeBackground = true\n+ var feature = features[i];\n+ if (this.geometryType && !(feature.geometry instanceof this.geometryType)) {\n+ throw new TypeError(\"addFeatures: component should be an \" + this.geometryType.prototype.CLASS_NAME)\n+ }\n+ feature.layer = this;\n+ if (!feature.style && this.style) {\n+ feature.style = OpenLayers.Util.extend({}, this.style)\n+ }\n+ if (notify) {\n+ if (this.events.triggerEvent(\"beforefeatureadded\", {\n+ feature: feature\n+ }) === false) {\n+ continue\n }\n- node.parentNode.removeChild(node)\n+ this.preFeatureInsert(feature)\n }\n- }\n- if (removeBackground) {\n- var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX);\n- if (node) {\n- node.parentNode.removeChild(node)\n+ featuresAdded.push(feature);\n+ this.features.push(feature);\n+ this.drawFeature(feature);\n+ if (notify) {\n+ this.events.triggerEvent(\"featureadded\", {\n+ feature: feature\n+ });\n+ this.onFeatureInsert(feature)\n }\n }\n- return rendered\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresadded\", {\n+ features: featuresAdded\n+ })\n+ }\n },\n- redrawNode: function(id, geometry, style, featureId) {\n- style = this.applyDefaultSymbolizer(style);\n- var node = this.nodeFactory(id, this.getNodeType(geometry, style));\n- node._featureId = featureId;\n- node._boundsBottom = geometry.getBounds().bottom;\n- node._geometryClass = geometry.CLASS_NAME;\n- node._style = style;\n- var drawResult = this.drawGeometryNode(node, geometry, style);\n- if (drawResult === false) {\n- return false\n+ removeFeatures: function(features, options) {\n+ if (!features || features.length === 0) {\n+ return\n }\n- node = drawResult.node;\n- if (this.indexer) {\n- var insert = this.indexer.insert(node);\n- if (insert) {\n- this.vectorRoot.insertBefore(node, insert)\n+ if (features === this.features) {\n+ return this.removeAllFeatures(options)\n+ }\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ if (features === this.selectedFeatures) {\n+ features = features.slice()\n+ }\n+ var notify = !options || !options.silent;\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeaturesremoved\", {\n+ features: features\n+ })\n+ }\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ if (i != 0 && features[i - 1].geometry) {\n+ this.renderer.locked = true\n } else {\n- this.vectorRoot.appendChild(node)\n+ this.renderer.locked = false\n }\n- } else {\n- if (node.parentNode !== this.vectorRoot) {\n- this.vectorRoot.appendChild(node)\n+ var feature = features[i];\n+ delete this.unrenderedFeatures[feature.id];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ this.features = OpenLayers.Util.removeItem(this.features, feature);\n+ feature.layer = null;\n+ if (feature.geometry) {\n+ this.renderer.eraseFeatures(feature)\n+ }\n+ if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {\n+ OpenLayers.Util.removeItem(this.selectedFeatures, feature)\n+ }\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ })\n }\n }\n- this.postDraw(node);\n- return drawResult.complete\n- },\n- redrawBackgroundNode: function(id, geometry, style, featureId) {\n- var backgroundStyle = OpenLayers.Util.extend({}, style);\n- backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;\n- backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;\n- backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;\n- backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;\n- backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;\n- backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;\n- backgroundStyle.backgroundGraphic = null;\n- backgroundStyle.backgroundXOffset = null;\n- backgroundStyle.backgroundYOffset = null;\n- backgroundStyle.backgroundGraphicZIndex = null;\n- return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null)\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ })\n+ }\n },\n- drawGeometryNode: function(node, geometry, style) {\n- style = style || node._style;\n- var options = {\n- isFilled: style.fill === undefined ? true : style.fill,\n- isStroked: style.stroke === undefined ? !!style.strokeWidth : style.stroke\n- };\n- var drawn;\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- if (style.graphic === false) {\n- options.isFilled = false;\n- options.isStroked = false\n- }\n- drawn = this.drawPoint(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- options.isFilled = false;\n- drawn = this.drawLineString(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- drawn = this.drawLinearRing(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- drawn = this.drawPolygon(node, geometry);\n- break;\n- case \"OpenLayers.Geometry.Rectangle\":\n- drawn = this.drawRectangle(node, geometry);\n- break;\n- default:\n- break\n+ removeAllFeatures: function(options) {\n+ var notify = !options || !options.silent;\n+ var features = this.features;\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeaturesremoved\", {\n+ features: features\n+ })\n }\n- node._options = options;\n- if (drawn != false) {\n- return {\n- node: this.setStyle(node, style, options, geometry),\n- complete: drawn\n+ var feature;\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ feature = features[i];\n+ if (notify) {\n+ this.events.triggerEvent(\"beforefeatureremoved\", {\n+ feature: feature\n+ })\n }\n- } else {\n- return false\n+ feature.layer = null;\n+ if (notify) {\n+ this.events.triggerEvent(\"featureremoved\", {\n+ feature: feature\n+ })\n+ }\n+ }\n+ this.renderer.clear();\n+ this.features = [];\n+ this.unrenderedFeatures = {};\n+ this.selectedFeatures = [];\n+ if (notify) {\n+ this.events.triggerEvent(\"featuresremoved\", {\n+ features: features\n+ })\n }\n },\n- postDraw: function(node) {},\n- drawPoint: function(node, geometry) {},\n- drawLineString: function(node, geometry) {},\n- drawLinearRing: function(node, geometry) {},\n- drawPolygon: function(node, geometry) {},\n- drawRectangle: function(node, geometry) {},\n- drawCircle: function(node, geometry) {},\n- removeText: function(featureId) {\n- var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);\n- if (label) {\n- this.textRoot.removeChild(label)\n+ destroyFeatures: function(features, options) {\n+ var all = features == undefined;\n+ if (all) {\n+ features = this.features\n }\n- var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);\n- if (outline) {\n- this.textRoot.removeChild(outline)\n+ if (features) {\n+ this.removeFeatures(features, options);\n+ for (var i = features.length - 1; i >= 0; i--) {\n+ features[i].destroy()\n+ }\n }\n },\n- getFeatureIdFromEvent: function(evt) {\n- var target = evt.target;\n- var useElement = target && target.correspondingUseElement;\n- var node = useElement ? useElement : target || evt.srcElement;\n- return node._featureId\n- },\n- eraseGeometry: function(geometry, featureId) {\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPoint\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiLineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPolygon\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.Collection\") {\n- for (var i = 0, len = geometry.components.length; i < len; i++) {\n- this.eraseGeometry(geometry.components[i], featureId)\n+ drawFeature: function(feature, style) {\n+ if (!this.drawn) {\n+ return\n+ }\n+ if (typeof style != \"object\") {\n+ if (!style && feature.state === OpenLayers.State.DELETE) {\n+ style = \"delete\"\n }\n- } else {\n- var element = OpenLayers.Util.getElement(geometry.id);\n- if (element && element.parentNode) {\n- if (element.geometry) {\n- element.geometry.destroy();\n- element.geometry = null\n- }\n- element.parentNode.removeChild(element);\n- if (this.indexer) {\n- this.indexer.remove(element)\n- }\n- if (element._style.backgroundGraphic) {\n- var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;\n- var bElem = OpenLayers.Util.getElement(backgroundId);\n- if (bElem && bElem.parentNode) {\n- bElem.parentNode.removeChild(bElem)\n- }\n- }\n+ var renderIntent = style || feature.renderIntent;\n+ style = feature.style || this.style;\n+ if (!style) {\n+ style = this.styleMap.createSymbolizer(feature, renderIntent)\n }\n }\n+ var drawn = this.renderer.drawFeature(feature, style);\n+ if (drawn === false || drawn === null) {\n+ this.unrenderedFeatures[feature.id] = feature\n+ } else {\n+ delete this.unrenderedFeatures[feature.id]\n+ }\n },\n- nodeFactory: function(id, type) {\n- var node = OpenLayers.Util.getElement(id);\n- if (node) {\n- if (!this.nodeTypeCompare(node, type)) {\n- node.parentNode.removeChild(node);\n- node = this.nodeFactory(id, type)\n+ eraseFeatures: function(features) {\n+ this.renderer.eraseFeatures(features)\n+ },\n+ getFeatureFromEvent: function(evt) {\n+ if (!this.renderer) {\n+ throw new Error(\"getFeatureFromEvent called on layer with no \" + \"renderer. This usually means you destroyed a \" + \"layer, but not some handler which is associated \" + \"with it.\")\n+ }\n+ var feature = null;\n+ var featureId = this.renderer.getFeatureIdFromEvent(evt);\n+ if (featureId) {\n+ if (typeof featureId === \"string\") {\n+ feature = this.getFeatureById(featureId)\n+ } else {\n+ feature = featureId\n }\n- } else {\n- node = this.createNode(type, id)\n }\n- return node\n+ return feature\n },\n- nodeTypeCompare: function(node, type) {},\n- createNode: function(type, id) {},\n- moveRoot: function(renderer) {\n- var root = this.root;\n- if (renderer.root.parentNode == this.rendererRoot) {\n- root = renderer.root\n+ getFeatureBy: function(property, value) {\n+ var feature = null;\n+ for (var i = 0, len = this.features.length; i < len; ++i) {\n+ if (this.features[i][property] == value) {\n+ feature = this.features[i];\n+ break\n+ }\n }\n- root.parentNode.removeChild(root);\n- renderer.rendererRoot.appendChild(root)\n+ return feature\n },\n- getRenderLayerId: function() {\n- return this.root.parentNode.parentNode.id\n+ getFeatureById: function(featureId) {\n+ return this.getFeatureBy(\"id\", featureId)\n },\n- isComplexSymbol: function(graphicName) {\n- return graphicName != \"circle\" && !!graphicName\n+ getFeatureByFid: function(featureFid) {\n+ return this.getFeatureBy(\"fid\", featureFid)\n },\n- CLASS_NAME: \"OpenLayers.Renderer.Elements\"\n-});\n-OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {\n- xmlns: \"http://www.w3.org/2000/svg\",\n- xlinkns: \"http://www.w3.org/1999/xlink\",\n- MAX_PIXEL: 15e3,\n- translationParameters: null,\n- symbolMetrics: null,\n- initialize: function(containerID) {\n- if (!this.supported()) {\n- return\n+ getFeaturesByAttribute: function(attrName, attrValue) {\n+ var i, feature, len = this.features.length,\n+ foundFeatures = [];\n+ for (i = 0; i < len; i++) {\n+ feature = this.features[i];\n+ if (feature && feature.attributes) {\n+ if (feature.attributes[attrName] === attrValue) {\n+ foundFeatures.push(feature)\n+ }\n+ }\n }\n- OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);\n- this.translationParameters = {\n- x: 0,\n- y: 0\n- };\n- this.symbolMetrics = {}\n- },\n- supported: function() {\n- var svgFeature = \"http://www.w3.org/TR/SVG11/feature#\";\n- return document.implementation && (document.implementation.hasFeature(\"org.w3c.svg\", \"1.0\") || document.implementation.hasFeature(svgFeature + \"SVG\", \"1.1\") || document.implementation.hasFeature(svgFeature + \"BasicStructure\", \"1.1\"))\n- },\n- inValidRange: function(x, y, xyOnly) {\n- var left = x + (xyOnly ? 0 : this.translationParameters.x);\n- var top = y + (xyOnly ? 0 : this.translationParameters.y);\n- return left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL && top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL\n+ return foundFeatures\n },\n- setExtent: function(extent, resolutionChanged) {\n- var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n- var resolution = this.getResolution(),\n- left = -extent.left / resolution,\n- top = extent.top / resolution;\n- if (resolutionChanged) {\n- this.left = left;\n- this.top = top;\n- var extentString = \"0 0 \" + this.size.w + \" \" + this.size.h;\n- this.rendererRoot.setAttributeNS(null, \"viewBox\", extentString);\n- this.translate(this.xOffset, 0);\n- return true\n- } else {\n- var inRange = this.translate(left - this.left + this.xOffset, top - this.top);\n- if (!inRange) {\n- this.setExtent(extent, true)\n+ onFeatureInsert: function(feature) {},\n+ preFeatureInsert: function(feature) {},\n+ getDataExtent: function() {\n+ var maxExtent = null;\n+ var features = this.features;\n+ if (features && features.length > 0) {\n+ var geometry = null;\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ geometry = features[i].geometry;\n+ if (geometry) {\n+ if (maxExtent === null) {\n+ maxExtent = new OpenLayers.Bounds\n+ }\n+ maxExtent.extend(geometry.getBounds())\n+ }\n }\n- return coordSysUnchanged && inRange\n }\n+ return maxExtent\n },\n- translate: function(x, y) {\n- if (!this.inValidRange(x, y, true)) {\n- return false\n- } else {\n- var transformString = \"\";\n- if (x || y) {\n- transformString = \"translate(\" + x + \",\" + y + \")\"\n+ CLASS_NAME: \"OpenLayers.Layer.Vector\"\n+});\n+OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n+ displayInLayerSwitcher: false,\n+ layers: null,\n+ display: function() {},\n+ getFeatureFromEvent: function(evt) {\n+ var layers = this.layers;\n+ var feature;\n+ for (var i = 0; i < layers.length; i++) {\n+ feature = layers[i].getFeatureFromEvent(evt);\n+ if (feature) {\n+ return feature\n }\n- this.root.setAttributeNS(null, \"transform\", transformString);\n- this.translationParameters = {\n- x: x,\n- y: y\n- };\n- return true\n }\n },\n- setSize: function(size) {\n- OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n- this.rendererRoot.setAttributeNS(null, \"width\", this.size.w);\n- this.rendererRoot.setAttributeNS(null, \"height\", this.size.h)\n+ setMap: function(map) {\n+ OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n+ this.collectRoots();\n+ map.events.register(\"changelayer\", this, this.handleChangeLayer)\n },\n- getNodeType: function(geometry, style) {\n- var nodeType = null;\n- switch (geometry.CLASS_NAME) {\n- case \"OpenLayers.Geometry.Point\":\n- if (style.externalGraphic) {\n- nodeType = \"image\"\n- } else if (this.isComplexSymbol(style.graphicName)) {\n- nodeType = \"svg\"\n- } else {\n- nodeType = \"circle\"\n- }\n- break;\n- case \"OpenLayers.Geometry.Rectangle\":\n- nodeType = \"rect\";\n- break;\n- case \"OpenLayers.Geometry.LineString\":\n- nodeType = \"polyline\";\n- break;\n- case \"OpenLayers.Geometry.LinearRing\":\n- nodeType = \"polygon\";\n- break;\n- case \"OpenLayers.Geometry.Polygon\":\n- case \"OpenLayers.Geometry.Curve\":\n- nodeType = \"path\";\n- break;\n- default:\n- break\n- }\n- return nodeType\n+ removeMap: function(map) {\n+ map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n+ this.resetRoots();\n+ OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments)\n },\n- setStyle: function(node, style, options) {\n- style = style || node._style;\n- options = options || node._options;\n- var title = style.title || style.graphicTitle;\n- if (title) {\n- node.setAttributeNS(null, \"title\", title);\n- var titleNode = node.getElementsByTagName(\"title\");\n- if (titleNode.length > 0) {\n- titleNode[0].firstChild.textContent = title\n- } else {\n- var label = this.nodeFactory(null, \"title\");\n- label.textContent = title;\n- node.appendChild(label)\n+ collectRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.map.layers.length; ++i) {\n+ layer = this.map.layers[i];\n+ if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ layer.renderer.moveRoot(this.renderer)\n }\n }\n- var r = parseFloat(node.getAttributeNS(null, \"r\"));\n- var widthFactor = 1;\n- var pos;\n- if (node._geometryClass == \"OpenLayers.Geometry.Point\" && r) {\n- node.style.visibility = \"\";\n- if (style.graphic === false) {\n- node.style.visibility = \"hidden\"\n- } else if (style.externalGraphic) {\n- pos = this.getPosition(node);\n- if (style.graphicWidth && style.graphicHeight) {\n- node.setAttributeNS(null, \"preserveAspectRatio\", \"none\")\n- }\n- var width = style.graphicWidth || style.graphicHeight;\n- var height = style.graphicHeight || style.graphicWidth;\n- width = width ? width : style.pointRadius * 2;\n- height = height ? height : style.pointRadius * 2;\n- var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n- var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n- var opacity = style.graphicOpacity || style.fillOpacity;\n- node.setAttributeNS(null, \"x\", (pos.x + xOffset).toFixed());\n- node.setAttributeNS(null, \"y\", (pos.y + yOffset).toFixed());\n- node.setAttributeNS(null, \"width\", width);\n- node.setAttributeNS(null, \"height\", height);\n- node.setAttributeNS(this.xlinkns, \"xlink:href\", style.externalGraphic);\n- node.setAttributeNS(null, \"style\", \"opacity: \" + opacity);\n- node.onclick = OpenLayers.Event.preventDefault\n- } else if (this.isComplexSymbol(style.graphicName)) {\n- var offset = style.pointRadius * 3;\n- var size = offset * 2;\n- var src = this.importSymbol(style.graphicName);\n- pos = this.getPosition(node);\n- widthFactor = this.symbolMetrics[src.id][0] * 3 / size;\n- var parent = node.parentNode;\n- var nextSibling = node.nextSibling;\n- if (parent) {\n- parent.removeChild(node)\n- }\n- node.firstChild && node.removeChild(node.firstChild);\n- node.appendChild(src.firstChild.cloneNode(true));\n- node.setAttributeNS(null, \"viewBox\", src.getAttributeNS(null, \"viewBox\"));\n- node.setAttributeNS(null, \"width\", size);\n- node.setAttributeNS(null, \"height\", size);\n- node.setAttributeNS(null, \"x\", pos.x - offset);\n- node.setAttributeNS(null, \"y\", pos.y - offset);\n- if (nextSibling) {\n- parent.insertBefore(node, nextSibling)\n- } else if (parent) {\n- parent.appendChild(node)\n- }\n- } else {\n- node.setAttributeNS(null, \"r\", style.pointRadius)\n- }\n- var rotation = style.rotation;\n- if ((rotation !== undefined || node._rotation !== undefined) && pos) {\n- node._rotation = rotation;\n- rotation |= 0;\n- if (node.nodeName !== \"svg\") {\n- node.setAttributeNS(null, \"transform\", \"rotate(\" + rotation + \" \" + pos.x + \" \" + pos.y + \")\")\n- } else {\n- var metrics = this.symbolMetrics[src.id];\n- node.firstChild.setAttributeNS(null, \"transform\", \"rotate(\" + rotation + \" \" + metrics[1] + \" \" + metrics[2] + \")\")\n- }\n+ },\n+ resetRoots: function() {\n+ var layer;\n+ for (var i = 0; i < this.layers.length; ++i) {\n+ layer = this.layers[i];\n+ if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n+ this.renderer.moveRoot(layer.renderer)\n }\n }\n- if (options.isFilled) {\n- node.setAttributeNS(null, \"fill\", style.fillColor);\n- node.setAttributeNS(null, \"fill-opacity\", style.fillOpacity)\n- } else {\n- node.setAttributeNS(null, \"fill\", \"none\")\n+ },\n+ handleChangeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (evt.property == \"order\" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n+ this.resetRoots();\n+ this.collectRoots()\n }\n- if (options.isStroked) {\n- node.setAttributeNS(null, \"stroke\", style.strokeColor);\n- node.setAttributeNS(null, \"stroke-opacity\", style.strokeOpacity);\n- node.setAttributeNS(null, \"stroke-width\", style.strokeWidth * widthFactor);\n- node.setAttributeNS(null, \"stroke-linecap\", style.strokeLinecap || \"round\");\n- node.setAttributeNS(null, \"stroke-linejoin\", \"round\");\n- style.strokeDashstyle && node.setAttributeNS(null, \"stroke-dasharray\", this.dashStyle(style, widthFactor))\n- } else {\n- node.setAttributeNS(null, \"stroke\", \"none\")\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n+});\n+OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {\n+ multipleKey: null,\n+ toggleKey: null,\n+ multiple: false,\n+ clickout: true,\n+ toggle: false,\n+ hover: false,\n+ highlightOnly: false,\n+ box: false,\n+ onBeforeSelect: function() {},\n+ onSelect: function() {},\n+ onUnselect: function() {},\n+ scope: null,\n+ geometryTypes: null,\n+ layer: null,\n+ layers: null,\n+ callbacks: null,\n+ selectStyle: null,\n+ renderIntent: \"select\",\n+ handlers: null,\n+ initialize: function(layers, options) {\n+ OpenLayers.Control.prototype.initialize.apply(this, [options]);\n+ if (this.scope === null) {\n+ this.scope = this\n }\n- if (style.pointerEvents) {\n- node.setAttributeNS(null, \"pointer-events\", style.pointerEvents)\n+ this.initLayer(layers);\n+ var callbacks = {\n+ click: this.clickFeature,\n+ clickout: this.clickoutFeature\n+ };\n+ if (this.hover) {\n+ callbacks.over = this.overFeature;\n+ callbacks.out = this.outFeature\n }\n- if (style.cursor != null) {\n- node.setAttributeNS(null, \"cursor\", style.cursor)\n+ this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);\n+ this.handlers = {\n+ feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, {\n+ geometryTypes: this.geometryTypes\n+ })\n+ };\n+ if (this.box) {\n+ this.handlers.box = new OpenLayers.Handler.Box(this, {\n+ done: this.selectBox\n+ }, {\n+ boxDivClassName: \"olHandlerBoxSelectFeature\"\n+ })\n }\n- return node\n },\n- dashStyle: function(style, widthFactor) {\n- var w = style.strokeWidth * widthFactor;\n- var str = style.strokeDashstyle;\n- switch (str) {\n- case \"solid\":\n- return \"none\";\n- case \"dot\":\n- return [1, 4 * w].join();\n- case \"dash\":\n- return [4 * w, 4 * w].join();\n- case \"dashdot\":\n- return [4 * w, 4 * w, 1, 4 * w].join();\n- case \"longdash\":\n- return [8 * w, 4 * w].join();\n- case \"longdashdot\":\n- return [8 * w, 4 * w, 1, 4 * w].join();\n- default:\n- return OpenLayers.String.trim(str).replace(/\\s+/g, \",\")\n+ initLayer: function(layers) {\n+ if (OpenLayers.Util.isArray(layers)) {\n+ this.layers = layers;\n+ this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + \"_container\", {\n+ layers: layers\n+ })\n+ } else {\n+ this.layer = layers\n }\n },\n- createNode: function(type, id) {\n- var node = document.createElementNS(this.xmlns, type);\n- if (id) {\n- node.setAttributeNS(null, \"id\", id)\n+ destroy: function() {\n+ if (this.active && this.layers) {\n+ this.map.removeLayer(this.layer)\n+ }\n+ OpenLayers.Control.prototype.destroy.apply(this, arguments);\n+ if (this.layers) {\n+ this.layer.destroy()\n }\n- return node\n },\n- nodeTypeCompare: function(node, type) {\n- return type == node.nodeName\n+ activate: function() {\n+ if (!this.active) {\n+ if (this.layers) {\n+ this.map.addLayer(this.layer)\n+ }\n+ this.handlers.feature.activate();\n+ if (this.box && this.handlers.box) {\n+ this.handlers.box.activate()\n+ }\n+ }\n+ return OpenLayers.Control.prototype.activate.apply(this, arguments)\n },\n- createRenderRoot: function() {\n- var svg = this.nodeFactory(this.container.id + \"_svgRoot\", \"svg\");\n- svg.style.display = \"block\";\n- return svg\n+ deactivate: function() {\n+ if (this.active) {\n+ this.handlers.feature.deactivate();\n+ if (this.handlers.box) {\n+ this.handlers.box.deactivate()\n+ }\n+ if (this.layers) {\n+ this.map.removeLayer(this.layer)\n+ }\n+ }\n+ return OpenLayers.Control.prototype.deactivate.apply(this, arguments)\n },\n- createRoot: function(suffix) {\n- return this.nodeFactory(this.container.id + suffix, \"g\")\n+ unselectAll: function(options) {\n+ var layers = this.layers || [this.layer],\n+ layer, feature, l, numExcept;\n+ for (l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ numExcept = 0;\n+ if (layer.selectedFeatures != null) {\n+ while (layer.selectedFeatures.length > numExcept) {\n+ feature = layer.selectedFeatures[numExcept];\n+ if (!options || options.except != feature) {\n+ this.unselect(feature)\n+ } else {\n+ ++numExcept\n+ }\n+ }\n+ }\n+ }\n },\n- createDefs: function() {\n- var defs = this.nodeFactory(this.container.id + \"_defs\", \"defs\");\n- this.rendererRoot.appendChild(defs);\n- return defs\n+ clickFeature: function(feature) {\n+ if (!this.hover) {\n+ var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1;\n+ if (selected) {\n+ if (this.toggleSelect()) {\n+ this.unselect(feature)\n+ } else if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ })\n+ }\n+ } else {\n+ if (!this.multipleSelect()) {\n+ this.unselectAll({\n+ except: feature\n+ })\n+ }\n+ this.select(feature)\n+ }\n+ }\n },\n- drawPoint: function(node, geometry) {\n- return this.drawCircle(node, geometry, 1)\n+ multipleSelect: function() {\n+ return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]\n },\n- drawCircle: function(node, geometry, radius) {\n- var resolution = this.getResolution();\n- var x = (geometry.x - this.featureDx) / resolution + this.left;\n- var y = this.top - geometry.y / resolution;\n- if (this.inValidRange(x, y)) {\n- node.setAttributeNS(null, \"cx\", x);\n- node.setAttributeNS(null, \"cy\", y);\n- node.setAttributeNS(null, \"r\", radius);\n- return node\n- } else {\n- return false\n- }\n+ toggleSelect: function() {\n+ return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]\n },\n- drawLineString: function(node, geometry) {\n- var componentsResult = this.getComponentsString(geometry.components);\n- if (componentsResult.path) {\n- node.setAttributeNS(null, \"points\", componentsResult.path);\n- return componentsResult.complete ? node : null\n- } else {\n- return false\n+ clickoutFeature: function(feature) {\n+ if (!this.hover && this.clickout) {\n+ this.unselectAll()\n }\n },\n- drawLinearRing: function(node, geometry) {\n- var componentsResult = this.getComponentsString(geometry.components);\n- if (componentsResult.path) {\n- node.setAttributeNS(null, \"points\", componentsResult.path);\n- return componentsResult.complete ? node : null\n- } else {\n- return false\n+ overFeature: function(feature) {\n+ var layer = feature.layer;\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ this.highlight(feature)\n+ } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature)\n+ }\n }\n },\n- drawPolygon: function(node, geometry) {\n- var d = \"\";\n- var draw = true;\n- var complete = true;\n- var linearRingResult, path;\n- for (var j = 0, len = geometry.components.length; j < len; j++) {\n- d += \" M\";\n- linearRingResult = this.getComponentsString(geometry.components[j].components, \" \");\n- path = linearRingResult.path;\n- if (path) {\n- d += \" \" + path;\n- complete = linearRingResult.complete && complete\n+ outFeature: function(feature) {\n+ if (this.hover) {\n+ if (this.highlightOnly) {\n+ if (feature._lastHighlighter == this.id) {\n+ if (feature._prevHighlighter && feature._prevHighlighter != this.id) {\n+ delete feature._lastHighlighter;\n+ var control = this.map.getControl(feature._prevHighlighter);\n+ if (control) {\n+ control.highlight(feature)\n+ }\n+ } else {\n+ this.unhighlight(feature)\n+ }\n+ }\n } else {\n- draw = false\n+ this.unselect(feature)\n }\n }\n- d += \" z\";\n- if (draw) {\n- node.setAttributeNS(null, \"d\", d);\n- node.setAttributeNS(null, \"fill-rule\", \"evenodd\");\n- return complete ? node : null\n- } else {\n- return false\n+ },\n+ highlight: function(feature) {\n+ var layer = feature.layer;\n+ var cont = this.events.triggerEvent(\"beforefeaturehighlighted\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ feature._prevHighlighter = feature._lastHighlighter;\n+ feature._lastHighlighter = this.id;\n+ var style = this.selectStyle || this.renderIntent;\n+ layer.drawFeature(feature, style);\n+ this.events.triggerEvent(\"featurehighlighted\", {\n+ feature: feature\n+ })\n }\n },\n- drawRectangle: function(node, geometry) {\n- var resolution = this.getResolution();\n- var x = (geometry.x - this.featureDx) / resolution + this.left;\n- var y = this.top - geometry.y / resolution;\n- if (this.inValidRange(x, y)) {\n- node.setAttributeNS(null, \"x\", x);\n- node.setAttributeNS(null, \"y\", y);\n- node.setAttributeNS(null, \"width\", geometry.width / resolution);\n- node.setAttributeNS(null, \"height\", geometry.height / resolution);\n- return node\n+ unhighlight: function(feature) {\n+ var layer = feature.layer;\n+ if (feature._prevHighlighter == undefined) {\n+ delete feature._lastHighlighter\n+ } else if (feature._prevHighlighter == this.id) {\n+ delete feature._prevHighlighter\n } else {\n- return false\n+ feature._lastHighlighter = feature._prevHighlighter;\n+ delete feature._prevHighlighter\n }\n+ layer.drawFeature(feature, feature.style || feature.layer.style || \"default\");\n+ this.events.triggerEvent(\"featureunhighlighted\", {\n+ feature: feature\n+ })\n },\n- drawText: function(featureId, style, location) {\n- var drawOutline = !!style.labelOutlineWidth;\n- if (drawOutline) {\n- var outlineStyle = OpenLayers.Util.extend({}, style);\n- outlineStyle.fontColor = outlineStyle.labelOutlineColor;\n- outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;\n- outlineStyle.fontStrokeWidth = style.labelOutlineWidth;\n- if (style.labelOutlineOpacity) {\n- outlineStyle.fontOpacity = style.labelOutlineOpacity\n+ select: function(feature) {\n+ var cont = this.onBeforeSelect.call(this.scope, feature);\n+ var layer = feature.layer;\n+ if (cont !== false) {\n+ cont = layer.events.triggerEvent(\"beforefeatureselected\", {\n+ feature: feature\n+ });\n+ if (cont !== false) {\n+ layer.selectedFeatures.push(feature);\n+ this.highlight(feature);\n+ if (!this.handlers.feature.lastFeature) {\n+ this.handlers.feature.lastFeature = layer.selectedFeatures[0]\n+ }\n+ layer.events.triggerEvent(\"featureselected\", {\n+ feature: feature\n+ });\n+ this.onSelect.call(this.scope, feature)\n }\n- delete outlineStyle.labelOutlineWidth;\n- this.drawText(featureId, outlineStyle, location)\n }\n- var resolution = this.getResolution();\n- var x = (location.x - this.featureDx) / resolution + this.left;\n- var y = location.y / resolution - this.top;\n- var suffix = drawOutline ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX;\n- var label = this.nodeFactory(featureId + suffix, \"text\");\n- label.setAttributeNS(null, \"x\", x);\n- label.setAttributeNS(null, \"y\", -y);\n- if (style.fontColor) {\n- label.setAttributeNS(null, \"fill\", style.fontColor)\n+ },\n+ unselect: function(feature) {\n+ var layer = feature.layer;\n+ this.unhighlight(feature);\n+ OpenLayers.Util.removeItem(layer.selectedFeatures, feature);\n+ layer.events.triggerEvent(\"featureunselected\", {\n+ feature: feature\n+ });\n+ this.onUnselect.call(this.scope, feature)\n+ },\n+ selectBox: function(position) {\n+ if (position instanceof OpenLayers.Bounds) {\n+ var minXY = this.map.getLonLatFromPixel({\n+ x: position.left,\n+ y: position.bottom\n+ });\n+ var maxXY = this.map.getLonLatFromPixel({\n+ x: position.right,\n+ y: position.top\n+ });\n+ var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);\n+ if (!this.multipleSelect()) {\n+ this.unselectAll()\n+ }\n+ var prevMultiple = this.multiple;\n+ this.multiple = true;\n+ var layers = this.layers || [this.layer];\n+ this.events.triggerEvent(\"boxselectionstart\", {\n+ layers: layers\n+ });\n+ var layer;\n+ for (var l = 0; l < layers.length; ++l) {\n+ layer = layers[l];\n+ for (var i = 0, len = layer.features.length; i < len; ++i) {\n+ var feature = layer.features[i];\n+ if (!feature.getVisibility()) {\n+ continue\n+ }\n+ if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {\n+ if (bounds.toGeometry().intersects(feature.geometry)) {\n+ if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {\n+ this.select(feature)\n+ }\n+ }\n+ }\n+ }\n+ }\n+ this.multiple = prevMultiple;\n+ this.events.triggerEvent(\"boxselectionend\", {\n+ layers: layers\n+ })\n }\n- if (style.fontStrokeColor) {\n- label.setAttributeNS(null, \"stroke\", style.fontStrokeColor)\n+ },\n+ setMap: function(map) {\n+ this.handlers.feature.setMap(map);\n+ if (this.box) {\n+ this.handlers.box.setMap(map)\n }\n- if (style.fontStrokeWidth) {\n- label.setAttributeNS(null, \"stroke-width\", style.fontStrokeWidth)\n+ OpenLayers.Control.prototype.setMap.apply(this, arguments)\n+ },\n+ setLayer: function(layers) {\n+ var isActive = this.active;\n+ this.unselectAll();\n+ this.deactivate();\n+ if (this.layers) {\n+ this.layer.destroy();\n+ this.layers = null\n }\n- if (style.fontOpacity) {\n- label.setAttributeNS(null, \"opacity\", style.fontOpacity)\n+ this.initLayer(layers);\n+ this.handlers.feature.layer = this.layer;\n+ if (isActive) {\n+ this.activate()\n }\n- if (style.fontFamily) {\n- label.setAttributeNS(null, \"font-family\", style.fontFamily)\n+ },\n+ CLASS_NAME: \"OpenLayers.Control.SelectFeature\"\n+});\n+OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ isBaseLayer: true,\n+ sphericalMercator: false,\n+ zoomOffset: 0,\n+ serverResolutions: null,\n+ initialize: function(name, url, options) {\n+ if (options && options.sphericalMercator || this.sphericalMercator) {\n+ options = OpenLayers.Util.extend({\n+ projection: \"EPSG:900913\",\n+ numZoomLevels: 19\n+ }, options)\n }\n- if (style.fontSize) {\n- label.setAttributeNS(null, \"font-size\", style.fontSize)\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options])\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions())\n }\n- if (style.fontWeight) {\n- label.setAttributeNS(null, \"font-weight\", style.fontWeight)\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ getURL: function(bounds) {\n+ var xyz = this.getXYZ(bounds);\n+ var url = this.url;\n+ if (OpenLayers.Util.isArray(url)) {\n+ var s = \"\" + xyz.x + xyz.y + xyz.z;\n+ url = this.selectUrl(s, url)\n }\n- if (style.fontStyle) {\n- label.setAttributeNS(null, \"font-style\", style.fontStyle)\n+ return OpenLayers.String.format(url, xyz)\n+ },\n+ getXYZ: function(bounds) {\n+ var res = this.getServerResolution();\n+ var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));\n+ var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));\n+ var z = this.getServerZoom();\n+ if (this.wrapDateLine) {\n+ var limit = Math.pow(2, z);\n+ x = (x % limit + limit) % limit\n }\n- if (style.labelSelect === true) {\n- label.setAttributeNS(null, \"pointer-events\", \"visible\");\n- label._featureId = featureId\n- } else {\n- label.setAttributeNS(null, \"pointer-events\", \"none\")\n+ return {\n+ x: x,\n+ y: y,\n+ z: z\n }\n- var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;\n- label.setAttributeNS(null, \"text-anchor\", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || \"middle\");\n- if (OpenLayers.IS_GECKO === true) {\n- label.setAttributeNS(null, \"dominant-baseline\", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || \"central\")\n+ },\n+ setMap: function(map) {\n+ OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n+ if (!this.tileOrigin) {\n+ this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom)\n }\n- var labelRows = style.label.split(\"\\n\");\n- var numRows = labelRows.length;\n- while (label.childNodes.length > numRows) {\n- label.removeChild(label.lastChild)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+});\n+OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ name: \"OpenStreetMap\",\n+ url: [\"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png\", \"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png\"],\n+ attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n+ sphericalMercator: true,\n+ wrapDateLine: true,\n+ tileOptions: null,\n+ initialize: function(name, url, options) {\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options && this.options.tileOptions)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions())\n }\n- for (var i = 0; i < numRows; i++) {\n- var tspan = this.nodeFactory(featureId + suffix + \"_tspan_\" + i, \"tspan\");\n- if (style.labelSelect === true) {\n- tspan._featureId = featureId;\n- tspan._geometry = location;\n- tspan._geometryClass = location.CLASS_NAME\n- }\n- if (OpenLayers.IS_GECKO === false) {\n- tspan.setAttributeNS(null, \"baseline-shift\", OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || \"-35%\")\n- }\n- tspan.setAttribute(\"x\", x);\n- if (i == 0) {\n- var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];\n- if (vfactor == null) {\n- vfactor = -.5\n- }\n- tspan.setAttribute(\"dy\", vfactor * (numRows - 1) + \"em\")\n- } else {\n- tspan.setAttribute(\"dy\", \"1em\")\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.OSM\"\n+});\n+OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+ DEFAULT_PARAMS: {\n+ service: \"WMS\",\n+ version: \"1.1.1\",\n+ request: \"GetMap\",\n+ styles: \"\",\n+ format: \"image/jpeg\"\n+ },\n+ isBaseLayer: true,\n+ encodeBBOX: false,\n+ noMagic: false,\n+ yx: {},\n+ initialize: function(name, url, params, options) {\n+ var newArguments = [];\n+ params = OpenLayers.Util.upperCaseObject(params);\n+ if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {\n+ params.EXCEPTIONS = \"INIMAGE\"\n+ }\n+ newArguments.push(name, url, params, options);\n+ OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));\n+ if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n+ if (options == null || !options.isBaseLayer) {\n+ this.isBaseLayer = false\n }\n- tspan.textContent = labelRows[i] === \"\" ? \" \" : labelRows[i];\n- if (!tspan.parentNode) {\n- label.appendChild(tspan)\n+ if (this.params.FORMAT == \"image/jpeg\") {\n+ this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\"\n }\n }\n- if (!label.parentNode) {\n- this.textRoot.appendChild(label)\n- }\n },\n- getComponentsString: function(components, separator) {\n- var renderCmp = [];\n- var complete = true;\n- var len = components.length;\n- var strings = [];\n- var str, component;\n- for (var i = 0; i < len; i++) {\n- component = components[i];\n- renderCmp.push(component);\n- str = this.getShortString(component);\n- if (str) {\n- strings.push(str)\n- } else {\n- if (i > 0) {\n- if (this.getShortString(components[i - 1])) {\n- strings.push(this.clipLine(components[i], components[i - 1]))\n- }\n- }\n- if (i < len - 1) {\n- if (this.getShortString(components[i + 1])) {\n- strings.push(this.clipLine(components[i], components[i + 1]))\n- }\n- }\n- complete = false\n- }\n- }\n- return {\n- path: strings.join(separator || \",\"),\n- complete: complete\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions())\n }\n+ obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- clipLine: function(badComponent, goodComponent) {\n- if (goodComponent.equals(badComponent)) {\n- return \"\"\n- }\n- var resolution = this.getResolution();\n- var maxX = this.MAX_PIXEL - this.translationParameters.x;\n- var maxY = this.MAX_PIXEL - this.translationParameters.y;\n- var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;\n- var y1 = this.top - goodComponent.y / resolution;\n- var x2 = (badComponent.x - this.featureDx) / resolution + this.left;\n- var y2 = this.top - badComponent.y / resolution;\n- var k;\n- if (x2 < -maxX || x2 > maxX) {\n- k = (y2 - y1) / (x2 - x1);\n- x2 = x2 < 0 ? -maxX : maxX;\n- y2 = y1 + (x2 - x1) * k\n- }\n- if (y2 < -maxY || y2 > maxY) {\n- k = (x2 - x1) / (y2 - y1);\n- y2 = y2 < 0 ? -maxY : maxY;\n- x2 = x1 + (y2 - y1) * k\n- }\n- return x2 + \",\" + y2\n+ reverseAxisOrder: function() {\n+ var projCode = this.projection.getCode();\n+ return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx)\n },\n- getShortString: function(point) {\n- var resolution = this.getResolution();\n- var x = (point.x - this.featureDx) / resolution + this.left;\n- var y = this.top - point.y / resolution;\n- if (this.inValidRange(x, y)) {\n- return x + \",\" + y\n+ getURL: function(bounds) {\n+ bounds = this.adjustBounds(bounds);\n+ var imageSize = this.getImageSize();\n+ var newParams = {};\n+ var reverseAxisOrder = this.reverseAxisOrder();\n+ newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);\n+ newParams.WIDTH = imageSize.w;\n+ newParams.HEIGHT = imageSize.h;\n+ var requestString = this.getFullRequestString(newParams);\n+ return requestString\n+ },\n+ mergeNewParams: function(newParams) {\n+ var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n+ var newArguments = [upperParams];\n+ return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments)\n+ },\n+ getFullRequestString: function(newParams, altUrl) {\n+ var mapProjection = this.map.getProjectionObject();\n+ var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();\n+ var value = projectionCode == \"none\" ? null : projectionCode;\n+ if (parseFloat(this.params.VERSION) >= 1.3) {\n+ this.params.CRS = value\n } else {\n- return false\n+ this.params.SRS = value\n }\n+ if (typeof this.params.TRANSPARENT == \"boolean\") {\n+ newParams.TRANSPARENT = this.params.TRANSPARENT ? \"TRUE\" : \"FALSE\"\n+ }\n+ return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments)\n },\n- getPosition: function(node) {\n- return {\n- x: parseFloat(node.getAttributeNS(null, \"cx\")),\n- y: parseFloat(node.getAttributeNS(null, \"cy\"))\n+ CLASS_NAME: \"OpenLayers.Layer.WMS\"\n+});\n+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+ key: null,\n+ serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169],\n+ attributionTemplate: '<span class=\"olBingAttribution ${type}\">' + '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' + '<img src=\"${logo}\" /></a></div>${copyrights}' + '<a style=\"white-space: nowrap\" target=\"_blank\" ' + 'href=\"http://www.microsoft.com/maps/product/terms.html\">' + \"Terms of Use</a></span>\",\n+ metadata: null,\n+ protocolRegex: /^http:/i,\n+ type: \"Road\",\n+ culture: \"en-US\",\n+ metadataParams: null,\n+ tileOptions: null,\n+ protocol: ~window.location.href.indexOf(\"http\") ? \"\" : \"http:\",\n+ initialize: function(options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ sphericalMercator: true\n+ }, options);\n+ var name = options.name || \"Bing \" + (options.type || this.type);\n+ var newArgs = [name, null, options];\n+ OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n+ this.tileOptions = OpenLayers.Util.extend({\n+ crossOriginKeyword: \"anonymous\"\n+ }, this.options.tileOptions);\n+ this.loadMetadata()\n+ },\n+ loadMetadata: function() {\n+ this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n+ window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);\n+ var params = OpenLayers.Util.applyDefaults({\n+ key: this.key,\n+ jsonp: this._callbackId,\n+ include: \"ImageryProviders\"\n+ }, this.metadataParams);\n+ var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" + this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n+ var script = document.createElement(\"script\");\n+ script.type = \"text/javascript\";\n+ script.src = url;\n+ script.id = this._callbackId;\n+ document.getElementsByTagName(\"head\")[0].appendChild(script)\n+ },\n+ initLayer: function() {\n+ var res = this.metadata.resourceSets[0].resources[0];\n+ var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n+ url = url.replace(\"{culture}\", this.culture);\n+ url = url.replace(this.protocolRegex, this.protocol);\n+ this.url = [];\n+ for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n+ this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]))\n }\n+ this.addOptions({\n+ maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY),\n+ numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)\n+ }, true);\n+ if (!this.isBaseLayer) {\n+ this.redraw()\n+ }\n+ this.updateAttribution()\n },\n- importSymbol: function(graphicName) {\n- if (!this.defs) {\n- this.defs = this.createDefs()\n+ getURL: function(bounds) {\n+ if (!this.url) {\n+ return\n }\n- var id = this.container.id + \"-\" + graphicName;\n- var existing = document.getElementById(id);\n- if (existing != null) {\n- return existing\n+ var xyz = this.getXYZ(bounds),\n+ x = xyz.x,\n+ y = xyz.y,\n+ z = xyz.z;\n+ var quadDigits = [];\n+ for (var i = z; i > 0; --i) {\n+ var digit = \"0\";\n+ var mask = 1 << i - 1;\n+ if ((x & mask) != 0) {\n+ digit++\n+ }\n+ if ((y & mask) != 0) {\n+ digit++;\n+ digit++\n+ }\n+ quadDigits.push(digit)\n }\n- var symbol = OpenLayers.Renderer.symbol[graphicName];\n- if (!symbol) {\n- throw new Error(graphicName + \" is not a valid symbol name\")\n+ var quadKey = quadDigits.join(\"\");\n+ var url = this.selectUrl(\"\" + x + y + z, this.url);\n+ return OpenLayers.String.format(url, {\n+ quadkey: quadKey\n+ })\n+ },\n+ updateAttribution: function() {\n+ var metadata = this.metadata;\n+ if (!metadata.resourceSets || !this.map || !this.map.center) {\n+ return\n }\n- var symbolNode = this.nodeFactory(id, \"symbol\");\n- var node = this.nodeFactory(null, \"polygon\");\n- symbolNode.appendChild(node);\n- var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);\n- var points = [];\n- var x, y;\n- for (var i = 0; i < symbol.length; i = i + 2) {\n- x = symbol[i];\n- y = symbol[i + 1];\n- symbolExtent.left = Math.min(symbolExtent.left, x);\n- symbolExtent.bottom = Math.min(symbolExtent.bottom, y);\n- symbolExtent.right = Math.max(symbolExtent.right, x);\n- symbolExtent.top = Math.max(symbolExtent.top, y);\n- points.push(x, \",\", y)\n+ var res = metadata.resourceSets[0].resources[0];\n+ var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection(\"EPSG:4326\"));\n+ var providers = res.imageryProviders || [],\n+ zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()),\n+ copyrights = \"\",\n+ provider, i, ii, j, jj, bbox, coverage;\n+ for (i = 0, ii = providers.length; i < ii; ++i) {\n+ provider = providers[i];\n+ for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n+ coverage = provider.coverageAreas[j];\n+ bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n+ if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n+ copyrights += provider.attribution + \" \"\n+ }\n+ }\n }\n- node.setAttributeNS(null, \"points\", points.join(\" \"));\n- var width = symbolExtent.getWidth();\n- var height = symbolExtent.getHeight();\n- var viewBox = [symbolExtent.left - width, symbolExtent.bottom - height, width * 3, height * 3];\n- symbolNode.setAttributeNS(null, \"viewBox\", viewBox.join(\" \"));\n- this.symbolMetrics[id] = [Math.max(width, height), symbolExtent.getCenterLonLat().lon, symbolExtent.getCenterLonLat().lat];\n- this.defs.appendChild(symbolNode);\n- return symbolNode\n+ var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n+ this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n+ type: this.type.toLowerCase(),\n+ logo: logo,\n+ copyrights: copyrights\n+ });\n+ this.map && this.map.events.triggerEvent(\"changelayer\", {\n+ layer: this,\n+ property: \"attribution\"\n+ })\n },\n- getFeatureIdFromEvent: function(evt) {\n- var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);\n- if (!featureId) {\n- var target = evt.target;\n- featureId = target.parentNode && target != this.rendererRoot ? target.parentNode._featureId : undefined\n+ setMap: function() {\n+ OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n+ this.map.events.register(\"moveend\", this, this.updateAttribution)\n+ },\n+ clone: function(obj) {\n+ if (obj == null) {\n+ obj = new OpenLayers.Layer.Bing(this.options)\n }\n- return featureId\n+ obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ return obj\n },\n- CLASS_NAME: \"OpenLayers.Renderer.SVG\"\n+ destroy: function() {\n+ this.map && this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n+ OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments)\n+ },\n+ CLASS_NAME: \"OpenLayers.Layer.Bing\"\n });\n-OpenLayers.Renderer.SVG.LABEL_ALIGN = {\n- l: \"start\",\n- r: \"end\",\n- b: \"bottom\",\n- t: \"hanging\"\n-};\n-OpenLayers.Renderer.SVG.LABEL_VSHIFT = {\n- t: \"-70%\",\n- b: \"0\"\n-};\n-OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n- t: 0,\n- b: -1\n-};\n-OpenLayers.Renderer.SVG.preventDefault = function(e) {\n- OpenLayers.Event.preventDefault(e)\n+OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n+ this.metadata = metadata;\n+ this.initLayer();\n+ var script = document.getElementById(this._callbackId);\n+ script.parentNode.removeChild(script);\n+ window[this._callbackId] = undefined;\n+ delete this._callbackId\n };\n OpenLayers.Protocol = OpenLayers.Class({\n format: null,\n options: null,\n autoDestroy: true,\n defaultFilter: null,\n initialize: function(options) {\n@@ -14988,42 +13432,14 @@\n success: function() {\n return this.code > 0\n },\n CLASS_NAME: \"OpenLayers.Protocol.Response\"\n });\n OpenLayers.Protocol.Response.SUCCESS = 1;\n OpenLayers.Protocol.Response.FAILURE = 0;\n-OpenLayers.Protocol.WFS = function(options) {\n- options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS);\n- var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported WFS version: \" + options.version\n- }\n- return new cls(options)\n-};\n-OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n- var typeName, featurePrefix;\n- var param = layer.params[\"LAYERS\"];\n- var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n- if (parts.length > 1) {\n- featurePrefix = parts[0]\n- }\n- typeName = parts.pop();\n- var protocolOptions = {\n- url: layer.url,\n- featureType: typeName,\n- featurePrefix: featurePrefix,\n- srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(),\n- version: \"1.1.0\"\n- };\n- return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions))\n-};\n-OpenLayers.Protocol.WFS.DEFAULTS = {\n- version: \"1.0.0\"\n-};\n OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {\n url: null,\n headers: null,\n params: null,\n callback: null,\n scope: null,\n readWithPOST: false,\n@@ -15245,14 +13661,42 @@\n var opt = options[resp.requestType];\n if (opt && opt.callback) {\n opt.callback.call(opt.scope, resp)\n }\n },\n CLASS_NAME: \"OpenLayers.Protocol.HTTP\"\n });\n+OpenLayers.Protocol.WFS = function(options) {\n+ options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS);\n+ var cls = OpenLayers.Protocol.WFS[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported WFS version: \" + options.version\n+ }\n+ return new cls(options)\n+};\n+OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {\n+ var typeName, featurePrefix;\n+ var param = layer.params[\"LAYERS\"];\n+ var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(\":\");\n+ if (parts.length > 1) {\n+ featurePrefix = parts[0]\n+ }\n+ typeName = parts.pop();\n+ var protocolOptions = {\n+ url: layer.url,\n+ featureType: typeName,\n+ featurePrefix: featurePrefix,\n+ srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(),\n+ version: \"1.1.0\"\n+ };\n+ return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions))\n+};\n+OpenLayers.Protocol.WFS.DEFAULTS = {\n+ version: \"1.0.0\"\n+};\n OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {\n version: null,\n srsName: \"EPSG:4326\",\n featureType: null,\n featureNS: null,\n geometryName: \"the_geom\",\n schema: null,\n@@ -17598,8 +16042,1564 @@\n ogc: OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n },\n CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n });\n OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {\n version: \"1.0.0\",\n CLASS_NAME: \"OpenLayers.Protocol.WFS.v1_0_0\"\n+});\n+OpenLayers.ElementsIndexer = OpenLayers.Class({\n+ maxZIndex: null,\n+ order: null,\n+ indices: null,\n+ compare: null,\n+ initialize: function(yOrdering) {\n+ this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;\n+ this.clear()\n+ },\n+ insert: function(newNode) {\n+ if (this.exists(newNode)) {\n+ this.remove(newNode)\n+ }\n+ var nodeId = newNode.id;\n+ this.determineZIndex(newNode);\n+ var leftIndex = -1;\n+ var rightIndex = this.order.length;\n+ var middle;\n+ while (rightIndex - leftIndex > 1) {\n+ middle = parseInt((leftIndex + rightIndex) / 2);\n+ var placement = this.compare(this, newNode, OpenLayers.Util.getElement(this.order[middle]));\n+ if (placement > 0) {\n+ leftIndex = middle\n+ } else {\n+ rightIndex = middle\n+ }\n+ }\n+ this.order.splice(rightIndex, 0, nodeId);\n+ this.indices[nodeId] = this.getZIndex(newNode);\n+ return this.getNextElement(rightIndex)\n+ },\n+ remove: function(node) {\n+ var nodeId = node.id;\n+ var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);\n+ if (arrayIndex >= 0) {\n+ this.order.splice(arrayIndex, 1);\n+ delete this.indices[nodeId];\n+ if (this.order.length > 0) {\n+ var lastId = this.order[this.order.length - 1];\n+ this.maxZIndex = this.indices[lastId]\n+ } else {\n+ this.maxZIndex = 0\n+ }\n+ }\n+ },\n+ clear: function() {\n+ this.order = [];\n+ this.indices = {};\n+ this.maxZIndex = 0\n+ },\n+ exists: function(node) {\n+ return this.indices[node.id] != null\n+ },\n+ getZIndex: function(node) {\n+ return node._style.graphicZIndex\n+ },\n+ determineZIndex: function(node) {\n+ var zIndex = node._style.graphicZIndex;\n+ if (zIndex == null) {\n+ zIndex = this.maxZIndex;\n+ node._style.graphicZIndex = zIndex\n+ } else if (zIndex > this.maxZIndex) {\n+ this.maxZIndex = zIndex\n+ }\n+ },\n+ getNextElement: function(index) {\n+ var nextIndex = index + 1;\n+ if (nextIndex < this.order.length) {\n+ var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);\n+ if (nextElement == undefined) {\n+ nextElement = this.getNextElement(nextIndex)\n+ }\n+ return nextElement\n+ } else {\n+ return null\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.ElementsIndexer\"\n+});\n+OpenLayers.ElementsIndexer.IndexingMethods = {\n+ Z_ORDER: function(indexer, newNode, nextNode) {\n+ var newZIndex = indexer.getZIndex(newNode);\n+ var returnVal = 0;\n+ if (nextNode) {\n+ var nextZIndex = indexer.getZIndex(nextNode);\n+ returnVal = newZIndex - nextZIndex\n+ }\n+ return returnVal\n+ },\n+ Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);\n+ if (nextNode && returnVal == 0) {\n+ returnVal = 1\n+ }\n+ return returnVal\n+ },\n+ Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {\n+ var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);\n+ if (nextNode && returnVal === 0) {\n+ var result = nextNode._boundsBottom - newNode._boundsBottom;\n+ returnVal = result === 0 ? 1 : result\n+ }\n+ return returnVal\n+ }\n+};\n+OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {\n+ rendererRoot: null,\n+ root: null,\n+ vectorRoot: null,\n+ textRoot: null,\n+ xmlns: null,\n+ xOffset: 0,\n+ indexer: null,\n+ BACKGROUND_ID_SUFFIX: \"_background\",\n+ LABEL_ID_SUFFIX: \"_label\",\n+ LABEL_OUTLINE_SUFFIX: \"_outline\",\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.rendererRoot = this.createRenderRoot();\n+ this.root = this.createRoot(\"_root\");\n+ this.vectorRoot = this.createRoot(\"_vroot\");\n+ this.textRoot = this.createRoot(\"_troot\");\n+ this.root.appendChild(this.vectorRoot);\n+ this.root.appendChild(this.textRoot);\n+ this.rendererRoot.appendChild(this.root);\n+ this.container.appendChild(this.rendererRoot);\n+ if (options && (options.zIndexing || options.yOrdering)) {\n+ this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering)\n+ }\n+ },\n+ destroy: function() {\n+ this.clear();\n+ this.rendererRoot = null;\n+ this.root = null;\n+ this.xmlns = null;\n+ OpenLayers.Renderer.prototype.destroy.apply(this, arguments)\n+ },\n+ clear: function() {\n+ var child;\n+ var root = this.vectorRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child)\n+ }\n+ }\n+ root = this.textRoot;\n+ if (root) {\n+ while (child = root.firstChild) {\n+ root.removeChild(child)\n+ }\n+ }\n+ if (this.indexer) {\n+ this.indexer.clear()\n+ }\n+ },\n+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ var resolution = this.getResolution();\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ var rightOfDateLine, ratio = extent.getWidth() / this.map.getExtent().getWidth(),\n+ extent = extent.scale(1 / ratio),\n+ world = this.map.getMaxExtent();\n+ if (world.right > extent.left && world.right < extent.right) {\n+ rightOfDateLine = true\n+ } else if (world.left > extent.left && world.left < extent.right) {\n+ rightOfDateLine = false\n+ }\n+ if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {\n+ coordSysUnchanged = false;\n+ this.xOffset = rightOfDateLine === true ? world.getWidth() / resolution : 0\n+ }\n+ this.rightOfDateLine = rightOfDateLine\n+ }\n+ return coordSysUnchanged\n+ },\n+ getNodeType: function(geometry, style) {},\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ var rendered = true;\n+ if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ rendered = this.drawGeometry(geometry.components[i], style, featureId) && rendered\n+ }\n+ return rendered\n+ }\n+ rendered = false;\n+ var removeBackground = false;\n+ if (style.display != \"none\") {\n+ if (style.backgroundGraphic) {\n+ this.redrawBackgroundNode(geometry.id, geometry, style, featureId)\n+ } else {\n+ removeBackground = true\n+ }\n+ rendered = this.redrawNode(geometry.id, geometry, style, featureId)\n+ }\n+ if (rendered == false) {\n+ var node = document.getElementById(geometry.id);\n+ if (node) {\n+ if (node._style.backgroundGraphic) {\n+ removeBackground = true\n+ }\n+ node.parentNode.removeChild(node)\n+ }\n+ }\n+ if (removeBackground) {\n+ var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX);\n+ if (node) {\n+ node.parentNode.removeChild(node)\n+ }\n+ }\n+ return rendered\n+ },\n+ redrawNode: function(id, geometry, style, featureId) {\n+ style = this.applyDefaultSymbolizer(style);\n+ var node = this.nodeFactory(id, this.getNodeType(geometry, style));\n+ node._featureId = featureId;\n+ node._boundsBottom = geometry.getBounds().bottom;\n+ node._geometryClass = geometry.CLASS_NAME;\n+ node._style = style;\n+ var drawResult = this.drawGeometryNode(node, geometry, style);\n+ if (drawResult === false) {\n+ return false\n+ }\n+ node = drawResult.node;\n+ if (this.indexer) {\n+ var insert = this.indexer.insert(node);\n+ if (insert) {\n+ this.vectorRoot.insertBefore(node, insert)\n+ } else {\n+ this.vectorRoot.appendChild(node)\n+ }\n+ } else {\n+ if (node.parentNode !== this.vectorRoot) {\n+ this.vectorRoot.appendChild(node)\n+ }\n+ }\n+ this.postDraw(node);\n+ return drawResult.complete\n+ },\n+ redrawBackgroundNode: function(id, geometry, style, featureId) {\n+ var backgroundStyle = OpenLayers.Util.extend({}, style);\n+ backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;\n+ backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;\n+ backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;\n+ backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;\n+ backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;\n+ backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;\n+ backgroundStyle.backgroundGraphic = null;\n+ backgroundStyle.backgroundXOffset = null;\n+ backgroundStyle.backgroundYOffset = null;\n+ backgroundStyle.backgroundGraphicZIndex = null;\n+ return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null)\n+ },\n+ drawGeometryNode: function(node, geometry, style) {\n+ style = style || node._style;\n+ var options = {\n+ isFilled: style.fill === undefined ? true : style.fill,\n+ isStroked: style.stroke === undefined ? !!style.strokeWidth : style.stroke\n+ };\n+ var drawn;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.graphic === false) {\n+ options.isFilled = false;\n+ options.isStroked = false\n+ }\n+ drawn = this.drawPoint(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ options.isFilled = false;\n+ drawn = this.drawLineString(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ drawn = this.drawLinearRing(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ drawn = this.drawPolygon(node, geometry);\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ drawn = this.drawRectangle(node, geometry);\n+ break;\n+ default:\n+ break\n+ }\n+ node._options = options;\n+ if (drawn != false) {\n+ return {\n+ node: this.setStyle(node, style, options, geometry),\n+ complete: drawn\n+ }\n+ } else {\n+ return false\n+ }\n+ },\n+ postDraw: function(node) {},\n+ drawPoint: function(node, geometry) {},\n+ drawLineString: function(node, geometry) {},\n+ drawLinearRing: function(node, geometry) {},\n+ drawPolygon: function(node, geometry) {},\n+ drawRectangle: function(node, geometry) {},\n+ drawCircle: function(node, geometry) {},\n+ removeText: function(featureId) {\n+ var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);\n+ if (label) {\n+ this.textRoot.removeChild(label)\n+ }\n+ var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);\n+ if (outline) {\n+ this.textRoot.removeChild(outline)\n+ }\n+ },\n+ getFeatureIdFromEvent: function(evt) {\n+ var target = evt.target;\n+ var useElement = target && target.correspondingUseElement;\n+ var node = useElement ? useElement : target || evt.srcElement;\n+ return node._featureId\n+ },\n+ eraseGeometry: function(geometry, featureId) {\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPoint\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiLineString\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.MultiPolygon\" || geometry.CLASS_NAME == \"OpenLayers.Geometry.Collection\") {\n+ for (var i = 0, len = geometry.components.length; i < len; i++) {\n+ this.eraseGeometry(geometry.components[i], featureId)\n+ }\n+ } else {\n+ var element = OpenLayers.Util.getElement(geometry.id);\n+ if (element && element.parentNode) {\n+ if (element.geometry) {\n+ element.geometry.destroy();\n+ element.geometry = null\n+ }\n+ element.parentNode.removeChild(element);\n+ if (this.indexer) {\n+ this.indexer.remove(element)\n+ }\n+ if (element._style.backgroundGraphic) {\n+ var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;\n+ var bElem = OpenLayers.Util.getElement(backgroundId);\n+ if (bElem && bElem.parentNode) {\n+ bElem.parentNode.removeChild(bElem)\n+ }\n+ }\n+ }\n+ }\n+ },\n+ nodeFactory: function(id, type) {\n+ var node = OpenLayers.Util.getElement(id);\n+ if (node) {\n+ if (!this.nodeTypeCompare(node, type)) {\n+ node.parentNode.removeChild(node);\n+ node = this.nodeFactory(id, type)\n+ }\n+ } else {\n+ node = this.createNode(type, id)\n+ }\n+ return node\n+ },\n+ nodeTypeCompare: function(node, type) {},\n+ createNode: function(type, id) {},\n+ moveRoot: function(renderer) {\n+ var root = this.root;\n+ if (renderer.root.parentNode == this.rendererRoot) {\n+ root = renderer.root\n+ }\n+ root.parentNode.removeChild(root);\n+ renderer.rendererRoot.appendChild(root)\n+ },\n+ getRenderLayerId: function() {\n+ return this.root.parentNode.parentNode.id\n+ },\n+ isComplexSymbol: function(graphicName) {\n+ return graphicName != \"circle\" && !!graphicName\n+ },\n+ CLASS_NAME: \"OpenLayers.Renderer.Elements\"\n+});\n+OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {\n+ xmlns: \"http://www.w3.org/2000/svg\",\n+ xlinkns: \"http://www.w3.org/1999/xlink\",\n+ MAX_PIXEL: 15e3,\n+ translationParameters: null,\n+ symbolMetrics: null,\n+ initialize: function(containerID) {\n+ if (!this.supported()) {\n+ return\n+ }\n+ OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);\n+ this.translationParameters = {\n+ x: 0,\n+ y: 0\n+ };\n+ this.symbolMetrics = {}\n+ },\n+ supported: function() {\n+ var svgFeature = \"http://www.w3.org/TR/SVG11/feature#\";\n+ return document.implementation && (document.implementation.hasFeature(\"org.w3c.svg\", \"1.0\") || document.implementation.hasFeature(svgFeature + \"SVG\", \"1.1\") || document.implementation.hasFeature(svgFeature + \"BasicStructure\", \"1.1\"))\n+ },\n+ inValidRange: function(x, y, xyOnly) {\n+ var left = x + (xyOnly ? 0 : this.translationParameters.x);\n+ var top = y + (xyOnly ? 0 : this.translationParameters.y);\n+ return left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL && top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL\n+ },\n+ setExtent: function(extent, resolutionChanged) {\n+ var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);\n+ var resolution = this.getResolution(),\n+ left = -extent.left / resolution,\n+ top = extent.top / resolution;\n+ if (resolutionChanged) {\n+ this.left = left;\n+ this.top = top;\n+ var extentString = \"0 0 \" + this.size.w + \" \" + this.size.h;\n+ this.rendererRoot.setAttributeNS(null, \"viewBox\", extentString);\n+ this.translate(this.xOffset, 0);\n+ return true\n+ } else {\n+ var inRange = this.translate(left - this.left + this.xOffset, top - this.top);\n+ if (!inRange) {\n+ this.setExtent(extent, true)\n+ }\n+ return coordSysUnchanged && inRange\n+ }\n+ },\n+ translate: function(x, y) {\n+ if (!this.inValidRange(x, y, true)) {\n+ return false\n+ } else {\n+ var transformString = \"\";\n+ if (x || y) {\n+ transformString = \"translate(\" + x + \",\" + y + \")\"\n+ }\n+ this.root.setAttributeNS(null, \"transform\", transformString);\n+ this.translationParameters = {\n+ x: x,\n+ y: y\n+ };\n+ return true\n+ }\n+ },\n+ setSize: function(size) {\n+ OpenLayers.Renderer.prototype.setSize.apply(this, arguments);\n+ this.rendererRoot.setAttributeNS(null, \"width\", this.size.w);\n+ this.rendererRoot.setAttributeNS(null, \"height\", this.size.h)\n+ },\n+ getNodeType: function(geometry, style) {\n+ var nodeType = null;\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ if (style.externalGraphic) {\n+ nodeType = \"image\"\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ nodeType = \"svg\"\n+ } else {\n+ nodeType = \"circle\"\n+ }\n+ break;\n+ case \"OpenLayers.Geometry.Rectangle\":\n+ nodeType = \"rect\";\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ nodeType = \"polyline\";\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ nodeType = \"polygon\";\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ case \"OpenLayers.Geometry.Curve\":\n+ nodeType = \"path\";\n+ break;\n+ default:\n+ break\n+ }\n+ return nodeType\n+ },\n+ setStyle: function(node, style, options) {\n+ style = style || node._style;\n+ options = options || node._options;\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ node.setAttributeNS(null, \"title\", title);\n+ var titleNode = node.getElementsByTagName(\"title\");\n+ if (titleNode.length > 0) {\n+ titleNode[0].firstChild.textContent = title\n+ } else {\n+ var label = this.nodeFactory(null, \"title\");\n+ label.textContent = title;\n+ node.appendChild(label)\n+ }\n+ }\n+ var r = parseFloat(node.getAttributeNS(null, \"r\"));\n+ var widthFactor = 1;\n+ var pos;\n+ if (node._geometryClass == \"OpenLayers.Geometry.Point\" && r) {\n+ node.style.visibility = \"\";\n+ if (style.graphic === false) {\n+ node.style.visibility = \"hidden\"\n+ } else if (style.externalGraphic) {\n+ pos = this.getPosition(node);\n+ if (style.graphicWidth && style.graphicHeight) {\n+ node.setAttributeNS(null, \"preserveAspectRatio\", \"none\")\n+ }\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n+ var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+ node.setAttributeNS(null, \"x\", (pos.x + xOffset).toFixed());\n+ node.setAttributeNS(null, \"y\", (pos.y + yOffset).toFixed());\n+ node.setAttributeNS(null, \"width\", width);\n+ node.setAttributeNS(null, \"height\", height);\n+ node.setAttributeNS(this.xlinkns, \"xlink:href\", style.externalGraphic);\n+ node.setAttributeNS(null, \"style\", \"opacity: \" + opacity);\n+ node.onclick = OpenLayers.Event.preventDefault\n+ } else if (this.isComplexSymbol(style.graphicName)) {\n+ var offset = style.pointRadius * 3;\n+ var size = offset * 2;\n+ var src = this.importSymbol(style.graphicName);\n+ pos = this.getPosition(node);\n+ widthFactor = this.symbolMetrics[src.id][0] * 3 / size;\n+ var parent = node.parentNode;\n+ var nextSibling = node.nextSibling;\n+ if (parent) {\n+ parent.removeChild(node)\n+ }\n+ node.firstChild && node.removeChild(node.firstChild);\n+ node.appendChild(src.firstChild.cloneNode(true));\n+ node.setAttributeNS(null, \"viewBox\", src.getAttributeNS(null, \"viewBox\"));\n+ node.setAttributeNS(null, \"width\", size);\n+ node.setAttributeNS(null, \"height\", size);\n+ node.setAttributeNS(null, \"x\", pos.x - offset);\n+ node.setAttributeNS(null, \"y\", pos.y - offset);\n+ if (nextSibling) {\n+ parent.insertBefore(node, nextSibling)\n+ } else if (parent) {\n+ parent.appendChild(node)\n+ }\n+ } else {\n+ node.setAttributeNS(null, \"r\", style.pointRadius)\n+ }\n+ var rotation = style.rotation;\n+ if ((rotation !== undefined || node._rotation !== undefined) && pos) {\n+ node._rotation = rotation;\n+ rotation |= 0;\n+ if (node.nodeName !== \"svg\") {\n+ node.setAttributeNS(null, \"transform\", \"rotate(\" + rotation + \" \" + pos.x + \" \" + pos.y + \")\")\n+ } else {\n+ var metrics = this.symbolMetrics[src.id];\n+ node.firstChild.setAttributeNS(null, \"transform\", \"rotate(\" + rotation + \" \" + metrics[1] + \" \" + metrics[2] + \")\")\n+ }\n+ }\n+ }\n+ if (options.isFilled) {\n+ node.setAttributeNS(null, \"fill\", style.fillColor);\n+ node.setAttributeNS(null, \"fill-opacity\", style.fillOpacity)\n+ } else {\n+ node.setAttributeNS(null, \"fill\", \"none\")\n+ }\n+ if (options.isStroked) {\n+ node.setAttributeNS(null, \"stroke\", style.strokeColor);\n+ node.setAttributeNS(null, \"stroke-opacity\", style.strokeOpacity);\n+ node.setAttributeNS(null, \"stroke-width\", style.strokeWidth * widthFactor);\n+ node.setAttributeNS(null, \"stroke-linecap\", style.strokeLinecap || \"round\");\n+ node.setAttributeNS(null, \"stroke-linejoin\", \"round\");\n+ style.strokeDashstyle && node.setAttributeNS(null, \"stroke-dasharray\", this.dashStyle(style, widthFactor))\n+ } else {\n+ node.setAttributeNS(null, \"stroke\", \"none\")\n+ }\n+ if (style.pointerEvents) {\n+ node.setAttributeNS(null, \"pointer-events\", style.pointerEvents)\n+ }\n+ if (style.cursor != null) {\n+ node.setAttributeNS(null, \"cursor\", style.cursor)\n+ }\n+ return node\n+ },\n+ dashStyle: function(style, widthFactor) {\n+ var w = style.strokeWidth * widthFactor;\n+ var str = style.strokeDashstyle;\n+ switch (str) {\n+ case \"solid\":\n+ return \"none\";\n+ case \"dot\":\n+ return [1, 4 * w].join();\n+ case \"dash\":\n+ return [4 * w, 4 * w].join();\n+ case \"dashdot\":\n+ return [4 * w, 4 * w, 1, 4 * w].join();\n+ case \"longdash\":\n+ return [8 * w, 4 * w].join();\n+ case \"longdashdot\":\n+ return [8 * w, 4 * w, 1, 4 * w].join();\n+ default:\n+ return OpenLayers.String.trim(str).replace(/\\s+/g, \",\")\n+ }\n+ },\n+ createNode: function(type, id) {\n+ var node = document.createElementNS(this.xmlns, type);\n+ if (id) {\n+ node.setAttributeNS(null, \"id\", id)\n+ }\n+ return node\n+ },\n+ nodeTypeCompare: function(node, type) {\n+ return type == node.nodeName\n+ },\n+ createRenderRoot: function() {\n+ var svg = this.nodeFactory(this.container.id + \"_svgRoot\", \"svg\");\n+ svg.style.display = \"block\";\n+ return svg\n+ },\n+ createRoot: function(suffix) {\n+ return this.nodeFactory(this.container.id + suffix, \"g\")\n+ },\n+ createDefs: function() {\n+ var defs = this.nodeFactory(this.container.id + \"_defs\", \"defs\");\n+ this.rendererRoot.appendChild(defs);\n+ return defs\n+ },\n+ drawPoint: function(node, geometry) {\n+ return this.drawCircle(node, geometry, 1)\n+ },\n+ drawCircle: function(node, geometry, radius) {\n+ var resolution = this.getResolution();\n+ var x = (geometry.x - this.featureDx) / resolution + this.left;\n+ var y = this.top - geometry.y / resolution;\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"cx\", x);\n+ node.setAttributeNS(null, \"cy\", y);\n+ node.setAttributeNS(null, \"r\", radius);\n+ return node\n+ } else {\n+ return false\n+ }\n+ },\n+ drawLineString: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return componentsResult.complete ? node : null\n+ } else {\n+ return false\n+ }\n+ },\n+ drawLinearRing: function(node, geometry) {\n+ var componentsResult = this.getComponentsString(geometry.components);\n+ if (componentsResult.path) {\n+ node.setAttributeNS(null, \"points\", componentsResult.path);\n+ return componentsResult.complete ? node : null\n+ } else {\n+ return false\n+ }\n+ },\n+ drawPolygon: function(node, geometry) {\n+ var d = \"\";\n+ var draw = true;\n+ var complete = true;\n+ var linearRingResult, path;\n+ for (var j = 0, len = geometry.components.length; j < len; j++) {\n+ d += \" M\";\n+ linearRingResult = this.getComponentsString(geometry.components[j].components, \" \");\n+ path = linearRingResult.path;\n+ if (path) {\n+ d += \" \" + path;\n+ complete = linearRingResult.complete && complete\n+ } else {\n+ draw = false\n+ }\n+ }\n+ d += \" z\";\n+ if (draw) {\n+ node.setAttributeNS(null, \"d\", d);\n+ node.setAttributeNS(null, \"fill-rule\", \"evenodd\");\n+ return complete ? node : null\n+ } else {\n+ return false\n+ }\n+ },\n+ drawRectangle: function(node, geometry) {\n+ var resolution = this.getResolution();\n+ var x = (geometry.x - this.featureDx) / resolution + this.left;\n+ var y = this.top - geometry.y / resolution;\n+ if (this.inValidRange(x, y)) {\n+ node.setAttributeNS(null, \"x\", x);\n+ node.setAttributeNS(null, \"y\", y);\n+ node.setAttributeNS(null, \"width\", geometry.width / resolution);\n+ node.setAttributeNS(null, \"height\", geometry.height / resolution);\n+ return node\n+ } else {\n+ return false\n+ }\n+ },\n+ drawText: function(featureId, style, location) {\n+ var drawOutline = !!style.labelOutlineWidth;\n+ if (drawOutline) {\n+ var outlineStyle = OpenLayers.Util.extend({}, style);\n+ outlineStyle.fontColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;\n+ outlineStyle.fontStrokeWidth = style.labelOutlineWidth;\n+ if (style.labelOutlineOpacity) {\n+ outlineStyle.fontOpacity = style.labelOutlineOpacity\n+ }\n+ delete outlineStyle.labelOutlineWidth;\n+ this.drawText(featureId, outlineStyle, location)\n+ }\n+ var resolution = this.getResolution();\n+ var x = (location.x - this.featureDx) / resolution + this.left;\n+ var y = location.y / resolution - this.top;\n+ var suffix = drawOutline ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX;\n+ var label = this.nodeFactory(featureId + suffix, \"text\");\n+ label.setAttributeNS(null, \"x\", x);\n+ label.setAttributeNS(null, \"y\", -y);\n+ if (style.fontColor) {\n+ label.setAttributeNS(null, \"fill\", style.fontColor)\n+ }\n+ if (style.fontStrokeColor) {\n+ label.setAttributeNS(null, \"stroke\", style.fontStrokeColor)\n+ }\n+ if (style.fontStrokeWidth) {\n+ label.setAttributeNS(null, \"stroke-width\", style.fontStrokeWidth)\n+ }\n+ if (style.fontOpacity) {\n+ label.setAttributeNS(null, \"opacity\", style.fontOpacity)\n+ }\n+ if (style.fontFamily) {\n+ label.setAttributeNS(null, \"font-family\", style.fontFamily)\n+ }\n+ if (style.fontSize) {\n+ label.setAttributeNS(null, \"font-size\", style.fontSize)\n+ }\n+ if (style.fontWeight) {\n+ label.setAttributeNS(null, \"font-weight\", style.fontWeight)\n+ }\n+ if (style.fontStyle) {\n+ label.setAttributeNS(null, \"font-style\", style.fontStyle)\n+ }\n+ if (style.labelSelect === true) {\n+ label.setAttributeNS(null, \"pointer-events\", \"visible\");\n+ label._featureId = featureId\n+ } else {\n+ label.setAttributeNS(null, \"pointer-events\", \"none\")\n+ }\n+ var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;\n+ label.setAttributeNS(null, \"text-anchor\", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || \"middle\");\n+ if (OpenLayers.IS_GECKO === true) {\n+ label.setAttributeNS(null, \"dominant-baseline\", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || \"central\")\n+ }\n+ var labelRows = style.label.split(\"\\n\");\n+ var numRows = labelRows.length;\n+ while (label.childNodes.length > numRows) {\n+ label.removeChild(label.lastChild)\n+ }\n+ for (var i = 0; i < numRows; i++) {\n+ var tspan = this.nodeFactory(featureId + suffix + \"_tspan_\" + i, \"tspan\");\n+ if (style.labelSelect === true) {\n+ tspan._featureId = featureId;\n+ tspan._geometry = location;\n+ tspan._geometryClass = location.CLASS_NAME\n+ }\n+ if (OpenLayers.IS_GECKO === false) {\n+ tspan.setAttributeNS(null, \"baseline-shift\", OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || \"-35%\")\n+ }\n+ tspan.setAttribute(\"x\", x);\n+ if (i == 0) {\n+ var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5\n+ }\n+ tspan.setAttribute(\"dy\", vfactor * (numRows - 1) + \"em\")\n+ } else {\n+ tspan.setAttribute(\"dy\", \"1em\")\n+ }\n+ tspan.textContent = labelRows[i] === \"\" ? \" \" : labelRows[i];\n+ if (!tspan.parentNode) {\n+ label.appendChild(tspan)\n+ }\n+ }\n+ if (!label.parentNode) {\n+ this.textRoot.appendChild(label)\n+ }\n+ },\n+ getComponentsString: function(components, separator) {\n+ var renderCmp = [];\n+ var complete = true;\n+ var len = components.length;\n+ var strings = [];\n+ var str, component;\n+ for (var i = 0; i < len; i++) {\n+ component = components[i];\n+ renderCmp.push(component);\n+ str = this.getShortString(component);\n+ if (str) {\n+ strings.push(str)\n+ } else {\n+ if (i > 0) {\n+ if (this.getShortString(components[i - 1])) {\n+ strings.push(this.clipLine(components[i], components[i - 1]))\n+ }\n+ }\n+ if (i < len - 1) {\n+ if (this.getShortString(components[i + 1])) {\n+ strings.push(this.clipLine(components[i], components[i + 1]))\n+ }\n+ }\n+ complete = false\n+ }\n+ }\n+ return {\n+ path: strings.join(separator || \",\"),\n+ complete: complete\n+ }\n+ },\n+ clipLine: function(badComponent, goodComponent) {\n+ if (goodComponent.equals(badComponent)) {\n+ return \"\"\n+ }\n+ var resolution = this.getResolution();\n+ var maxX = this.MAX_PIXEL - this.translationParameters.x;\n+ var maxY = this.MAX_PIXEL - this.translationParameters.y;\n+ var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;\n+ var y1 = this.top - goodComponent.y / resolution;\n+ var x2 = (badComponent.x - this.featureDx) / resolution + this.left;\n+ var y2 = this.top - badComponent.y / resolution;\n+ var k;\n+ if (x2 < -maxX || x2 > maxX) {\n+ k = (y2 - y1) / (x2 - x1);\n+ x2 = x2 < 0 ? -maxX : maxX;\n+ y2 = y1 + (x2 - x1) * k\n+ }\n+ if (y2 < -maxY || y2 > maxY) {\n+ k = (x2 - x1) / (y2 - y1);\n+ y2 = y2 < 0 ? -maxY : maxY;\n+ x2 = x1 + (y2 - y1) * k\n+ }\n+ return x2 + \",\" + y2\n+ },\n+ getShortString: function(point) {\n+ var resolution = this.getResolution();\n+ var x = (point.x - this.featureDx) / resolution + this.left;\n+ var y = this.top - point.y / resolution;\n+ if (this.inValidRange(x, y)) {\n+ return x + \",\" + y\n+ } else {\n+ return false\n+ }\n+ },\n+ getPosition: function(node) {\n+ return {\n+ x: parseFloat(node.getAttributeNS(null, \"cx\")),\n+ y: parseFloat(node.getAttributeNS(null, \"cy\"))\n+ }\n+ },\n+ importSymbol: function(graphicName) {\n+ if (!this.defs) {\n+ this.defs = this.createDefs()\n+ }\n+ var id = this.container.id + \"-\" + graphicName;\n+ var existing = document.getElementById(id);\n+ if (existing != null) {\n+ return existing\n+ }\n+ var symbol = OpenLayers.Renderer.symbol[graphicName];\n+ if (!symbol) {\n+ throw new Error(graphicName + \" is not a valid symbol name\")\n+ }\n+ var symbolNode = this.nodeFactory(id, \"symbol\");\n+ var node = this.nodeFactory(null, \"polygon\");\n+ symbolNode.appendChild(node);\n+ var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);\n+ var points = [];\n+ var x, y;\n+ for (var i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ symbolExtent.left = Math.min(symbolExtent.left, x);\n+ symbolExtent.bottom = Math.min(symbolExtent.bottom, y);\n+ symbolExtent.right = Math.max(symbolExtent.right, x);\n+ symbolExtent.top = Math.max(symbolExtent.top, y);\n+ points.push(x, \",\", y)\n+ }\n+ node.setAttributeNS(null, \"points\", points.join(\" \"));\n+ var width = symbolExtent.getWidth();\n+ var height = symbolExtent.getHeight();\n+ var viewBox = [symbolExtent.left - width, symbolExtent.bottom - height, width * 3, height * 3];\n+ symbolNode.setAttributeNS(null, \"viewBox\", viewBox.join(\" \"));\n+ this.symbolMetrics[id] = [Math.max(width, height), symbolExtent.getCenterLonLat().lon, symbolExtent.getCenterLonLat().lat];\n+ this.defs.appendChild(symbolNode);\n+ return symbolNode\n+ },\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);\n+ if (!featureId) {\n+ var target = evt.target;\n+ featureId = target.parentNode && target != this.rendererRoot ? target.parentNode._featureId : undefined\n+ }\n+ return featureId\n+ },\n+ CLASS_NAME: \"OpenLayers.Renderer.SVG\"\n+});\n+OpenLayers.Renderer.SVG.LABEL_ALIGN = {\n+ l: \"start\",\n+ r: \"end\",\n+ b: \"bottom\",\n+ t: \"hanging\"\n+};\n+OpenLayers.Renderer.SVG.LABEL_VSHIFT = {\n+ t: \"-70%\",\n+ b: \"0\"\n+};\n+OpenLayers.Renderer.SVG.LABEL_VFACTOR = {\n+ t: 0,\n+ b: -1\n+};\n+OpenLayers.Renderer.SVG.preventDefault = function(e) {\n+ OpenLayers.Event.preventDefault(e)\n+};\n+OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {\n+ hitDetection: true,\n+ hitOverflow: 0,\n+ canvas: null,\n+ features: null,\n+ pendingRedraw: false,\n+ cachedSymbolBounds: {},\n+ initialize: function(containerID, options) {\n+ OpenLayers.Renderer.prototype.initialize.apply(this, arguments);\n+ this.root = document.createElement(\"canvas\");\n+ this.container.appendChild(this.root);\n+ this.canvas = this.root.getContext(\"2d\");\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitCanvas = document.createElement(\"canvas\");\n+ this.hitContext = this.hitCanvas.getContext(\"2d\")\n+ }\n+ },\n+ setExtent: function() {\n+ OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);\n+ return false\n+ },\n+ eraseGeometry: function(geometry, featureId) {\n+ this.eraseFeatures(this.features[featureId][0])\n+ },\n+ supported: function() {\n+ return OpenLayers.CANVAS_SUPPORTED\n+ },\n+ setSize: function(size) {\n+ this.size = size.clone();\n+ var root = this.root;\n+ root.style.width = size.w + \"px\";\n+ root.style.height = size.h + \"px\";\n+ root.width = size.w;\n+ root.height = size.h;\n+ this.resolution = null;\n+ if (this.hitDetection) {\n+ var hitCanvas = this.hitCanvas;\n+ hitCanvas.style.width = size.w + \"px\";\n+ hitCanvas.style.height = size.h + \"px\";\n+ hitCanvas.width = size.w;\n+ hitCanvas.height = size.h\n+ }\n+ },\n+ drawFeature: function(feature, style) {\n+ var rendered;\n+ if (feature.geometry) {\n+ style = this.applyDefaultSymbolizer(style || feature.style);\n+ var bounds = feature.geometry.getBounds();\n+ var worldBounds;\n+ if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {\n+ worldBounds = this.map.getMaxExtent()\n+ }\n+ var intersects = bounds && bounds.intersectsBounds(this.extent, {\n+ worldBounds: worldBounds\n+ });\n+ rendered = style.display !== \"none\" && !!bounds && intersects;\n+ if (rendered) {\n+ this.features[feature.id] = [feature, style]\n+ } else {\n+ delete this.features[feature.id]\n+ }\n+ this.pendingRedraw = true\n+ }\n+ if (this.pendingRedraw && !this.locked) {\n+ this.redraw();\n+ this.pendingRedraw = false\n+ }\n+ return rendered\n+ },\n+ drawGeometry: function(geometry, style, featureId) {\n+ var className = geometry.CLASS_NAME;\n+ if (className == \"OpenLayers.Geometry.Collection\" || className == \"OpenLayers.Geometry.MultiPoint\" || className == \"OpenLayers.Geometry.MultiLineString\" || className == \"OpenLayers.Geometry.MultiPolygon\") {\n+ for (var i = 0; i < geometry.components.length; i++) {\n+ this.drawGeometry(geometry.components[i], style, featureId)\n+ }\n+ return\n+ }\n+ switch (geometry.CLASS_NAME) {\n+ case \"OpenLayers.Geometry.Point\":\n+ this.drawPoint(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LineString\":\n+ this.drawLineString(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.LinearRing\":\n+ this.drawLinearRing(geometry, style, featureId);\n+ break;\n+ case \"OpenLayers.Geometry.Polygon\":\n+ this.drawPolygon(geometry, style, featureId);\n+ break;\n+ default:\n+ break\n+ }\n+ },\n+ drawExternalGraphic: function(geometry, style, featureId) {\n+ var img = new Image;\n+ var title = style.title || style.graphicTitle;\n+ if (title) {\n+ img.title = title\n+ }\n+ var width = style.graphicWidth || style.graphicHeight;\n+ var height = style.graphicHeight || style.graphicWidth;\n+ width = width ? width : style.pointRadius * 2;\n+ height = height ? height : style.pointRadius * 2;\n+ var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width);\n+ var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height);\n+ var opacity = style.graphicOpacity || style.fillOpacity;\n+ var onLoad = function() {\n+ if (!this.features[featureId]) {\n+ return\n+ }\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var x = p0 + xOffset | 0;\n+ var y = p1 + yOffset | 0;\n+ var canvas = this.canvas;\n+ canvas.globalAlpha = opacity;\n+ var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || (OpenLayers.Renderer.Canvas.drawImageScaleFactor = /android 2.1/.test(navigator.userAgent.toLowerCase()) ? 320 / window.screen.width : 1);\n+ canvas.drawImage(img, x * factor, y * factor, width * factor, height * factor);\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId);\n+ this.hitContext.fillRect(x, y, width, height)\n+ }\n+ }\n+ };\n+ img.onload = OpenLayers.Function.bind(onLoad, this);\n+ img.src = style.externalGraphic\n+ },\n+ drawNamedSymbol: function(geometry, style, featureId) {\n+ var x, y, cx, cy, i, symbolBounds, scaling, angle;\n+ var unscaledStrokeWidth;\n+ var deg2rad = Math.PI / 180;\n+ var symbol = OpenLayers.Renderer.symbol[style.graphicName];\n+ if (!symbol) {\n+ throw new Error(style.graphicName + \" is not a valid symbol name\")\n+ }\n+ if (!symbol.length || symbol.length < 2) return;\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (isNaN(p0) || isNaN(p1)) return;\n+ this.canvas.lineCap = \"round\";\n+ this.canvas.lineJoin = \"round\";\n+ if (this.hitDetection) {\n+ this.hitContext.lineCap = \"round\";\n+ this.hitContext.lineJoin = \"round\"\n+ }\n+ if (style.graphicName in this.cachedSymbolBounds) {\n+ symbolBounds = this.cachedSymbolBounds[style.graphicName]\n+ } else {\n+ symbolBounds = new OpenLayers.Bounds;\n+ for (i = 0; i < symbol.length; i += 2) {\n+ symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i + 1]))\n+ }\n+ this.cachedSymbolBounds[style.graphicName] = symbolBounds\n+ }\n+ this.canvas.save();\n+ if (this.hitDetection) {\n+ this.hitContext.save()\n+ }\n+ this.canvas.translate(p0, p1);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(p0, p1)\n+ }\n+ angle = deg2rad * style.rotation;\n+ if (!isNaN(angle)) {\n+ this.canvas.rotate(angle);\n+ if (this.hitDetection) {\n+ this.hitContext.rotate(angle)\n+ }\n+ }\n+ scaling = 2 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());\n+ this.canvas.scale(scaling, scaling);\n+ if (this.hitDetection) {\n+ this.hitContext.scale(scaling, scaling)\n+ }\n+ cx = symbolBounds.getCenterLonLat().lon;\n+ cy = symbolBounds.getCenterLonLat().lat;\n+ this.canvas.translate(-cx, -cy);\n+ if (this.hitDetection) {\n+ this.hitContext.translate(-cx, -cy)\n+ }\n+ unscaledStrokeWidth = style.strokeWidth;\n+ style.strokeWidth = unscaledStrokeWidth / scaling;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y)\n+ }\n+ this.canvas.closePath();\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.hitContext.lineTo(x, y)\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.fill()\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.canvas.moveTo(x, y);\n+ this.canvas.lineTo(x, y)\n+ }\n+ this.canvas.closePath();\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style, scaling);\n+ this.hitContext.beginPath();\n+ for (i = 0; i < symbol.length; i = i + 2) {\n+ x = symbol[i];\n+ y = symbol[i + 1];\n+ if (i == 0) this.hitContext.moveTo(x, y);\n+ this.hitContext.lineTo(x, y)\n+ }\n+ this.hitContext.closePath();\n+ this.hitContext.stroke()\n+ }\n+ }\n+ style.strokeWidth = unscaledStrokeWidth;\n+ this.canvas.restore();\n+ if (this.hitDetection) {\n+ this.hitContext.restore()\n+ }\n+ this.setCanvasStyle(\"reset\")\n+ },\n+ setCanvasStyle: function(type, style) {\n+ if (type === \"fill\") {\n+ this.canvas.globalAlpha = style[\"fillOpacity\"];\n+ this.canvas.fillStyle = style[\"fillColor\"]\n+ } else if (type === \"stroke\") {\n+ this.canvas.globalAlpha = style[\"strokeOpacity\"];\n+ this.canvas.strokeStyle = style[\"strokeColor\"];\n+ this.canvas.lineWidth = style[\"strokeWidth\"]\n+ } else {\n+ this.canvas.globalAlpha = 0;\n+ this.canvas.lineWidth = 1\n+ }\n+ },\n+ featureIdToHex: function(featureId) {\n+ var id = Number(featureId.split(\"_\").pop()) + 1;\n+ if (id >= 16777216) {\n+ this.hitOverflow = id - 16777215;\n+ id = id % 16777216 + 1\n+ }\n+ var hex = \"000000\" + id.toString(16);\n+ var len = hex.length;\n+ hex = \"#\" + hex.substring(len - 6, len);\n+ return hex\n+ },\n+ setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {\n+ var hex = this.featureIdToHex(featureId);\n+ if (type == \"fill\") {\n+ this.hitContext.globalAlpha = 1;\n+ this.hitContext.fillStyle = hex\n+ } else if (type == \"stroke\") {\n+ this.hitContext.globalAlpha = 1;\n+ this.hitContext.strokeStyle = hex;\n+ if (typeof strokeScaling === \"undefined\") {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2\n+ } else {\n+ if (!isNaN(strokeScaling)) {\n+ this.hitContext.lineWidth = symbolizer.strokeWidth + 2 / strokeScaling\n+ }\n+ }\n+ } else {\n+ this.hitContext.globalAlpha = 0;\n+ this.hitContext.lineWidth = 1\n+ }\n+ },\n+ drawPoint: function(geometry, style, featureId) {\n+ if (style.graphic !== false) {\n+ if (style.externalGraphic) {\n+ this.drawExternalGraphic(geometry, style, featureId)\n+ } else if (style.graphicName && style.graphicName != \"circle\") {\n+ this.drawNamedSymbol(geometry, style, featureId)\n+ } else {\n+ var pt = this.getLocalXY(geometry);\n+ var p0 = pt[0];\n+ var p1 = pt[1];\n+ if (!isNaN(p0) && !isNaN(p1)) {\n+ var twoPi = Math.PI * 2;\n+ var radius = style.pointRadius;\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.fill();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.fill()\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.canvas.beginPath();\n+ this.canvas.arc(p0, p1, radius, 0, twoPi, true);\n+ this.canvas.stroke();\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.hitContext.beginPath();\n+ this.hitContext.arc(p0, p1, radius, 0, twoPi, true);\n+ this.hitContext.stroke()\n+ }\n+ this.setCanvasStyle(\"reset\")\n+ }\n+ }\n+ }\n+ }\n+ },\n+ drawLineString: function(geometry, style, featureId) {\n+ style = OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style);\n+ this.drawLinearRing(geometry, style, featureId)\n+ },\n+ drawLinearRing: function(geometry, style, featureId) {\n+ if (style.fill !== false) {\n+ this.setCanvasStyle(\"fill\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"fill\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"fill\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"fill\")\n+ }\n+ }\n+ if (style.stroke !== false) {\n+ this.setCanvasStyle(\"stroke\", style);\n+ this.renderPath(this.canvas, geometry, style, featureId, \"stroke\");\n+ if (this.hitDetection) {\n+ this.setHitContextStyle(\"stroke\", featureId, style);\n+ this.renderPath(this.hitContext, geometry, style, featureId, \"stroke\")\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\")\n+ },\n+ renderPath: function(context, geometry, style, featureId, type) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ context.beginPath();\n+ var start = this.getLocalXY(components[0]);\n+ var x = start[0];\n+ var y = start[1];\n+ if (!isNaN(x) && !isNaN(y)) {\n+ context.moveTo(start[0], start[1]);\n+ for (var i = 1; i < len; ++i) {\n+ var pt = this.getLocalXY(components[i]);\n+ context.lineTo(pt[0], pt[1])\n+ }\n+ if (type === \"fill\") {\n+ context.fill()\n+ } else {\n+ context.stroke()\n+ }\n+ }\n+ },\n+ drawPolygon: function(geometry, style, featureId) {\n+ var components = geometry.components;\n+ var len = components.length;\n+ this.drawLinearRing(components[0], style, featureId);\n+ for (var i = 1; i < len; ++i) {\n+ this.canvas.globalCompositeOperation = \"destination-out\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"destination-out\"\n+ }\n+ this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n+ stroke: false,\n+ fillOpacity: 1\n+ }, style), featureId);\n+ this.canvas.globalCompositeOperation = \"source-over\";\n+ if (this.hitDetection) {\n+ this.hitContext.globalCompositeOperation = \"source-over\"\n+ }\n+ this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({\n+ fill: false\n+ }, style), featureId)\n+ }\n+ },\n+ drawText: function(location, style) {\n+ var pt = this.getLocalXY(location);\n+ this.setCanvasStyle(\"reset\");\n+ this.canvas.fillStyle = style.fontColor;\n+ this.canvas.globalAlpha = style.fontOpacity || 1;\n+ var fontStyle = [style.fontStyle ? style.fontStyle : \"normal\", \"normal\", style.fontWeight ? style.fontWeight : \"normal\", style.fontSize ? style.fontSize : \"1em\", style.fontFamily ? style.fontFamily : \"sans-serif\"].join(\" \");\n+ var labelRows = style.label.split(\"\\n\");\n+ var numRows = labelRows.length;\n+ if (this.canvas.fillText) {\n+ this.canvas.font = fontStyle;\n+ this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || \"center\";\n+ this.canvas.textBaseline = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || \"middle\";\n+ var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5\n+ }\n+ var lineHeight = this.canvas.measureText(\"Mg\").height || this.canvas.measureText(\"xx\").width;\n+ pt[1] += lineHeight * vfactor * (numRows - 1);\n+ for (var i = 0; i < numRows; i++) {\n+ if (style.labelOutlineWidth) {\n+ this.canvas.save();\n+ this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1;\n+ this.canvas.strokeStyle = style.labelOutlineColor;\n+ this.canvas.lineWidth = style.labelOutlineWidth;\n+ this.canvas.strokeText(labelRows[i], pt[0], pt[1] + lineHeight * i + 1);\n+ this.canvas.restore()\n+ }\n+ this.canvas.fillText(labelRows[i], pt[0], pt[1] + lineHeight * i)\n+ }\n+ } else if (this.canvas.mozDrawText) {\n+ this.canvas.mozTextStyle = fontStyle;\n+ var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];\n+ if (hfactor == null) {\n+ hfactor = -.5\n+ }\n+ var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];\n+ if (vfactor == null) {\n+ vfactor = -.5\n+ }\n+ var lineHeight = this.canvas.mozMeasureText(\"xx\");\n+ pt[1] += lineHeight * (1 + vfactor * numRows);\n+ for (var i = 0; i < numRows; i++) {\n+ var x = pt[0] + hfactor * this.canvas.mozMeasureText(labelRows[i]);\n+ var y = pt[1] + i * lineHeight;\n+ this.canvas.translate(x, y);\n+ this.canvas.mozDrawText(labelRows[i]);\n+ this.canvas.translate(-x, -y)\n+ }\n+ }\n+ this.setCanvasStyle(\"reset\")\n+ },\n+ getLocalXY: function(point) {\n+ var resolution = this.getResolution();\n+ var extent = this.extent;\n+ var x = (point.x - this.featureDx) / resolution + -extent.left / resolution;\n+ var y = extent.top / resolution - point.y / resolution;\n+ return [x, y]\n+ },\n+ clear: function() {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ this.features = {};\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height)\n+ }\n+ },\n+ getFeatureIdFromEvent: function(evt) {\n+ var featureId, feature;\n+ if (this.hitDetection && this.root.style.display !== \"none\") {\n+ if (!this.map.dragging) {\n+ var xy = evt.xy;\n+ var x = xy.x | 0;\n+ var y = xy.y | 0;\n+ var data = this.hitContext.getImageData(x, y, 1, 1).data;\n+ if (data[3] === 255) {\n+ var id = data[2] + 256 * (data[1] + 256 * data[0]);\n+ if (id) {\n+ featureId = \"OpenLayers_Feature_Vector_\" + (id - 1 + this.hitOverflow);\n+ try {\n+ feature = this.features[featureId][0]\n+ } catch (err) {}\n+ }\n+ }\n+ }\n+ }\n+ return feature\n+ },\n+ eraseFeatures: function(features) {\n+ if (!OpenLayers.Util.isArray(features)) {\n+ features = [features]\n+ }\n+ for (var i = 0; i < features.length; ++i) {\n+ delete this.features[features[i].id]\n+ }\n+ this.redraw()\n+ },\n+ redraw: function() {\n+ if (!this.locked) {\n+ var height = this.root.height;\n+ var width = this.root.width;\n+ this.canvas.clearRect(0, 0, width, height);\n+ if (this.hitDetection) {\n+ this.hitContext.clearRect(0, 0, width, height)\n+ }\n+ var labelMap = [];\n+ var feature, geometry, style;\n+ var worldBounds = this.map.baseLayer && this.map.baseLayer.wrapDateLine && this.map.getMaxExtent();\n+ for (var id in this.features) {\n+ if (!this.features.hasOwnProperty(id)) {\n+ continue\n+ }\n+ feature = this.features[id][0];\n+ geometry = feature.geometry;\n+ this.calculateFeatureDx(geometry.getBounds(), worldBounds);\n+ style = this.features[id][1];\n+ this.drawGeometry(geometry, style, feature.id);\n+ if (style.label) {\n+ labelMap.push([feature, style])\n+ }\n+ }\n+ var item;\n+ for (var i = 0, len = labelMap.length; i < len; ++i) {\n+ item = labelMap[i];\n+ this.drawText(item[0].geometry.getCentroid(), item[1])\n+ }\n+ }\n+ },\n+ CLASS_NAME: \"OpenLayers.Renderer.Canvas\"\n+});\n+OpenLayers.Renderer.Canvas.LABEL_ALIGN = {\n+ l: \"left\",\n+ r: \"right\",\n+ t: \"top\",\n+ b: \"bottom\"\n+};\n+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {\n+ l: 0,\n+ r: -1,\n+ t: 0,\n+ b: -1\n+};\n+OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;\n+OpenLayers.Strategy = OpenLayers.Class({\n+ layer: null,\n+ options: null,\n+ active: null,\n+ autoActivate: true,\n+ autoDestroy: true,\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ this.active = false\n+ },\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null\n+ },\n+ setLayer: function(layer) {\n+ this.layer = layer\n+ },\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true\n+ }\n+ return false\n+ },\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true\n+ }\n+ return false\n+ },\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n+});\n+OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {\n+ preload: false,\n+ activate: function() {\n+ var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);\n+ if (activated) {\n+ this.layer.events.on({\n+ refresh: this.load,\n+ scope: this\n+ });\n+ if (this.layer.visibility == true || this.preload) {\n+ this.load()\n+ } else {\n+ this.layer.events.on({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n+ }\n+ }\n+ return activated\n+ },\n+ deactivate: function() {\n+ var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);\n+ if (deactivated) {\n+ this.layer.events.un({\n+ refresh: this.load,\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n+ }\n+ return deactivated\n+ },\n+ load: function(options) {\n+ var layer = this.layer;\n+ layer.events.triggerEvent(\"loadstart\", {\n+ filter: layer.filter\n+ });\n+ layer.protocol.read(OpenLayers.Util.applyDefaults({\n+ callback: this.merge,\n+ filter: layer.filter,\n+ scope: this\n+ }, options));\n+ layer.events.un({\n+ visibilitychanged: this.load,\n+ scope: this\n+ })\n+ },\n+ merge: function(resp) {\n+ var layer = this.layer;\n+ layer.destroyFeatures();\n+ var features = resp.features;\n+ if (features && features.length > 0) {\n+ var remote = layer.projection;\n+ var local = layer.map.getProjectionObject();\n+ if (!local.equals(remote)) {\n+ var geom;\n+ for (var i = 0, len = features.length; i < len; ++i) {\n+ geom = features[i].geometry;\n+ if (geom) {\n+ geom.transform(remote, local)\n+ }\n+ }\n+ }\n+ layer.addFeatures(features)\n+ }\n+ layer.events.triggerEvent(\"loadend\", {\n+ response: resp\n+ })\n+ },\n+ CLASS_NAME: \"OpenLayers.Strategy.Fixed\"\n });\n"}]}, {"source1": "./usr/share/javascript/openlayers/OpenLayers.tests.js", "source2": "./usr/share/javascript/openlayers/OpenLayers.tests.js", "unified_diff": null, "details": [{"source1": "js-beautify {}", "source2": "js-beautify {}", "unified_diff": "@@ -33176,1903 +33176,14 @@\n \n /**\n * Constant: CORNER_SIZE\n * {Integer} 5. Border space for the RICO corners.\n */\n OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;\n /* ======================================================================\n- OpenLayers/Kinetic.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Animation.js\n- */\n-\n-OpenLayers.Kinetic = OpenLayers.Class({\n-\n- /**\n- * Property: threshold\n- * In most cases changing the threshold isn't needed.\n- * In px/ms, default to 0.\n- */\n- threshold: 0,\n-\n- /**\n- * Property: deceleration\n- * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n- */\n- deceleration: 0.0035,\n-\n- /**\n- * Property: nbPoints\n- * {Integer} the number of points we use to calculate the kinetic\n- * initial values.\n- */\n- nbPoints: 100,\n-\n- /**\n- * Property: delay\n- * {Float} time to consider to calculate the kinetic initial values.\n- * In ms, default to 200.\n- */\n- delay: 200,\n-\n- /**\n- * Property: points\n- * List of points use to calculate the kinetic initial values.\n- */\n- points: undefined,\n-\n- /**\n- * Property: timerId\n- * ID of the timer.\n- */\n- timerId: undefined,\n-\n- /**\n- * Constructor: OpenLayers.Kinetic\n- *\n- * Parameters:\n- * options - {Object}\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /**\n- * Method: begin\n- * Begins the dragging.\n- */\n- begin: function() {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = undefined;\n- this.points = [];\n- },\n-\n- /**\n- * Method: update\n- * Updates during the dragging.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The new position.\n- */\n- update: function(xy) {\n- this.points.unshift({\n- xy: xy,\n- tick: new Date().getTime()\n- });\n- if (this.points.length > this.nbPoints) {\n- this.points.pop();\n- }\n- },\n-\n- /**\n- * Method: end\n- * Ends the dragging, start the kinetic.\n- *\n- * Parameters:\n- * xy - {<OpenLayers.Pixel>} The last position.\n- *\n- * Returns:\n- * {Object} An object with two properties: \"speed\", and \"theta\". The\n- * \"speed\" and \"theta\" values are to be passed to the move \n- * function when starting the animation.\n- */\n- end: function(xy) {\n- var last, now = new Date().getTime();\n- for (var i = 0, l = this.points.length, point; i < l; i++) {\n- point = this.points[i];\n- if (now - point.tick > this.delay) {\n- break;\n- }\n- last = point;\n- }\n- if (!last) {\n- return;\n- }\n- var time = new Date().getTime() - last.tick;\n- var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n- Math.pow(xy.y - last.xy.y, 2));\n- var speed = dist / time;\n- if (speed == 0 || speed < this.threshold) {\n- return;\n- }\n- var theta = Math.asin((xy.y - last.xy.y) / dist);\n- if (last.xy.x <= xy.x) {\n- theta = Math.PI - theta;\n- }\n- return {\n- speed: speed,\n- theta: theta\n- };\n- },\n-\n- /**\n- * Method: move\n- * Launch the kinetic move pan.\n- *\n- * Parameters:\n- * info - {Object} An object with two properties, \"speed\", and \"theta\".\n- * These values are those returned from the \"end\" call.\n- * callback - {Function} Function called on every step of the animation,\n- * receives x, y (values to pan), end (is the last point).\n- */\n- move: function(info, callback) {\n- var v0 = info.speed;\n- var fx = Math.cos(info.theta);\n- var fy = -Math.sin(info.theta);\n-\n- var initialTime = new Date().getTime();\n-\n- var lastX = 0;\n- var lastY = 0;\n-\n- var timerCallback = function() {\n- if (this.timerId == null) {\n- return;\n- }\n-\n- var t = new Date().getTime() - initialTime;\n-\n- var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n- var x = p * fx;\n- var y = p * fy;\n-\n- var args = {};\n- args.end = false;\n- var v = -this.deceleration * t + v0;\n-\n- if (v <= 0) {\n- OpenLayers.Animation.stop(this.timerId);\n- this.timerId = null;\n- args.end = true;\n- }\n-\n- args.x = x - lastX;\n- args.y = y - lastY;\n- lastX = x;\n- lastY = y;\n- callback(args.x, args.y, args.end);\n- };\n-\n- this.timerId = OpenLayers.Animation.start(\n- OpenLayers.Function.bind(timerCallback, this)\n- );\n- },\n-\n- CLASS_NAME: \"OpenLayers.Kinetic\"\n-});\n-/* ======================================================================\n- OpenLayers/Icon.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Icon\n- * \n- * The icon represents a graphical icon on the screen. Typically used in\n- * conjunction with a <OpenLayers.Marker> to represent markers on a screen.\n- *\n- * An icon has a url, size and position. It also contains an offset which \n- * allows the center point to be represented correctly. This can be\n- * provided either as a fixed offset or a function provided to calculate\n- * the desired offset. \n- * \n- */\n-OpenLayers.Icon = OpenLayers.Class({\n-\n- /** \n- * Property: url \n- * {String} image url\n- */\n- url: null,\n-\n- /** \n- * Property: size \n- * {<OpenLayers.Size>|Object} An OpenLayers.Size or\n- * an object with a 'w' and 'h' properties.\n- */\n- size: null,\n-\n- /** \n- * Property: offset \n- * {<OpenLayers.Pixel>|Object} distance in pixels to offset the\n- * image when being rendered. An OpenLayers.Pixel or an object\n- * with a 'x' and 'y' properties.\n- */\n- offset: null,\n-\n- /** \n- * Property: calculateOffset \n- * {Function} Function to calculate the offset (based on the size)\n- */\n- calculateOffset: null,\n-\n- /** \n- * Property: imageDiv \n- * {DOMElement} \n- */\n- imageDiv: null,\n-\n- /** \n- * Property: px \n- * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object\n- * with a 'x' and 'y' properties.\n- */\n- px: null,\n-\n- /** \n- * Constructor: OpenLayers.Icon\n- * Creates an icon, which is an image tag in a div. \n- *\n- * url - {String} \n- * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an\n- * object with a 'w' and 'h'\n- * properties.\n- * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n- * object with a 'x' and 'y'\n- * properties.\n- * calculateOffset - {Function} \n- */\n- initialize: function(url, size, offset, calculateOffset) {\n- this.url = url;\n- this.size = size || {\n- w: 20,\n- h: 20\n- };\n- this.offset = offset || {\n- x: -(this.size.w / 2),\n- y: -(this.size.h / 2)\n- };\n- this.calculateOffset = calculateOffset;\n-\n- var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n- this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);\n- },\n-\n- /** \n- * Method: destroy\n- * Nullify references and remove event listeners to prevent circular \n- * references and memory leaks\n- */\n- destroy: function() {\n- // erase any drawn elements\n- this.erase();\n-\n- OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n- this.imageDiv.innerHTML = \"\";\n- this.imageDiv = null;\n- },\n-\n- /** \n- * Method: clone\n- * \n- * Returns:\n- * {<OpenLayers.Icon>} A fresh copy of the icon.\n- */\n- clone: function() {\n- return new OpenLayers.Icon(this.url,\n- this.size,\n- this.offset,\n- this.calculateOffset);\n- },\n-\n- /**\n- * Method: setSize\n- * \n- * Parameters:\n- * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or\n- * an object with a 'w' and 'h' properties.\n- */\n- setSize: function(size) {\n- if (size != null) {\n- this.size = size;\n- }\n- this.draw();\n- },\n-\n- /**\n- * Method: setUrl\n- * \n- * Parameters:\n- * url - {String} \n- */\n- setUrl: function(url) {\n- if (url != null) {\n- this.url = url;\n- }\n- this.draw();\n- },\n-\n- /** \n- * Method: draw\n- * Move the div to the given pixel.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n- * object with a 'x' and 'y' properties.\n- * \n- * Returns:\n- * {DOMElement} A new DOM Image of this icon set at the location passed-in\n- */\n- draw: function(px) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,\n- null,\n- null,\n- this.size,\n- this.url,\n- \"absolute\");\n- this.moveTo(px);\n- return this.imageDiv;\n- },\n-\n- /** \n- * Method: erase\n- * Erase the underlying image element.\n- */\n- erase: function() {\n- if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n- OpenLayers.Element.remove(this.imageDiv);\n- }\n- },\n-\n- /** \n- * Method: setOpacity\n- * Change the icon's opacity\n- *\n- * Parameters:\n- * opacity - {float} \n- */\n- setOpacity: function(opacity) {\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,\n- null, null, null, null, opacity);\n-\n- },\n-\n- /**\n- * Method: moveTo\n- * move icon to passed in px.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n- * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n- */\n- moveTo: function(px) {\n- //if no px passed in, use stored location\n- if (px != null) {\n- this.px = px;\n- }\n-\n- if (this.imageDiv != null) {\n- if (this.px == null) {\n- this.display(false);\n- } else {\n- if (this.calculateOffset) {\n- this.offset = this.calculateOffset(this.size);\n- }\n- OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n- x: this.px.x + this.offset.x,\n- y: this.px.y + this.offset.y\n- });\n- }\n- }\n- },\n-\n- /** \n- * Method: display\n- * Hide or show the icon\n- *\n- * Parameters:\n- * display - {Boolean} \n- */\n- display: function(display) {\n- this.imageDiv.style.display = (display) ? \"\" : \"none\";\n- },\n-\n-\n- /**\n- * APIMethod: isDrawn\n- * \n- * Returns:\n- * {Boolean} Whether or not the icon is drawn.\n- */\n- isDrawn: function() {\n- // nodeType 11 for ie, whose nodes *always* have a parentNode\n- // (of type document fragment)\n- var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&\n- (this.imageDiv.parentNode.nodeType != 11));\n-\n- return isDrawn;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Icon\"\n-});\n-/* ======================================================================\n- OpenLayers/Marker.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/Icon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Marker\n- * Instances of OpenLayers.Marker are a combination of a \n- * <OpenLayers.LonLat> and an <OpenLayers.Icon>. \n- *\n- * Markers are generally added to a special layer called\n- * <OpenLayers.Layer.Markers>.\n- *\n- * Example:\n- * (code)\n- * var markers = new OpenLayers.Layer.Markers( \"Markers\" );\n- * map.addLayer(markers);\n- *\n- * var size = new OpenLayers.Size(21,25);\n- * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);\n- * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);\n- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));\n- * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));\n- *\n- * (end)\n- *\n- * Note that if you pass an icon into the Marker constructor, it will take\n- * that icon and use it. This means that you should not share icons between\n- * markers -- you use them once, but you should clone() for any additional\n- * markers using that same icon.\n- */\n-OpenLayers.Marker = OpenLayers.Class({\n-\n- /** \n- * Property: icon \n- * {<OpenLayers.Icon>} The icon used by this marker.\n- */\n- icon: null,\n-\n- /** \n- * Property: lonlat \n- * {<OpenLayers.LonLat>} location of object\n- */\n- lonlat: null,\n-\n- /** \n- * Property: events \n- * {<OpenLayers.Events>} the event handler.\n- */\n- events: null,\n-\n- /** \n- * Property: map \n- * {<OpenLayers.Map>} the map this marker is attached to\n- */\n- map: null,\n-\n- /** \n- * Constructor: OpenLayers.Marker\n- *\n- * Parameters:\n- * lonlat - {<OpenLayers.LonLat>} the position of this marker\n- * icon - {<OpenLayers.Icon>} the icon for this marker\n- */\n- initialize: function(lonlat, icon) {\n- this.lonlat = lonlat;\n-\n- var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();\n- if (this.icon == null) {\n- this.icon = newIcon;\n- } else {\n- this.icon.url = newIcon.url;\n- this.icon.size = newIcon.size;\n- this.icon.offset = newIcon.offset;\n- this.icon.calculateOffset = newIcon.calculateOffset;\n- }\n- this.events = new OpenLayers.Events(this, this.icon.imageDiv);\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Destroy the marker. You must first remove the marker from any \n- * layer which it has been added to, or you will get buggy behavior.\n- * (This can not be done within the marker since the marker does not\n- * know which layer it is attached to.)\n- */\n- destroy: function() {\n- // erase any drawn features\n- this.erase();\n-\n- this.map = null;\n-\n- this.events.destroy();\n- this.events = null;\n-\n- if (this.icon != null) {\n- this.icon.destroy();\n- this.icon = null;\n- }\n- },\n-\n- /** \n- * Method: draw\n- * Calls draw on the icon, and returns that output.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {DOMElement} A new DOM Image with this marker's icon set at the \n- * location passed-in\n- */\n- draw: function(px) {\n- return this.icon.draw(px);\n- },\n-\n- /** \n- * Method: erase\n- * Erases any drawn elements for this marker.\n- */\n- erase: function() {\n- if (this.icon != null) {\n- this.icon.erase();\n- }\n- },\n-\n- /**\n- * Method: moveTo\n- * Move the marker to the new location.\n- *\n- * Parameters:\n- * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n- * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n- */\n- moveTo: function(px) {\n- if ((px != null) && (this.icon != null)) {\n- this.icon.moveTo(px);\n- }\n- this.lonlat = this.map.getLonLatFromLayerPx(px);\n- },\n-\n- /**\n- * APIMethod: isDrawn\n- * \n- * Returns:\n- * {Boolean} Whether or not the marker is drawn.\n- */\n- isDrawn: function() {\n- var isDrawn = (this.icon && this.icon.isDrawn());\n- return isDrawn;\n- },\n-\n- /**\n- * Method: onScreen\n- *\n- * Returns:\n- * {Boolean} Whether or not the marker is currently visible on screen.\n- */\n- onScreen: function() {\n-\n- var onScreen = false;\n- if (this.map) {\n- var screenBounds = this.map.getExtent();\n- onScreen = screenBounds.containsLonLat(this.lonlat);\n- }\n- return onScreen;\n- },\n-\n- /**\n- * Method: inflate\n- * Englarges the markers icon by the specified ratio.\n- *\n- * Parameters:\n- * inflate - {float} the ratio to enlarge the marker by (passing 2\n- * will double the size).\n- */\n- inflate: function(inflate) {\n- if (this.icon) {\n- this.icon.setSize({\n- w: this.icon.size.w * inflate,\n- h: this.icon.size.h * inflate\n- });\n- }\n- },\n-\n- /** \n- * Method: setOpacity\n- * Change the opacity of the marker by changin the opacity of \n- * its icon\n- * \n- * Parameters:\n- * opacity - {float} Specified as fraction (0.4, etc)\n- */\n- setOpacity: function(opacity) {\n- this.icon.setOpacity(opacity);\n- },\n-\n- /**\n- * Method: setUrl\n- * Change URL of the Icon Image.\n- * \n- * url - {String} \n- */\n- setUrl: function(url) {\n- this.icon.setUrl(url);\n- },\n-\n- /** \n- * Method: display\n- * Hide or show the icon\n- * \n- * display - {Boolean} \n- */\n- display: function(display) {\n- this.icon.display(display);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Marker\"\n-});\n-\n-\n-/**\n- * Function: defaultIcon\n- * Creates a default <OpenLayers.Icon>.\n- * \n- * Returns:\n- * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker\n- */\n-OpenLayers.Marker.defaultIcon = function() {\n- return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n- w: 21,\n- h: 25\n- }, {\n- x: -10.5,\n- y: -25\n- });\n-};\n-\n-\n-/* ======================================================================\n- OpenLayers/Handler.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Events.js\n- */\n-\n-/**\n- * Class: OpenLayers.Handler\n- * Base class to construct a higher-level handler for event sequences. All\n- * handlers have activate and deactivate methods. In addition, they have\n- * methods named like browser events. When a handler is activated, any\n- * additional methods named like a browser event is registered as a\n- * listener for the corresponding event. When a handler is deactivated,\n- * those same methods are unregistered as event listeners.\n- *\n- * Handlers also typically have a callbacks object with keys named like\n- * the abstracted events or event sequences that they are in charge of\n- * handling. The controls that wrap handlers define the methods that\n- * correspond to these abstract events - so instead of listening for\n- * individual browser events, they only listen for the abstract events\n- * defined by the handler.\n- * \n- * Handlers are created by controls, which ultimately have the responsibility\n- * of making changes to the the state of the application. Handlers\n- * themselves may make temporary changes, but in general are expected to\n- * return the application in the same state that they found it.\n- */\n-OpenLayers.Handler = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String}\n- */\n- id: null,\n-\n- /**\n- * APIProperty: control\n- * {<OpenLayers.Control>}. The control that initialized this handler. The\n- * control is assumed to have a valid map property - that map is used\n- * in the handler's own setMap method.\n- */\n- control: null,\n-\n- /**\n- * Property: map\n- * {<OpenLayers.Map>}\n- */\n- map: null,\n-\n- /**\n- * APIProperty: keyMask\n- * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n- * constants to construct a keyMask. The keyMask is used by\n- * <checkModifiers>. If the keyMask matches the combination of keys\n- * down on an event, checkModifiers returns true.\n- *\n- * Example:\n- * (code)\n- * // handler only responds if the Shift key is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n- *\n- * // handler only responds if Ctrl-Shift is down\n- * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n- * OpenLayers.Handler.MOD_CTRL;\n- * (end)\n- */\n- keyMask: null,\n-\n- /**\n- * Property: active\n- * {Boolean}\n- */\n- active: false,\n-\n- /**\n- * Property: evt\n- * {Event} This property references the last event handled by the handler.\n- * Note that this property is not part of the stable API. Use of the\n- * evt property should be restricted to controls in the library\n- * or other applications that are willing to update with changes to\n- * the OpenLayers code.\n- */\n- evt: null,\n-\n- /**\n- * Property: touch\n- * {Boolean} Indicates the support of touch events. When touch events are \n- * started touch will be true and all mouse related listeners will do \n- * nothing.\n- */\n- touch: false,\n-\n- /**\n- * Constructor: OpenLayers.Handler\n- * Construct a handler.\n- *\n- * Parameters:\n- * control - {<OpenLayers.Control>} The control that initialized this\n- * handler. The control is assumed to have a valid map property; that\n- * map is used in the handler's own setMap method. If a map property\n- * is present in the options argument it will be used instead.\n- * callbacks - {Object} An object whose properties correspond to abstracted\n- * events or sequences of browser events. The values for these\n- * properties are functions defined by the control that get called by\n- * the handler.\n- * options - {Object} An optional object whose properties will be set on\n- * the handler.\n- */\n- initialize: function(control, callbacks, options) {\n- OpenLayers.Util.extend(this, options);\n- this.control = control;\n- this.callbacks = callbacks;\n-\n- var map = this.map || control.map;\n- if (map) {\n- this.setMap(map);\n- }\n-\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /**\n- * Method: setMap\n- */\n- setMap: function(map) {\n- this.map = map;\n- },\n-\n- /**\n- * Method: checkModifiers\n- * Check the keyMask on the handler. If no <keyMask> is set, this always\n- * returns true. If a <keyMask> is set and it matches the combination\n- * of keys down on an event, this returns true.\n- *\n- * Returns:\n- * {Boolean} The keyMask matches the keys down on an event.\n- */\n- checkModifiers: function(evt) {\n- if (this.keyMask == null) {\n- return true;\n- }\n- /* calculate the keyboard modifier mask for this event */\n- var keyModifiers =\n- (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n- (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n- (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n- (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n-\n- /* if it differs from the handler object's key mask,\n- bail out of the event handler */\n- return (keyModifiers == this.keyMask);\n- },\n-\n- /**\n- * APIMethod: activate\n- * Turn on the handler. Returns false if the handler was already active.\n- * \n- * Returns: \n- * {Boolean} The handler was activated.\n- */\n- activate: function() {\n- if (this.active) {\n- return false;\n- }\n- // register for event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.register(events[i], this[events[i]]);\n- }\n- }\n- this.active = true;\n- return true;\n- },\n-\n- /**\n- * APIMethod: deactivate\n- * Turn off the handler. Returns false if the handler was already inactive.\n- * \n- * Returns:\n- * {Boolean} The handler was deactivated.\n- */\n- deactivate: function() {\n- if (!this.active) {\n- return false;\n- }\n- // unregister event handlers defined on this class.\n- var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n- }\n- }\n- this.touch = false;\n- this.active = false;\n- return true;\n- },\n-\n- /**\n- * Method: startTouch\n- * Start touch events, this method must be called by subclasses in \n- * \"touchstart\" method. When touch events are started <touch> will be\n- * true and all mouse related listeners will do nothing.\n- */\n- startTouch: function() {\n- if (!this.touch) {\n- this.touch = true;\n- var events = [\n- \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n- \"mouseout\"\n- ];\n- for (var i = 0, len = events.length; i < len; i++) {\n- if (this[events[i]]) {\n- this.unregister(events[i], this[events[i]]);\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: callback\n- * Trigger the control's named callback with the given arguments\n- *\n- * Parameters:\n- * name - {String} The key for the callback that is one of the properties\n- * of the handler's callbacks object.\n- * args - {Array(*)} An array of arguments (any type) with which to call \n- * the callback (defined by the control).\n- */\n- callback: function(name, args) {\n- if (name && this.callbacks[name]) {\n- this.callbacks[name].apply(this.control, args);\n- }\n- },\n-\n- /**\n- * Method: register\n- * register an event on the map\n- */\n- register: function(name, method) {\n- // TODO: deal with registerPriority in 3.0\n- this.map.events.registerPriority(name, this, method);\n- this.map.events.registerPriority(name, this, this.setEvent);\n- },\n-\n- /**\n- * Method: unregister\n- * unregister an event from the map\n- */\n- unregister: function(name, method) {\n- this.map.events.unregister(name, this, method);\n- this.map.events.unregister(name, this, this.setEvent);\n- },\n-\n- /**\n- * Method: setEvent\n- * With each registered browser event, the handler sets its own evt\n- * property. This property can be accessed by controls if needed\n- * to get more information about the event that the handler is\n- * processing.\n- *\n- * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n- * and meta cannot be checked with the keyboard handler). For a\n- * control to determine which modifier keys are associated with the\n- * event that a handler is currently processing, it should access\n- * (code)handler.evt.altKey || handler.evt.shiftKey ||\n- * handler.evt.ctrlKey || handler.evt.metaKey(end).\n- *\n- * Parameters:\n- * evt - {Event} The browser event.\n- */\n- setEvent: function(evt) {\n- this.evt = evt;\n- return true;\n- },\n-\n- /**\n- * Method: destroy\n- * Deconstruct the handler.\n- */\n- destroy: function() {\n- // unregister event listeners\n- this.deactivate();\n- // eliminate circular references\n- this.control = this.map = null;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Handler\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_NONE\n- * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n- */\n-OpenLayers.Handler.MOD_NONE = 0;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_SHIFT\n- * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n- */\n-OpenLayers.Handler.MOD_SHIFT = 1;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_CTRL\n- * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n- */\n-OpenLayers.Handler.MOD_CTRL = 2;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_ALT\n- * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n- */\n-OpenLayers.Handler.MOD_ALT = 4;\n-\n-/**\n- * Constant: OpenLayers.Handler.MOD_META\n- * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n- */\n-OpenLayers.Handler.MOD_META = 8;\n-\n-\n-/* ======================================================================\n- OpenLayers/Filter.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Filter\n- * This class represents an OGC Filter.\n- */\n-OpenLayers.Filter = OpenLayers.Class({\n-\n- /** \n- * Constructor: OpenLayers.Filter\n- * This class represents a generic filter.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- * \n- * Returns:\n- * {<OpenLayers.Filter>}\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- },\n-\n- /** \n- * APIMethod: destroy\n- * Remove reference to anything added.\n- */\n- destroy: function() {},\n-\n- /**\n- * APIMethod: evaluate\n- * Evaluates this filter in a specific context. Instances or subclasses\n- * are supposed to override this method.\n- * \n- * Parameters:\n- * context - {Object} Context to use in evaluating the filter. If a vector\n- * feature is provided, the feature.attributes will be used as context.\n- * \n- * Returns:\n- * {Boolean} The filter applies.\n- */\n- evaluate: function(context) {\n- return true;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this filter. Should be implemented by subclasses.\n- * \n- * Returns:\n- * {<OpenLayers.Filter>} Clone of this filter.\n- */\n- clone: function() {\n- return null;\n- },\n-\n- /**\n- * APIMethod: toString\n- *\n- * Returns:\n- * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n- * representation of the filter returned. Otherwise \"[Object object]\"\n- * will be returned.\n- */\n- toString: function() {\n- var string;\n- if (OpenLayers.Format && OpenLayers.Format.CQL) {\n- string = OpenLayers.Format.CQL.prototype.write(this);\n- } else {\n- string = Object.prototype.toString.call(this);\n- }\n- return string;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Filter\"\n-});\n-/* ======================================================================\n- OpenLayers/TileManager.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes.js\n- * @requires OpenLayers/BaseTypes/Element.js\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Tile/Image.js\n- */\n-\n-/**\n- * Class: OpenLayers.TileManager\n- * Provides queueing of image requests and caching of image elements.\n- *\n- * Queueing avoids unnecessary image requests while changing zoom levels\n- * quickly, and helps improve dragging performance on mobile devices that show\n- * a lag in dragging when loading of new images starts. <zoomDelay> and\n- * <moveDelay> are the configuration options to control this behavior.\n- *\n- * Caching avoids setting the src on image elements for images that have already\n- * been used. Several maps can share a TileManager instance, in which case each\n- * map gets its own tile queue, but all maps share the same tile cache.\n- */\n-OpenLayers.TileManager = OpenLayers.Class({\n-\n- /**\n- * APIProperty: cacheSize\n- * {Number} Number of image elements to keep referenced in this instance's\n- * cache for fast reuse. Default is 256.\n- */\n- cacheSize: 256,\n-\n- /**\n- * APIProperty: tilesPerFrame\n- * {Number} Number of queued tiles to load per frame (see <frameDelay>).\n- * Default is 2.\n- */\n- tilesPerFrame: 2,\n-\n- /**\n- * APIProperty: frameDelay\n- * {Number} Delay between tile loading frames (see <tilesPerFrame>) in\n- * milliseconds. Default is 16.\n- */\n- frameDelay: 16,\n-\n- /**\n- * APIProperty: moveDelay\n- * {Number} Delay in milliseconds after a map's move event before loading\n- * tiles. Default is 100.\n- */\n- moveDelay: 100,\n-\n- /**\n- * APIProperty: zoomDelay\n- * {Number} Delay in milliseconds after a map's zoomend event before loading\n- * tiles. Default is 200.\n- */\n- zoomDelay: 200,\n-\n- /**\n- * Property: maps\n- * {Array(<OpenLayers.Map>)} The maps to manage tiles on.\n- */\n- maps: null,\n-\n- /**\n- * Property: tileQueueId\n- * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.\n- */\n- tileQueueId: null,\n-\n- /**\n- * Property: tileQueue\n- * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by\n- * map id.\n- */\n- tileQueue: null,\n-\n- /**\n- * Property: tileCache\n- * {Object} Cached image elements, keyed by URL.\n- */\n- tileCache: null,\n-\n- /**\n- * Property: tileCacheIndex\n- * {Array(String)} URLs of cached tiles. First entry is the least recently\n- * used.\n- */\n- tileCacheIndex: null,\n-\n- /** \n- * Constructor: OpenLayers.TileManager\n- * Constructor for a new <OpenLayers.TileManager> instance.\n- * \n- * Parameters:\n- * options - {Object} Configuration for this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.maps = [];\n- this.tileQueueId = {};\n- this.tileQueue = {};\n- this.tileCache = {};\n- this.tileCacheIndex = [];\n- },\n-\n- /**\n- * Method: addMap\n- * Binds this instance to a map\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- addMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- this.maps.push(map);\n- this.tileQueue[map.id] = [];\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.addLayer({\n- layer: map.layers[i]\n- });\n- }\n- map.events.on({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: removeMap\n- * Unbinds this instance from a map\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- if (this._destroyed || !OpenLayers.Layer.Grid) {\n- return;\n- }\n- window.clearTimeout(this.tileQueueId[map.id]);\n- if (map.layers) {\n- for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n- this.removeLayer({\n- layer: map.layers[i]\n- });\n- }\n- }\n- if (map.events) {\n- map.events.un({\n- move: this.move,\n- zoomend: this.zoomEnd,\n- changelayer: this.changeLayer,\n- addlayer: this.addLayer,\n- preremovelayer: this.removeLayer,\n- scope: this\n- });\n- }\n- delete this.tileQueue[map.id];\n- delete this.tileQueueId[map.id];\n- OpenLayers.Util.removeItem(this.maps, map);\n- },\n-\n- /**\n- * Method: move\n- * Handles the map's move event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- move: function(evt) {\n- this.updateTimeout(evt.object, this.moveDelay, true);\n- },\n-\n- /**\n- * Method: zoomEnd\n- * Handles the map's zoomEnd event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- zoomEnd: function(evt) {\n- this.updateTimeout(evt.object, this.zoomDelay);\n- },\n-\n- /**\n- * Method: changeLayer\n- * Handles the map's changeLayer event\n- *\n- * Parameters:\n- * evt - {Object} Listener argument\n- */\n- changeLayer: function(evt) {\n- if (evt.property === 'visibility' || evt.property === 'params') {\n- this.updateTimeout(evt.object, 0);\n- }\n- },\n-\n- /**\n- * Method: addLayer\n- * Handles the map's addlayer event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- addLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- layer.events.on({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.addTile({\n- tile: tile\n- });\n- if (tile.url && !tile.imgDiv) {\n- this.manageTileCache({\n- object: tile\n- });\n- }\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: removeLayer\n- * Handles the map's preremovelayer event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- removeLayer: function(evt) {\n- var layer = evt.layer;\n- if (layer instanceof OpenLayers.Layer.Grid) {\n- this.clearTileQueue({\n- object: layer\n- });\n- if (layer.events) {\n- layer.events.un({\n- addtile: this.addTile,\n- retile: this.clearTileQueue,\n- scope: this\n- });\n- }\n- if (layer.grid) {\n- var i, j, tile;\n- for (i = layer.grid.length - 1; i >= 0; --i) {\n- for (j = layer.grid[i].length - 1; j >= 0; --j) {\n- tile = layer.grid[i][j];\n- this.unloadTile({\n- object: tile\n- });\n- }\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: updateTimeout\n- * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,\n- * and schedules more queue processing after <frameDelay> if there are still\n- * tiles in the queue.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>} The map to update the timeout for\n- * delay - {Number} The delay to apply\n- * nice - {Boolean} If true, the timeout function will only be created if\n- * the tilequeue is not empty. This is used by the move handler to\n- * avoid impacts on dragging performance. For other events, the tile\n- * queue may not be populated yet, so we need to set the timer\n- * regardless of the queue size.\n- */\n- updateTimeout: function(map, delay, nice) {\n- window.clearTimeout(this.tileQueueId[map.id]);\n- var tileQueue = this.tileQueue[map.id];\n- if (!nice || tileQueue.length) {\n- this.tileQueueId[map.id] = window.setTimeout(\n- OpenLayers.Function.bind(function() {\n- this.drawTilesFromQueue(map);\n- if (tileQueue.length) {\n- this.updateTimeout(map, this.frameDelay);\n- }\n- }, this), delay\n- );\n- }\n- },\n-\n- /**\n- * Method: addTile\n- * Listener for the layer's addtile event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- addTile: function(evt) {\n- if (evt.tile instanceof OpenLayers.Tile.Image) {\n- evt.tile.events.on({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- } else {\n- // Layer has the wrong tile type, so don't handle it any longer\n- this.removeLayer({\n- layer: evt.tile.layer\n- });\n- }\n- },\n-\n- /**\n- * Method: unloadTile\n- * Listener for the tile's unload event\n- *\n- * Parameters:\n- * evt - {Object} The listener argument\n- */\n- unloadTile: function(evt) {\n- var tile = evt.object;\n- tile.events.un({\n- beforedraw: this.queueTileDraw,\n- beforeload: this.manageTileCache,\n- loadend: this.addToCache,\n- unload: this.unloadTile,\n- scope: this\n- });\n- OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n- },\n-\n- /**\n- * Method: queueTileDraw\n- * Adds a tile to the queue that will draw it.\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the tile's beforedraw event\n- */\n- queueTileDraw: function(evt) {\n- var tile = evt.object;\n- var queued = false;\n- var layer = tile.layer;\n- var url = layer.getURL(tile.bounds);\n- var img = this.tileCache[url];\n- if (img && img.className !== 'olTileImage') {\n- // cached image no longer valid, e.g. because we're olTileReplacing\n- delete this.tileCache[url];\n- OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n- img = null;\n- }\n- // queue only if image with same url not cached already\n- if (layer.url && (layer.async || !img)) {\n- // add to queue only if not in queue already\n- var tileQueue = this.tileQueue[layer.map.id];\n- if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n- tileQueue.push(tile);\n- }\n- queued = true;\n- }\n- return !queued;\n- },\n-\n- /**\n- * Method: drawTilesFromQueue\n- * Draws tiles from the tileQueue, and unqueues the tiles\n- */\n- drawTilesFromQueue: function(map) {\n- var tileQueue = this.tileQueue[map.id];\n- var limit = this.tilesPerFrame;\n- var animating = map.zoomTween && map.zoomTween.playing;\n- while (!animating && tileQueue.length && limit) {\n- tileQueue.shift().draw(true);\n- --limit;\n- }\n- },\n-\n- /**\n- * Method: manageTileCache\n- * Adds, updates, removes and fetches cache entries.\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the tile's beforeload event\n- */\n- manageTileCache: function(evt) {\n- var tile = evt.object;\n- var img = this.tileCache[tile.url];\n- if (img) {\n- // if image is on its layer's backbuffer, remove it from backbuffer\n- if (img.parentNode &&\n- OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n- img.parentNode.removeChild(img);\n- img.id = null;\n- }\n- // only use image from cache if it is not on a layer already\n- if (!img.parentNode) {\n- img.style.visibility = 'hidden';\n- img.style.opacity = 0;\n- tile.setImage(img);\n- // LRU - move tile to the end of the array to mark it as the most\n- // recently used\n- OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n- this.tileCacheIndex.push(tile.url);\n- }\n- }\n- },\n-\n- /**\n- * Method: addToCache\n- *\n- * Parameters:\n- * evt - {Object} Listener argument for the tile's loadend event\n- */\n- addToCache: function(evt) {\n- var tile = evt.object;\n- if (!this.tileCache[tile.url]) {\n- if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n- if (this.tileCacheIndex.length >= this.cacheSize) {\n- delete this.tileCache[this.tileCacheIndex[0]];\n- this.tileCacheIndex.shift();\n- }\n- this.tileCache[tile.url] = tile.imgDiv;\n- this.tileCacheIndex.push(tile.url);\n- }\n- }\n- },\n-\n- /**\n- * Method: clearTileQueue\n- * Clears the tile queue from tiles of a specific layer\n- *\n- * Parameters:\n- * evt - {Object} Listener argument of the layer's retile event\n- */\n- clearTileQueue: function(evt) {\n- var layer = evt.object;\n- var tileQueue = this.tileQueue[layer.map.id];\n- for (var i = tileQueue.length - 1; i >= 0; --i) {\n- if (tileQueue[i].layer === layer) {\n- tileQueue.splice(i, 1);\n- }\n- }\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- for (var i = this.maps.length - 1; i >= 0; --i) {\n- this.removeMap(this.maps[i]);\n- }\n- this.maps = null;\n- this.tileQueue = null;\n- this.tileQueueId = null;\n- this.tileCache = null;\n- this.tileCacheIndex = null;\n- this._destroyed = true;\n- }\n-\n-});\n-/* ======================================================================\n- OpenLayers/Symbolizer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- */\n-\n-/**\n- * Class: OpenLayers.Symbolizer\n- * Base class representing a symbolizer used for feature rendering.\n- */\n-OpenLayers.Symbolizer = OpenLayers.Class({\n-\n-\n- /**\n- * APIProperty: zIndex\n- * {Number} The zIndex determines the rendering order for a symbolizer.\n- * Symbolizers with larger zIndex values are rendered over symbolizers\n- * with smaller zIndex values. Default is 0.\n- */\n- zIndex: 0,\n-\n- /**\n- * Constructor: OpenLayers.Symbolizer\n- * Instances of this class are not useful. See one of the subclasses.\n- *\n- * Parameters:\n- * config - {Object} An object containing properties to be set on the \n- * symbolizer. Any documented symbolizer property can be set at \n- * construction.\n- *\n- * Returns:\n- * A new symbolizer.\n- */\n- initialize: function(config) {\n- OpenLayers.Util.extend(this, config);\n- },\n-\n- /** \n- * APIMethod: clone\n- * Create a copy of this symbolizer.\n- *\n- * Returns a symbolizer of the same type with the same properties.\n- */\n- clone: function() {\n- var Type = eval(this.CLASS_NAME);\n- return new Type(OpenLayers.Util.extend({}, this));\n- },\n-\n- CLASS_NAME: \"OpenLayers.Symbolizer\"\n-\n-});\n-\n-/* ======================================================================\n- OpenLayers/Rule.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/BaseTypes/Class.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/Style.js\n- */\n-\n-/**\n- * Class: OpenLayers.Rule\n- * This class represents an SLD Rule, as being used for rule-based SLD styling.\n- */\n-OpenLayers.Rule = OpenLayers.Class({\n-\n- /**\n- * Property: id\n- * {String} A unique id for this session.\n- */\n- id: null,\n-\n- /**\n- * APIProperty: name\n- * {String} name of this rule\n- */\n- name: null,\n-\n- /**\n- * Property: title\n- * {String} Title of this rule (set if included in SLD)\n- */\n- title: null,\n-\n- /**\n- * Property: description\n- * {String} Description of this rule (set if abstract is included in SLD)\n- */\n- description: null,\n-\n- /**\n- * Property: context\n- * {Object} An optional object with properties that the rule should be\n- * evaluated against. If no context is specified, feature.attributes will\n- * be used.\n- */\n- context: null,\n-\n- /**\n- * Property: filter\n- * {<OpenLayers.Filter>} Optional filter for the rule.\n- */\n- filter: null,\n-\n- /**\n- * Property: elseFilter\n- * {Boolean} Determines whether this rule is only to be applied only if\n- * no other rules match (ElseFilter according to the SLD specification). \n- * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n- * false, the rule will always apply. For subclasses, the else property is \n- * ignored.\n- */\n- elseFilter: false,\n-\n- /**\n- * Property: symbolizer\n- * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n- * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n- * latter if useful if it is required to style e.g. vertices of a line\n- * with a point symbolizer. Note, however, that this is not implemented\n- * yet in OpenLayers, but it is the way how symbolizers are defined in\n- * SLD.\n- */\n- symbolizer: null,\n-\n- /**\n- * Property: symbolizers\n- * {Array} Collection of symbolizers associated with this rule. If \n- * provided at construction, the symbolizers array has precedence\n- * over the deprecated symbolizer property. Note that multiple \n- * symbolizers are not currently supported by the vector renderers.\n- * Rules with multiple symbolizers are currently only useful for\n- * maintaining elements in an SLD document.\n- */\n- symbolizers: null,\n-\n- /**\n- * APIProperty: minScaleDenominator\n- * {Number} or {String} minimum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n- */\n- minScaleDenominator: null,\n-\n- /**\n- * APIProperty: maxScaleDenominator\n- * {Number} or {String} maximum scale at which to draw the feature.\n- * In the case of a String, this can be a combination of text and\n- * propertyNames in the form \"literal ${propertyName}\"\n- */\n- maxScaleDenominator: null,\n-\n- /** \n- * Constructor: OpenLayers.Rule\n- * Creates a Rule.\n- *\n- * Parameters:\n- * options - {Object} An optional object with properties to set on the\n- * rule\n- * \n- * Returns:\n- * {<OpenLayers.Rule>}\n- */\n- initialize: function(options) {\n- this.symbolizer = {};\n- OpenLayers.Util.extend(this, options);\n- if (this.symbolizers) {\n- delete this.symbolizer;\n- }\n- this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n- },\n-\n- /** \n- * APIMethod: destroy\n- * nullify references to prevent circular references and memory leaks\n- */\n- destroy: function() {\n- for (var i in this.symbolizer) {\n- this.symbolizer[i] = null;\n- }\n- this.symbolizer = null;\n- delete this.symbolizers;\n- },\n-\n- /**\n- * APIMethod: evaluate\n- * evaluates this rule for a specific feature\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n- * \n- * Returns:\n- * {Boolean} true if the rule applies, false if it does not.\n- * This rule is the default rule and always returns true.\n- */\n- evaluate: function(feature) {\n- var context = this.getContext(feature);\n- var applies = true;\n-\n- if (this.minScaleDenominator || this.maxScaleDenominator) {\n- var scale = feature.layer.map.getScale();\n- }\n-\n- // check if within minScale/maxScale bounds\n- if (this.minScaleDenominator) {\n- applies = scale >= OpenLayers.Style.createLiteral(\n- this.minScaleDenominator, context);\n- }\n- if (applies && this.maxScaleDenominator) {\n- applies = scale < OpenLayers.Style.createLiteral(\n- this.maxScaleDenominator, context);\n- }\n-\n- // check if optional filter applies\n- if (applies && this.filter) {\n- // feature id filters get the feature, others get the context\n- if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n- applies = this.filter.evaluate(feature);\n- } else {\n- applies = this.filter.evaluate(context);\n- }\n- }\n-\n- return applies;\n- },\n-\n- /**\n- * Method: getContext\n- * Gets the context for evaluating this rule\n- * \n- * Paramters:\n- * feature - {<OpenLayers.Feature>} feature to take the context from if\n- * none is specified.\n- */\n- getContext: function(feature) {\n- var context = this.context;\n- if (!context) {\n- context = feature.attributes || feature.data;\n- }\n- if (typeof this.context == \"function\") {\n- context = this.context(feature);\n- }\n- return context;\n- },\n-\n- /**\n- * APIMethod: clone\n- * Clones this rule.\n- * \n- * Returns:\n- * {<OpenLayers.Rule>} Clone of this rule.\n- */\n- clone: function() {\n- var options = OpenLayers.Util.extend({}, this);\n- if (this.symbolizers) {\n- // clone symbolizers\n- var len = this.symbolizers.length;\n- options.symbolizers = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- options.symbolizers[i] = this.symbolizers[i].clone();\n- }\n- } else {\n- // clone symbolizer\n- options.symbolizer = {};\n- var value, type;\n- for (var key in this.symbolizer) {\n- value = this.symbolizer[key];\n- type = typeof value;\n- if (type === \"object\") {\n- options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n- } else if (type === \"string\") {\n- options.symbolizer[key] = value;\n- }\n- }\n- }\n- // clone filter\n- options.filter = this.filter && this.filter.clone();\n- // clone context\n- options.context = this.context && OpenLayers.Util.extend({}, this.context);\n- return new OpenLayers.Rule(options);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Rule\"\n-});\n-/* ======================================================================\n OpenLayers/Format/GeoJSON.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -36892,14 +35003,104 @@\n * Constant: OpenLayers.Format.WFST.DEFAULTS\n * {Object} Default properties for the WFST format.\n */\n OpenLayers.Format.WFST.DEFAULTS = {\n \"version\": \"1.0.0\"\n };\n /* ======================================================================\n+ OpenLayers/Filter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Filter\n+ * This class represents an OGC Filter.\n+ */\n+OpenLayers.Filter = OpenLayers.Class({\n+\n+ /** \n+ * Constructor: OpenLayers.Filter\n+ * This class represents a generic filter.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter>}\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ },\n+\n+ /** \n+ * APIMethod: destroy\n+ * Remove reference to anything added.\n+ */\n+ destroy: function() {},\n+\n+ /**\n+ * APIMethod: evaluate\n+ * Evaluates this filter in a specific context. Instances or subclasses\n+ * are supposed to override this method.\n+ * \n+ * Parameters:\n+ * context - {Object} Context to use in evaluating the filter. If a vector\n+ * feature is provided, the feature.attributes will be used as context.\n+ * \n+ * Returns:\n+ * {Boolean} The filter applies.\n+ */\n+ evaluate: function(context) {\n+ return true;\n+ },\n+\n+ /**\n+ * APIMethod: clone\n+ * Clones this filter. Should be implemented by subclasses.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Filter>} Clone of this filter.\n+ */\n+ clone: function() {\n+ return null;\n+ },\n+\n+ /**\n+ * APIMethod: toString\n+ *\n+ * Returns:\n+ * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL\n+ * representation of the filter returned. Otherwise \"[Object object]\"\n+ * will be returned.\n+ */\n+ toString: function() {\n+ var string;\n+ if (OpenLayers.Format && OpenLayers.Format.CQL) {\n+ string = OpenLayers.Format.CQL.prototype.write(this);\n+ } else {\n+ string = Object.prototype.toString.call(this);\n+ }\n+ return string;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Filter\"\n+});\n+/* ======================================================================\n OpenLayers/Filter/Spatial.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -42205,626 +40406,313 @@\n OpenLayers.Util.extend(this, options);\n },\n \n CLASS_NAME: \"OpenLayers.WPSProcess.ChainLink\"\n \n });\n /* ======================================================================\n- OpenLayers/Strategy.js\n+ OpenLayers/Symbolizer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/BaseTypes/Class.js\n */\n \n /**\n- * Class: OpenLayers.Strategy\n- * Abstract vector layer strategy class. Not to be instantiated directly. Use\n- * one of the strategy subclasses instead.\n+ * Class: OpenLayers.Symbolizer\n+ * Base class representing a symbolizer used for feature rendering.\n */\n-OpenLayers.Strategy = OpenLayers.Class({\n-\n- /**\n- * Property: layer\n- * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n- */\n- layer: null,\n-\n- /**\n- * Property: options\n- * {Object} Any options sent to the constructor.\n- */\n- options: null,\n-\n- /** \n- * Property: active \n- * {Boolean} The control is active.\n- */\n- active: null,\n-\n- /**\n- * Property: autoActivate\n- * {Boolean} The creator of the strategy can set autoActivate to false\n- * to fully control when the protocol is activated and deactivated.\n- * Defaults to true.\n- */\n- autoActivate: true,\n-\n- /**\n- * Property: autoDestroy\n- * {Boolean} The creator of the strategy can set autoDestroy to false\n- * to fully control when the strategy is destroyed. Defaults to\n- * true.\n- */\n- autoDestroy: true,\n+OpenLayers.Symbolizer = OpenLayers.Class({\n \n- /**\n- * Constructor: OpenLayers.Strategy\n- * Abstract class for vector strategies. Create instances of a subclass.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Util.extend(this, options);\n- this.options = options;\n- // set the active property here, so that user cannot override it\n- this.active = false;\n- },\n \n /**\n- * APIMethod: destroy\n- * Clean up the strategy.\n+ * APIProperty: zIndex\n+ * {Number} The zIndex determines the rendering order for a symbolizer.\n+ * Symbolizers with larger zIndex values are rendered over symbolizers\n+ * with smaller zIndex values. Default is 0.\n */\n- destroy: function() {\n- this.deactivate();\n- this.layer = null;\n- this.options = null;\n- },\n+ zIndex: 0,\n \n /**\n- * Method: setLayer\n- * Called to set the <layer> property.\n+ * Constructor: OpenLayers.Symbolizer\n+ * Instances of this class are not useful. See one of the subclasses.\n *\n * Parameters:\n- * layer - {<OpenLayers.Layer.Vector>}\n- */\n- setLayer: function(layer) {\n- this.layer = layer;\n- },\n-\n- /**\n- * Method: activate\n- * Activate the strategy. Register any listeners, do appropriate setup.\n+ * config - {Object} An object containing properties to be set on the \n+ * symbolizer. Any documented symbolizer property can be set at \n+ * construction.\n *\n * Returns:\n- * {Boolean} True if the strategy was successfully activated or false if\n- * the strategy was already active.\n+ * A new symbolizer.\n */\n- activate: function() {\n- if (!this.active) {\n- this.active = true;\n- return true;\n- }\n- return false;\n+ initialize: function(config) {\n+ OpenLayers.Util.extend(this, config);\n },\n \n- /**\n- * Method: deactivate\n- * Deactivate the strategy. Unregister any listeners, do appropriate\n- * tear-down.\n+ /** \n+ * APIMethod: clone\n+ * Create a copy of this symbolizer.\n *\n- * Returns:\n- * {Boolean} True if the strategy was successfully deactivated or false if\n- * the strategy was already inactive.\n+ * Returns a symbolizer of the same type with the same properties.\n */\n- deactivate: function() {\n- if (this.active) {\n- this.active = false;\n- return true;\n- }\n- return false;\n+ clone: function() {\n+ var Type = eval(this.CLASS_NAME);\n+ return new Type(OpenLayers.Util.extend({}, this));\n },\n \n- CLASS_NAME: \"OpenLayers.Strategy\"\n+ CLASS_NAME: \"OpenLayers.Symbolizer\"\n+\n });\n+\n /* ======================================================================\n- OpenLayers/Format/WPSDescribeProcess.js\n+ OpenLayers/Rule.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n+\n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/Style.js\n */\n \n /**\n- * Class: OpenLayers.Format.WPSDescribeProcess\n- * Read WPS DescribeProcess responses. \n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * Class: OpenLayers.Rule\n+ * This class represents an SLD Rule, as being used for rule-based SLD styling.\n */\n-OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n-\n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- ows: \"http://www.opengis.net/ows/1.1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n-\n- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"wps\",\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WPSDescribeProcess\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Parse a WPS DescribeProcess and return an object with its information.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object}\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var info = {};\n- this.readNode(data, info);\n- return info;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wps\": {\n- \"ProcessDescriptions\": function(node, obj) {\n- obj.processDescriptions = {};\n- this.readChildNodes(node, obj.processDescriptions);\n- },\n- \"ProcessDescription\": function(node, processDescriptions) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var processDescription = {\n- processVersion: processVersion,\n- statusSupported: (node.getAttribute(\"statusSupported\") === \"true\"),\n- storeSupported: (node.getAttribute(\"storeSupported\") === \"true\")\n- };\n- this.readChildNodes(node, processDescription);\n- processDescriptions[processDescription.identifier] = processDescription;\n- },\n- \"DataInputs\": function(node, processDescription) {\n- processDescription.dataInputs = [];\n- this.readChildNodes(node, processDescription.dataInputs);\n- },\n- \"ProcessOutputs\": function(node, processDescription) {\n- processDescription.processOutputs = [];\n- this.readChildNodes(node, processDescription.processOutputs);\n- },\n- \"Output\": function(node, processOutputs) {\n- var output = {};\n- this.readChildNodes(node, output);\n- processOutputs.push(output);\n- },\n- \"ComplexOutput\": function(node, output) {\n- output.complexOutput = {};\n- this.readChildNodes(node, output.complexOutput);\n- },\n- \"LiteralOutput\": function(node, output) {\n- output.literalOutput = {};\n- this.readChildNodes(node, output.literalOutput);\n- },\n- \"Input\": function(node, dataInputs) {\n- var input = {\n- maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n- minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n- };\n- this.readChildNodes(node, input);\n- dataInputs.push(input);\n- },\n- \"BoundingBoxData\": function(node, input) {\n- input.boundingBoxData = {};\n- this.readChildNodes(node, input.boundingBoxData);\n- },\n- \"CRS\": function(node, obj) {\n- if (!obj.CRSs) {\n- obj.CRSs = {};\n- }\n- obj.CRSs[this.getChildValue(node)] = true;\n- },\n- \"LiteralData\": function(node, input) {\n- input.literalData = {};\n- this.readChildNodes(node, input.literalData);\n- },\n- \"ComplexData\": function(node, input) {\n- input.complexData = {};\n- this.readChildNodes(node, input.complexData);\n- },\n- \"Default\": function(node, complexData) {\n- complexData[\"default\"] = {};\n- this.readChildNodes(node, complexData[\"default\"]);\n- },\n- \"Supported\": function(node, complexData) {\n- complexData[\"supported\"] = {};\n- this.readChildNodes(node, complexData[\"supported\"]);\n- },\n- \"Format\": function(node, obj) {\n- var format = {};\n- this.readChildNodes(node, format);\n- if (!obj.formats) {\n- obj.formats = {};\n- }\n- obj.formats[format.mimeType] = true;\n- },\n- \"MimeType\": function(node, format) {\n- format.mimeType = this.getChildValue(node);\n- }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n+OpenLayers.Rule = OpenLayers.Class({\n \n- CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n+ /**\n+ * Property: id\n+ * {String} A unique id for this session.\n+ */\n+ id: null,\n \n- });\n-/* ======================================================================\n- OpenLayers/WPSClient.js\n- ====================================================================== */\n+ /**\n+ * APIProperty: name\n+ * {String} name of this rule\n+ */\n+ name: null,\n \n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n+ /**\n+ * Property: title\n+ * {String} Title of this rule (set if included in SLD)\n+ */\n+ title: null,\n \n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n+ /**\n+ * Property: description\n+ * {String} Description of this rule (set if abstract is included in SLD)\n+ */\n+ description: null,\n \n-/**\n- * @requires OpenLayers/Events.js\n- * @requires OpenLayers/WPSProcess.js\n- * @requires OpenLayers/Format/WPSDescribeProcess.js\n- * @requires OpenLayers/Request.js\n- */\n+ /**\n+ * Property: context\n+ * {Object} An optional object with properties that the rule should be\n+ * evaluated against. If no context is specified, feature.attributes will\n+ * be used.\n+ */\n+ context: null,\n \n-/**\n- * Class: OpenLayers.WPSClient\n- * High level API for interaction with Web Processing Services (WPS).\n- * An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess>\n- * instances for servers known to the WPSClient. The WPSClient also caches\n- * DescribeProcess responses to reduce the number of requests sent to servers\n- * when processes are created.\n- */\n-OpenLayers.WPSClient = OpenLayers.Class({\n+ /**\n+ * Property: filter\n+ * {<OpenLayers.Filter>} Optional filter for the rule.\n+ */\n+ filter: null,\n \n /**\n- * Property: servers\n- * {Object} Service metadata, keyed by a local identifier.\n- *\n- * Properties:\n- * url - {String} the url of the server\n- * version - {String} WPS version of the server\n- * processDescription - {Object} Cache of raw DescribeProcess\n- * responses, keyed by process identifier.\n+ * Property: elseFilter\n+ * {Boolean} Determines whether this rule is only to be applied only if\n+ * no other rules match (ElseFilter according to the SLD specification). \n+ * Default is false. For instances of OpenLayers.Rule, if elseFilter is\n+ * false, the rule will always apply. For subclasses, the else property is \n+ * ignored.\n */\n- servers: null,\n+ elseFilter: false,\n \n /**\n- * Property: version\n- * {String} The default WPS version to use if none is configured. Default\n- * is '1.0.0'.\n+ * Property: symbolizer\n+ * {Object} Symbolizer or hash of symbolizers for this rule. If hash of\n+ * symbolizers, keys are one or more of [\"Point\", \"Line\", \"Polygon\"]. The\n+ * latter if useful if it is required to style e.g. vertices of a line\n+ * with a point symbolizer. Note, however, that this is not implemented\n+ * yet in OpenLayers, but it is the way how symbolizers are defined in\n+ * SLD.\n */\n- version: '1.0.0',\n+ symbolizer: null,\n \n /**\n- * Property: lazy\n- * {Boolean} Should the DescribeProcess be deferred until a process is\n- * fully configured? Default is false.\n+ * Property: symbolizers\n+ * {Array} Collection of symbolizers associated with this rule. If \n+ * provided at construction, the symbolizers array has precedence\n+ * over the deprecated symbolizer property. Note that multiple \n+ * symbolizers are not currently supported by the vector renderers.\n+ * Rules with multiple symbolizers are currently only useful for\n+ * maintaining elements in an SLD document.\n */\n- lazy: false,\n+ symbolizers: null,\n \n /**\n- * Property: events\n- * {<OpenLayers.Events>}\n- *\n- * Supported event types:\n- * describeprocess - Fires when the process description is available.\n- * Listeners receive an object with a 'raw' property holding the raw\n- * DescribeProcess response, and an 'identifier' property holding the\n- * process identifier of the described process.\n+ * APIProperty: minScaleDenominator\n+ * {Number} or {String} minimum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n */\n- events: null,\n+ minScaleDenominator: null,\n \n /**\n- * Constructor: OpenLayers.WPSClient\n+ * APIProperty: maxScaleDenominator\n+ * {Number} or {String} maximum scale at which to draw the feature.\n+ * In the case of a String, this can be a combination of text and\n+ * propertyNames in the form \"literal ${propertyName}\"\n+ */\n+ maxScaleDenominator: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Rule\n+ * Creates a Rule.\n *\n * Parameters:\n- * options - {Object} Object whose properties will be set on the instance.\n- *\n- * Avaliable options:\n- * servers - {Object} Mandatory. Service metadata, keyed by a local\n- * identifier. Can either be a string with the service url or an\n- * object literal with additional metadata:\n- *\n- * (code)\n- * servers: {\n- * local: '/geoserver/wps'\n- * }, {\n- * opengeo: {\n- * url: 'http://demo.opengeo.org/geoserver/wps',\n- * version: '1.0.0'\n- * }\n- * }\n- * (end)\n- *\n- * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be\n- * requested until a process is fully configured. Default is false.\n+ * options - {Object} An optional object with properties to set on the\n+ * rule\n+ * \n+ * Returns:\n+ * {<OpenLayers.Rule>}\n */\n initialize: function(options) {\n+ this.symbolizer = {};\n OpenLayers.Util.extend(this, options);\n- this.events = new OpenLayers.Events(this);\n- this.servers = {};\n- for (var s in options.servers) {\n- this.servers[s] = typeof options.servers[s] == 'string' ? {\n- url: options.servers[s],\n- version: this.version,\n- processDescription: {}\n- } : options.servers[s];\n+ if (this.symbolizers) {\n+ delete this.symbolizer;\n }\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n },\n \n- /**\n- * APIMethod: execute\n- * Shortcut to execute a process with a single function call. This is\n- * equivalent to using <getProcess> and then calling execute on the\n- * process.\n- *\n- * Parameters:\n- * options - {Object} Options for the execute operation.\n- *\n- * Available options:\n- * server - {String} Mandatory. One of the local identifiers of the\n- * configured servers.\n- * process - {String} Mandatory. A process identifier known to the\n- * server.\n- * inputs - {Object} The inputs for the process, keyed by input identifier.\n- * For spatial data inputs, the value of an input is usually an\n- * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of\n- * geometries or features.\n- * output - {String} The identifier of an output to parse. Optional. If not\n- * provided, the first output will be parsed.\n- * success - {Function} Callback to call when the process is complete.\n- * This function is called with an outputs object as argument, which\n- * will have a property with the identifier of the requested output\n- * (e.g. 'result'). For processes that generate spatial output, the\n- * value will either be a single <OpenLayers.Feature.Vector> or an\n- * array of features.\n- * scope - {Object} Optional scope for the success callback.\n+ /** \n+ * APIMethod: destroy\n+ * nullify references to prevent circular references and memory leaks\n */\n- execute: function(options) {\n- var process = this.getProcess(options.server, options.process);\n- process.execute({\n- inputs: options.inputs,\n- success: options.success,\n- scope: options.scope\n- });\n+ destroy: function() {\n+ for (var i in this.symbolizer) {\n+ this.symbolizer[i] = null;\n+ }\n+ this.symbolizer = null;\n+ delete this.symbolizers;\n },\n \n /**\n- * APIMethod: getProcess\n- * Creates an <OpenLayers.WPSProcess>.\n- *\n+ * APIMethod: evaluate\n+ * evaluates this rule for a specific feature\n+ * \n * Parameters:\n- * serverID - {String} Local identifier from the servers that this instance\n- * was constructed with.\n- * processID - {String} Process identifier known to the server.\n- *\n+ * feature - {<OpenLayers.Feature>} feature to apply the rule to.\n+ * \n * Returns:\n- * {<OpenLayers.WPSProcess>}\n+ * {Boolean} true if the rule applies, false if it does not.\n+ * This rule is the default rule and always returns true.\n */\n- getProcess: function(serverID, processID) {\n- var process = new OpenLayers.WPSProcess({\n- client: this,\n- server: serverID,\n- identifier: processID\n- });\n- if (!this.lazy) {\n- process.describe();\n+ evaluate: function(feature) {\n+ var context = this.getContext(feature);\n+ var applies = true;\n+\n+ if (this.minScaleDenominator || this.maxScaleDenominator) {\n+ var scale = feature.layer.map.getScale();\n }\n- return process;\n- },\n \n- /**\n- * Method: describeProcess\n- *\n- * Parameters:\n- * serverID - {String} Identifier of the server\n- * processID - {String} Identifier of the requested process\n- * callback - {Function} Callback to call when the description is available\n- * scope - {Object} Optional execution scope for the callback function\n- */\n- describeProcess: function(serverID, processID, callback, scope) {\n- var server = this.servers[serverID];\n- if (!server.processDescription[processID]) {\n- if (!(processID in server.processDescription)) {\n- // set to null so we know a describeFeature request is pending\n- server.processDescription[processID] = null;\n- OpenLayers.Request.GET({\n- url: server.url,\n- params: {\n- SERVICE: 'WPS',\n- VERSION: server.version,\n- REQUEST: 'DescribeProcess',\n- IDENTIFIER: processID\n- },\n- success: function(response) {\n- server.processDescription[processID] = response.responseText;\n- this.events.triggerEvent('describeprocess', {\n- identifier: processID,\n- raw: response.responseText\n- });\n- },\n- scope: this\n- });\n+ // check if within minScale/maxScale bounds\n+ if (this.minScaleDenominator) {\n+ applies = scale >= OpenLayers.Style.createLiteral(\n+ this.minScaleDenominator, context);\n+ }\n+ if (applies && this.maxScaleDenominator) {\n+ applies = scale < OpenLayers.Style.createLiteral(\n+ this.maxScaleDenominator, context);\n+ }\n+\n+ // check if optional filter applies\n+ if (applies && this.filter) {\n+ // feature id filters get the feature, others get the context\n+ if (this.filter.CLASS_NAME == \"OpenLayers.Filter.FeatureId\") {\n+ applies = this.filter.evaluate(feature);\n } else {\n- // pending request\n- this.events.register('describeprocess', this, function describe(evt) {\n- if (evt.identifier === processID) {\n- this.events.unregister('describeprocess', this, describe);\n- callback.call(scope, evt.raw);\n- }\n- });\n+ applies = this.filter.evaluate(context);\n }\n- } else {\n- window.setTimeout(function() {\n- callback.call(scope, server.processDescription[processID]);\n- }, 0);\n }\n+\n+ return applies;\n },\n \n /**\n- * Method: destroy\n+ * Method: getContext\n+ * Gets the context for evaluating this rule\n+ * \n+ * Paramters:\n+ * feature - {<OpenLayers.Feature>} feature to take the context from if\n+ * none is specified.\n */\n- destroy: function() {\n- this.events.destroy();\n- this.events = null;\n- this.servers = null;\n+ getContext: function(feature) {\n+ var context = this.context;\n+ if (!context) {\n+ context = feature.attributes || feature.data;\n+ }\n+ if (typeof this.context == \"function\") {\n+ context = this.context(feature);\n+ }\n+ return context;\n },\n \n- CLASS_NAME: 'OpenLayers.WPSClient'\n+ /**\n+ * APIMethod: clone\n+ * Clones this rule.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Rule>} Clone of this rule.\n+ */\n+ clone: function() {\n+ var options = OpenLayers.Util.extend({}, this);\n+ if (this.symbolizers) {\n+ // clone symbolizers\n+ var len = this.symbolizers.length;\n+ options.symbolizers = new Array(len);\n+ for (var i = 0; i < len; ++i) {\n+ options.symbolizers[i] = this.symbolizers[i].clone();\n+ }\n+ } else {\n+ // clone symbolizer\n+ options.symbolizer = {};\n+ var value, type;\n+ for (var key in this.symbolizer) {\n+ value = this.symbolizer[key];\n+ type = typeof value;\n+ if (type === \"object\") {\n+ options.symbolizer[key] = OpenLayers.Util.extend({}, value);\n+ } else if (type === \"string\") {\n+ options.symbolizer[key] = value;\n+ }\n+ }\n+ }\n+ // clone filter\n+ options.filter = this.filter && this.filter.clone();\n+ // clone context\n+ options.context = this.context && OpenLayers.Util.extend({}, this.context);\n+ return new OpenLayers.Rule(options);\n+ },\n \n+ CLASS_NAME: \"OpenLayers.Rule\"\n });\n /* ======================================================================\n- OpenLayers/Spherical.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/SingleFile.js\n- */\n-\n-/**\n- * Namespace: Spherical\n- * The OpenLayers.Spherical namespace includes utility functions for\n- * calculations on the basis of a spherical earth (ignoring ellipsoidal\n- * effects), which is accurate enough for most purposes.\n- *\n- * Relevant links:\n- * * http://www.movable-type.co.uk/scripts/latlong.html\n- * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical\n- */\n-\n-OpenLayers.Spherical = OpenLayers.Spherical || {};\n-\n-OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n-\n-/**\n- * APIFunction: computeDistanceBetween\n- * Computes the distance between two LonLats.\n- *\n- * Parameters:\n- * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n- * JavaScript literal with lon lat properties.\n- * radius - {Float} The radius. Optional. Defaults to 6378137 meters.\n- *\n- * Returns:\n- * {Float} The distance in meters.\n- */\n-OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n- var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n- var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n- var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n- var a = sinHalfDeltaLat * sinHalfDeltaLat +\n- sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n- return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n-};\n-\n-\n-/**\n- * APIFunction: computeHeading\n- * Computes the heading from one LonLat to another LonLat.\n- *\n- * Parameters:\n- * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n- * a JavaScript literal with lon lat properties.\n- * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n- * JavaScript literal with lon lat properties.\n- *\n- * Returns:\n- * {Float} The heading in degrees.\n- */\n-OpenLayers.Spherical.computeHeading = function(from, to) {\n- var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n- var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) -\n- Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n- return 180 * Math.atan2(y, x) / Math.PI;\n-};\n-/* ======================================================================\n OpenLayers/Symbolizer/Point.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -43376,1141 +41264,5017 @@\n }\n return new OpenLayers.Style2(config);\n },\n \n CLASS_NAME: \"OpenLayers.Style2\"\n });\n /* ======================================================================\n- OpenLayers/Layer/TileCache.js\n+ OpenLayers/Kinetic.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-\n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Animation.js\n */\n \n-/**\n- * Class: OpenLayers.Layer.TileCache\n- * A read only TileCache layer. Used to requests tiles cached by TileCache in\n- * a web accessible cache. This means that you have to pre-populate your\n- * cache before this layer can be used. It is meant only to read tiles\n- * created by TileCache, and not to make calls to TileCache for tile\n- * creation. Create a new instance with the\n- * <OpenLayers.Layer.TileCache> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Kinetic = OpenLayers.Class({\n \n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} Treat this layer as a base layer. Default is true.\n+ /**\n+ * Property: threshold\n+ * In most cases changing the threshold isn't needed.\n+ * In px/ms, default to 0.\n */\n- isBaseLayer: true,\n+ threshold: 0,\n \n- /** \n- * APIProperty: format\n- * {String} Mime type of the images returned. Default is image/png.\n+ /**\n+ * Property: deceleration\n+ * {Float} the deseleration in px/ms\u00b2, default to 0.0035.\n */\n- format: 'image/png',\n+ deceleration: 0.0035,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer. (b) The map can work with resolutions\n- * that aren't supported by the server, i.e. that aren't in\n- * <serverResolutions>. When the map is displayed in such a resolution\n- * data for the closest server-supported resolution is loaded and the\n- * layer div is stretched as necessary.\n+ * Property: nbPoints\n+ * {Integer} the number of points we use to calculate the kinetic\n+ * initial values.\n */\n- serverResolutions: null,\n+ nbPoints: 100,\n \n /**\n- * Constructor: OpenLayers.Layer.TileCache\n- * Create a new read only TileCache layer.\n+ * Property: delay\n+ * {Float} time to consider to calculate the kinetic initial values.\n+ * In ms, default to 200.\n+ */\n+ delay: 200,\n+\n+ /**\n+ * Property: points\n+ * List of points use to calculate the kinetic initial values.\n+ */\n+ points: undefined,\n+\n+ /**\n+ * Property: timerId\n+ * ID of the timer.\n+ */\n+ timerId: undefined,\n+\n+ /**\n+ * Constructor: OpenLayers.Kinetic\n *\n * Parameters:\n- * name - {String} Name of the layer displayed in the interface\n- * url - {String} Location of the web accessible cache (not the location of\n- * your tilecache script!)\n- * layername - {String} Layer name as defined in the TileCache \n- * configuration\n- * options - {Object} Optional object with properties to be set on the\n- * layer. Note that you should speficy your resolutions to match\n- * your TileCache configuration. This can be done by setting\n- * the resolutions array directly (here or on the map), by setting\n- * maxResolution and numZoomLevels, or by using scale based properties.\n+ * options - {Object}\n */\n- initialize: function(name, url, layername, options) {\n- this.layername = layername;\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this,\n- [name, url, {}, options]);\n- this.extension = this.format.split('/')[1].toLowerCase();\n- this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n },\n \n /**\n- * APIMethod: clone\n- * obj - {Object} \n- * \n- * Returns:\n- * {<OpenLayers.Layer.TileCache>} An exact clone of this \n- * <OpenLayers.Layer.TileCache>\n+ * Method: begin\n+ * Begins the dragging.\n */\n- clone: function(obj) {\n+ begin: function() {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = undefined;\n+ this.points = [];\n+ },\n \n- if (obj == null) {\n- obj = new OpenLayers.Layer.TileCache(this.name,\n- this.url,\n- this.layername,\n- this.getOptions());\n+ /**\n+ * Method: update\n+ * Updates during the dragging.\n+ *\n+ * Parameters:\n+ * xy - {<OpenLayers.Pixel>} The new position.\n+ */\n+ update: function(xy) {\n+ this.points.unshift({\n+ xy: xy,\n+ tick: new Date().getTime()\n+ });\n+ if (this.points.length > this.nbPoints) {\n+ this.points.pop();\n }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n },\n \n /**\n- * Method: getURL\n+ * Method: end\n+ * Ends the dragging, start the kinetic.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n+ * xy - {<OpenLayers.Pixel>} The last position.\n+ *\n * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as parameters.\n+ * {Object} An object with two properties: \"speed\", and \"theta\". The\n+ * \"speed\" and \"theta\" values are to be passed to the move \n+ * function when starting the animation.\n */\n- getURL: function(bounds) {\n- var res = this.getServerResolution();\n- var bbox = this.maxExtent;\n- var size = this.tileSize;\n- var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));\n- var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));\n- var tileZ = this.serverResolutions != null ?\n- OpenLayers.Util.indexOf(this.serverResolutions, res) :\n- this.map.getZoom();\n-\n- var components = [\n- this.layername,\n- OpenLayers.Number.zeroPad(tileZ, 2),\n- OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3),\n- OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3),\n- OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension\n- ];\n- var path = components.join('/');\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n+ end: function(xy) {\n+ var last, now = new Date().getTime();\n+ for (var i = 0, l = this.points.length, point; i < l; i++) {\n+ point = this.points[i];\n+ if (now - point.tick > this.delay) {\n+ break;\n+ }\n+ last = point;\n }\n- url = (url.charAt(url.length - 1) == '/') ? url : url + '/';\n- return url + path;\n+ if (!last) {\n+ return;\n+ }\n+ var time = new Date().getTime() - last.tick;\n+ var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) +\n+ Math.pow(xy.y - last.xy.y, 2));\n+ var speed = dist / time;\n+ if (speed == 0 || speed < this.threshold) {\n+ return;\n+ }\n+ var theta = Math.asin((xy.y - last.xy.y) / dist);\n+ if (last.xy.x <= xy.x) {\n+ theta = Math.PI - theta;\n+ }\n+ return {\n+ speed: speed,\n+ theta: theta\n+ };\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.TileCache\"\n+ /**\n+ * Method: move\n+ * Launch the kinetic move pan.\n+ *\n+ * Parameters:\n+ * info - {Object} An object with two properties, \"speed\", and \"theta\".\n+ * These values are those returned from the \"end\" call.\n+ * callback - {Function} Function called on every step of the animation,\n+ * receives x, y (values to pan), end (is the last point).\n+ */\n+ move: function(info, callback) {\n+ var v0 = info.speed;\n+ var fx = Math.cos(info.theta);\n+ var fy = -Math.sin(info.theta);\n+\n+ var initialTime = new Date().getTime();\n+\n+ var lastX = 0;\n+ var lastY = 0;\n+\n+ var timerCallback = function() {\n+ if (this.timerId == null) {\n+ return;\n+ }\n+\n+ var t = new Date().getTime() - initialTime;\n+\n+ var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;\n+ var x = p * fx;\n+ var y = p * fy;\n+\n+ var args = {};\n+ args.end = false;\n+ var v = -this.deceleration * t + v0;\n+\n+ if (v <= 0) {\n+ OpenLayers.Animation.stop(this.timerId);\n+ this.timerId = null;\n+ args.end = true;\n+ }\n+\n+ args.x = x - lastX;\n+ args.y = y - lastY;\n+ lastX = x;\n+ lastY = y;\n+ callback(args.x, args.y, args.end);\n+ };\n+\n+ this.timerId = OpenLayers.Animation.start(\n+ OpenLayers.Function.bind(timerCallback, this)\n+ );\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Kinetic\"\n });\n /* ======================================================================\n- OpenLayers/Layer/WorldWind.js\n+ OpenLayers/TileManager.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n \n /**\n+ * @requires OpenLayers/Util.js\n+ * @requires OpenLayers/BaseTypes.js\n+ * @requires OpenLayers/BaseTypes/Element.js\n * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/Tile/Image.js\n */\n \n /**\n- * Class: OpenLayers.Layer.WorldWind\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+ * Class: OpenLayers.TileManager\n+ * Provides queueing of image requests and caching of image elements.\n+ *\n+ * Queueing avoids unnecessary image requests while changing zoom levels\n+ * quickly, and helps improve dragging performance on mobile devices that show\n+ * a lag in dragging when loading of new images starts. <zoomDelay> and\n+ * <moveDelay> are the configuration options to control this behavior.\n+ *\n+ * Caching avoids setting the src on image elements for images that have already\n+ * been used. Several maps can share a TileManager instance, in which case each\n+ * map gets its own tile queue, but all maps share the same tile cache.\n */\n-OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.TileManager = OpenLayers.Class({\n \n- DEFAULT_PARAMS: {},\n+ /**\n+ * APIProperty: cacheSize\n+ * {Number} Number of image elements to keep referenced in this instance's\n+ * cache for fast reuse. Default is 256.\n+ */\n+ cacheSize: 256,\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} WorldWind layer is a base layer by default.\n+ * APIProperty: tilesPerFrame\n+ * {Number} Number of queued tiles to load per frame (see <frameDelay>).\n+ * Default is 2.\n */\n- isBaseLayer: true,\n+ tilesPerFrame: 2,\n \n- /** \n- * APIProperty: lzd\n- * {Float} LevelZeroTileSizeDegrees\n+ /**\n+ * APIProperty: frameDelay\n+ * {Number} Delay between tile loading frames (see <tilesPerFrame>) in\n+ * milliseconds. Default is 16.\n */\n- lzd: null,\n+ frameDelay: 16,\n \n /**\n- * APIProperty: zoomLevels\n- * {Integer} Number of zoom levels.\n+ * APIProperty: moveDelay\n+ * {Number} Delay in milliseconds after a map's move event before loading\n+ * tiles. Default is 100.\n */\n- zoomLevels: null,\n+ moveDelay: 100,\n \n /**\n- * Constructor: OpenLayers.Layer.WorldWind\n+ * APIProperty: zoomDelay\n+ * {Number} Delay in milliseconds after a map's zoomend event before loading\n+ * tiles. Default is 200.\n+ */\n+ zoomDelay: 200,\n+\n+ /**\n+ * Property: maps\n+ * {Array(<OpenLayers.Map>)} The maps to manage tiles on.\n+ */\n+ maps: null,\n+\n+ /**\n+ * Property: tileQueueId\n+ * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id.\n+ */\n+ tileQueueId: null,\n+\n+ /**\n+ * Property: tileQueue\n+ * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by\n+ * map id.\n+ */\n+ tileQueue: null,\n+\n+ /**\n+ * Property: tileCache\n+ * {Object} Cached image elements, keyed by URL.\n+ */\n+ tileCache: null,\n+\n+ /**\n+ * Property: tileCacheIndex\n+ * {Array(String)} URLs of cached tiles. First entry is the least recently\n+ * used.\n+ */\n+ tileCacheIndex: null,\n+\n+ /** \n+ * Constructor: OpenLayers.TileManager\n+ * Constructor for a new <OpenLayers.TileManager> instance.\n * \n * Parameters:\n- * name - {String} Name of Layer\n- * url - {String} Base URL \n- * lzd - {Float} Level zero tile size degrees \n- * zoomLevels - {Integer} number of zoom levels\n- * params - {Object} additional parameters\n- * options - {Object} additional options\n+ * options - {Object} Configuration for this instance.\n */\n- initialize: function(name, url, lzd, zoomLevels, params, options) {\n- this.lzd = lzd;\n- this.zoomLevels = zoomLevels;\n- var newArguments = [];\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- this.params = OpenLayers.Util.applyDefaults(\n- this.params, this.DEFAULT_PARAMS\n- );\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.maps = [];\n+ this.tileQueueId = {};\n+ this.tileQueue = {};\n+ this.tileCache = {};\n+ this.tileCacheIndex = [];\n },\n \n /**\n- * Method: getZoom\n- * Convert map zoom to WW zoom.\n+ * Method: addMap\n+ * Binds this instance to a map\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>}\n */\n- getZoom: function() {\n- var zoom = this.map.getZoom();\n- var extent = this.map.getMaxExtent();\n- zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);\n- return zoom;\n+ addMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n+ return;\n+ }\n+ this.maps.push(map);\n+ this.tileQueue[map.id] = [];\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.addLayer({\n+ layer: map.layers[i]\n+ });\n+ }\n+ map.events.on({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n+ });\n },\n \n /**\n- * Method: getURL\n+ * Method: removeMap\n+ * Unbinds this instance from a map\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n+ * map - {<OpenLayers.Map>}\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var zoom = this.getZoom();\n- var extent = this.map.getMaxExtent();\n- var deg = this.lzd / Math.pow(2, this.getZoom());\n- var x = Math.floor((bounds.left - extent.left) / deg);\n- var y = Math.floor((bounds.bottom - extent.bottom) / deg);\n- if (this.map.getResolution() <= (this.lzd / 512) &&\n- this.getZoom() <= this.zoomLevels) {\n- return this.getFullRequestString({\n- L: zoom,\n- X: x,\n- Y: y\n+ removeMap: function(map) {\n+ if (this._destroyed || !OpenLayers.Layer.Grid) {\n+ return;\n+ }\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ if (map.layers) {\n+ for (var i = 0, ii = map.layers.length; i < ii; ++i) {\n+ this.removeLayer({\n+ layer: map.layers[i]\n+ });\n+ }\n+ }\n+ if (map.events) {\n+ map.events.un({\n+ move: this.move,\n+ zoomend: this.zoomEnd,\n+ changelayer: this.changeLayer,\n+ addlayer: this.addLayer,\n+ preremovelayer: this.removeLayer,\n+ scope: this\n });\n- } else {\n- return OpenLayers.Util.getImageLocation(\"blank.gif\");\n }\n-\n+ delete this.tileQueue[map.id];\n+ delete this.tileQueueId[map.id];\n+ OpenLayers.Util.removeItem(this.maps, map);\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.WorldWind\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/TMS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.TMS\n- * Create a layer for accessing tiles from services that conform with the \n- * Tile Map Service Specification \n- * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification).\n- *\n- * Example:\n- * (code)\n- * var layer = new OpenLayers.Layer.TMS(\n- * \"My Layer\", // name for display in LayerSwitcher\n- * \"http://tilecache.osgeo.org/wms-c/Basic.py/\", // service endpoint\n- * {layername: \"basic\", type: \"png\"} // required properties\n- * );\n- * (end)\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n /**\n- * APIProperty: serviceVersion\n- * {String} Service version for tile requests. Default is \"1.0.0\".\n+ * Method: move\n+ * Handles the map's move event\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument\n */\n- serviceVersion: \"1.0.0\",\n+ move: function(evt) {\n+ this.updateTimeout(evt.object, this.moveDelay, true);\n+ },\n \n /**\n- * APIProperty: layername\n- * {String} The identifier for the <TileMap> as advertised by the service. \n- * For example, if the service advertises a <TileMap> with \n- * 'href=\"http://tms.osgeo.org/1.0.0/vmap0\"', the <layername> property \n- * would be set to \"vmap0\".\n+ * Method: zoomEnd\n+ * Handles the map's zoomEnd event\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument\n */\n- layername: null,\n+ zoomEnd: function(evt) {\n+ this.updateTimeout(evt.object, this.zoomDelay);\n+ },\n \n /**\n- * APIProperty: type\n- * {String} The format extension corresponding to the requested tile image\n- * type. This is advertised in a <TileFormat> element as the \n- * \"extension\" attribute. For example, if the service advertises a \n- * <TileMap> with <TileFormat width=\"256\" height=\"256\" mime-type=\"image/jpeg\" extension=\"jpg\" />,\n- * the <type> property would be set to \"jpg\".\n+ * Method: changeLayer\n+ * Handles the map's changeLayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument\n */\n- type: null,\n+ changeLayer: function(evt) {\n+ if (evt.property === 'visibility' || evt.property === 'params') {\n+ this.updateTimeout(evt.object, 0);\n+ }\n+ },\n \n /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Make this layer a base layer. Default is true. Set false to\n- * use the layer as an overlay.\n+ * Method: addLayer\n+ * Handles the map's addlayer event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n */\n- isBaseLayer: true,\n+ addLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ layer.events.on({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.addTile({\n+ tile: tile\n+ });\n+ if (tile.url && !tile.imgDiv) {\n+ this.manageTileCache({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.\n- * If provided, requests for tiles at all resolutions will be aligned\n- * with this location (no tiles shall overlap this location). If\n- * not provided, the grid of tiles will be aligned with the bottom-left\n- * corner of the map's <maxExtent>. Default is ``null``.\n+ * Method: removeLayer\n+ * Handles the map's preremovelayer event\n *\n- * Example:\n- * (code)\n- * var layer = new OpenLayers.Layer.TMS(\n- * \"My Layer\",\n- * \"http://tilecache.osgeo.org/wms-c/Basic.py/\",\n- * {\n- * layername: \"basic\", \n- * type: \"png\",\n- * // set if different than the bottom left of map.maxExtent\n- * tileOrigin: new OpenLayers.LonLat(-180, -90)\n- * }\n- * );\n- * (end)\n+ * Parameters:\n+ * evt - {Object} The listener argument\n */\n- tileOrigin: null,\n+ removeLayer: function(evt) {\n+ var layer = evt.layer;\n+ if (layer instanceof OpenLayers.Layer.Grid) {\n+ this.clearTileQueue({\n+ object: layer\n+ });\n+ if (layer.events) {\n+ layer.events.un({\n+ addtile: this.addTile,\n+ retile: this.clearTileQueue,\n+ scope: this\n+ });\n+ }\n+ if (layer.grid) {\n+ var i, j, tile;\n+ for (i = layer.grid.length - 1; i >= 0; --i) {\n+ for (j = layer.grid[i].length - 1; j >= 0; --j) {\n+ tile = layer.grid[i][j];\n+ this.unloadTile({\n+ object: tile\n+ });\n+ }\n+ }\n+ }\n+ }\n+ },\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * Method: updateTimeout\n+ * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop,\n+ * and schedules more queue processing after <frameDelay> if there are still\n+ * tiles in the queue.\n+ *\n+ * Parameters:\n+ * map - {<OpenLayers.Map>} The map to update the timeout for\n+ * delay - {Number} The delay to apply\n+ * nice - {Boolean} If true, the timeout function will only be created if\n+ * the tilequeue is not empty. This is used by the move handler to\n+ * avoid impacts on dragging performance. For other events, the tile\n+ * queue may not be populated yet, so we need to set the timer\n+ * regardless of the queue size.\n */\n- serverResolutions: null,\n+ updateTimeout: function(map, delay, nice) {\n+ window.clearTimeout(this.tileQueueId[map.id]);\n+ var tileQueue = this.tileQueue[map.id];\n+ if (!nice || tileQueue.length) {\n+ this.tileQueueId[map.id] = window.setTimeout(\n+ OpenLayers.Function.bind(function() {\n+ this.drawTilesFromQueue(map);\n+ if (tileQueue.length) {\n+ this.updateTimeout(map, this.frameDelay);\n+ }\n+ }, this), delay\n+ );\n+ }\n+ },\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * Method: addTile\n+ * Listener for the layer's addtile event\n+ *\n+ * Parameters:\n+ * evt - {Object} The listener argument\n */\n- zoomOffset: 0,\n+ addTile: function(evt) {\n+ if (evt.tile instanceof OpenLayers.Tile.Image) {\n+ evt.tile.events.on({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ } else {\n+ // Layer has the wrong tile type, so don't handle it any longer\n+ this.removeLayer({\n+ layer: evt.tile.layer\n+ });\n+ }\n+ },\n \n /**\n- * Constructor: OpenLayers.Layer.TMS\n- * \n+ * Method: unloadTile\n+ * Listener for the tile's unload event\n+ *\n * Parameters:\n- * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher>\n- * url - {String} Service endpoint (without the version number). E.g.\n- * \"http://tms.osgeo.org/\".\n- * options - {Object} Additional properties to be set on the layer. The\n- * <layername> and <type> properties must be set here.\n+ * evt - {Object} The listener argument\n */\n- initialize: function(name, url, options) {\n- var newArguments = [];\n- newArguments.push(name, url, {}, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n+ unloadTile: function(evt) {\n+ var tile = evt.object;\n+ tile.events.un({\n+ beforedraw: this.queueTileDraw,\n+ beforeload: this.manageTileCache,\n+ loadend: this.addToCache,\n+ unload: this.unloadTile,\n+ scope: this\n+ });\n+ OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile);\n },\n \n /**\n- * APIMethod: clone\n- * Create a complete copy of this layer.\n+ * Method: queueTileDraw\n+ * Adds a tile to the queue that will draw it.\n *\n * Parameters:\n- * obj - {Object} Should only be provided by subclasses that call this\n- * method.\n- * \n- * Returns:\n- * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS>\n+ * evt - {Object} Listener argument of the tile's beforedraw event\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.TMS(this.name,\n- this.url,\n- this.getOptions());\n+ queueTileDraw: function(evt) {\n+ var tile = evt.object;\n+ var queued = false;\n+ var layer = tile.layer;\n+ var url = layer.getURL(tile.bounds);\n+ var img = this.tileCache[url];\n+ if (img && img.className !== 'olTileImage') {\n+ // cached image no longer valid, e.g. because we're olTileReplacing\n+ delete this.tileCache[url];\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, url);\n+ img = null;\n }\n+ // queue only if image with same url not cached already\n+ if (layer.url && (layer.async || !img)) {\n+ // add to queue only if not in queue already\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ if (!~OpenLayers.Util.indexOf(tileQueue, tile)) {\n+ tileQueue.push(tile);\n+ }\n+ queued = true;\n+ }\n+ return !queued;\n+ },\n \n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n+ /**\n+ * Method: drawTilesFromQueue\n+ * Draws tiles from the tileQueue, and unqueues the tiles\n+ */\n+ drawTilesFromQueue: function(map) {\n+ var tileQueue = this.tileQueue[map.id];\n+ var limit = this.tilesPerFrame;\n+ var animating = map.zoomTween && map.zoomTween.playing;\n+ while (!animating && tileQueue.length && limit) {\n+ tileQueue.shift().draw(true);\n+ --limit;\n+ }\n+ },\n \n- return obj;\n+ /**\n+ * Method: manageTileCache\n+ * Adds, updates, removes and fetches cache entries.\n+ *\n+ * Parameters:\n+ * evt - {Object} Listener argument of the tile's beforeload event\n+ */\n+ manageTileCache: function(evt) {\n+ var tile = evt.object;\n+ var img = this.tileCache[tile.url];\n+ if (img) {\n+ // if image is on its layer's backbuffer, remove it from backbuffer\n+ if (img.parentNode &&\n+ OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) {\n+ img.parentNode.removeChild(img);\n+ img.id = null;\n+ }\n+ // only use image from cache if it is not on a layer already\n+ if (!img.parentNode) {\n+ img.style.visibility = 'hidden';\n+ img.style.opacity = 0;\n+ tile.setImage(img);\n+ // LRU - move tile to the end of the array to mark it as the most\n+ // recently used\n+ OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);\n+ this.tileCacheIndex.push(tile.url);\n+ }\n+ }\n },\n \n /**\n- * Method: getURL\n- * \n+ * Method: addToCache\n+ *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n+ * evt - {Object} Listener argument for the tile's loadend event\n */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));\n- var z = this.getServerZoom();\n- var path = this.serviceVersion + \"/\" + this.layername + \"/\" + z + \"/\" + x + \"/\" + y + \".\" + this.type;\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n+ addToCache: function(evt) {\n+ var tile = evt.object;\n+ if (!this.tileCache[tile.url]) {\n+ if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) {\n+ if (this.tileCacheIndex.length >= this.cacheSize) {\n+ delete this.tileCache[this.tileCacheIndex[0]];\n+ this.tileCacheIndex.shift();\n+ }\n+ this.tileCache[tile.url] = tile.imgDiv;\n+ this.tileCacheIndex.push(tile.url);\n+ }\n }\n- return url + path;\n },\n \n- /** \n- * Method: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n+ /**\n+ * Method: clearTileQueue\n+ * Clears the tile queue from tiles of a specific layer\n+ *\n * Parameters:\n- * map - {<OpenLayers.Map>}\n+ * evt - {Object} Listener argument of the layer's retile event\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n- this.map.maxExtent.bottom);\n+ clearTileQueue: function(evt) {\n+ var layer = evt.object;\n+ var tileQueue = this.tileQueue[layer.map.id];\n+ for (var i = tileQueue.length - 1; i >= 0; --i) {\n+ if (tileQueue[i].layer === layer) {\n+ tileQueue.splice(i, 1);\n+ }\n }\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.TMS\"\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ for (var i = this.maps.length - 1; i >= 0; --i) {\n+ this.removeMap(this.maps[i]);\n+ }\n+ this.maps = null;\n+ this.tileQueue = null;\n+ this.tileQueueId = null;\n+ this.tileCache = null;\n+ this.tileCacheIndex = null;\n+ this._destroyed = true;\n+ }\n+\n });\n /* ======================================================================\n- OpenLayers/Layer/XYZ.js\n+ OpenLayers/Strategy.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/Grid.js\n+ * @requires OpenLayers/BaseTypes/Class.js\n */\n \n-/** \n- * Class: OpenLayers.Layer.XYZ\n- * The XYZ class is designed to make it easier for people who have tiles\n- * arranged by a standard XYZ grid. \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n+/**\n+ * Class: OpenLayers.Strategy\n+ * Abstract vector layer strategy class. Not to be instantiated directly. Use\n+ * one of the strategy subclasses instead.\n */\n-OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {\n+OpenLayers.Strategy = OpenLayers.Class({\n \n /**\n- * APIProperty: isBaseLayer\n- * Default is true, as this is designed to be a base tile source. \n+ * Property: layer\n+ * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.\n */\n- isBaseLayer: true,\n+ layer: null,\n \n /**\n- * APIProperty: sphericalMercator\n- * Whether the tile extents should be set to the defaults for \n- * spherical mercator. Useful for things like OpenStreetMap.\n- * Default is false, except for the OSM subclass.\n+ * Property: options\n+ * {Object} Any options sent to the constructor.\n */\n- sphericalMercator: false,\n+ options: null,\n+\n+ /** \n+ * Property: active \n+ * {Boolean} The control is active.\n+ */\n+ active: null,\n \n /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more zoom levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Using <zoomOffset> is an alternative to\n- * setting <serverResolutions> if you only want to expose a subset\n- * of the server resolutions.\n+ * Property: autoActivate\n+ * {Boolean} The creator of the strategy can set autoActivate to false\n+ * to fully control when the protocol is activated and deactivated.\n+ * Defaults to true.\n */\n- zoomOffset: 0,\n+ autoActivate: true,\n \n /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n+ * Property: autoDestroy\n+ * {Boolean} The creator of the strategy can set autoDestroy to false\n+ * to fully control when the strategy is destroyed. Defaults to\n+ * true.\n */\n- serverResolutions: null,\n+ autoDestroy: true,\n \n /**\n- * Constructor: OpenLayers.Layer.XYZ\n+ * Constructor: OpenLayers.Strategy\n+ * Abstract class for vector strategies. Create instances of a subclass.\n *\n * Parameters:\n- * name - {String}\n- * url - {String}\n- * options - {Object} Hashtable of extra options to tag onto the layer\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n */\n- initialize: function(name, url, options) {\n- if (options && options.sphericalMercator || this.sphericalMercator) {\n- options = OpenLayers.Util.extend({\n- projection: \"EPSG:900913\",\n- numZoomLevels: 19\n- }, options);\n- }\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name || this.name, url || this.url, {},\n- options\n- ]);\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.options = options;\n+ // set the active property here, so that user cannot override it\n+ this.active = false;\n },\n \n /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Is this ever used?\n- * \n- * Returns:\n- * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ\n+ * APIMethod: destroy\n+ * Clean up the strategy.\n */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.XYZ(this.name,\n- this.url,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n+ destroy: function() {\n+ this.deactivate();\n+ this.layer = null;\n+ this.options = null;\n },\n \n /**\n- * Method: getURL\n+ * Method: setLayer\n+ * Called to set the <layer> property.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n+ * layer - {<OpenLayers.Layer.Vector>}\n */\n- getURL: function(bounds) {\n- var xyz = this.getXYZ(bounds);\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- var s = '' + xyz.x + xyz.y + xyz.z;\n- url = this.selectUrl(s, url);\n- }\n-\n- return OpenLayers.String.format(url, xyz);\n+ setLayer: function(layer) {\n+ this.layer = layer;\n },\n \n /**\n- * Method: getXYZ\n- * Calculates x, y and z for the given bounds.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * Method: activate\n+ * Activate the strategy. Register any listeners, do appropriate setup.\n *\n * Returns:\n- * {Object} - an object with x, y and z properties.\n+ * {Boolean} True if the strategy was successfully activated or false if\n+ * the strategy was already active.\n */\n- getXYZ: function(bounds) {\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.maxExtent.left) /\n- (res * this.tileSize.w));\n- var y = Math.round((this.maxExtent.top - bounds.top) /\n- (res * this.tileSize.h));\n- var z = this.getServerZoom();\n-\n- if (this.wrapDateLine) {\n- var limit = Math.pow(2, z);\n- x = ((x % limit) + limit) % limit;\n+ activate: function() {\n+ if (!this.active) {\n+ this.active = true;\n+ return true;\n }\n-\n- return {\n- 'x': x,\n- 'y': y,\n- 'z': z\n- };\n+ return false;\n },\n \n- /* APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin \n- * (if we don't have one.) \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n+ /**\n+ * Method: deactivate\n+ * Deactivate the strategy. Unregister any listeners, do appropriate\n+ * tear-down.\n+ *\n+ * Returns:\n+ * {Boolean} True if the strategy was successfully deactivated or false if\n+ * the strategy was already inactive.\n */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,\n- this.maxExtent.bottom);\n+ deactivate: function() {\n+ if (this.active) {\n+ this.active = false;\n+ return true;\n }\n+ return false;\n },\n \n- CLASS_NAME: \"OpenLayers.Layer.XYZ\"\n+ CLASS_NAME: \"OpenLayers.Strategy\"\n });\n /* ======================================================================\n- OpenLayers/Layer/ArcGISCache.js\n+ OpenLayers/Spherical.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n-/** \n- * @requires OpenLayers/Layer/XYZ.js\n+/**\n+ * @requires OpenLayers/SingleFile.js\n */\n \n-/** \n- * Class: OpenLayers.Layer.ArcGISCache \n- * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. \n- * Tile must already be cached for this layer to access it. This does not require \n- * ArcGIS Server itself.\n- * \n- * A few attempts have been made at this kind of layer before. See \n- * http://trac.osgeo.org/openlayers/ticket/1967 \n- * and \n- * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js\n+/**\n+ * Namespace: Spherical\n+ * The OpenLayers.Spherical namespace includes utility functions for\n+ * calculations on the basis of a spherical earth (ignoring ellipsoidal\n+ * effects), which is accurate enough for most purposes.\n *\n- * Typically the problem encountered is that the tiles seem to \"jump around\".\n- * This is due to the fact that the actual max extent for the tiles on AGS layers\n- * changes at each zoom level due to the way these caches are constructed.\n- * We have attempted to use the resolutions, tile size, and tile origin\n- * from the cache meta data to make the appropriate changes to the max extent\n- * of the tile to compensate for this behavior. This must be done as zoom levels change\n- * and before tiles are requested, which is why methods from base classes are overridden.\n+ * Relevant links:\n+ * * http://www.movable-type.co.uk/scripts/latlong.html\n+ * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical\n+ */\n+\n+OpenLayers.Spherical = OpenLayers.Spherical || {};\n+\n+OpenLayers.Spherical.DEFAULT_RADIUS = 6378137;\n+\n+/**\n+ * APIFunction: computeDistanceBetween\n+ * Computes the distance between two LonLats.\n *\n- * For reference, you can access mapcache meta data in two ways. For accessing a \n- * mapcache through ArcGIS Server, you can simply go to the landing page for the\n- * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer)\n- * For accessing it directly through HTTP, there should always be a conf.xml file\n- * in the root directory. \n- * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml)\n- * \n- *Inherits from: \n- * - <OpenLayers.Layer.XYZ> \n+ * Parameters:\n+ * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n+ * JavaScript literal with lon lat properties.\n+ * radius - {Float} The radius. Optional. Defaults to 6378137 meters.\n+ *\n+ * Returns:\n+ * {Float} The distance in meters.\n */\n-OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n+OpenLayers.Spherical.computeDistanceBetween = function(from, to, radius) {\n+ var R = radius || OpenLayers.Spherical.DEFAULT_RADIUS;\n+ var sinHalfDeltaLon = Math.sin(Math.PI * (to.lon - from.lon) / 360);\n+ var sinHalfDeltaLat = Math.sin(Math.PI * (to.lat - from.lat) / 360);\n+ var a = sinHalfDeltaLat * sinHalfDeltaLat +\n+ sinHalfDeltaLon * sinHalfDeltaLon * Math.cos(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180);\n+ return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n+};\n+\n+\n+/**\n+ * APIFunction: computeHeading\n+ * Computes the heading from one LonLat to another LonLat.\n+ *\n+ * Parameters:\n+ * from - {<OpenLayers.LonLat>} or {Object} Starting point. A LonLat or\n+ * a JavaScript literal with lon lat properties.\n+ * to - {<OpenLayers.LonLat>} or {Object} Ending point. A LonLat or a\n+ * JavaScript literal with lon lat properties.\n+ *\n+ * Returns:\n+ * {Float} The heading in degrees.\n+ */\n+OpenLayers.Spherical.computeHeading = function(from, to) {\n+ var y = Math.sin(Math.PI * (from.lon - to.lon) / 180) * Math.cos(Math.PI * to.lat / 180);\n+ var x = Math.cos(Math.PI * from.lat / 180) * Math.sin(Math.PI * to.lat / 180) -\n+ Math.sin(Math.PI * from.lat / 180) * Math.cos(Math.PI * to.lat / 180) * Math.cos(Math.PI * (from.lon - to.lon) / 180);\n+ return 180 * Math.atan2(y, x) / Math.PI;\n+};\n+/* ======================================================================\n+ OpenLayers/Handler.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Handler\n+ * Base class to construct a higher-level handler for event sequences. All\n+ * handlers have activate and deactivate methods. In addition, they have\n+ * methods named like browser events. When a handler is activated, any\n+ * additional methods named like a browser event is registered as a\n+ * listener for the corresponding event. When a handler is deactivated,\n+ * those same methods are unregistered as event listeners.\n+ *\n+ * Handlers also typically have a callbacks object with keys named like\n+ * the abstracted events or event sequences that they are in charge of\n+ * handling. The controls that wrap handlers define the methods that\n+ * correspond to these abstract events - so instead of listening for\n+ * individual browser events, they only listen for the abstract events\n+ * defined by the handler.\n+ * \n+ * Handlers are created by controls, which ultimately have the responsibility\n+ * of making changes to the the state of the application. Handlers\n+ * themselves may make temporary changes, but in general are expected to\n+ * return the application in the same state that they found it.\n+ */\n+OpenLayers.Handler = OpenLayers.Class({\n \n /**\n- * APIProperty: url\n- * {String | Array} The base URL for the layer cache. You can also\n- * provide a list of URL strings for the layer if your cache is\n- * available from multiple origins. This must be set before the layer\n- * is drawn.\n+ * Property: id\n+ * {String}\n */\n- url: null,\n+ id: null,\n \n /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} The location of the tile origin for the cache.\n- * An ArcGIS cache has it's origin at the upper-left (lowest x value\n- * and highest y value of the coordinate system). The units for the\n- * tile origin should be the same as the units for the cached data.\n+ * APIProperty: control\n+ * {<OpenLayers.Control>}. The control that initialized this handler. The\n+ * control is assumed to have a valid map property - that map is used\n+ * in the handler's own setMap method.\n */\n- tileOrigin: null,\n+ control: null,\n \n /**\n- * APIProperty: tileSize\n- * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels.\n+ * Property: map\n+ * {<OpenLayers.Map>}\n */\n- tileSize: new OpenLayers.Size(256, 256),\n+ map: null,\n \n /**\n- * APIProperty: useAGS\n- * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS)\n- * cache via an AGS MapServer or directly through HTTP. When accessing via\n- * AGS the path structure uses a standard z/y/x structure. But AGS actually\n- * stores the tile images on disk using a hex based folder structure that looks\n- * like \"http://example.com/mylayer/L00/R00000000/C00000000.png\". Learn more\n- * about this here:\n- * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx\n- * Defaults to true;\n+ * APIProperty: keyMask\n+ * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler\n+ * constants to construct a keyMask. The keyMask is used by\n+ * <checkModifiers>. If the keyMask matches the combination of keys\n+ * down on an event, checkModifiers returns true.\n+ *\n+ * Example:\n+ * (code)\n+ * // handler only responds if the Shift key is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT;\n+ *\n+ * // handler only responds if Ctrl-Shift is down\n+ * handler.keyMask = OpenLayers.Handler.MOD_SHIFT |\n+ * OpenLayers.Handler.MOD_CTRL;\n+ * (end)\n */\n- useArcGISServer: true,\n+ keyMask: null,\n \n /**\n- * APIProperty: type\n- * {String} Image type for the layer. This becomes the filename extension\n- * in tile requests. Default is \"png\" (generating a url like\n- * \"http://example.com/mylayer/L00/R00000000/C00000000.png\").\n+ * Property: active\n+ * {Boolean}\n */\n- type: 'png',\n+ active: false,\n \n /**\n- * APIProperty: useScales\n- * {Boolean} Optional override to indicate that the layer should use 'scale' information\n- * returned from the server capabilities object instead of 'resolution' information.\n- * This can be important if your tile server uses an unusual DPI for the tiles.\n+ * Property: evt\n+ * {Event} This property references the last event handled by the handler.\n+ * Note that this property is not part of the stable API. Use of the\n+ * evt property should be restricted to controls in the library\n+ * or other applications that are willing to update with changes to\n+ * the OpenLayers code.\n */\n- useScales: false,\n+ evt: null,\n \n /**\n- * APIProperty: overrideDPI\n- * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based \n- * on the tile information in the server capabilities object. This can be useful \n- * if your server has a non-standard DPI setting on its tiles, and you're only using \n- * tiles with that DPI. This value is used while OpenLayers is calculating resolution\n- * using scales, and is not necessary if you have resolution information. (This is\n- * typically the case) Regardless, this setting can be useful, but is dangerous\n- * because it will impact other layers while calculating resolution. Only use this\n- * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale)\n+ * Property: touch\n+ * {Boolean} Indicates the support of touch events. When touch events are \n+ * started touch will be true and all mouse related listeners will do \n+ * nothing.\n */\n- overrideDPI: false,\n+ touch: false,\n \n /**\n- * Constructor: OpenLayers.Layer.ArcGISCache \n- * Creates a new instance of this class \n- * \n- * Parameters: \n- * name - {String} \n- * url - {String} \n- * options - {Object} extra layer options\n+ * Constructor: OpenLayers.Handler\n+ * Construct a handler.\n+ *\n+ * Parameters:\n+ * control - {<OpenLayers.Control>} The control that initialized this\n+ * handler. The control is assumed to have a valid map property; that\n+ * map is used in the handler's own setMap method. If a map property\n+ * is present in the options argument it will be used instead.\n+ * callbacks - {Object} An object whose properties correspond to abstracted\n+ * events or sequences of browser events. The values for these\n+ * properties are functions defined by the control that get called by\n+ * the handler.\n+ * options - {Object} An optional object whose properties will be set on\n+ * the handler.\n */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n+ initialize: function(control, callbacks, options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.control = control;\n+ this.callbacks = callbacks;\n \n- if (this.resolutions) {\n- this.serverResolutions = this.resolutions;\n- this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);\n+ var map = this.map || control.map;\n+ if (map) {\n+ this.setMap(map);\n }\n \n- // this block steps through translating the values from the server layer JSON \n- // capabilities object into values that we can use. This is also a helpful\n- // reference when configuring this layer directly.\n- if (this.layerInfo) {\n- // alias the object\n- var info = this.layerInfo;\n+ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + \"_\");\n+ },\n \n- // build our extents\n- var startingTileExtent = new OpenLayers.Bounds(\n- info.fullExtent.xmin,\n- info.fullExtent.ymin,\n- info.fullExtent.xmax,\n- info.fullExtent.ymax\n- );\n+ /**\n+ * Method: setMap\n+ */\n+ setMap: function(map) {\n+ this.map = map;\n+ },\n \n- // set our projection based on the given spatial reference.\n- // esri uses slightly different IDs, so this may not be comprehensive\n- this.projection = 'EPSG:' + info.spatialReference.wkid;\n- this.sphericalMercator = (info.spatialReference.wkid == 102100);\n+ /**\n+ * Method: checkModifiers\n+ * Check the keyMask on the handler. If no <keyMask> is set, this always\n+ * returns true. If a <keyMask> is set and it matches the combination\n+ * of keys down on an event, this returns true.\n+ *\n+ * Returns:\n+ * {Boolean} The keyMask matches the keys down on an event.\n+ */\n+ checkModifiers: function(evt) {\n+ if (this.keyMask == null) {\n+ return true;\n+ }\n+ /* calculate the keyboard modifier mask for this event */\n+ var keyModifiers =\n+ (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |\n+ (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) |\n+ (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) |\n+ (evt.metaKey ? OpenLayers.Handler.MOD_META : 0);\n \n- // convert esri units into openlayers units (basic feet or meters only)\n- this.units = (info.units == \"esriFeet\") ? 'ft' : 'm';\n+ /* if it differs from the handler object's key mask,\n+ bail out of the event handler */\n+ return (keyModifiers == this.keyMask);\n+ },\n \n- // optional extended section based on whether or not the server returned\n- // specific tile information\n- if (!!info.tileInfo) {\n- // either set the tiles based on rows/columns, or specific width/height\n- this.tileSize = new OpenLayers.Size(\n- info.tileInfo.width || info.tileInfo.cols,\n- info.tileInfo.height || info.tileInfo.rows\n- );\n+ /**\n+ * APIMethod: activate\n+ * Turn on the handler. Returns false if the handler was already active.\n+ * \n+ * Returns: \n+ * {Boolean} The handler was activated.\n+ */\n+ activate: function() {\n+ if (this.active) {\n+ return false;\n+ }\n+ // register for event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.register(events[i], this[events[i]]);\n+ }\n+ }\n+ this.active = true;\n+ return true;\n+ },\n \n- // this must be set when manually configuring this layer\n- this.tileOrigin = new OpenLayers.LonLat(\n- info.tileInfo.origin.x,\n- info.tileInfo.origin.y\n- );\n+ /**\n+ * APIMethod: deactivate\n+ * Turn off the handler. Returns false if the handler was already inactive.\n+ * \n+ * Returns:\n+ * {Boolean} The handler was deactivated.\n+ */\n+ deactivate: function() {\n+ if (!this.active) {\n+ return false;\n+ }\n+ // unregister event handlers defined on this class.\n+ var events = OpenLayers.Events.prototype.BROWSER_EVENTS;\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n+ }\n+ }\n+ this.touch = false;\n+ this.active = false;\n+ return true;\n+ },\n \n- var upperLeft = new OpenLayers.Geometry.Point(\n- startingTileExtent.left,\n- startingTileExtent.top\n- );\n+ /**\n+ * Method: startTouch\n+ * Start touch events, this method must be called by subclasses in \n+ * \"touchstart\" method. When touch events are started <touch> will be\n+ * true and all mouse related listeners will do nothing.\n+ */\n+ startTouch: function() {\n+ if (!this.touch) {\n+ this.touch = true;\n+ var events = [\n+ \"mousedown\", \"mouseup\", \"mousemove\", \"click\", \"dblclick\",\n+ \"mouseout\"\n+ ];\n+ for (var i = 0, len = events.length; i < len; i++) {\n+ if (this[events[i]]) {\n+ this.unregister(events[i], this[events[i]]);\n+ }\n+ }\n+ }\n+ },\n \n- var bottomRight = new OpenLayers.Geometry.Point(\n- startingTileExtent.right,\n- startingTileExtent.bottom\n- );\n+ /**\n+ * Method: callback\n+ * Trigger the control's named callback with the given arguments\n+ *\n+ * Parameters:\n+ * name - {String} The key for the callback that is one of the properties\n+ * of the handler's callbacks object.\n+ * args - {Array(*)} An array of arguments (any type) with which to call \n+ * the callback (defined by the control).\n+ */\n+ callback: function(name, args) {\n+ if (name && this.callbacks[name]) {\n+ this.callbacks[name].apply(this.control, args);\n+ }\n+ },\n \n- if (this.useScales) {\n- this.scales = [];\n- } else {\n- this.resolutions = [];\n- }\n+ /**\n+ * Method: register\n+ * register an event on the map\n+ */\n+ register: function(name, method) {\n+ // TODO: deal with registerPriority in 3.0\n+ this.map.events.registerPriority(name, this, method);\n+ this.map.events.registerPriority(name, this, this.setEvent);\n+ },\n \n- this.lods = [];\n- for (var key in info.tileInfo.lods) {\n- if (info.tileInfo.lods.hasOwnProperty(key)) {\n- var lod = info.tileInfo.lods[key];\n- if (this.useScales) {\n- this.scales.push(lod.scale);\n- } else {\n- this.resolutions.push(lod.resolution);\n- }\n+ /**\n+ * Method: unregister\n+ * unregister an event from the map\n+ */\n+ unregister: function(name, method) {\n+ this.map.events.unregister(name, this, method);\n+ this.map.events.unregister(name, this, this.setEvent);\n+ },\n \n- var start = this.getContainingTileCoords(upperLeft, lod.resolution);\n- lod.startTileCol = start.x;\n- lod.startTileRow = start.y;\n+ /**\n+ * Method: setEvent\n+ * With each registered browser event, the handler sets its own evt\n+ * property. This property can be accessed by controls if needed\n+ * to get more information about the event that the handler is\n+ * processing.\n+ *\n+ * This allows modifier keys on the event to be checked (alt, shift, ctrl,\n+ * and meta cannot be checked with the keyboard handler). For a\n+ * control to determine which modifier keys are associated with the\n+ * event that a handler is currently processing, it should access\n+ * (code)handler.evt.altKey || handler.evt.shiftKey ||\n+ * handler.evt.ctrlKey || handler.evt.metaKey(end).\n+ *\n+ * Parameters:\n+ * evt - {Event} The browser event.\n+ */\n+ setEvent: function(evt) {\n+ this.evt = evt;\n+ return true;\n+ },\n \n- var end = this.getContainingTileCoords(bottomRight, lod.resolution);\n- lod.endTileCol = end.x;\n- lod.endTileRow = end.y;\n- this.lods.push(lod);\n- }\n- }\n+ /**\n+ * Method: destroy\n+ * Deconstruct the handler.\n+ */\n+ destroy: function() {\n+ // unregister event listeners\n+ this.deactivate();\n+ // eliminate circular references\n+ this.control = this.map = null;\n+ },\n \n- this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);\n- this.serverResolutions = this.resolutions;\n- if (this.overrideDPI && info.tileInfo.dpi) {\n- // see comment above for 'overrideDPI'\n- OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;\n- }\n- }\n- }\n+ CLASS_NAME: \"OpenLayers.Handler\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_NONE\n+ * If set as the <keyMask>, <checkModifiers> returns false if any key is down.\n+ */\n+OpenLayers.Handler.MOD_NONE = 0;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_SHIFT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.\n+ */\n+OpenLayers.Handler.MOD_SHIFT = 1;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_CTRL\n+ * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.\n+ */\n+OpenLayers.Handler.MOD_CTRL = 2;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_ALT\n+ * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.\n+ */\n+OpenLayers.Handler.MOD_ALT = 4;\n+\n+/**\n+ * Constant: OpenLayers.Handler.MOD_META\n+ * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.\n+ */\n+OpenLayers.Handler.MOD_META = 8;\n+\n+\n+/* ======================================================================\n+ OpenLayers/Icon.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Icon\n+ * \n+ * The icon represents a graphical icon on the screen. Typically used in\n+ * conjunction with a <OpenLayers.Marker> to represent markers on a screen.\n+ *\n+ * An icon has a url, size and position. It also contains an offset which \n+ * allows the center point to be represented correctly. This can be\n+ * provided either as a fixed offset or a function provided to calculate\n+ * the desired offset. \n+ * \n+ */\n+OpenLayers.Icon = OpenLayers.Class({\n+\n+ /** \n+ * Property: url \n+ * {String} image url\n+ */\n+ url: null,\n+\n+ /** \n+ * Property: size \n+ * {<OpenLayers.Size>|Object} An OpenLayers.Size or\n+ * an object with a 'w' and 'h' properties.\n+ */\n+ size: null,\n+\n+ /** \n+ * Property: offset \n+ * {<OpenLayers.Pixel>|Object} distance in pixels to offset the\n+ * image when being rendered. An OpenLayers.Pixel or an object\n+ * with a 'x' and 'y' properties.\n+ */\n+ offset: null,\n+\n+ /** \n+ * Property: calculateOffset \n+ * {Function} Function to calculate the offset (based on the size)\n+ */\n+ calculateOffset: null,\n+\n+ /** \n+ * Property: imageDiv \n+ * {DOMElement} \n+ */\n+ imageDiv: null,\n+\n+ /** \n+ * Property: px \n+ * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object\n+ * with a 'x' and 'y' properties.\n+ */\n+ px: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Icon\n+ * Creates an icon, which is an image tag in a div. \n+ *\n+ * url - {String} \n+ * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an\n+ * object with a 'w' and 'h'\n+ * properties.\n+ * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n+ * object with a 'x' and 'y'\n+ * properties.\n+ * calculateOffset - {Function} \n+ */\n+ initialize: function(url, size, offset, calculateOffset) {\n+ this.url = url;\n+ this.size = size || {\n+ w: 20,\n+ h: 20\n+ };\n+ this.offset = offset || {\n+ x: -(this.size.w / 2),\n+ y: -(this.size.h / 2)\n+ };\n+ this.calculateOffset = calculateOffset;\n+\n+ var id = OpenLayers.Util.createUniqueID(\"OL_Icon_\");\n+ this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);\n },\n \n /** \n- * Method: getContainingTileCoords\n- * Calculates the x/y pixel corresponding to the position of the tile\n- * that contains the given point and for the for the given resolution.\n+ * Method: destroy\n+ * Nullify references and remove event listeners to prevent circular \n+ * references and memory leaks\n+ */\n+ destroy: function() {\n+ // erase any drawn elements\n+ this.erase();\n+\n+ OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);\n+ this.imageDiv.innerHTML = \"\";\n+ this.imageDiv = null;\n+ },\n+\n+ /** \n+ * Method: clone\n+ * \n+ * Returns:\n+ * {<OpenLayers.Icon>} A fresh copy of the icon.\n+ */\n+ clone: function() {\n+ return new OpenLayers.Icon(this.url,\n+ this.size,\n+ this.offset,\n+ this.calculateOffset);\n+ },\n+\n+ /**\n+ * Method: setSize\n * \n * Parameters:\n- * point - {<OpenLayers.Geometry.Point>} \n- * res - {Float} The resolution for which to compute the extent.\n+ * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or\n+ * an object with a 'w' and 'h' properties.\n+ */\n+ setSize: function(size) {\n+ if (size != null) {\n+ this.size = size;\n+ }\n+ this.draw();\n+ },\n+\n+ /**\n+ * Method: setUrl\n * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n+ * Parameters:\n+ * url - {String} \n */\n- getContainingTileCoords: function(point, res) {\n- return new OpenLayers.Pixel(\n- Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0),\n- Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0)\n- );\n+ setUrl: function(url) {\n+ if (url != null) {\n+ this.url = url;\n+ }\n+ this.draw();\n },\n \n /** \n- * Method: calculateMaxExtentWithLOD\n- * Given a Level of Detail object from the server, this function\n- * calculates the actual max extent\n+ * Method: draw\n+ * Move the div to the given pixel.\n * \n- * Parameters: \n- * lod - {Object} a Level of Detail Object from the server capabilities object \n- representing a particular zoom level\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an\n+ * object with a 'x' and 'y' properties.\n * \n- * Returns: \n- * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n+ * Returns:\n+ * {DOMElement} A new DOM Image of this icon set at the location passed-in\n */\n- calculateMaxExtentWithLOD: function(lod) {\n- // the max extent we're provided with just overlaps some tiles\n- // our real extent is the bounds of all the tiles we touch\n+ draw: function(px) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv,\n+ null,\n+ null,\n+ this.size,\n+ this.url,\n+ \"absolute\");\n+ this.moveTo(px);\n+ return this.imageDiv;\n+ },\n \n- var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;\n- var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;\n+ /** \n+ * Method: erase\n+ * Erase the underlying image element.\n+ */\n+ erase: function() {\n+ if (this.imageDiv != null && this.imageDiv.parentNode != null) {\n+ OpenLayers.Element.remove(this.imageDiv);\n+ }\n+ },\n \n- var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);\n- var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);\n+ /** \n+ * Method: setOpacity\n+ * Change the icon's opacity\n+ *\n+ * Parameters:\n+ * opacity - {float} \n+ */\n+ setOpacity: function(opacity) {\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null,\n+ null, null, null, null, opacity);\n \n- var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);\n- var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * move icon to passed in px.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n+ * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n+ */\n+ moveTo: function(px) {\n+ //if no px passed in, use stored location\n+ if (px != null) {\n+ this.px = px;\n+ }\n+\n+ if (this.imageDiv != null) {\n+ if (this.px == null) {\n+ this.display(false);\n+ } else {\n+ if (this.calculateOffset) {\n+ this.offset = this.calculateOffset(this.size);\n+ }\n+ OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {\n+ x: this.px.x + this.offset.x,\n+ y: this.px.y + this.offset.y\n+ });\n+ }\n+ }\n },\n \n /** \n- * Method: calculateMaxExtentWithExtent\n- * Given a 'suggested' max extent from the server, this function uses\n- * information about the actual tile sizes to determine the actual\n- * extent of the layer.\n- * \n- * Parameters: \n- * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer\n- * res - {Float} The resolution for which to compute the extent.\n+ * Method: display\n+ * Hide or show the icon\n+ *\n+ * Parameters:\n+ * display - {Boolean} \n+ */\n+ display: function(display) {\n+ this.imageDiv.style.display = (display) ? \"\" : \"none\";\n+ },\n+\n+\n+ /**\n+ * APIMethod: isDrawn\n * \n- * Returns: \n- * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level\n+ * Returns:\n+ * {Boolean} Whether or not the icon is drawn.\n */\n- calculateMaxExtentWithExtent: function(extent, res) {\n- var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);\n- var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);\n- var start = this.getContainingTileCoords(upperLeft, res);\n- var end = this.getContainingTileCoords(bottomRight, res);\n- var lod = {\n- resolution: res,\n- startTileCol: start.x,\n- startTileRow: start.y,\n- endTileCol: end.x,\n- endTileRow: end.y\n- };\n- return this.calculateMaxExtentWithLOD(lod);\n+ isDrawn: function() {\n+ // nodeType 11 for ie, whose nodes *always* have a parentNode\n+ // (of type document fragment)\n+ var isDrawn = (this.imageDiv && this.imageDiv.parentNode &&\n+ (this.imageDiv.parentNode.nodeType != 11));\n+\n+ return isDrawn;\n },\n \n+ CLASS_NAME: \"OpenLayers.Icon\"\n+});\n+/* ======================================================================\n+ OpenLayers/Marker.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+\n+/**\n+ * @requires OpenLayers/BaseTypes/Class.js\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/Icon.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Marker\n+ * Instances of OpenLayers.Marker are a combination of a \n+ * <OpenLayers.LonLat> and an <OpenLayers.Icon>. \n+ *\n+ * Markers are generally added to a special layer called\n+ * <OpenLayers.Layer.Markers>.\n+ *\n+ * Example:\n+ * (code)\n+ * var markers = new OpenLayers.Layer.Markers( \"Markers\" );\n+ * map.addLayer(markers);\n+ *\n+ * var size = new OpenLayers.Size(21,25);\n+ * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);\n+ * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);\n+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));\n+ * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));\n+ *\n+ * (end)\n+ *\n+ * Note that if you pass an icon into the Marker constructor, it will take\n+ * that icon and use it. This means that you should not share icons between\n+ * markers -- you use them once, but you should clone() for any additional\n+ * markers using that same icon.\n+ */\n+OpenLayers.Marker = OpenLayers.Class({\n+\n /** \n- * Method: getUpperLeftTileCoord\n- * Calculates the x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n+ * Property: icon \n+ * {<OpenLayers.Icon>} The icon used by this marker.\n+ */\n+ icon: null,\n+\n+ /** \n+ * Property: lonlat \n+ * {<OpenLayers.LonLat>} location of object\n+ */\n+ lonlat: null,\n+\n+ /** \n+ * Property: events \n+ * {<OpenLayers.Events>} the event handler.\n+ */\n+ events: null,\n+\n+ /** \n+ * Property: map \n+ * {<OpenLayers.Map>} the map this marker is attached to\n+ */\n+ map: null,\n+\n+ /** \n+ * Constructor: OpenLayers.Marker\n+ *\n+ * Parameters:\n+ * lonlat - {<OpenLayers.LonLat>} the position of this marker\n+ * icon - {<OpenLayers.Icon>} the icon for this marker\n+ */\n+ initialize: function(lonlat, icon) {\n+ this.lonlat = lonlat;\n+\n+ var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();\n+ if (this.icon == null) {\n+ this.icon = newIcon;\n+ } else {\n+ this.icon.url = newIcon.url;\n+ this.icon.size = newIcon.size;\n+ this.icon.offset = newIcon.offset;\n+ this.icon.calculateOffset = newIcon.calculateOffset;\n+ }\n+ this.events = new OpenLayers.Events(this, this.icon.imageDiv);\n+ },\n+\n+ /**\n+ * APIMethod: destroy\n+ * Destroy the marker. You must first remove the marker from any \n+ * layer which it has been added to, or you will get buggy behavior.\n+ * (This can not be done within the marker since the marker does not\n+ * know which layer it is attached to.)\n+ */\n+ destroy: function() {\n+ // erase any drawn features\n+ this.erase();\n+\n+ this.map = null;\n+\n+ this.events.destroy();\n+ this.events = null;\n+\n+ if (this.icon != null) {\n+ this.icon.destroy();\n+ this.icon = null;\n+ }\n+ },\n+\n+ /** \n+ * Method: draw\n+ * Calls draw on the icon, and returns that output.\n * \n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>}\n * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position \n- * of the upper left tile for the given resolution.\n+ * Returns:\n+ * {DOMElement} A new DOM Image with this marker's icon set at the \n+ * location passed-in\n */\n- getUpperLeftTileCoord: function(res) {\n- var upperLeft = new OpenLayers.Geometry.Point(\n- this.maxExtent.left,\n- this.maxExtent.top);\n- return this.getContainingTileCoords(upperLeft, res);\n+ draw: function(px) {\n+ return this.icon.draw(px);\n },\n \n /** \n- * Method: getLowerRightTileCoord\n- * Calculates the x/y pixel corresponding to the position \n- * of the lower right tile for the given resolution.\n- * \n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n+ * Method: erase\n+ * Erases any drawn elements for this marker.\n+ */\n+ erase: function() {\n+ if (this.icon != null) {\n+ this.icon.erase();\n+ }\n+ },\n+\n+ /**\n+ * Method: moveTo\n+ * Move the marker to the new location.\n+ *\n+ * Parameters:\n+ * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.\n+ * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.\n+ */\n+ moveTo: function(px) {\n+ if ((px != null) && (this.icon != null)) {\n+ this.icon.moveTo(px);\n+ }\n+ this.lonlat = this.map.getLonLatFromLayerPx(px);\n+ },\n+\n+ /**\n+ * APIMethod: isDrawn\n * \n- * Returns: \n- * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position\n- * of the lower right tile for the given resolution.\n+ * Returns:\n+ * {Boolean} Whether or not the marker is drawn.\n */\n- getLowerRightTileCoord: function(res) {\n- var bottomRight = new OpenLayers.Geometry.Point(\n- this.maxExtent.right,\n- this.maxExtent.bottom);\n- return this.getContainingTileCoords(bottomRight, res);\n+ isDrawn: function() {\n+ var isDrawn = (this.icon && this.icon.isDrawn());\n+ return isDrawn;\n },\n \n- /** \n- * Method: getMaxExtentForResolution\n- * Since the max extent of a set of tiles can change from zoom level\n- * to zoom level, we need to be able to calculate that max extent \n- * for a given resolution.\n+ /**\n+ * Method: onScreen\n *\n- * Parameters: \n- * res - {Float} The resolution for which to compute the extent.\n+ * Returns:\n+ * {Boolean} Whether or not the marker is currently visible on screen.\n+ */\n+ onScreen: function() {\n+\n+ var onScreen = false;\n+ if (this.map) {\n+ var screenBounds = this.map.getExtent();\n+ onScreen = screenBounds.containsLonLat(this.lonlat);\n+ }\n+ return onScreen;\n+ },\n+\n+ /**\n+ * Method: inflate\n+ * Englarges the markers icon by the specified ratio.\n+ *\n+ * Parameters:\n+ * inflate - {float} the ratio to enlarge the marker by (passing 2\n+ * will double the size).\n+ */\n+ inflate: function(inflate) {\n+ if (this.icon) {\n+ this.icon.setSize({\n+ w: this.icon.size.w * inflate,\n+ h: this.icon.size.h * inflate\n+ });\n+ }\n+ },\n+\n+ /** \n+ * Method: setOpacity\n+ * Change the opacity of the marker by changin the opacity of \n+ * its icon\n * \n- * Returns: \n- * {<OpenLayers.Bounds>} The extent for this resolution\n+ * Parameters:\n+ * opacity - {float} Specified as fraction (0.4, etc)\n */\n- getMaxExtentForResolution: function(res) {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n+ setOpacity: function(opacity) {\n+ this.icon.setOpacity(opacity);\n+ },\n \n- var numTileCols = (end.x - start.x) + 1;\n- var numTileRows = (end.y - start.y) + 1;\n+ /**\n+ * Method: setUrl\n+ * Change URL of the Icon Image.\n+ * \n+ * url - {String} \n+ */\n+ setUrl: function(url) {\n+ this.icon.setUrl(url);\n+ },\n \n- var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);\n- var maxX = minX + (numTileCols * this.tileSize.w * res);\n+ /** \n+ * Method: display\n+ * Hide or show the icon\n+ * \n+ * display - {Boolean} \n+ */\n+ display: function(display) {\n+ this.icon.display(display);\n+ },\n \n- var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);\n- var minY = maxY - (numTileRows * this.tileSize.h * res);\n- return new OpenLayers.Bounds(minX, minY, maxX, maxY);\n+ CLASS_NAME: \"OpenLayers.Marker\"\n+});\n+\n+\n+/**\n+ * Function: defaultIcon\n+ * Creates a default <OpenLayers.Icon>.\n+ * \n+ * Returns:\n+ * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker\n+ */\n+OpenLayers.Marker.defaultIcon = function() {\n+ return new OpenLayers.Icon(OpenLayers.Util.getImageLocation(\"marker.png\"), {\n+ w: 21,\n+ h: 25\n+ }, {\n+ x: -10.5,\n+ y: -25\n+ });\n+};\n+\n+\n+/* ======================================================================\n+ OpenLayers/Format/WPSDescribeProcess.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSDescribeProcess\n+ * Read WPS DescribeProcess responses. \n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n+\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location\n+ */\n+ schemaLocation: \"http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd\",\n+\n+ /**\n+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"wps\",\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSDescribeProcess\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Parse a WPS DescribeProcess and return an object with its information.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object}\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var info = {};\n+ this.readNode(data, info);\n+ return info;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wps\": {\n+ \"ProcessDescriptions\": function(node, obj) {\n+ obj.processDescriptions = {};\n+ this.readChildNodes(node, obj.processDescriptions);\n+ },\n+ \"ProcessDescription\": function(node, processDescriptions) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var processDescription = {\n+ processVersion: processVersion,\n+ statusSupported: (node.getAttribute(\"statusSupported\") === \"true\"),\n+ storeSupported: (node.getAttribute(\"storeSupported\") === \"true\")\n+ };\n+ this.readChildNodes(node, processDescription);\n+ processDescriptions[processDescription.identifier] = processDescription;\n+ },\n+ \"DataInputs\": function(node, processDescription) {\n+ processDescription.dataInputs = [];\n+ this.readChildNodes(node, processDescription.dataInputs);\n+ },\n+ \"ProcessOutputs\": function(node, processDescription) {\n+ processDescription.processOutputs = [];\n+ this.readChildNodes(node, processDescription.processOutputs);\n+ },\n+ \"Output\": function(node, processOutputs) {\n+ var output = {};\n+ this.readChildNodes(node, output);\n+ processOutputs.push(output);\n+ },\n+ \"ComplexOutput\": function(node, output) {\n+ output.complexOutput = {};\n+ this.readChildNodes(node, output.complexOutput);\n+ },\n+ \"LiteralOutput\": function(node, output) {\n+ output.literalOutput = {};\n+ this.readChildNodes(node, output.literalOutput);\n+ },\n+ \"Input\": function(node, dataInputs) {\n+ var input = {\n+ maxOccurs: parseInt(node.getAttribute(\"maxOccurs\")),\n+ minOccurs: parseInt(node.getAttribute(\"minOccurs\"))\n+ };\n+ this.readChildNodes(node, input);\n+ dataInputs.push(input);\n+ },\n+ \"BoundingBoxData\": function(node, input) {\n+ input.boundingBoxData = {};\n+ this.readChildNodes(node, input.boundingBoxData);\n+ },\n+ \"CRS\": function(node, obj) {\n+ if (!obj.CRSs) {\n+ obj.CRSs = {};\n+ }\n+ obj.CRSs[this.getChildValue(node)] = true;\n+ },\n+ \"LiteralData\": function(node, input) {\n+ input.literalData = {};\n+ this.readChildNodes(node, input.literalData);\n+ },\n+ \"ComplexData\": function(node, input) {\n+ input.complexData = {};\n+ this.readChildNodes(node, input.complexData);\n+ },\n+ \"Default\": function(node, complexData) {\n+ complexData[\"default\"] = {};\n+ this.readChildNodes(node, complexData[\"default\"]);\n+ },\n+ \"Supported\": function(node, complexData) {\n+ complexData[\"supported\"] = {};\n+ this.readChildNodes(node, complexData[\"supported\"]);\n+ },\n+ \"Format\": function(node, obj) {\n+ var format = {};\n+ this.readChildNodes(node, format);\n+ if (!obj.formats) {\n+ obj.formats = {};\n+ }\n+ obj.formats[format.mimeType] = true;\n+ },\n+ \"MimeType\": function(node, format) {\n+ format.mimeType = this.getChildValue(node);\n+ }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSDescribeProcess\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/WPSClient.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/SingleFile.js\n+ */\n+\n+/**\n+ * @requires OpenLayers/Events.js\n+ * @requires OpenLayers/WPSProcess.js\n+ * @requires OpenLayers/Format/WPSDescribeProcess.js\n+ * @requires OpenLayers/Request.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.WPSClient\n+ * High level API for interaction with Web Processing Services (WPS).\n+ * An <OpenLayers.WPSClient> instance is used to create <OpenLayers.WPSProcess>\n+ * instances for servers known to the WPSClient. The WPSClient also caches\n+ * DescribeProcess responses to reduce the number of requests sent to servers\n+ * when processes are created.\n+ */\n+OpenLayers.WPSClient = OpenLayers.Class({\n+\n+ /**\n+ * Property: servers\n+ * {Object} Service metadata, keyed by a local identifier.\n+ *\n+ * Properties:\n+ * url - {String} the url of the server\n+ * version - {String} WPS version of the server\n+ * processDescription - {Object} Cache of raw DescribeProcess\n+ * responses, keyed by process identifier.\n+ */\n+ servers: null,\n+\n+ /**\n+ * Property: version\n+ * {String} The default WPS version to use if none is configured. Default\n+ * is '1.0.0'.\n+ */\n+ version: '1.0.0',\n+\n+ /**\n+ * Property: lazy\n+ * {Boolean} Should the DescribeProcess be deferred until a process is\n+ * fully configured? Default is false.\n+ */\n+ lazy: false,\n+\n+ /**\n+ * Property: events\n+ * {<OpenLayers.Events>}\n+ *\n+ * Supported event types:\n+ * describeprocess - Fires when the process description is available.\n+ * Listeners receive an object with a 'raw' property holding the raw\n+ * DescribeProcess response, and an 'identifier' property holding the\n+ * process identifier of the described process.\n+ */\n+ events: null,\n+\n+ /**\n+ * Constructor: OpenLayers.WPSClient\n+ *\n+ * Parameters:\n+ * options - {Object} Object whose properties will be set on the instance.\n+ *\n+ * Avaliable options:\n+ * servers - {Object} Mandatory. Service metadata, keyed by a local\n+ * identifier. Can either be a string with the service url or an\n+ * object literal with additional metadata:\n+ *\n+ * (code)\n+ * servers: {\n+ * local: '/geoserver/wps'\n+ * }, {\n+ * opengeo: {\n+ * url: 'http://demo.opengeo.org/geoserver/wps',\n+ * version: '1.0.0'\n+ * }\n+ * }\n+ * (end)\n+ *\n+ * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be\n+ * requested until a process is fully configured. Default is false.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Util.extend(this, options);\n+ this.events = new OpenLayers.Events(this);\n+ this.servers = {};\n+ for (var s in options.servers) {\n+ this.servers[s] = typeof options.servers[s] == 'string' ? {\n+ url: options.servers[s],\n+ version: this.version,\n+ processDescription: {}\n+ } : options.servers[s];\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: execute\n+ * Shortcut to execute a process with a single function call. This is\n+ * equivalent to using <getProcess> and then calling execute on the\n+ * process.\n+ *\n+ * Parameters:\n+ * options - {Object} Options for the execute operation.\n+ *\n+ * Available options:\n+ * server - {String} Mandatory. One of the local identifiers of the\n+ * configured servers.\n+ * process - {String} Mandatory. A process identifier known to the\n+ * server.\n+ * inputs - {Object} The inputs for the process, keyed by input identifier.\n+ * For spatial data inputs, the value of an input is usually an\n+ * <OpenLayers.Geometry>, an <OpenLayers.Feature.Vector> or an array of\n+ * geometries or features.\n+ * output - {String} The identifier of an output to parse. Optional. If not\n+ * provided, the first output will be parsed.\n+ * success - {Function} Callback to call when the process is complete.\n+ * This function is called with an outputs object as argument, which\n+ * will have a property with the identifier of the requested output\n+ * (e.g. 'result'). For processes that generate spatial output, the\n+ * value will either be a single <OpenLayers.Feature.Vector> or an\n+ * array of features.\n+ * scope - {Object} Optional scope for the success callback.\n+ */\n+ execute: function(options) {\n+ var process = this.getProcess(options.server, options.process);\n+ process.execute({\n+ inputs: options.inputs,\n+ success: options.success,\n+ scope: options.scope\n+ });\n+ },\n+\n+ /**\n+ * APIMethod: getProcess\n+ * Creates an <OpenLayers.WPSProcess>.\n+ *\n+ * Parameters:\n+ * serverID - {String} Local identifier from the servers that this instance\n+ * was constructed with.\n+ * processID - {String} Process identifier known to the server.\n+ *\n+ * Returns:\n+ * {<OpenLayers.WPSProcess>}\n+ */\n+ getProcess: function(serverID, processID) {\n+ var process = new OpenLayers.WPSProcess({\n+ client: this,\n+ server: serverID,\n+ identifier: processID\n+ });\n+ if (!this.lazy) {\n+ process.describe();\n+ }\n+ return process;\n+ },\n+\n+ /**\n+ * Method: describeProcess\n+ *\n+ * Parameters:\n+ * serverID - {String} Identifier of the server\n+ * processID - {String} Identifier of the requested process\n+ * callback - {Function} Callback to call when the description is available\n+ * scope - {Object} Optional execution scope for the callback function\n+ */\n+ describeProcess: function(serverID, processID, callback, scope) {\n+ var server = this.servers[serverID];\n+ if (!server.processDescription[processID]) {\n+ if (!(processID in server.processDescription)) {\n+ // set to null so we know a describeFeature request is pending\n+ server.processDescription[processID] = null;\n+ OpenLayers.Request.GET({\n+ url: server.url,\n+ params: {\n+ SERVICE: 'WPS',\n+ VERSION: server.version,\n+ REQUEST: 'DescribeProcess',\n+ IDENTIFIER: processID\n+ },\n+ success: function(response) {\n+ server.processDescription[processID] = response.responseText;\n+ this.events.triggerEvent('describeprocess', {\n+ identifier: processID,\n+ raw: response.responseText\n+ });\n+ },\n+ scope: this\n+ });\n+ } else {\n+ // pending request\n+ this.events.register('describeprocess', this, function describe(evt) {\n+ if (evt.identifier === processID) {\n+ this.events.unregister('describeprocess', this, describe);\n+ callback.call(scope, evt.raw);\n+ }\n+ });\n+ }\n+ } else {\n+ window.setTimeout(function() {\n+ callback.call(scope, server.processDescription[processID]);\n+ }, 0);\n+ }\n+ },\n+\n+ /**\n+ * Method: destroy\n+ */\n+ destroy: function() {\n+ this.events.destroy();\n+ this.events = null;\n+ this.servers = null;\n },\n \n+ CLASS_NAME: 'OpenLayers.WPSClient'\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/GPX.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Projection.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.GPX\n+ * Read/write GPX parser. Create a new instance with the \n+ * <OpenLayers.Format.GPX> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+\n /** \n- * APIMethod: clone \n- * Returns an exact clone of this OpenLayers.Layer.ArcGISCache\n+ * APIProperty: defaultDesc\n+ * {String} Default description for the waypoints/tracks in the case\n+ * where the feature has no \"description\" attribute.\n+ * Default is \"No description available\".\n+ */\n+ defaultDesc: \"No description available\",\n+\n+ /**\n+ * APIProperty: extractWaypoints\n+ * {Boolean} Extract waypoints from GPX. (default: true)\n+ */\n+ extractWaypoints: true,\n+\n+ /**\n+ * APIProperty: extractTracks\n+ * {Boolean} Extract tracks from GPX. (default: true)\n+ */\n+ extractTracks: true,\n+\n+ /**\n+ * APIProperty: extractRoutes\n+ * {Boolean} Extract routes from GPX. (default: true)\n+ */\n+ extractRoutes: true,\n+\n+ /**\n+ * APIProperty: extractAttributes\n+ * {Boolean} Extract feature attributes from GPX. (default: true)\n+ * NOTE: Attributes as part of extensions to the GPX standard may not\n+ * be extracted.\n+ */\n+ extractAttributes: true,\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ gpx: \"http://www.topografix.com/GPX/1/1\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location. Defaults to\n+ * \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\"\n+ */\n+ schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n+\n+ /**\n+ * APIProperty: creator\n+ * {String} The creator attribute to be added to the written GPX files.\n+ * Defaults to \"OpenLayers\"\n+ */\n+ creator: \"OpenLayers\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.GPX\n+ * Create a new parser for GPX.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ // GPX coordinates are always in longlat WGS84\n+ this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n+\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Return a list of features from a GPX doc\n+ *\n+ * Parameters:\n+ * doc - {Element} \n+ *\n+ * Returns:\n+ * Array({<OpenLayers.Feature.Vector>})\n+ */\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ }\n+ var features = [];\n+\n+ if (this.extractTracks) {\n+ var tracks = doc.getElementsByTagName(\"trk\");\n+ for (var i = 0, len = tracks.length; i < len; i++) {\n+ // Attributes are only in trk nodes, not trkseg nodes\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(tracks[i]);\n+ }\n+\n+ var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n+ for (var j = 0, seglen = segs.length; j < seglen; j++) {\n+ // We don't yet support extraction of trkpt attributes\n+ // All trksegs of a trk get that trk's attributes\n+ var track = this.extractSegment(segs[j], \"trkpt\");\n+ features.push(new OpenLayers.Feature.Vector(track, attrs));\n+ }\n+ }\n+ }\n+\n+ if (this.extractRoutes) {\n+ var routes = doc.getElementsByTagName(\"rte\");\n+ for (var k = 0, klen = routes.length; k < klen; k++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(routes[k]);\n+ }\n+ var route = this.extractSegment(routes[k], \"rtept\");\n+ features.push(new OpenLayers.Feature.Vector(route, attrs));\n+ }\n+ }\n+\n+ if (this.extractWaypoints) {\n+ var waypoints = doc.getElementsByTagName(\"wpt\");\n+ for (var l = 0, len = waypoints.length; l < len; l++) {\n+ var attrs = {};\n+ if (this.extractAttributes) {\n+ attrs = this.parseAttributes(waypoints[l]);\n+ }\n+ var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n+ features.push(new OpenLayers.Feature.Vector(wpt, attrs));\n+ }\n+ }\n+\n+ if (this.internalProjection && this.externalProjection) {\n+ for (var g = 0, featLength = features.length; g < featLength; g++) {\n+ features[g].geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ }\n+\n+ return features;\n+ },\n+\n+ /**\n+ * Method: extractSegment\n+ *\n+ * Parameters:\n+ * segment - {DOMElement} a trkseg or rte node to parse\n+ * segmentType - {String} nodeName of waypoints that form the line\n+ *\n+ * Returns:\n+ * {<OpenLayers.Geometry.LineString>} A linestring geometry\n+ */\n+ extractSegment: function(segment, segmentType) {\n+ var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n+ var point_features = [];\n+ for (var i = 0, len = points.length; i < len; i++) {\n+ point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")));\n+ }\n+ return new OpenLayers.Geometry.LineString(point_features);\n+ },\n+\n+ /**\n+ * Method: parseAttributes\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An attributes object.\n+ */\n+ parseAttributes: function(node) {\n+ // node is either a wpt, trk or rte\n+ // attributes are children of the form <attr>value</attr>\n+ var attributes = {};\n+ var attrNode = node.firstChild,\n+ value, name;\n+ while (attrNode) {\n+ if (attrNode.nodeType == 1 && attrNode.firstChild) {\n+ value = attrNode.firstChild;\n+ if (value.nodeType == 3 || value.nodeType == 4) {\n+ name = (attrNode.prefix) ?\n+ attrNode.nodeName.split(\":\")[1] :\n+ attrNode.nodeName;\n+ if (name != \"trkseg\" && name != \"rtept\") {\n+ attributes[name] = value.nodeValue;\n+ }\n+ }\n+ }\n+ attrNode = attrNode.nextSibling;\n+ }\n+ return attributes;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Accepts Feature Collection, and returns a string. \n * \n * Parameters: \n- * [obj] - {Object} optional object to assign the cloned instance to.\n- * \n- * Returns: \n- * {<OpenLayers.Layer.ArcGISCache>} clone of this instance \n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n+ * metadata - {Object} A key/value pairs object to build a metadata node to\n+ * add to the gpx. Supported keys are 'name', 'desc', 'author'.\n */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);\n+ write: function(features, metadata) {\n+ features = OpenLayers.Util.isArray(features) ?\n+ features : [features];\n+ var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n+ gpx.setAttribute(\"version\", \"1.1\");\n+ gpx.setAttribute(\"creator\", this.creator);\n+ this.setAttributes(gpx, {\n+ \"xsi:schemaLocation\": this.schemaLocation\n+ });\n+\n+ if (metadata && typeof metadata == 'object') {\n+ gpx.appendChild(this.buildMetadataNode(metadata));\n }\n- return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ gpx.appendChild(this.buildFeatureNode(features[i]));\n+ }\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);\n },\n \n /**\n- * Method: initGriddedTiles\n+ * Method: buildMetadataNode\n+ * Creates a \"metadata\" node.\n+ *\n+ * Returns:\n+ * {DOMElement}\n+ */\n+ buildMetadataNode: function(metadata) {\n+ var types = ['name', 'desc', 'author'],\n+ node = this.createElementNS(this.namespaces.gpx, 'metadata');\n+ for (var i = 0; i < types.length; i++) {\n+ var type = types[i];\n+ if (metadata[type]) {\n+ var n = this.createElementNS(this.namespaces.gpx, type);\n+ n.appendChild(this.createTextNode(metadata[type]));\n+ node.appendChild(n);\n+ }\n+ }\n+ return node;\n+ },\n+\n+ /**\n+ * Method: buildFeatureNode\n+ * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.\n * \n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ *\n+ * Returns:\n+ * {DOMElement} - The created node, either a 'wpt' or a 'trk'.\n */\n- initGriddedTiles: function(bounds) {\n- delete this._tileOrigin;\n- OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments);\n+ buildFeatureNode: function(feature) {\n+ var geometry = feature.geometry;\n+ geometry = geometry.clone();\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n+ }\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ var wpt = this.buildWptNode(geometry);\n+ this.appendAttributesNode(wpt, feature);\n+ return wpt;\n+ } else {\n+ var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n+ this.appendAttributesNode(trkNode, feature);\n+ var trkSegNodes = this.buildTrkSegNode(geometry);\n+ trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?\n+ trkSegNodes : [trkSegNodes];\n+ for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n+ trkNode.appendChild(trkSegNodes[i]);\n+ }\n+ return trkNode;\n+ }\n },\n \n /**\n- * Method: getMaxExtent\n- * Get this layer's maximum extent.\n+ * Method: buildTrkSegNode\n+ * Builds trkseg node(s) given a geometry\n+ *\n+ * Parameters:\n+ * trknode\n+ * geometry - {<OpenLayers.Geometry>}\n+ */\n+ buildTrkSegNode: function(geometry) {\n+ var node,\n+ i,\n+ len,\n+ point,\n+ nodes;\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n+ geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n+ node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ point = geometry.components[i];\n+ node.appendChild(this.buildTrkPtNode(point));\n+ }\n+ return node;\n+ } else {\n+ nodes = [];\n+ for (i = 0, len = geometry.components.length; i < len; i++) {\n+ nodes.push(this.buildTrkSegNode(geometry.components[i]));\n+ }\n+ return nodes;\n+ }\n+ },\n+\n+ /**\n+ * Method: buildTrkPtNode\n+ * Builds a trkpt node given a point \n+ *\n+ * Parameters:\n+ * point - {<OpenLayers.Geometry.Point>}\n *\n * Returns:\n- * {<OpenLayers.Bounds>}\n+ * {DOMElement} A trkpt node\n */\n- getMaxExtent: function() {\n- var resolution = this.map.getResolution();\n- return this.maxExtent = this.getMaxExtentForResolution(resolution);\n+ buildTrkPtNode: function(point) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n+ node.setAttribute(\"lon\", point.x);\n+ node.setAttribute(\"lat\", point.y);\n+ return node;\n },\n \n /**\n- * Method: getTileOrigin\n- * Determine the origin for aligning the grid of tiles. \n- * The origin will be derived from the layer's <maxExtent> property. \n+ * Method: buildWptNode\n+ * Builds a wpt node given a point\n+ *\n+ * Parameters:\n+ * geometry - {<OpenLayers.Geometry.Point>}\n *\n * Returns:\n- * {<OpenLayers.LonLat>} The tile origin.\n+ * {DOMElement} A wpt node\n */\n- getTileOrigin: function() {\n- if (!this._tileOrigin) {\n- var extent = this.getMaxExtent();\n- this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom);\n+ buildWptNode: function(geometry) {\n+ var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n+ node.setAttribute(\"lon\", geometry.x);\n+ node.setAttribute(\"lat\", geometry.y);\n+ return node;\n+ },\n+\n+ /**\n+ * Method: appendAttributesNode\n+ * Adds some attributes node.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} the node to append the attribute nodes to.\n+ * feature - {<OpenLayers.Feature.Vector>}\n+ */\n+ appendAttributesNode: function(node, feature) {\n+ var name = this.createElementNS(this.namespaces.gpx, 'name');\n+ name.appendChild(this.createTextNode(\n+ feature.attributes.name || feature.id));\n+ node.appendChild(name);\n+ var desc = this.createElementNS(this.namespaces.gpx, 'desc');\n+ desc.appendChild(this.createTextNode(\n+ feature.attributes.description || this.defaultDesc));\n+ node.appendChild(desc);\n+ // TBD - deal with remaining (non name/description) attributes.\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.GPX\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/EncodedPolyline.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Feature/Vector.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.EncodedPolyline\n+ * Class for reading and writing encoded polylines. Create a new instance\n+ * with the <OpenLayers.Format.EncodedPolyline> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * APIProperty: geometryType\n+ * {String} Geometry type to output. One of: linestring (default),\n+ * linearring, point, multipoint or polygon. If the geometryType is\n+ * point, only the first point of the string is returned.\n+ */\n+ geometryType: \"linestring\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.EncodedPolyline\n+ * Create a new parser for encoded polylines\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Deserialize an encoded polyline string and return a vector feature.\n+ *\n+ * Parameters:\n+ * encoded - {String} An encoded polyline string\n+ *\n+ * Returns:\n+ * {<OpenLayers.Feature.Vector>} A vector feature with a linestring.\n+ */\n+ read: function(encoded) {\n+ var geomType;\n+ if (this.geometryType == \"linestring\")\n+ geomType = OpenLayers.Geometry.LineString;\n+ else if (this.geometryType == \"linearring\")\n+ geomType = OpenLayers.Geometry.LinearRing;\n+ else if (this.geometryType == \"multipoint\")\n+ geomType = OpenLayers.Geometry.MultiPoint;\n+ else if (this.geometryType != \"point\" && this.geometryType != \"polygon\")\n+ return null;\n+\n+ var flatPoints = this.decodeDeltas(encoded, 2);\n+ var flatPointsLength = flatPoints.length;\n+\n+ var pointGeometries = [];\n+ for (var i = 0; i + 1 < flatPointsLength;) {\n+ var y = flatPoints[i++],\n+ x = flatPoints[i++];\n+ pointGeometries.push(new OpenLayers.Geometry.Point(x, y));\n }\n- return this._tileOrigin;\n+\n+\n+ if (this.geometryType == \"point\")\n+ return new OpenLayers.Feature.Vector(\n+ pointGeometries[0]\n+ );\n+\n+ if (this.geometryType == \"polygon\")\n+ return new OpenLayers.Feature.Vector(\n+ new OpenLayers.Geometry.Polygon([\n+ new OpenLayers.Geometry.LinearRing(pointGeometries)\n+ ])\n+ );\n+\n+ return new OpenLayers.Feature.Vector(\n+ new geomType(pointGeometries)\n+ );\n },\n \n /**\n- * Method: getURL\n- * Determine the URL for a tile given the tile bounds. This is should support\n- * urls that access tiles through an ArcGIS Server MapServer or directly through\n- * the hex folder structure using HTTP. Just be sure to set the useArcGISServer\n- * property appropriately! This is basically the same as \n- * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing,\n- * and tile rounding.\n+ * APIMethod: decode\n+ * Deserialize an encoded string and return an array of n-dimensional\n+ * points.\n *\n * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n+ * encoded - {String} An encoded string\n+ * dims - {int} The dimension of the points that are returned\n *\n * Returns:\n- * {String} The URL for a tile based on given bounds.\n+ * {Array(Array(int))} An array containing n-dimensional arrays of\n+ * coordinates.\n */\n- getURL: function(bounds) {\n- var res = this.getResolution();\n+ decode: function(encoded, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = this.decodeDeltas(encoded, dims, factor);\n+ var flatPointsLength = flatPoints.length;\n \n- // tile center\n- var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2));\n- var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2));\n+ var points = [];\n+ for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n+ var point = [];\n \n- var center = bounds.getCenterLonLat();\n- var point = {\n- x: center.lon,\n- y: center.lat\n- };\n- var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))));\n- var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))));\n- var z = this.map.getZoom();\n+ for (var dim = 0; dim < dims; ++dim) {\n+ point.push(flatPoints[i++])\n+ }\n \n- // this prevents us from getting pink tiles (non-existant tiles)\n- if (this.lods) {\n- var lod = this.lods[this.map.getZoom()];\n- if ((x < lod.startTileCol || x > lod.endTileCol) ||\n- (y < lod.startTileRow || y > lod.endTileRow)) {\n- return null;\n+ points.push(point);\n+ }\n+\n+ return points;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Serialize a feature or array of features into a WKT string.\n+ *\n+ * Parameters:\n+ * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of\n+ * features\n+ *\n+ * Returns:\n+ * {String} The WKT string representation of the input geometries\n+ */\n+ write: function(features) {\n+ var feature;\n+ if (features.constructor == Array)\n+ feature = features[0];\n+ else\n+ feature = features;\n+\n+ var geometry = feature.geometry;\n+ var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n+\n+ var pointGeometries;\n+ if (type == \"point\")\n+ pointGeometries = new Array(geometry);\n+ else if (type == \"linestring\" ||\n+ type == \"linearring\" ||\n+ type == \"multipoint\")\n+ pointGeometries = geometry.components;\n+ else if (type == \"polygon\")\n+ pointGeometries = geometry.components[0].components;\n+ else\n+ return null;\n+\n+ var flatPoints = [];\n+\n+ var pointGeometriesLength = pointGeometries.length;\n+ for (var i = 0; i < pointGeometriesLength; ++i) {\n+ var pointGeometry = pointGeometries[i];\n+ flatPoints.push(pointGeometry.y);\n+ flatPoints.push(pointGeometry.x);\n+ }\n+\n+ return this.encodeDeltas(flatPoints, 2);\n+ },\n+\n+ /**\n+ * APIMethod: encode\n+ * Serialize an array of n-dimensional points and return an encoded string\n+ *\n+ * Parameters:\n+ * points - {Array(Array(int))} An array containing n-dimensional\n+ * arrays of coordinates\n+ * dims - {int} The dimension of the points that should be read\n+ *\n+ * Returns:\n+ * {String} An encoded string\n+ */\n+ encode: function(points, dims, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var flatPoints = [];\n+\n+ var pointsLength = points.length;\n+ for (var i = 0; i < pointsLength; ++i) {\n+ var point = points[i];\n+\n+ for (var dim = 0; dim < dims; ++dim) {\n+ flatPoints.push(point[dim]);\n }\n- } else {\n- var start = this.getUpperLeftTileCoord(res);\n- var end = this.getLowerRightTileCoord(res);\n- if ((x < start.x || x >= end.x) ||\n- (y < start.y || y >= end.y)) {\n- return null;\n+ }\n+\n+ return this.encodeDeltas(flatPoints, dims, factor);\n+ },\n+\n+ /**\n+ * APIMethod: encodeDeltas\n+ * Encode a list of n-dimensional points and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of n-dimensional points.\n+ * dimension - {number} The dimension of the points in the list.\n+ * opt_factor - {number=} The factor by which the numbers will be\n+ * multiplied. The remaining decimal places will get rounded away.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeDeltas: function(numbers, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n+\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0;\n+ }\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ var num = numbers[i];\n+ var delta = num - lastNumbers[d];\n+ lastNumbers[d] = num;\n+\n+ numbers[i] = delta;\n }\n }\n \n- // Construct the url string\n- var url = this.url;\n- var s = '' + x + y + z;\n+ return this.encodeFloats(numbers, factor);\n+ },\n \n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(s, url);\n+\n+ /**\n+ * APIMethod: decodeDeltas\n+ * Decode a list of n-dimensional points from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ * dimension - {number} The dimension of the points in the encoded string.\n+ * opt_factor - {number=} The factor by which the resulting numbers will\n+ * be divided.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of n-dimensional points.\n+ */\n+ decodeDeltas: function(encoded, dimension, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+ var d;\n+\n+ var lastNumbers = new Array(dimension);\n+ for (d = 0; d < dimension; ++d) {\n+ lastNumbers[d] = 0;\n }\n \n- // Accessing tiles through ArcGIS Server uses a different path\n- // structure than direct access via the folder structure.\n- if (this.useArcGISServer) {\n- // AGS MapServers have pretty url access to tiles\n- url = url + '/tile/${z}/${y}/${x}';\n+ var numbers = this.decodeFloats(encoded, factor);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength;) {\n+ for (d = 0; d < dimension; ++d, ++i) {\n+ lastNumbers[d] += numbers[i];\n+\n+ numbers[i] = lastNumbers[d];\n+ }\n+ }\n+\n+ return numbers;\n+ },\n+\n+\n+ /**\n+ * APIMethod: encodeFloats\n+ * Encode a list of floating point numbers and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of floating point numbers.\n+ * opt_factor - {number=} The factor by which the numbers will be\n+ * multiplied. The remaining decimal places will get rounded away.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeFloats: function(numbers, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] = Math.round(numbers[i] * factor);\n+ }\n+\n+ return this.encodeSignedIntegers(numbers);\n+ },\n+\n+\n+ /**\n+ * APIMethod: decodeFloats\n+ * Decode a list of floating point numbers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ * opt_factor - {number=} The factor by which the result will be divided.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of floating point numbers.\n+ */\n+ decodeFloats: function(encoded, opt_factor) {\n+ var factor = opt_factor || 1e5;\n+\n+ var numbers = this.decodeSignedIntegers(encoded);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ numbers[i] /= factor;\n+ }\n+\n+ return numbers;\n+ },\n+\n+\n+ /**\n+ * APIMethod: encodeSignedIntegers\n+ * Encode a list of signed integers and return an encoded string\n+ *\n+ * Attention: This function will modify the passed array!\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of signed integers.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeSignedIntegers: function(numbers) {\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~(signedNum);\n+ }\n+\n+ numbers[i] = signedNum;\n+ }\n+\n+ return this.encodeUnsignedIntegers(numbers);\n+ },\n+\n+\n+ /**\n+ * APIMethod: decodeSignedIntegers\n+ * Decode a list of signed integers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of signed integers.\n+ */\n+ decodeSignedIntegers: function(encoded) {\n+ var numbers = this.decodeUnsignedIntegers(encoded);\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ var num = numbers[i];\n+ numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);\n+ }\n+\n+ return numbers;\n+ },\n+\n+\n+ /**\n+ * APIMethod: encodeUnsignedIntegers\n+ * Encode a list of unsigned integers and return an encoded string\n+ *\n+ * Parameters:\n+ * numbers - {Array.<number>} A list of unsigned integers.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeUnsignedIntegers: function(numbers) {\n+ var encoded = '';\n+\n+ var numbersLength = numbers.length;\n+ for (var i = 0; i < numbersLength; ++i) {\n+ encoded += this.encodeUnsignedInteger(numbers[i]);\n+ }\n+\n+ return encoded;\n+ },\n+\n+\n+ /**\n+ * APIMethod: decodeUnsignedIntegers\n+ * Decode a list of unsigned integers from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {Array.<number>} A list of unsigned integers.\n+ */\n+ decodeUnsignedIntegers: function(encoded) {\n+ var numbers = [];\n+\n+ var current = 0;\n+ var shift = 0;\n+\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n+\n+ current |= (b & 0x1f) << shift;\n+\n+ if (b < 0x20) {\n+ numbers.push(current);\n+ current = 0;\n+ shift = 0;\n+ } else {\n+ shift += 5;\n+ }\n+ }\n+\n+ return numbers;\n+ },\n+\n+\n+ /**\n+ * Method: encodeFloat\n+ * Encode one single floating point number and return an encoded string\n+ *\n+ * Parameters:\n+ * num - {number} Floating point number that should be encoded.\n+ * opt_factor - {number=} The factor by which num will be multiplied.\n+ * The remaining decimal places will get rounded away.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeFloat: function(num, opt_factor) {\n+ num = Math.round(num * (opt_factor || 1e5));\n+ return this.encodeSignedInteger(num);\n+ },\n+\n+\n+ /**\n+ * Method: decodeFloat\n+ * Decode one single floating point number from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ * opt_factor - {number=} The factor by which the result will be divided.\n+ *\n+ * Returns:\n+ * {number} The decoded floating point number.\n+ */\n+ decodeFloat: function(encoded, opt_factor) {\n+ var result = this.decodeSignedInteger(encoded);\n+ return result / (opt_factor || 1e5);\n+ },\n+\n+\n+ /**\n+ * Method: encodeSignedInteger\n+ * Encode one single signed integer and return an encoded string\n+ *\n+ * Parameters:\n+ * num - {number} Signed integer that should be encoded.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeSignedInteger: function(num) {\n+ var signedNum = num << 1;\n+ if (num < 0) {\n+ signedNum = ~(signedNum);\n+ }\n+\n+ return this.encodeUnsignedInteger(signedNum);\n+ },\n+\n+\n+ /**\n+ * Method: decodeSignedInteger\n+ * Decode one single signed integer from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {number} The decoded signed integer.\n+ */\n+ decodeSignedInteger: function(encoded) {\n+ var result = this.decodeUnsignedInteger(encoded);\n+ return ((result & 1) ? ~(result >> 1) : (result >> 1));\n+ },\n+\n+\n+ /**\n+ * Method: encodeUnsignedInteger\n+ * Encode one single unsigned integer and return an encoded string\n+ *\n+ * Parameters:\n+ * num - {number} Unsigned integer that should be encoded.\n+ *\n+ * Returns:\n+ * {string} The encoded string.\n+ */\n+ encodeUnsignedInteger: function(num) {\n+ var value, encoded = '';\n+ while (num >= 0x20) {\n+ value = (0x20 | (num & 0x1f)) + 63;\n+ encoded += (String.fromCharCode(value));\n+ num >>= 5;\n+ }\n+ value = num + 63;\n+ encoded += (String.fromCharCode(value));\n+ return encoded;\n+ },\n+\n+\n+ /**\n+ * Method: decodeUnsignedInteger\n+ * Decode one single unsigned integer from an encoded string\n+ *\n+ * Parameters:\n+ * encoded - {string} An encoded string.\n+ *\n+ * Returns:\n+ * {number} The decoded unsigned integer.\n+ */\n+ decodeUnsignedInteger: function(encoded) {\n+ var result = 0;\n+ var shift = 0;\n+\n+ var encodedLength = encoded.length;\n+ for (var i = 0; i < encodedLength; ++i) {\n+ var b = encoded.charCodeAt(i) - 63;\n+\n+ result |= (b & 0x1f) << shift;\n+\n+ if (b < 0x20)\n+ break;\n+\n+ shift += 5;\n+ }\n+\n+ return result;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/Context.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.Context\n+ * Base class for both Format.WMC and Format.OWSContext\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * Property: layerOptions\n+ * {Object} Default options for layers created by the parser. These\n+ * options are overridden by the options which are read from the\n+ * capabilities document.\n+ */\n+ layerOptions: null,\n+\n+ /**\n+ * Property: layerParams\n+ * {Object} Default parameters for layers created by the parser. This\n+ * can be used e.g. to override DEFAULT_PARAMS for \n+ * OpenLayers.Layer.WMS.\n+ */\n+ layerParams: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.Context\n+ * Create a new parser for Context documents.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read Context data from a string, and return an object with map\n+ * properties and a list of layers.\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ * options - {Object} The options object must contain a map property. If\n+ * the map property is a string, it must be the id of a dom element\n+ * where the new map will be placed. If the map property is an\n+ * <OpenLayers.Map>, the layers from the context document will be added\n+ * to the map.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} A map based on the context.\n+ */\n+ read: function(data, options) {\n+ var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,\n+ arguments);\n+ var map;\n+ if (options && options.map) {\n+ this.context = context;\n+ if (options.map instanceof OpenLayers.Map) {\n+ map = this.mergeContextToMap(context, options.map);\n+ } else {\n+ var mapOptions = options.map;\n+ if (OpenLayers.Util.isElement(mapOptions) ||\n+ typeof mapOptions == \"string\") {\n+ // we assume mapOptions references a div\n+ // element\n+ mapOptions = {\n+ div: mapOptions\n+ };\n+ }\n+ map = this.contextToMap(context, mapOptions);\n+ }\n } else {\n- // The tile images are stored using hex values on disk.\n- x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16);\n- y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16);\n- z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10);\n- url = url + '/${z}/${y}/${x}.' + this.type;\n+ // not documented as part of the API, provided as a non-API option\n+ map = context;\n }\n+ return map;\n+ },\n \n- // Write the values into our formatted url\n- url = OpenLayers.String.format(url, {\n- 'x': x,\n- 'y': y,\n- 'z': z\n+ /**\n+ * Method: getLayerFromContext\n+ * Create a WMS layer from a layerContext object.\n+ *\n+ * Parameters:\n+ * layerContext - {Object} An object representing a WMS layer.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Layer.WMS>} A WMS layer.\n+ */\n+ getLayerFromContext: function(layerContext) {\n+ var i, len;\n+ // fill initial options object from layerContext\n+ var options = {\n+ queryable: layerContext.queryable, //keep queryable for api compatibility\n+ visibility: layerContext.visibility,\n+ maxExtent: layerContext.maxExtent,\n+ metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n+ styles: layerContext.styles,\n+ formats: layerContext.formats,\n+ \"abstract\": layerContext[\"abstract\"],\n+ dataURL: layerContext.dataURL\n+ }),\n+ numZoomLevels: layerContext.numZoomLevels,\n+ units: layerContext.units,\n+ isBaseLayer: layerContext.isBaseLayer,\n+ opacity: layerContext.opacity,\n+ displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n+ singleTile: layerContext.singleTile,\n+ tileSize: (layerContext.tileSize) ?\n+ new OpenLayers.Size(\n+ layerContext.tileSize.width,\n+ layerContext.tileSize.height\n+ ) : undefined,\n+ minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n+ maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n+ srs: layerContext.srs,\n+ dimensions: layerContext.dimensions,\n+ metadataURL: layerContext.metadataURL\n+ };\n+ if (this.layerOptions) {\n+ OpenLayers.Util.applyDefaults(options, this.layerOptions);\n+ }\n+\n+ var params = {\n+ layers: layerContext.name,\n+ transparent: layerContext.transparent,\n+ version: layerContext.version\n+ };\n+ if (layerContext.formats && layerContext.formats.length > 0) {\n+ // set default value for params if current attribute is not positionned\n+ params.format = layerContext.formats[0].value;\n+ for (i = 0, len = layerContext.formats.length; i < len; i++) {\n+ var format = layerContext.formats[i];\n+ if (format.current == true) {\n+ params.format = format.value;\n+ break;\n+ }\n+ }\n+ }\n+ if (layerContext.styles && layerContext.styles.length > 0) {\n+ for (i = 0, len = layerContext.styles.length; i < len; i++) {\n+ var style = layerContext.styles[i];\n+ if (style.current == true) {\n+ // three style types to consider\n+ // 1) linked SLD\n+ // 2) inline SLD\n+ // 3) named style\n+ if (style.href) {\n+ params.sld = style.href;\n+ } else if (style.body) {\n+ params.sld_body = style.body;\n+ } else {\n+ params.styles = style.name;\n+ }\n+ break;\n+ }\n+ }\n+ }\n+ if (this.layerParams) {\n+ OpenLayers.Util.applyDefaults(params, this.layerParams);\n+ }\n+\n+ var layer = null;\n+ var service = layerContext.service;\n+ if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n+ options.strategies = [new OpenLayers.Strategy.BBOX()];\n+ options.protocol = new OpenLayers.Protocol.WFS({\n+ url: layerContext.url,\n+ // since we do not know featureNS, let the protocol\n+ // determine it automagically using featurePrefix\n+ featurePrefix: layerContext.name.split(\":\")[0],\n+ featureType: layerContext.name.split(\":\").pop()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n+ // use a vector layer with an HTTP Protcol and a Fixed strategy\n+ options.strategies = [new OpenLayers.Strategy.Fixed()];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.KML()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n+ // use a vector layer with a HTTP Protocol and a Fixed strategy\n+ options.strategies = [new OpenLayers.Strategy.Fixed()];\n+ options.protocol = new OpenLayers.Protocol.HTTP({\n+ url: layerContext.url,\n+ format: new OpenLayers.Format.GML()\n+ });\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ } else if (layerContext.features) {\n+ // inline GML or KML features\n+ layer = new OpenLayers.Layer.Vector(\n+ layerContext.title || layerContext.name,\n+ options\n+ );\n+ layer.addFeatures(layerContext.features);\n+ } else if (layerContext.categoryLayer !== true) {\n+ layer = new OpenLayers.Layer.WMS(\n+ layerContext.title || layerContext.name,\n+ layerContext.url,\n+ params,\n+ options\n+ );\n+ }\n+ return layer;\n+ },\n+\n+ /**\n+ * Method: getLayersFromContext\n+ * Create an array of layers from an array of layerContext objects.\n+ *\n+ * Parameters:\n+ * layersContext - {Array(Object)} An array of objects representing layers.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Layer>)} An array of layers.\n+ */\n+ getLayersFromContext: function(layersContext) {\n+ var layers = [];\n+ for (var i = 0, len = layersContext.length; i < len; i++) {\n+ var layer = this.getLayerFromContext(layersContext[i]);\n+ if (layer !== null) {\n+ layers.push(layer);\n+ }\n+ }\n+ return layers;\n+ },\n+\n+ /**\n+ * Method: contextToMap\n+ * Create a map given a context object.\n+ *\n+ * Parameters:\n+ * context - {Object} The context object.\n+ * options - {Object} Default map options.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} A map based on the context object.\n+ */\n+ contextToMap: function(context, options) {\n+ options = OpenLayers.Util.applyDefaults({\n+ maxExtent: context.maxExtent,\n+ projection: context.projection,\n+ units: context.units\n+ }, options);\n+\n+ if (options.maxExtent) {\n+ options.maxResolution =\n+ options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;\n+ }\n+\n+ var metadata = {\n+ contactInformation: context.contactInformation,\n+ \"abstract\": context[\"abstract\"],\n+ keywords: context.keywords,\n+ logo: context.logo,\n+ descriptionURL: context.descriptionURL\n+ };\n+\n+ options.metadata = metadata;\n+\n+ var map = new OpenLayers.Map(options);\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ map.setCenter(\n+ context.bounds.getCenterLonLat(),\n+ map.getZoomForExtent(context.bounds, true)\n+ );\n+ return map;\n+ },\n+\n+ /**\n+ * Method: mergeContextToMap\n+ * Add layers from a context object to a map.\n+ *\n+ * Parameters:\n+ * context - {Object} The context object.\n+ * map - {<OpenLayers.Map>} The map.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Map>} The same map with layers added.\n+ */\n+ mergeContextToMap: function(context, map) {\n+ map.addLayers(this.getLayersFromContext(context.layersContext));\n+ return map;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Write a context document given a map.\n+ *\n+ * Parameters:\n+ * obj - {<OpenLayers.Map> | Object} A map or context object.\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} A context document string.\n+ */\n+ write: function(obj, options) {\n+ obj = this.toContext(obj);\n+ return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,\n+ arguments);\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.Context\"\n+});\n+\n+/**\n+ * Constant: OpenLayers.Format.Context.serviceTypes\n+ * Enumeration for service types\n+ */\n+OpenLayers.Format.Context.serviceTypes = {\n+ \"WMS\": \"urn:ogc:serviceType:WMS\",\n+ \"WFS\": \"urn:ogc:serviceType:WFS\",\n+ \"WCS\": \"urn:ogc:serviceType:WCS\",\n+ \"GML\": \"urn:ogc:serviceType:GML\",\n+ \"SLD\": \"urn:ogc:serviceType:SLD\",\n+ \"FES\": \"urn:ogc:serviceType:FES\",\n+ \"KML\": \"urn:ogc:serviceType:KML\"\n+};\n+/* ======================================================================\n+ OpenLayers/Format/OWSContext.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/Context.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.OWSContext\n+ * Read and write OWS Context documents. OWS Context documents are a \n+ * preliminary OGC (Open Geospatial Consortium) standard for storing the \n+ * state of a web mapping application. In a way it is the successor to\n+ * Web Map Context (WMC), since it is more generic and more types of layers\n+ * can be stored. Also, nesting of layers is supported since version 0.3.1.\n+ * For more information see: http://www.ogcnetwork.net/context\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.Context>\n+ */\n+OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"0.3.1\".\n+ */\n+ defaultVersion: \"0.3.1\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.OWSContext\n+ * Create a new parser for OWS Context documents.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Method: getVersion\n+ * Returns the version to use. Subclasses can override this function\n+ * if a different version detection is needed.\n+ *\n+ * Parameters:\n+ * root - {DOMElement}\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} The version to use.\n+ */\n+ getVersion: function(root, options) {\n+ var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(\n+ this, arguments);\n+ // 0.3.1 is backwards compatible with 0.3.0\n+ if (version === \"0.3.0\") {\n+ version = this.defaultVersion;\n+ }\n+ return version;\n+ },\n+\n+ /**\n+ * Method: toContext\n+ * Create a context object free from layer given a map or a\n+ * context object.\n+ *\n+ * Parameters:\n+ * obj - {<OpenLayers.Map> | Object} The map or context.\n+ *\n+ * Returns:\n+ * {Object} A context object.\n+ */\n+ toContext: function(obj) {\n+ var context = {};\n+ if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n+ context.bounds = obj.getExtent();\n+ context.maxExtent = obj.maxExtent;\n+ context.projection = obj.projection;\n+ context.size = obj.getSize();\n+ context.layers = obj.layers;\n+ }\n+ return context;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/XLS.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.XLS\n+ * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS>\n+ * constructor. Currently only implemented for Location Utility Services, more\n+ * specifically only for Geocoding. No support for Reverse Geocoding as yet.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * APIProperty: stringifyOutput\n+ * {Boolean} If true, write will return a string otherwise a DOMElement.\n+ * Default is true.\n+ */\n+ stringifyOutput: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.XLS\n+ * Create a new parser for XLS.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: write\n+ * Write out an XLS request.\n+ *\n+ * Parameters:\n+ * request - {Object} An object representing the LUS request.\n+ * options - {Object} Optional configuration object.\n+ *\n+ * Returns:\n+ * {String} An XLS document string.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read an XLS doc and return an object representing the result.\n+ *\n+ * Parameters:\n+ * data - {String | DOMElement} Data to read.\n+ * options - {Object} Options for the reader.\n+ *\n+ * Returns:\n+ * {Object} An object representing the GeocodeResponse.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.XLS\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/Text.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.Text\n+ * Read Text format. Create a new instance with the <OpenLayers.Format.Text>\n+ * constructor. This reads text which is formatted like CSV text, using\n+ * tabs as the seperator by default. It provides parsing of data originally\n+ * used in the MapViewerService, described on the wiki. This Format is used\n+ * by the <OpenLayers.Layer.Text> class.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * APIProperty: defaultStyle\n+ * defaultStyle allows one to control the default styling of the features.\n+ * It should be a symbolizer hash. By default, this is set to match the\n+ * Layer.Text behavior, which is to use the default OpenLayers Icon.\n+ */\n+ defaultStyle: null,\n+\n+ /**\n+ * APIProperty: extractStyles\n+ * set to true to extract styles from the TSV files, using information\n+ * from the image or icon, iconSize and iconOffset fields. This will result\n+ * in features with a symbolizer (style) property set, using the\n+ * default symbolizer specified in <defaultStyle>. Set to false if you\n+ * wish to use a styleMap or OpenLayers.Style options to style your\n+ * layer instead.\n+ */\n+ extractStyles: true,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.Text\n+ * Create a new parser for TSV Text.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ options = options || {};\n+\n+ if (options.extractStyles !== false) {\n+ options.defaultStyle = {\n+ 'externalGraphic': OpenLayers.Util.getImageLocation(\"marker.png\"),\n+ 'graphicWidth': 21,\n+ 'graphicHeight': 25,\n+ 'graphicXOffset': -10.5,\n+ 'graphicYOffset': -12.5\n+ };\n+ }\n+\n+ OpenLayers.Format.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Return a list of features from a Tab Seperated Values text string.\n+ * \n+ * Parameters:\n+ * text - {String} \n+ *\n+ * Returns:\n+ * Array({<OpenLayers.Feature.Vector>})\n+ */\n+ read: function(text) {\n+ var lines = text.split('\\n');\n+ var columns;\n+ var features = [];\n+ // length - 1 to allow for trailing new line\n+ for (var lcv = 0; lcv < (lines.length - 1); lcv++) {\n+ var currLine = lines[lcv].replace(/^\\s*/, '').replace(/\\s*$/, '');\n+\n+ if (currLine.charAt(0) != '#') {\n+ /* not a comment */\n+\n+ if (!columns) {\n+ //First line is columns\n+ columns = currLine.split('\\t');\n+ } else {\n+ var vals = currLine.split('\\t');\n+ var geometry = new OpenLayers.Geometry.Point(0, 0);\n+ var attributes = {};\n+ var style = this.defaultStyle ?\n+ OpenLayers.Util.applyDefaults({}, this.defaultStyle) :\n+ null;\n+ var icon, iconSize, iconOffset, overflow;\n+ var set = false;\n+ for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n+ if (vals[valIndex]) {\n+ if (columns[valIndex] == 'point') {\n+ var coords = vals[valIndex].split(',');\n+ geometry.y = parseFloat(coords[0]);\n+ geometry.x = parseFloat(coords[1]);\n+ set = true;\n+ } else if (columns[valIndex] == 'lat') {\n+ geometry.y = parseFloat(vals[valIndex]);\n+ set = true;\n+ } else if (columns[valIndex] == 'lon') {\n+ geometry.x = parseFloat(vals[valIndex]);\n+ set = true;\n+ } else if (columns[valIndex] == 'title')\n+ attributes['title'] = vals[valIndex];\n+ else if (columns[valIndex] == 'image' ||\n+ columns[valIndex] == 'icon' && style) {\n+ style['externalGraphic'] = vals[valIndex];\n+ } else if (columns[valIndex] == 'iconSize' && style) {\n+ var size = vals[valIndex].split(',');\n+ style['graphicWidth'] = parseFloat(size[0]);\n+ style['graphicHeight'] = parseFloat(size[1]);\n+ } else if (columns[valIndex] == 'iconOffset' && style) {\n+ var offset = vals[valIndex].split(',');\n+ style['graphicXOffset'] = parseFloat(offset[0]);\n+ style['graphicYOffset'] = parseFloat(offset[1]);\n+ } else if (columns[valIndex] == 'description') {\n+ attributes['description'] = vals[valIndex];\n+ } else if (columns[valIndex] == 'overflow') {\n+ attributes['overflow'] = vals[valIndex];\n+ } else {\n+ // For StyleMap filtering, allow additional\n+ // columns to be stored as attributes.\n+ attributes[columns[valIndex]] = vals[valIndex];\n+ }\n+ }\n+ }\n+ if (set) {\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n+ }\n+ var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n+ features.push(feature);\n+ }\n+ }\n+ }\n+ }\n+ return features;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.Text\"\n+});\n+/* ======================================================================\n+ OpenLayers/Format/SOSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.SOSCapabilities\n+ * Read SOS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n+ */\n+ defaultVersion: \"1.0.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.SOSCapabilities\n+ * Create a new parser for SOS Capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return information about\n+ * the service (offering and observedProperty mostly).\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Info about the SOS\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.SOSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/QueryStringFilter.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Console.js\n+ * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Filter/Spatial.js\n+ * @requires OpenLayers/Filter/Comparison.js\n+ * @requires OpenLayers/Filter/Logical.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.QueryStringFilter\n+ * Parser for reading a query string and creating a simple filter.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format>\n+ */\n+OpenLayers.Format.QueryStringFilter = (function() {\n+\n+ /** \n+ * Map the OpenLayers.Filter.Comparison types to the operation strings of \n+ * the protocol.\n+ */\n+ var cmpToStr = {};\n+ cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n+ cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n+ cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n+ cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n+\n+ /**\n+ * Function: regex2value\n+ * Convert the value from a regular expression string to a LIKE/ILIKE\n+ * string known to the web service.\n+ *\n+ * Parameters:\n+ * value - {String} The regex string.\n+ *\n+ * Returns:\n+ * {String} The converted string.\n+ */\n+ function regex2value(value) {\n+\n+ // highly sensitive!! Do not change this without running the\n+ // Protocol/HTTP.html unit tests\n+\n+ // convert % to \\%\n+ value = value.replace(/%/g, \"\\\\%\");\n+\n+ // convert \\\\. to \\\\_ (\\\\.* occurences converted later)\n+ value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n+ return $1 ? $0 : \"\\\\\\\\_\";\n });\n \n- return OpenLayers.Util.urlAppend(\n- url, OpenLayers.Util.getParameterString(this.params)\n+ // convert \\\\.* to \\\\%\n+ value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n+\n+ // convert . to _ (\\. and .* occurences converted later)\n+ value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n+ return $1 || $2 ? $0 : \"_\";\n+ });\n+\n+ // convert .* to % (\\.* occurnces converted later)\n+ value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"%\";\n+ });\n+\n+ // convert \\. to .\n+ value = value.replace(/\\\\\\./g, \".\");\n+\n+ // replace \\* with * (watching out for \\\\*)\n+ value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n+ return $1 ? $0 : \"*\";\n+ });\n+\n+ return value;\n+ }\n+\n+ return OpenLayers.Class(OpenLayers.Format, {\n+\n+ /**\n+ * Property: wildcarded.\n+ * {Boolean} If true percent signs are added around values\n+ * read from LIKE filters, for example if the protocol\n+ * read method is passed a LIKE filter whose property\n+ * is \"foo\" and whose value is \"bar\" the string\n+ * \"foo__ilike=%bar%\" will be sent in the query string;\n+ * defaults to false.\n+ */\n+ wildcarded: false,\n+\n+ /**\n+ * APIProperty: srsInBBOX\n+ * {Boolean} Include the SRS identifier in BBOX query string parameter. \n+ * Default is false. If true and the layer has a projection object set,\n+ * any BBOX filter will be serialized with a fifth item identifying the\n+ * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n+ */\n+ srsInBBOX: false,\n+\n+ /**\n+ * APIMethod: write\n+ * Serialize an <OpenLayers.Filter> objects using the \"simple\" filter syntax for \n+ * query string parameters. This function must be called as a method of\n+ * a protocol instance.\n+ *\n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} filter to convert.\n+ * params - {Object} The parameters object.\n+ *\n+ * Returns:\n+ * {Object} The resulting parameters object.\n+ */\n+ write: function(filter, params) {\n+ params = params || {};\n+ var className = filter.CLASS_NAME;\n+ var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n+ switch (filterType) {\n+ case \"Spatial\":\n+ switch (filter.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ params.bbox = filter.value.toArray();\n+ if (this.srsInBBOX && filter.projection) {\n+ params.bbox.push(filter.projection.getCode());\n+ }\n+ break;\n+ case OpenLayers.Filter.Spatial.DWITHIN:\n+ params.tolerance = filter.distance;\n+ // no break here\n+ case OpenLayers.Filter.Spatial.WITHIN:\n+ params.lon = filter.value.x;\n+ params.lat = filter.value.y;\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\n+ \"Unknown spatial filter type \" + filter.type);\n+ }\n+ break;\n+ case \"Comparison\":\n+ var op = cmpToStr[filter.type];\n+ if (op !== undefined) {\n+ var value = filter.value;\n+ if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n+ value = regex2value(value);\n+ if (this.wildcarded) {\n+ value = \"%\" + value + \"%\";\n+ }\n+ }\n+ params[filter.property + \"__\" + op] = value;\n+ params.queryable = params.queryable || [];\n+ params.queryable.push(filter.property);\n+ } else {\n+ OpenLayers.Console.warn(\n+ \"Unknown comparison filter type \" + filter.type);\n+ }\n+ break;\n+ case \"Logical\":\n+ if (filter.type === OpenLayers.Filter.Logical.AND) {\n+ for (var i = 0, len = filter.filters.length; i < len; i++) {\n+ params = this.write(filter.filters[i], params);\n+ }\n+ } else {\n+ OpenLayers.Console.warn(\n+ \"Unsupported logical filter type \" + filter.type);\n+ }\n+ break;\n+ default:\n+ OpenLayers.Console.warn(\"Unknown filter type \" + filterType);\n+ }\n+ return params;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n+\n+ });\n+\n+\n+})();\n+/* ======================================================================\n+ OpenLayers/Format/SOSGetFeatureOfInterest.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/GML/v3.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.SOSGetFeatureOfInterest\n+ * Read and write SOS GetFeatureOfInterest. This is used to get to\n+ * the location of the features (stations). The stations can have 1 or more\n+ * sensors.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n+\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ sos: \"http://www.opengis.net/sos/1.0\",\n+ gml: \"http://www.opengis.net/gml\",\n+ sa: \"http://www.opengis.net/sampling/1.0\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location\n+ */\n+ schemaLocation: \"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd\",\n+\n+ /**\n+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"sos\",\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.SOSGetFeatureOfInterest\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Parse a GetFeatureOfInterest response and return an array of features\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} An array of features. \n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+\n+ var info = {\n+ features: []\n+ };\n+ this.readNode(data, info);\n+\n+ var features = [];\n+ for (var i = 0, len = info.features.length; i < len; i++) {\n+ var container = info.features[i];\n+ // reproject features if needed\n+ if (this.internalProjection && this.externalProjection &&\n+ container.components[0]) {\n+ container.components[0].transform(\n+ this.externalProjection, this.internalProjection\n+ );\n+ }\n+ var feature = new OpenLayers.Feature.Vector(\n+ container.components[0], container.attributes);\n+ features.push(feature);\n+ }\n+ return features;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"sa\": {\n+ \"SamplingPoint\": function(node, obj) {\n+ // sampling point can also be without a featureMember if \n+ // there is only 1\n+ if (!obj.attributes) {\n+ var feature = {\n+ attributes: {}\n+ };\n+ obj.features.push(feature);\n+ obj = feature;\n+ }\n+ obj.attributes.id = this.getAttributeNS(node,\n+ this.namespaces.gml, \"id\");\n+ this.readChildNodes(node, obj);\n+ },\n+ \"position\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ }\n+ },\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"FeatureCollection\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"featureMember\": function(node, obj) {\n+ var feature = {\n+ attributes: {}\n+ };\n+ obj.features.push(feature);\n+ this.readChildNodes(node, feature);\n+ },\n+ \"name\": function(node, obj) {\n+ obj.attributes.name = this.getChildValue(node);\n+ },\n+ \"pos\": function(node, obj) {\n+ // we need to parse the srsName to get to the \n+ // externalProjection, that's why we cannot use\n+ // GML v3 for this\n+ if (!this.externalProjection) {\n+ this.externalProjection = new OpenLayers.Projection(\n+ node.getAttribute(\"srsName\"));\n+ }\n+ OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(\n+ this, [node, obj]);\n+ }\n+ }, OpenLayers.Format.GML.v3.prototype.readers.gml)\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"sos\": {\n+ \"GetFeatureOfInterest\": function(options) {\n+ var node = this.createElementNSPlus(\"GetFeatureOfInterest\", {\n+ attributes: {\n+ version: this.VERSION,\n+ service: 'SOS',\n+ \"xsi:schemaLocation\": this.schemaLocation\n+ }\n+ });\n+ for (var i = 0, len = options.fois.length; i < len; i++) {\n+ this.writeNode(\"FeatureOfInterestId\", {\n+ foi: options.fois[i]\n+ }, node);\n+ }\n+ return node;\n+ },\n+ \"FeatureOfInterestId\": function(options) {\n+ var node = this.createElementNSPlus(\"FeatureOfInterestId\", {\n+ value: options.foi\n+ });\n+ return node;\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.SOSGetFeatureOfInterest\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/SOSGetObservation.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.SOSGetObservation\n+ * Read and write SOS GetObersation (to get the actual values from a sensor) \n+ * version 1.0.0\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows\",\n+ gml: \"http://www.opengis.net/gml\",\n+ sos: \"http://www.opengis.net/sos/1.0\",\n+ ogc: \"http://www.opengis.net/ogc\",\n+ om: \"http://www.opengis.net/om/1.0\",\n+ sa: \"http://www.opengis.net/sampling/1.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n+ },\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constant: VERSION\n+ * {String} 1.0.0\n+ */\n+ VERSION: \"1.0.0\",\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} Schema location\n+ */\n+ schemaLocation: \"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd\",\n+\n+ /**\n+ * Property: defaultPrefix\n+ */\n+ defaultPrefix: \"sos\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.SOSGetObservation\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Method: read\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} An object containing the measurements\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var info = {\n+ measurements: [],\n+ observations: []\n+ };\n+ this.readNode(data, info);\n+ return info;\n+ },\n+\n+ /**\n+ * Method: write\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object.\n+ *\n+ * Returns:\n+ * {String} An SOS GetObservation request XML string.\n+ */\n+ write: function(options) {\n+ var node = this.writeNode(\"sos:GetObservation\", options);\n+ node.setAttribute(\"xmlns:om\", this.namespaces.om);\n+ node.setAttribute(\"xmlns:ogc\", this.namespaces.ogc);\n+ this.setAttributeNS(\n+ node, this.namespaces.xsi,\n+ \"xsi:schemaLocation\", this.schemaLocation\n );\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n },\n \n- CLASS_NAME: 'OpenLayers.Layer.ArcGISCache'\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"om\": {\n+ \"ObservationCollection\": function(node, obj) {\n+ obj.id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n+ this.readChildNodes(node, obj);\n+ },\n+ \"member\": function(node, observationCollection) {\n+ this.readChildNodes(node, observationCollection);\n+ },\n+ \"Measurement\": function(node, observationCollection) {\n+ var measurement = {};\n+ observationCollection.measurements.push(measurement);\n+ this.readChildNodes(node, measurement);\n+ },\n+ \"Observation\": function(node, observationCollection) {\n+ var observation = {};\n+ observationCollection.observations.push(observation);\n+ this.readChildNodes(node, observation);\n+ },\n+ \"samplingTime\": function(node, measurement) {\n+ var samplingTime = {};\n+ measurement.samplingTime = samplingTime;\n+ this.readChildNodes(node, samplingTime);\n+ },\n+ \"observedProperty\": function(node, measurement) {\n+ measurement.observedProperty =\n+ this.getAttributeNS(node, this.namespaces.xlink, \"href\");\n+ this.readChildNodes(node, measurement);\n+ },\n+ \"procedure\": function(node, measurement) {\n+ measurement.procedure =\n+ this.getAttributeNS(node, this.namespaces.xlink, \"href\");\n+ this.readChildNodes(node, measurement);\n+ },\n+ \"featureOfInterest\": function(node, observation) {\n+ var foi = {\n+ features: []\n+ };\n+ observation.fois = [];\n+ observation.fois.push(foi);\n+ this.readChildNodes(node, foi);\n+ // postprocessing to get actual features\n+ var features = [];\n+ for (var i = 0, len = foi.features.length; i < len; i++) {\n+ var feature = foi.features[i];\n+ features.push(new OpenLayers.Feature.Vector(\n+ feature.components[0], feature.attributes));\n+ }\n+ foi.features = features;\n+ },\n+ \"result\": function(node, measurement) {\n+ var result = {};\n+ measurement.result = result;\n+ if (this.getChildValue(node) !== '') {\n+ result.value = this.getChildValue(node);\n+ result.uom = node.getAttribute(\"uom\");\n+ } else {\n+ this.readChildNodes(node, result);\n+ }\n+ }\n+ },\n+ \"sa\": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa,\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"TimeInstant\": function(node, samplingTime) {\n+ var timeInstant = {};\n+ samplingTime.timeInstant = timeInstant;\n+ this.readChildNodes(node, timeInstant);\n+ },\n+ \"timePosition\": function(node, timeInstant) {\n+ timeInstant.timePosition = this.getChildValue(node);\n+ }\n+ }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml)\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"sos\": {\n+ \"GetObservation\": function(options) {\n+ var node = this.createElementNSPlus(\"GetObservation\", {\n+ attributes: {\n+ version: this.VERSION,\n+ service: 'SOS'\n+ }\n+ });\n+ this.writeNode(\"offering\", options, node);\n+ if (options.eventTime) {\n+ this.writeNode(\"eventTime\", options, node);\n+ }\n+ for (var procedure in options.procedures) {\n+ this.writeNode(\"procedure\", options.procedures[procedure], node);\n+ }\n+ for (var observedProperty in options.observedProperties) {\n+ this.writeNode(\"observedProperty\", options.observedProperties[observedProperty], node);\n+ }\n+ if (options.foi) {\n+ this.writeNode(\"featureOfInterest\", options.foi, node);\n+ }\n+ this.writeNode(\"responseFormat\", options, node);\n+ if (options.resultModel) {\n+ this.writeNode(\"resultModel\", options, node);\n+ }\n+ if (options.responseMode) {\n+ this.writeNode(\"responseMode\", options, node);\n+ }\n+ return node;\n+ },\n+ \"featureOfInterest\": function(foi) {\n+ var node = this.createElementNSPlus(\"featureOfInterest\");\n+ this.writeNode(\"ObjectID\", foi.objectId, node);\n+ return node;\n+ },\n+ \"ObjectID\": function(options) {\n+ return this.createElementNSPlus(\"ObjectID\", {\n+ value: options\n+ });\n+ },\n+ \"responseFormat\": function(options) {\n+ return this.createElementNSPlus(\"responseFormat\", {\n+ value: options.responseFormat\n+ });\n+ },\n+ \"procedure\": function(procedure) {\n+ return this.createElementNSPlus(\"procedure\", {\n+ value: procedure\n+ });\n+ },\n+ \"offering\": function(options) {\n+ return this.createElementNSPlus(\"offering\", {\n+ value: options.offering\n+ });\n+ },\n+ \"observedProperty\": function(observedProperty) {\n+ return this.createElementNSPlus(\"observedProperty\", {\n+ value: observedProperty\n+ });\n+ },\n+ \"eventTime\": function(options) {\n+ var node = this.createElementNSPlus(\"eventTime\");\n+ if (options.eventTime === 'latest') {\n+ this.writeNode(\"ogc:TM_Equals\", options, node);\n+ }\n+ return node;\n+ },\n+ \"resultModel\": function(options) {\n+ return this.createElementNSPlus(\"resultModel\", {\n+ value: options.resultModel\n+ });\n+ },\n+ \"responseMode\": function(options) {\n+ return this.createElementNSPlus(\"responseMode\", {\n+ value: options.responseMode\n+ });\n+ }\n+ },\n+ \"ogc\": {\n+ \"TM_Equals\": function(options) {\n+ var node = this.createElementNSPlus(\"ogc:TM_Equals\");\n+ this.writeNode(\"ogc:PropertyName\", {\n+ property: \"urn:ogc:data:time:iso8601\"\n+ }, node);\n+ if (options.eventTime === 'latest') {\n+ this.writeNode(\"gml:TimeInstant\", {\n+ value: 'latest'\n+ }, node);\n+ }\n+ return node;\n+ },\n+ \"PropertyName\": function(options) {\n+ return this.createElementNSPlus(\"ogc:PropertyName\", {\n+ value: options.property\n+ });\n+ }\n+ },\n+ \"gml\": {\n+ \"TimeInstant\": function(options) {\n+ var node = this.createElementNSPlus(\"gml:TimeInstant\");\n+ this.writeNode(\"gml:timePosition\", options, node);\n+ return node;\n+ },\n+ \"timePosition\": function(options) {\n+ var node = this.createElementNSPlus(\"gml:timePosition\", {\n+ value: options.value\n+ });\n+ return node;\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.SOSGetObservation\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WMSGetFeatureInfo.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSGetFeatureInfo\n+ * Class to read GetFeatureInfo responses from Web Mapping Services\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * APIProperty: layerIdentifier\n+ * {String} All xml nodes containing this search criteria will populate an\n+ * internal array of layer nodes.\n+ */\n+ layerIdentifier: '_layer',\n+\n+ /**\n+ * APIProperty: featureIdentifier\n+ * {String} All xml nodes containing this search criteria will populate an \n+ * internal array of feature nodes for each layer node found.\n+ */\n+ featureIdentifier: '_feature',\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Property: gmlFormat\n+ * {<OpenLayers.Format.GML>} internal GML format for parsing geometries\n+ * in msGMLOutput\n+ */\n+ gmlFormat: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSGetFeatureInfo\n+ * Create a new parser for WMS GetFeatureInfo responses\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read WMS GetFeatureInfo data from a string, and return an array of features\n+ *\n+ * Parameters:\n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array(<OpenLayers.Feature.Vector>)} An array of features.\n+ */\n+ read: function(data) {\n+ var result;\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ var root = data.documentElement;\n+ if (root) {\n+ var scope = this;\n+ var read = this[\"read_\" + root.nodeName];\n+ if (read) {\n+ result = read.call(this, root);\n+ } else {\n+ // fall-back to GML since this is a common output format for WMS\n+ // GetFeatureInfo responses\n+ result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);\n+ }\n+ } else {\n+ result = data;\n+ }\n+ return result;\n+ },\n+\n+\n+ /**\n+ * Method: read_msGMLOutput\n+ * Parse msGMLOutput nodes.\n+ *\n+ * Parameters:\n+ * data - {DOMElement}\n+ *\n+ * Returns:\n+ * {Array}\n+ */\n+ read_msGMLOutput: function(data) {\n+ var response = [];\n+ var layerNodes = this.getSiblingNodesByTagCriteria(data,\n+ this.layerIdentifier);\n+ if (layerNodes) {\n+ for (var i = 0, len = layerNodes.length; i < len; ++i) {\n+ var node = layerNodes[i];\n+ var layerName = node.nodeName;\n+ if (node.prefix) {\n+ layerName = layerName.split(':')[1];\n+ }\n+ var layerName = layerName.replace(this.layerIdentifier, '');\n+ var featureNodes = this.getSiblingNodesByTagCriteria(node,\n+ this.featureIdentifier);\n+ if (featureNodes) {\n+ for (var j = 0; j < featureNodes.length; j++) {\n+ var featureNode = featureNodes[j];\n+ var geomInfo = this.parseGeometry(featureNode);\n+ var attributes = this.parseAttributes(featureNode);\n+ var feature = new OpenLayers.Feature.Vector(geomInfo.geometry,\n+ attributes, null);\n+ feature.bounds = geomInfo.bounds;\n+ feature.type = layerName;\n+ response.push(feature);\n+ }\n+ }\n+ }\n+ }\n+ return response;\n+ },\n+\n+ /**\n+ * Method: read_FeatureInfoResponse\n+ * Parse FeatureInfoResponse nodes.\n+ *\n+ * Parameters:\n+ * data - {DOMElement}\n+ *\n+ * Returns:\n+ * {Array}\n+ */\n+ read_FeatureInfoResponse: function(data) {\n+ var response = [];\n+ var featureNodes = this.getElementsByTagNameNS(data, '*',\n+ 'FIELDS');\n+\n+ for (var i = 0, len = featureNodes.length; i < len; i++) {\n+ var featureNode = featureNodes[i];\n+ var geom = null;\n+\n+ // attributes can be actual attributes on the FIELDS tag, \n+ // or FIELD children\n+ var attributes = {};\n+ var j;\n+ var jlen = featureNode.attributes.length;\n+ if (jlen > 0) {\n+ for (j = 0; j < jlen; j++) {\n+ var attribute = featureNode.attributes[j];\n+ attributes[attribute.nodeName] = attribute.nodeValue;\n+ }\n+ } else {\n+ var nodes = featureNode.childNodes;\n+ for (j = 0, jlen = nodes.length; j < jlen; ++j) {\n+ var node = nodes[j];\n+ if (node.nodeType != 3) {\n+ attributes[node.getAttribute(\"name\")] =\n+ node.getAttribute(\"value\");\n+ }\n+ }\n+ }\n+\n+ response.push(\n+ new OpenLayers.Feature.Vector(geom, attributes, null)\n+ );\n+ }\n+ return response;\n+ },\n+\n+ /**\n+ * Method: getSiblingNodesByTagCriteria\n+ * Recursively searches passed xml node and all it's descendant levels for \n+ * nodes whose tagName contains the passed search string. This returns an \n+ * array of all sibling nodes which match the criteria from the highest \n+ * hierarchial level from which a match is found.\n+ * \n+ * Parameters:\n+ * node - {DOMElement} An xml node\n+ * criteria - {String} Search string which will match some part of a tagName \n+ * \n+ * Returns:\n+ * Array({DOMElement}) An array of sibling xml nodes\n+ */\n+ getSiblingNodesByTagCriteria: function(node, criteria) {\n+ var nodes = [];\n+ var children, tagName, n, matchNodes, child;\n+ if (node && node.hasChildNodes()) {\n+ children = node.childNodes;\n+ n = children.length;\n+\n+ for (var k = 0; k < n; k++) {\n+ child = children[k];\n+ while (child && child.nodeType != 1) {\n+ child = child.nextSibling;\n+ k++;\n+ }\n+ tagName = (child ? child.nodeName : '');\n+ if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {\n+ nodes.push(child);\n+ } else {\n+ matchNodes = this.getSiblingNodesByTagCriteria(\n+ child, criteria);\n+\n+ if (matchNodes.length > 0) {\n+ (nodes.length == 0) ?\n+ nodes = matchNodes: nodes.push(matchNodes);\n+ }\n+ }\n+ }\n+\n+ }\n+ return nodes;\n+ },\n+\n+ /**\n+ * Method: parseAttributes\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An attributes object.\n+ * \n+ * Notes:\n+ * Assumes that attributes are direct child xml nodes of the passed node\n+ * and contain only a single text node. \n+ */\n+ parseAttributes: function(node) {\n+ var attributes = {};\n+ if (node.nodeType == 1) {\n+ var children = node.childNodes;\n+ var n = children.length;\n+ for (var i = 0; i < n; ++i) {\n+ var child = children[i];\n+ if (child.nodeType == 1) {\n+ var grandchildren = child.childNodes;\n+ var name = (child.prefix) ?\n+ child.nodeName.split(\":\")[1] : child.nodeName;\n+ if (grandchildren.length == 0) {\n+ attributes[name] = null;\n+ } else if (grandchildren.length == 1) {\n+ var grandchild = grandchildren[0];\n+ if (grandchild.nodeType == 3 ||\n+ grandchild.nodeType == 4) {\n+ var value = grandchild.nodeValue.replace(\n+ this.regExes.trimSpace, \"\");\n+ attributes[name] = value;\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return attributes;\n+ },\n+\n+ /**\n+ * Method: parseGeometry\n+ * Parse the geometry and the feature bounds out of the node using \n+ * Format.GML\n+ *\n+ * Parameters:\n+ * node - {<DOMElement>}\n+ *\n+ * Returns:\n+ * {Object} An object containing the geometry and the feature bounds\n+ */\n+ parseGeometry: function(node) {\n+ // we need to use the old Format.GML parser since we do not know the \n+ // geometry name\n+ if (!this.gmlFormat) {\n+ this.gmlFormat = new OpenLayers.Format.GML();\n+ }\n+ var feature = this.gmlFormat.parseFeature(node);\n+ var geometry, bounds = null;\n+ if (feature) {\n+ geometry = feature.geometry && feature.geometry.clone();\n+ bounds = feature.bounds && feature.bounds.clone();\n+ feature.destroy();\n+ }\n+ return {\n+ geometry: geometry,\n+ bounds: bounds\n+ };\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WPSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSCapabilities\n+ * Read WPS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n+ */\n+ defaultVersion: \"1.0.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSCapabilities\n+ * Create a new parser for WPS Capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return information about\n+ * the service.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Info about the WPS\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WFSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFSCapabilities\n+ * Read WFS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFSCapabilities\n+ * Create a new parser for WFS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of layers. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named layers.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WMSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSCapabilities\n+ * Read WMS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.1\".\n+ */\n+ defaultVersion: \"1.1.1\",\n+\n+ /**\n+ * APIProperty: profile\n+ * {String} If provided, use a custom profile.\n+ *\n+ * Currently supported profiles:\n+ * - WMSC - parses vendor specific capabilities for WMS-C.\n+ */\n+ profile: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSCapabilities\n+ * Create a new parser for WMS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of layers. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named layers.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n+\n+});\n+/* ======================================================================\n+ OpenLayers/Format/WCSCapabilities.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WCSCapabilities\n+ * Read WCS Capabilities.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n+ */\n+OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+\n+ /**\n+ * APIProperty: defaultVersion\n+ * {String} Version number to assume if none found. Default is \"1.1.0\".\n+ */\n+ defaultVersion: \"1.1.0\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WCSCapabilities\n+ * Create a new parser for WCS capabilities.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return a list of coverages. \n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Array} List of named coverages.\n+ */\n+\n+ CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n+\n });\n /* ======================================================================\n OpenLayers/Format/ArcXML.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -45541,6648 +47305,26 @@\n \n return OpenLayers.Util.extend(this, defaults);\n },\n \n CLASS_NAME: \"OpenLayers.Format.ArcXML.Response\"\n });\n /* ======================================================================\n- OpenLayers/Layer/ArcIMS.js\n+ OpenLayers/Format/Atom.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Format/ArcXML.js\n- * @requires OpenLayers/Request.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.ArcIMS\n- * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS\n- * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS>\n- * constructor.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Default query string parameters.\n- */\n- DEFAULT_PARAMS: {\n- ClientVersion: \"9.2\",\n- ServiceName: ''\n- },\n-\n- /**\n- * APIProperty: featureCoordSys\n- * {String} Code for feature coordinate system. Default is \"4326\".\n- */\n- featureCoordSys: \"4326\",\n-\n- /**\n- * APIProperty: filterCoordSys\n- * {String} Code for filter coordinate system. Default is \"4326\".\n- */\n- filterCoordSys: \"4326\",\n-\n- /**\n- * APIProperty: layers\n- * {Array} An array of objects with layer properties.\n- */\n- layers: null,\n-\n- /**\n- * APIProperty: async\n- * {Boolean} Request images asynchronously. Default is true.\n- */\n- async: true,\n-\n- /**\n- * APIProperty: name\n- * {String} Layer name. Default is \"ArcIMS\".\n- */\n- name: \"ArcIMS\",\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is true.\n- */\n- isBaseLayer: true,\n-\n- /**\n- * Constant: DEFAULT_OPTIONS\n- * {Object} Default layers properties.\n- */\n- DEFAULT_OPTIONS: {\n- tileSize: new OpenLayers.Size(512, 512),\n- featureCoordSys: \"4326\",\n- filterCoordSys: \"4326\",\n- layers: null,\n- isBaseLayer: true,\n- async: true,\n- name: \"ArcIMS\"\n- },\n-\n- /**\n- * Constructor: OpenLayers.Layer.ArcIMS\n- * Create a new ArcIMS layer object.\n- *\n- * Example:\n- * (code)\n- * var arcims = new OpenLayers.Layer.ArcIMS(\n- * \"Global Sample\",\n- * \"http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap\", \n- * {\n- * service: \"OpenLayers_Sample\", \n- * layers: [\n- * // layers to manipulate\n- * {id: \"1\", visible: true}\n- * ]\n- * }\n- * );\n- * (end)\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the ArcIMS server\n- * options - {Object} Optional object with properties to be set on the\n- * layer.\n- */\n- initialize: function(name, url, options) {\n-\n- this.tileSize = new OpenLayers.Size(512, 512);\n-\n- // parameters\n- this.params = OpenLayers.Util.applyDefaults({\n- ServiceName: options.serviceName\n- },\n- this.DEFAULT_PARAMS\n- );\n- this.options = OpenLayers.Util.applyDefaults(\n- options, this.DEFAULT_OPTIONS\n- );\n-\n- OpenLayers.Layer.Grid.prototype.initialize.apply(\n- this, [name, url, this.params, options]\n- );\n-\n- //layer is transparent \n- if (this.transparent) {\n-\n- // unless explicitly set in options, make layer an overlay\n- if (!this.isBaseLayer) {\n- this.isBaseLayer = false;\n- }\n-\n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.format == \"image/jpeg\") {\n- this.format = OpenLayers.Util.alphaHack() ? \"image/gif\" : \"image/png\";\n- }\n- }\n-\n- // create an empty layer list if no layers specified in the options\n- if (this.options.layers === null) {\n- this.options.layers = [];\n- }\n- },\n-\n- /**\n- * Method: getURL\n- * Return an image url this layer.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the map image's url.\n- */\n- getURL: function(bounds) {\n- var url = \"\";\n- bounds = this.adjustBounds(bounds);\n-\n- // create an arcxml request to generate the image\n- var axlReq = new OpenLayers.Format.ArcXML(\n- OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- })\n- );\n-\n- // create a synchronous ajax request to get an arcims image\n- var req = new OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- data: axlReq.write(),\n- async: false\n- });\n-\n- // if the response exists\n- if (req != null) {\n- var doc = req.responseXML;\n-\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText;\n- }\n-\n- // create a new arcxml format to read the response\n- var axlResp = new OpenLayers.Format.ArcXML();\n- var arcxml = axlResp.read(doc);\n- url = this.getUrlOrImage(arcxml.image.output);\n- }\n-\n- return url;\n- },\n-\n-\n- /**\n- * Method: getURLasync\n- * Get an image url this layer asynchronously, and execute a callback\n- * when the image url is generated.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- * callback - {Function} Function to call when image url is retrieved.\n- * scope - {Object} The scope of the callback method.\n- */\n- getURLasync: function(bounds, callback, scope) {\n- bounds = this.adjustBounds(bounds);\n-\n- // create an arcxml request to generate the image\n- var axlReq = new OpenLayers.Format.ArcXML(\n- OpenLayers.Util.extend(this.options, {\n- requesttype: \"image\",\n- envelope: bounds.toArray(),\n- tileSize: this.tileSize\n- })\n- );\n-\n- // create an asynchronous ajax request to get an arcims image\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString(),\n- async: true,\n- data: axlReq.write(),\n- callback: function(req) {\n- // process the response from ArcIMS, and call the callback function\n- // to set the image URL\n- var doc = req.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = req.responseText;\n- }\n-\n- // create a new arcxml format to read the response\n- var axlResp = new OpenLayers.Format.ArcXML();\n- var arcxml = axlResp.read(doc);\n-\n- callback.call(scope, this.getUrlOrImage(arcxml.image.output));\n- },\n- scope: this\n- });\n- },\n-\n- /**\n- * Method: getUrlOrImage\n- * Extract a url or image from the ArcXML image output.\n- *\n- * Parameters:\n- * output - {Object} The image.output property of the object returned from\n- * the ArcXML format read method.\n- *\n- * Returns:\n- * {String} A URL for an image (potentially with the data protocol).\n- */\n- getUrlOrImage: function(output) {\n- var ret = \"\";\n- if (output.url) {\n- // If the image response output url is a string, then the image\n- // data is not inline.\n- ret = output.url;\n- } else if (output.data) {\n- // The image data is inline and base64 encoded, create a data\n- // url for the image. This will only work for small images,\n- // due to browser url length limits.\n- ret = \"data:image/\" + output.type +\n- \";base64,\" + output.data;\n- }\n- return ret;\n- },\n-\n- /**\n- * Method: setLayerQuery\n- * Set the query definition on this layer. Query definitions are used to\n- * render parts of the spatial data in an image, and can be used to\n- * filter features or layers in the ArcIMS service.\n- *\n- * Parameters:\n- * id - {String} The ArcIMS layer ID.\n- * querydef - {Object} The query definition to apply to this layer.\n- */\n- setLayerQuery: function(id, querydef) {\n- // find the matching layer, if it exists\n- for (var lyr = 0; lyr < this.options.layers.length; lyr++) {\n- if (id == this.options.layers[lyr].id) {\n- // replace this layer definition\n- this.options.layers[lyr].query = querydef;\n- return;\n- }\n- }\n-\n- // no layer found, create a new definition\n- this.options.layers.push({\n- id: id,\n- visible: true,\n- query: querydef\n- });\n- },\n-\n- /**\n- * Method: getFeatureInfo\n- * Get feature information from ArcIMS. Using the applied geometry, apply\n- * the options to the query (buffer, area/envelope intersection), and\n- * query the ArcIMS service.\n- *\n- * A note about accuracy:\n- * ArcIMS interprets the accuracy attribute in feature requests to be\n- * something like the 'modulus' operator on feature coordinates,\n- * applied to the database geometry of the feature. It doesn't round,\n- * so your feature coordinates may be up to (1 x accuracy) offset from\n- * the actual feature coordinates. If the accuracy of the layer is not\n- * specified, the accuracy will be computed to be approximately 1\n- * feature coordinate per screen pixel.\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The\n- * geometry to use when making the query. This should be a closed\n- * polygon for behavior approximating a free selection.\n- * layer - {Object} The ArcIMS layer definition. This is an anonymous object\n- * that looks like:\n- * (code)\n- * {\n- * id: \"ArcXML layer ID\", // the ArcXML layer ID\n- * query: {\n- * where: \"STATE = 'PA'\", // the where clause of the query\n- * accuracy: 100 // the accuracy of the returned feature\n- * }\n- * }\n- * (end)\n- * options - {Object} Object with non-default properties to set on the layer.\n- * Supported properties are buffer, callback, scope, and any other\n- * properties applicable to the ArcXML format. Set the 'callback' and\n- * 'scope' for an object and function to recieve the parsed features\n- * from ArcIMS.\n- */\n- getFeatureInfo: function(geometry, layer, options) {\n- // set the buffer to 1 unit (dd/m/ft?) by default\n- var buffer = options.buffer || 1;\n- // empty callback by default\n- var callback = options.callback || function() {};\n- // default scope is window (global)\n- var scope = options.scope || window;\n-\n- // apply these option to the request options\n- var requestOptions = {};\n- OpenLayers.Util.extend(requestOptions, this.options);\n-\n- // this is a feature request\n- requestOptions.requesttype = \"feature\";\n-\n- if (geometry instanceof OpenLayers.LonLat) {\n- // create an envelope if the geometry is really a lon/lat\n- requestOptions.polygon = null;\n- requestOptions.envelope = [\n- geometry.lon - buffer,\n- geometry.lat - buffer,\n- geometry.lon + buffer,\n- geometry.lat + buffer\n- ];\n- } else if (geometry instanceof OpenLayers.Geometry.Polygon) {\n- // use the polygon assigned, and empty the envelope\n- requestOptions.envelope = null;\n- requestOptions.polygon = geometry;\n- }\n-\n- // create an arcxml request to get feature requests\n- var arcxml = new OpenLayers.Format.ArcXML(requestOptions);\n-\n- // apply any get feature options to the arcxml request\n- OpenLayers.Util.extend(arcxml.request.get_feature, options);\n-\n- arcxml.request.get_feature.layer = layer.id;\n- if (typeof layer.query.accuracy == \"number\") {\n- // set the accuracy if it was specified\n- arcxml.request.get_feature.query.accuracy = layer.query.accuracy;\n- } else {\n- // guess that the accuracy is 1 per screen pixel\n- var mapCenter = this.map.getCenter();\n- var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);\n- viewPx.x++;\n- var mapOffCenter = this.map.getLonLatFromPixel(viewPx);\n- arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;\n- }\n-\n- // set the get_feature query to be the same as the layer passed in\n- arcxml.request.get_feature.query.where = layer.query.where;\n-\n- // use area_intersection\n- arcxml.request.get_feature.query.spatialfilter.relation = \"area_intersection\";\n-\n- // create a new asynchronous request to get the feature info\n- OpenLayers.Request.POST({\n- url: this.getFullRequestString({\n- 'CustomService': 'Query'\n- }),\n- data: arcxml.write(),\n- callback: function(request) {\n- // parse the arcxml response\n- var response = arcxml.parseResponse(request.responseText);\n-\n- if (!arcxml.iserror()) {\n- // if the arcxml is not an error, call the callback with the features parsed\n- callback.call(scope, response.features);\n- } else {\n- // if the arcxml is an error, return null features selected\n- callback.call(scope, null);\n- }\n- }\n- });\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcIMS(this.name,\n- this.url,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.ArcIMS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/OSM.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/XYZ.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.OSM\n- * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap\n- * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use\n- * a different layer instead, you need to provide a different\n- * URL to the constructor. Here's an example for using OpenCycleMap:\n- * \n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n- */\n-OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * APIProperty: name\n- * {String} The layer name. Defaults to \"OpenStreetMap\" if the first\n- * argument to the constructor is null or undefined.\n- */\n- name: \"OpenStreetMap\",\n-\n- /**\n- * APIProperty: url\n- * {String} The tileset URL scheme. Defaults to\n- * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png\n- * (the official OSM tileset) if the second argument to the constructor\n- * is null or undefined. To use another tileset you can have something\n- * like this:\n- * (code)\n- * new OpenLayers.Layer.OSM(\"OpenCycleMap\", \n- * [\"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\",\n- * \"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png\"]); \n- * (end)\n- */\n- url: [\n- 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',\n- 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'\n- ],\n-\n- /**\n- * Property: attribution\n- * {String} The layer attribution.\n- */\n- attribution: \"© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors\",\n-\n- /**\n- * Property: sphericalMercator\n- * {Boolean}\n- */\n- sphericalMercator: true,\n-\n- /**\n- * Property: wrapDateLine\n- * {Boolean}\n- */\n- wrapDateLine: true,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- *\n- * When using OSM tilesets other than the default ones, it may be\n- * necessary to set this to\n- *\n- * (code)\n- * {crossOriginKeyword: null}\n- * (end)\n- *\n- * if the server does not send Access-Control-Allow-Origin headers.\n- */\n- tileOptions: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.OSM\n- *\n- * Parameters:\n- * name - {String} The layer name.\n- * url - {String} The tileset URL scheme.\n- * options - {Object} Configuration options for the layer. Any inherited\n- * layer option can be set in this object (e.g.\n- * <OpenLayers.Layer.Grid.buffer>).\n- */\n- initialize: function(name, url, options) {\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options && this.options.tileOptions);\n- },\n-\n- /**\n- * Method: clone\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.OSM(\n- this.name, this.url, this.getOptions());\n- }\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- return obj;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.OSM\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/MapGuide.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.MapGuide\n- * Instances of OpenLayers.Layer.MapGuide are used to display\n- * data from a MapGuide OS instance.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} Treat this layer as a base layer. Default is true.\n- **/\n- isBaseLayer: true,\n-\n- /**\n- * APIProperty: useHttpTile\n- * {Boolean} use a tile cache exposed directly via a webserver rather than the \n- * via mapguide server. This does require extra configuration on the Mapguide Server,\n- * and will only work when singleTile is false. The url for the layer must be set to the\n- * webserver path rather than the Mapguide mapagent.\n- * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp\n- **/\n- useHttpTile: false,\n-\n- /** \n- * APIProperty: singleTile\n- * {Boolean} use tile server or request single tile image. \n- **/\n- singleTile: false,\n-\n- /** \n- * APIProperty: useOverlay\n- * {Boolean} flag to indicate if the layer should be retrieved using\n- * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests.\n- **/\n- useOverlay: false,\n-\n- /** \n- * APIProperty: useAsyncOverlay\n- * {Boolean} indicates if the MapGuide site supports the asynchronous \n- * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010\n- * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG \n- * is called asynchronously, allows selections to be drawn separately from \n- * the map and offers styling options.\n- * \n- * With older versions of MapGuide, set useAsyncOverlay=false. Note that in\n- * this case a synchronous AJAX call is issued and the mapname and session\n- * parameters must be used to initialize the layer, not the mapdefinition\n- * parameter. Also note that this will issue a synchronous AJAX request \n- * before the image request can be issued so the users browser may lock\n- * up if the MG Web tier does not respond in a timely fashion.\n- **/\n- useAsyncOverlay: true,\n-\n- /**\n- * Constant: TILE_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for tiled layer\n- */\n- TILE_PARAMS: {\n- operation: 'GETTILEIMAGE',\n- version: '1.2.0'\n- },\n-\n- /**\n- * Constant: SINGLE_TILE_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for untiled layer\n- */\n- SINGLE_TILE_PARAMS: {\n- operation: 'GETMAPIMAGE',\n- format: 'PNG',\n- locale: 'en',\n- clip: '1',\n- version: '1.0.0'\n- },\n-\n- /**\n- * Constant: OVERLAY_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs for untiled layer\n- */\n- OVERLAY_PARAMS: {\n- operation: 'GETDYNAMICMAPOVERLAYIMAGE',\n- format: 'PNG',\n- locale: 'en',\n- clip: '1',\n- version: '2.0.0'\n- },\n-\n- /** \n- * Constant: FOLDER_PARAMS\n- * {Object} Hashtable of parameter key/value pairs which describe \n- * the folder structure for tiles as configured in the mapguide \n- * serverconfig.ini section [TileServiceProperties]\n- */\n- FOLDER_PARAMS: {\n- tileColumnsPerFolder: 30,\n- tileRowsPerFolder: 30,\n- format: 'png',\n- querystring: null\n- },\n-\n- /** \n- * Property: defaultSize\n- * {<OpenLayers.Size>} Tile size as produced by MapGuide server\n- **/\n- defaultSize: new OpenLayers.Size(300, 300),\n-\n- /** \n- * Property: tileOriginCorner\n- * {String} MapGuide tile server uses top-left as tile origin\n- **/\n- tileOriginCorner: \"tl\",\n-\n- /**\n- * Constructor: OpenLayers.Layer.MapGuide\n- * Create a new Mapguide layer, either tiled or untiled. \n- *\n- * For tiled layers, the 'groupName' and 'mapDefinition' values \n- * must be specified as parameters in the constructor.\n- *\n- * For untiled base layers, specify either combination of 'mapName' and\n- * 'session', or 'mapDefinition' and 'locale'. \n- *\n- * For older versions of MapGuide and overlay layers, set useAsyncOverlay \n- * to false and in this case mapName and session are required parameters \n- * for the constructor.\n- *\n- * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion \n- * factor that are different than the defaults used in OpenLayers, \n- * so these must be adjusted accordingly in your application. \n- * See the MapGuide example for how to set these values for MGOS.\n- *\n- * Parameters:\n- * name - {String} Name of the layer displayed in the interface\n- * url - {String} Location of the MapGuide mapagent executable\n- * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi)\n- * params - {Object} hashtable of additional parameters to use. Some\n- * parameters may require additional code on the server. The ones that\n- * you may want to use are: \n- * - mapDefinition - {String} The MapGuide resource definition\n- * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition)\n- * - locale - Locale setting \n- * (for untiled overlays layers only)\n- * - mapName - {String} Name of the map as stored in the MapGuide session.\n- * (for untiled layers with a session parameter only)\n- * - session - { String} MapGuide session ID \n- * (for untiled overlays layers only)\n- * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only\n- * - format - Image format to be returned (for untiled overlay layers only)\n- * - showLayers - {String} A comma separated list of GUID's for the\n- * layers to display eg: 'cvc-xcv34,453-345-345sdf'.\n- * - hideLayers - {String} A comma separated list of GUID's for the\n- * layers to hide eg: 'cvc-xcv34,453-345-345sdf'.\n- * - showGroups - {String} A comma separated list of GUID's for the\n- * groups to display eg: 'cvc-xcv34,453-345-345sdf'.\n- * - hideGroups - {String} A comma separated list of GUID's for the\n- * groups to hide eg: 'cvc-xcv34,453-345-345sdf'\n- * - selectionXml - {String} A selection xml string Some server plumbing\n- * is required to read such a value.\n- * options - {Object} Hashtable of extra options to tag onto the layer; \n- * will vary depending if tiled or untiled maps are being requested\n- */\n- initialize: function(name, url, params, options) {\n-\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n-\n- // unless explicitly set in options, if the layer is transparent, \n- // it will be an overlay\n- if (options == null || options.isBaseLayer == null) {\n- this.isBaseLayer = ((this.transparent != \"true\") &&\n- (this.transparent != true));\n- }\n-\n- if (options && options.useOverlay != null) {\n- this.useOverlay = options.useOverlay;\n- }\n-\n- //initialize for untiled layers\n- if (this.singleTile) {\n- if (this.useOverlay) {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.OVERLAY_PARAMS\n- );\n- if (!this.useAsyncOverlay) {\n- this.params.version = \"1.0.0\";\n- }\n- } else {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.SINGLE_TILE_PARAMS\n- );\n- }\n- } else {\n- //initialize for tiled layers\n- if (this.useHttpTile) {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.FOLDER_PARAMS\n- );\n- } else {\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- this.TILE_PARAMS\n- );\n- }\n- this.setTileSize(this.defaultSize);\n- }\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.MapGuide(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n- },\n-\n- /**\n- * Method: getURL\n- * Return a query string for this layer\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox \n- * for the request\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also \n- * the passed-in bounds and appropriate tile size specified \n- * as parameters.\n- */\n- getURL: function(bounds) {\n- var url;\n- var center = bounds.getCenterLonLat();\n- var mapSize = this.map.getSize();\n-\n- if (this.singleTile) {\n- //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with\n- //dynamic map parameters\n- var params = {\n- setdisplaydpi: OpenLayers.DOTS_PER_INCH,\n- setdisplayheight: mapSize.h * this.ratio,\n- setdisplaywidth: mapSize.w * this.ratio,\n- setviewcenterx: center.lon,\n- setviewcentery: center.lat,\n- setviewscale: this.map.getScale()\n- };\n-\n- if (this.useOverlay && !this.useAsyncOverlay) {\n- //first we need to call GETVISIBLEMAPEXTENT to set the extent\n- var getVisParams = {};\n- getVisParams = OpenLayers.Util.extend(getVisParams, params);\n- getVisParams.operation = \"GETVISIBLEMAPEXTENT\";\n- getVisParams.version = \"1.0.0\";\n- getVisParams.session = this.params.session;\n- getVisParams.mapName = this.params.mapName;\n- getVisParams.format = 'text/xml';\n- url = this.getFullRequestString(getVisParams);\n-\n- OpenLayers.Request.GET({\n- url: url,\n- async: false\n- });\n- }\n- //construct the full URL\n- url = this.getFullRequestString(params);\n- } else {\n-\n- //tiled version\n- var currentRes = this.map.getResolution();\n- var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);\n- colidx = Math.round(colidx / this.tileSize.w);\n- var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);\n- rowidx = Math.round(rowidx / this.tileSize.h);\n-\n- if (this.useHttpTile) {\n- url = this.getImageFilePath({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- });\n-\n- } else {\n- url = this.getFullRequestString({\n- tilecol: colidx,\n- tilerow: rowidx,\n- scaleindex: this.resolutions.length - this.map.zoom - 1\n- });\n- }\n- }\n- return url;\n- },\n-\n- /**\n- * Method: getFullRequestString\n- * getFullRequestString on MapGuide layers is special, because we \n- * do a regular expression replace on ',' in parameters to '+'.\n- * This is why it is subclassed here.\n- *\n- * Parameters:\n- * altUrl - {String} Alternative base URL to use.\n- *\n- * Returns:\n- * {String} A string with the layer's url appropriately encoded for MapGuide\n- */\n- getFullRequestString: function(newParams, altUrl) {\n- // use layer's url unless altUrl passed in\n- var url = (altUrl == null) ? this.url : altUrl;\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will randomly select one of them in order\n- // to evenly distribute requests to different urls.\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)];\n- }\n- // requestString always starts with url\n- var requestString = url;\n-\n- // create a new params hashtable with all the layer params and the \n- // new params together. then convert to string\n- var allParams = OpenLayers.Util.extend({}, this.params);\n- allParams = OpenLayers.Util.extend(allParams, newParams);\n- // ignore parameters that are already in the url search string\n- var urlParams = OpenLayers.Util.upperCaseObject(\n- OpenLayers.Util.getParameters(url));\n- for (var key in allParams) {\n- if (key.toUpperCase() in urlParams) {\n- delete allParams[key];\n- }\n- }\n- var paramsString = OpenLayers.Util.getParameterString(allParams);\n-\n- /* MapGuide needs '+' seperating things like bounds/height/width.\n- Since typically this is URL encoded, we use a slight hack: we\n- depend on the list-like functionality of getParameterString to\n- leave ',' only in the case of list items (since otherwise it is\n- encoded) then do a regular expression replace on the , characters\n- to '+' */\n- paramsString = paramsString.replace(/,/g, \"+\");\n-\n- if (paramsString != \"\") {\n- var lastServerChar = url.charAt(url.length - 1);\n- if ((lastServerChar == \"&\") || (lastServerChar == \"?\")) {\n- requestString += paramsString;\n- } else {\n- if (url.indexOf('?') == -1) {\n- //serverPath has no ? -- add one\n- requestString += '?' + paramsString;\n- } else {\n- //serverPath contains ?, so must already have paramsString at the end\n- requestString += '&' + paramsString;\n- }\n- }\n- }\n- return requestString;\n- },\n-\n- /** \n- * Method: getImageFilePath\n- * special handler to request mapguide tiles from an http exposed tilecache \n- *\n- * Parameters:\n- * altUrl - {String} Alternative base URL to use.\n- *\n- * Returns:\n- * {String} A string with the url for the tile image\n- */\n- getImageFilePath: function(newParams, altUrl) {\n- // use layer's url unless altUrl passed in\n- var url = (altUrl == null) ? this.url : altUrl;\n-\n- // if url is not a string, it should be an array of strings, \n- // in which case we will randomly select one of them in order\n- // to evenly distribute requests to different urls.\n- if (typeof url == \"object\") {\n- url = url[Math.floor(Math.random() * url.length)];\n- }\n- // requestString always starts with url\n- var requestString = url;\n-\n- var tileRowGroup = \"\";\n- var tileColGroup = \"\";\n-\n- if (newParams.tilerow < 0) {\n- tileRowGroup = '-';\n- }\n-\n- if (newParams.tilerow == 0) {\n- tileRowGroup += '0';\n- } else {\n- tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;\n- }\n-\n- if (newParams.tilecol < 0) {\n- tileColGroup = '-';\n- }\n-\n- if (newParams.tilecol == 0) {\n- tileColGroup += '0';\n- } else {\n- tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;\n- }\n-\n- var tilePath = '/S' + Math.floor(newParams.scaleindex) +\n- '/' + this.params.basemaplayergroupname +\n- '/R' + tileRowGroup +\n- '/C' + tileColGroup +\n- '/' + (newParams.tilerow % this.params.tileRowsPerFolder) +\n- '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) +\n- '.' + this.params.format;\n-\n- if (this.params.querystring) {\n- tilePath += \"?\" + this.params.querystring;\n- }\n-\n- requestString += tilePath;\n- return requestString;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.MapGuide\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/PointTrack.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.PointTrack\n- * Vector layer to display ordered point features as a line, creating one\n- * LineString feature for each pair of two points.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector> \n- */\n-OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * APIProperty: dataFrom\n- * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n- * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n- * should get the data/attributes from one of the two points it is\n- * composed of, which one should it be?\n- */\n- dataFrom: null,\n-\n- /**\n- * APIProperty: styleFrom\n- * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or\n- * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines\n- * should get the style from one of the two points it is composed of,\n- * which one should it be?\n- */\n- styleFrom: null,\n-\n- /**\n- * Constructor: OpenLayers.PointTrack\n- * Constructor for a new OpenLayers.PointTrack instance.\n- *\n- * Parameters:\n- * name - {String} name of the layer\n- * options - {Object} Optional object with properties to tag onto the\n- * instance.\n- */\n-\n- /**\n- * APIMethod: addNodes\n- * Adds point features that will be used to create lines from, using point\n- * pairs. The first point of a pair will be the source node, the second\n- * will be the target node.\n- * \n- * Parameters:\n- * pointFeatures - {Array(<OpenLayers.Feature>)}\n- * options - {Object}\n- * \n- * Supported options:\n- * silent - {Boolean} true to suppress (before)feature(s)added events\n- */\n- addNodes: function(pointFeatures, options) {\n- if (pointFeatures.length < 2) {\n- throw new Error(\"At least two point features have to be added to \" +\n- \"create a line from\");\n- }\n-\n- var lines = new Array(pointFeatures.length - 1);\n-\n- var pointFeature, startPoint, endPoint;\n- for (var i = 0, len = pointFeatures.length; i < len; i++) {\n- pointFeature = pointFeatures[i];\n- endPoint = pointFeature.geometry;\n-\n- if (!endPoint) {\n- var lonlat = pointFeature.lonlat;\n- endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);\n- } else if (endPoint.CLASS_NAME != \"OpenLayers.Geometry.Point\") {\n- throw new TypeError(\"Only features with point geometries are supported.\");\n- }\n-\n- if (i > 0) {\n- var attributes = (this.dataFrom != null) ?\n- (pointFeatures[i + this.dataFrom].data ||\n- pointFeatures[i + this.dataFrom].attributes) :\n- null;\n- var style = (this.styleFrom != null) ?\n- (pointFeatures[i + this.styleFrom].style) :\n- null;\n- var line = new OpenLayers.Geometry.LineString([startPoint,\n- endPoint\n- ]);\n-\n- lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes,\n- style);\n- }\n-\n- startPoint = endPoint;\n- }\n-\n- this.addFeatures(lines, options);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.PointTrack\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE\n- * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n- * <OpenLayers.Layer.PointTrack.styleFrom>\n- */\n-OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE\n- * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and\n- * <OpenLayers.Layer.PointTrack.styleFrom>\n- */\n-OpenLayers.Layer.PointTrack.TARGET_NODE = 0;\n-\n-/**\n- * Constant: OpenLayers.Layer.PointTrack.dataFrom\n- * {Object} with the following keys - *deprecated*\n- * - SOURCE_NODE: take data/attributes from the source node of the line\n- * - TARGET_NODE: take data/attributes from the target node of the line\n- */\n-OpenLayers.Layer.PointTrack.dataFrom = {\n- 'SOURCE_NODE': -1,\n- 'TARGET_NODE': 0\n-};\n-/* ======================================================================\n- OpenLayers/Layer/Boxes.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Layer/Markers.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Boxes\n- * Draw divs as 'boxes' on the layer. \n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n- */\n-OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {\n-\n- /**\n- * Constructor: OpenLayers.Layer.Boxes\n- *\n- * Parameters:\n- * name - {String} \n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n-\n- /**\n- * Method: drawMarker \n- * Calculate the pixel location for the marker, create it, and\n- * add it to the layer's div\n- *\n- * Parameters: \n- * marker - {<OpenLayers.Marker.Box>} \n- */\n- drawMarker: function(marker) {\n- var topleft = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.left,\n- lat: marker.bounds.top\n- });\n- var botright = this.map.getLayerPxFromLonLat({\n- lon: marker.bounds.right,\n- lat: marker.bounds.bottom\n- });\n- if (botright == null || topleft == null) {\n- marker.display(false);\n- } else {\n- var markerDiv = marker.draw(topleft, {\n- w: Math.max(1, botright.x - topleft.x),\n- h: Math.max(1, botright.y - topleft.y)\n- });\n- if (!marker.drawn) {\n- this.div.appendChild(markerDiv);\n- marker.drawn = true;\n- }\n- }\n- },\n-\n-\n- /**\n- * APIMethod: removeMarker \n- * \n- * Parameters:\n- * marker - {<OpenLayers.Marker.Box>} \n- */\n- removeMarker: function(marker) {\n- OpenLayers.Util.removeItem(this.markers, marker);\n- if ((marker.div != null) &&\n- (marker.div.parentNode == this.div)) {\n- this.div.removeChild(marker.div);\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Boxes\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Image.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer.js\n- * @requires OpenLayers/Tile/Image.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Image\n- * Instances of OpenLayers.Layer.Image are used to display data from a web\n- * accessible image as a map layer. Create a new image layer with the\n- * <OpenLayers.Layer.Image> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer>\n- */\n-OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {\n-\n- /**\n- * Property: isBaseLayer\n- * {Boolean} The layer is a base layer. Default is true. Set this property\n- * in the layer options\n- */\n- isBaseLayer: true,\n-\n- /**\n- * Property: url\n- * {String} URL of the image to use\n- */\n- url: null,\n-\n- /**\n- * Property: extent\n- * {<OpenLayers.Bounds>} The image bounds in map units. This extent will\n- * also be used as the default maxExtent for the layer. If you wish\n- * to have a maxExtent that is different than the image extent, set the\n- * maxExtent property of the options argument (as with any other layer).\n- */\n- extent: null,\n-\n- /**\n- * Property: size\n- * {<OpenLayers.Size>} The image size in pixels\n- */\n- size: null,\n-\n- /**\n- * Property: tile\n- * {<OpenLayers.Tile.Image>}\n- */\n- tile: null,\n-\n- /**\n- * Property: aspectRatio\n- * {Float} The ratio of height/width represented by a single pixel in the\n- * graphic\n- */\n- aspectRatio: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Image\n- * Create a new image layer\n- *\n- * Parameters:\n- * name - {String} A name for the layer.\n- * url - {String} Relative or absolute path to the image\n- * extent - {<OpenLayers.Bounds>} The extent represented by the image\n- * size - {<OpenLayers.Size>} The size (in pixels) of the image\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, url, extent, size, options) {\n- this.url = url;\n- this.extent = extent;\n- this.maxExtent = extent;\n- this.size = size;\n- OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);\n-\n- this.aspectRatio = (this.extent.getHeight() / this.size.h) /\n- (this.extent.getWidth() / this.size.w);\n- },\n-\n- /**\n- * Method: destroy\n- * Destroy this layer\n- */\n- destroy: function() {\n- if (this.tile) {\n- this.removeTileMonitoringHooks(this.tile);\n- this.tile.destroy();\n- this.tile = null;\n- }\n- OpenLayers.Layer.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Paramters:\n- * obj - {Object} An optional layer (is this ever used?)\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Image>} An exact copy of this layer\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Image(this.name,\n- this.url,\n- this.extent,\n- this.size,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n- /**\n- * APIMethod: setMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- setMap: function(map) {\n- /**\n- * If nothing to do with resolutions has been set, assume a single\n- * resolution determined by ratio*extent/size - if an image has a\n- * pixel aspect ratio different than one (as calculated above), the\n- * image will be stretched in one dimension only.\n- */\n- if (this.options.maxResolution == null) {\n- this.options.maxResolution = this.aspectRatio *\n- this.extent.getWidth() /\n- this.size.w;\n- }\n- OpenLayers.Layer.prototype.setMap.apply(this, arguments);\n- },\n-\n- /** \n- * Method: moveTo\n- * Create the tile for the image or resize it for the new resolution\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean}\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- OpenLayers.Layer.prototype.moveTo.apply(this, arguments);\n-\n- var firstRendering = (this.tile == null);\n-\n- if (zoomChanged || firstRendering) {\n-\n- //determine new tile size\n- this.setTileSize();\n-\n- //determine new position (upper left corner of new bounds)\n- var ulPx = this.map.getLayerPxFromLonLat({\n- lon: this.extent.left,\n- lat: this.extent.top\n- });\n-\n- if (firstRendering) {\n- //create the new tile\n- this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent,\n- null, this.tileSize);\n- this.addTileMonitoringHooks(this.tile);\n- } else {\n- //just resize the tile and set it's new position\n- this.tile.size = this.tileSize.clone();\n- this.tile.position = ulPx.clone();\n- }\n- this.tile.draw();\n- }\n- },\n-\n- /**\n- * Set the tile size based on the map size.\n- */\n- setTileSize: function() {\n- var tileWidth = this.extent.getWidth() / this.map.getResolution();\n- var tileHeight = this.extent.getHeight() / this.map.getResolution();\n- this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);\n- },\n-\n- /** \n- * Method: addTileMonitoringHooks\n- * This function takes a tile as input and adds the appropriate hooks to \n- * the tile so that the layer can keep track of the loading tiles.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n- */\n- addTileMonitoringHooks: function(tile) {\n- tile.onLoadStart = function() {\n- this.events.triggerEvent(\"loadstart\");\n- };\n- tile.events.register(\"loadstart\", this, tile.onLoadStart);\n-\n- tile.onLoadEnd = function() {\n- this.events.triggerEvent(\"loadend\");\n- };\n- tile.events.register(\"loadend\", this, tile.onLoadEnd);\n- tile.events.register(\"unload\", this, tile.onLoadEnd);\n- },\n-\n- /** \n- * Method: removeTileMonitoringHooks\n- * This function takes a tile as input and removes the tile hooks \n- * that were added in <addTileMonitoringHooks>.\n- * \n- * Parameters: \n- * tile - {<OpenLayers.Tile>}\n- */\n- removeTileMonitoringHooks: function(tile) {\n- tile.unload();\n- tile.events.un({\n- \"loadstart\": tile.onLoadStart,\n- \"loadend\": tile.onLoadEnd,\n- \"unload\": tile.onLoadEnd,\n- scope: this\n- });\n- },\n-\n- /**\n- * APIMethod: setUrl\n- * \n- * Parameters:\n- * newUrl - {String}\n- */\n- setUrl: function(newUrl) {\n- this.url = newUrl;\n- this.tile.draw();\n- },\n-\n- /** \n- * APIMethod: getURL\n- * The url we return is always the same (the image itself never changes)\n- * so we can ignore the bounds parameter (it will always be the same, \n- * anyways) \n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- getURL: function(bounds) {\n- return this.url;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Image\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/PointGrid.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.PointGrid\n- * A point grid layer dynamically generates a regularly spaced grid of point\n- * features. This is a specialty layer for cases where an application needs\n- * a regular grid of points. It can be used, for example, in an editing\n- * environment to snap to a grid.\n- *\n- * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor.\n- * (code)\n- * // create a grid with points spaced at 10 map units\n- * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10});\n- *\n- * // create a grid with different x/y spacing rotated 15 degrees clockwise.\n- * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15});\n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n- */\n-OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * APIProperty: dx\n- * {Number} Point grid spacing in the x-axis direction (map units). \n- * Read-only. Use the <setSpacing> method to modify this value.\n- */\n- dx: null,\n-\n- /**\n- * APIProperty: dy\n- * {Number} Point grid spacing in the y-axis direction (map units). \n- * Read-only. Use the <setSpacing> method to modify this value.\n- */\n- dy: null,\n-\n- /**\n- * APIProperty: ratio\n- * {Number} Ratio of the desired grid size to the map viewport size. \n- * Default is 1.5. Larger ratios mean the grid is recalculated less often \n- * while panning. The <maxFeatures> setting has precedence when determining\n- * grid size. Read-only. Use the <setRatio> method to modify this value.\n- */\n- ratio: 1.5,\n-\n- /**\n- * APIProperty: maxFeatures\n- * {Number} The maximum number of points to generate in the grid. Default\n- * is 250. Read-only. Use the <setMaxFeatures> method to modify this value.\n- */\n- maxFeatures: 250,\n-\n- /**\n- * APIProperty: rotation\n- * {Number} Grid rotation (in degrees clockwise from the positive x-axis).\n- * Default is 0. Read-only. Use the <setRotation> method to modify this\n- * value.\n- */\n- rotation: 0,\n-\n- /**\n- * APIProperty: origin\n- * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with \n- * the origin. If not set at construction, the center of the map's maximum \n- * extent is used. Read-only. Use the <setOrigin> method to modify this \n- * value.\n- */\n- origin: null,\n-\n- /**\n- * Property: gridBounds\n- * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional \n- * rotation applied).\n- */\n- gridBounds: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.PointGrid\n- * Creates a new point grid layer.\n- *\n- * Parameters:\n- * config - {Object} An object containing all configuration properties for\n- * the layer. The <dx> and <dy> properties are required to be set at \n- * construction. Any other layer properties may be set in this object.\n- */\n- initialize: function(config) {\n- config = config || {};\n- OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);\n- },\n-\n- /** \n- * Method: setMap\n- * The layer has been added to the map. \n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>} \n- */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- map.events.register(\"moveend\", this, this.onMoveEnd);\n- },\n-\n- /**\n- * Method: removeMap\n- * The layer has been removed from the map.\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- map.events.unregister(\"moveend\", this, this.onMoveEnd);\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: setRatio\n- * Set the grid <ratio> property and update the grid. Can only be called\n- * after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * ratio - {Number}\n- */\n- setRatio: function(ratio) {\n- this.ratio = ratio;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * APIMethod: setMaxFeatures\n- * Set the grid <maxFeatures> property and update the grid. Can only be \n- * called after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * maxFeatures - {Number}\n- */\n- setMaxFeatures: function(maxFeatures) {\n- this.maxFeatures = maxFeatures;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * APIMethod: setSpacing\n- * Set the grid <dx> and <dy> properties and update the grid. If only one\n- * argument is provided, it will be set as <dx> and <dy>. Can only be \n- * called after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * dx - {Number}\n- * dy - {Number}\n- */\n- setSpacing: function(dx, dy) {\n- this.dx = dx;\n- this.dy = dy || dx;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * APIMethod: setOrigin\n- * Set the grid <origin> property and update the grid. Can only be called\n- * after the layer has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * origin - {<OpenLayers.LonLat>}\n- */\n- setOrigin: function(origin) {\n- this.origin = origin;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * APIMethod: getOrigin\n- * Get the grid <origin> property.\n- *\n- * Returns:\n- * {<OpenLayers.LonLat>} The grid origin.\n- */\n- getOrigin: function() {\n- if (!this.origin) {\n- this.origin = this.map.getExtent().getCenterLonLat();\n- }\n- return this.origin;\n- },\n-\n- /**\n- * APIMethod: setRotation\n- * Set the grid <rotation> property and update the grid. Rotation values\n- * are in degrees clockwise from the positive x-axis (negative values\n- * for counter-clockwise rotation). Can only be called after the layer \n- * has been added to a map with a center/extent.\n- *\n- * Parameters:\n- * rotation - {Number} Degrees clockwise from the positive x-axis.\n- */\n- setRotation: function(rotation) {\n- this.rotation = rotation;\n- this.updateGrid(true);\n- },\n-\n- /**\n- * Method: onMoveEnd\n- * Listener for map \"moveend\" events.\n- */\n- onMoveEnd: function() {\n- this.updateGrid();\n- },\n-\n- /**\n- * Method: getViewBounds\n- * Gets the (potentially rotated) view bounds for grid calculations.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>}\n- */\n- getViewBounds: function() {\n- var bounds = this.map.getExtent();\n- if (this.rotation) {\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var rect = bounds.toGeometry();\n- rect.rotate(-this.rotation, rotationOrigin);\n- bounds = rect.getBounds();\n- }\n- return bounds;\n- },\n-\n- /**\n- * Method: updateGrid\n- * Update the grid.\n- *\n- * Parameters:\n- * force - {Boolean} Update the grid even if the previous bounds are still\n- * valid.\n- */\n- updateGrid: function(force) {\n- if (force || this.invalidBounds()) {\n- var viewBounds = this.getViewBounds();\n- var origin = this.getOrigin();\n- var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);\n- var viewBoundsWidth = viewBounds.getWidth();\n- var viewBoundsHeight = viewBounds.getHeight();\n- var aspectRatio = viewBoundsWidth / viewBoundsHeight;\n- var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);\n- var maxWidth = maxHeight * aspectRatio;\n- var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);\n- var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);\n- var center = viewBounds.getCenterLonLat();\n- this.gridBounds = new OpenLayers.Bounds(\n- center.lon - (gridWidth / 2),\n- center.lat - (gridHeight / 2),\n- center.lon + (gridWidth / 2),\n- center.lat + (gridHeight / 2)\n- );\n- var rows = Math.floor(gridHeight / this.dy);\n- var cols = Math.floor(gridWidth / this.dx);\n- var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));\n- var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));\n- var features = new Array(rows * cols);\n- var x, y, point;\n- for (var i = 0; i < cols; ++i) {\n- x = gridLeft + (i * this.dx);\n- for (var j = 0; j < rows; ++j) {\n- y = gridBottom + (j * this.dy);\n- point = new OpenLayers.Geometry.Point(x, y);\n- if (this.rotation) {\n- point.rotate(this.rotation, rotationOrigin);\n- }\n- features[(i * rows) + j] = new OpenLayers.Feature.Vector(point);\n- }\n- }\n- this.destroyFeatures(this.features, {\n- silent: true\n- });\n- this.addFeatures(features, {\n- silent: true\n- });\n- }\n- },\n-\n- /**\n- * Method: invalidBounds\n- * Determine whether the previously generated point grid is invalid. \n- * This occurs when the map bounds extends beyond the previously \n- * generated grid bounds.\n- *\n- * Returns:\n- * {Boolean} \n- */\n- invalidBounds: function() {\n- return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.PointGrid\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Layer/GeoRSS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Markers.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.GeoRSS\n- * Add GeoRSS Point features to your map. \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n- */\n-OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {\n-\n- /** \n- * Property: location \n- * {String} store url of text file \n- */\n- location: null,\n-\n- /** \n- * Property: features \n- * {Array(<OpenLayers.Feature>)} \n- */\n- features: null,\n-\n- /**\n- * APIProperty: formatOptions\n- * {Object} Hash of options which should be passed to the format when it is\n- * created. Must be passed in the constructor.\n- */\n- formatOptions: null,\n-\n- /** \n- * Property: selectedFeature \n- * {<OpenLayers.Feature>} \n- */\n- selectedFeature: null,\n-\n- /** \n- * APIProperty: icon \n- * {<OpenLayers.Icon>}. This determines the Icon to be used on the map\n- * for this GeoRSS layer.\n- */\n- icon: null,\n-\n- /**\n- * APIProperty: popupSize\n- * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If \n- * not provided, defaults to 250px by 120px. \n- */\n- popupSize: null,\n-\n- /** \n- * APIProperty: useFeedTitle \n- * {Boolean} Set layer.name to the first <title> element in the feed. Default is true. \n- */\n- useFeedTitle: true,\n-\n- /**\n- * Constructor: OpenLayers.Layer.GeoRSS\n- * Create a GeoRSS Layer.\n- *\n- * Parameters:\n- * name - {String} \n- * location - {String} \n- * options - {Object}\n- */\n- initialize: function(name, location, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);\n- this.location = location;\n- this.features = [];\n- },\n-\n- /**\n- * Method: destroy \n- */\n- destroy: function() {\n- // Warning: Layer.Markers.destroy() must be called prior to calling\n- // clearFeatures() here, otherwise we leak memory. Indeed, if\n- // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n- // able to remove the marker image elements from the layer's div since\n- // the markers will have been destroyed by clearFeatures().\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null;\n- },\n-\n- /**\n- * Method: loadRSS\n- * Start the load of the RSS data. Don't do this when we first add the layer,\n- * since we may not be visible at any point, and it would therefore be a waste.\n- */\n- loadRSS: function() {\n- if (!this.loaded) {\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- scope: this\n- });\n- this.loaded = true;\n- }\n- },\n-\n- /**\n- * Method: moveTo\n- * If layer is visible and RSS has not been loaded, load RSS. \n- * \n- * Parameters:\n- * bounds - {Object} \n- * zoomChanged - {Object} \n- * minor - {Object} \n- */\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadRSS();\n- }\n- },\n-\n- /**\n- * Method: parseData\n- * Parse the data returned from the Events call.\n- *\n- * Parameters:\n- * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n- */\n- parseData: function(ajaxRequest) {\n- var doc = ajaxRequest.responseXML;\n- if (!doc || !doc.documentElement) {\n- doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);\n- }\n-\n- if (this.useFeedTitle) {\n- var name = null;\n- try {\n- name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;\n- } catch (e) {\n- try {\n- name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;\n- } catch (e) {}\n- }\n- if (name) {\n- this.setName(name);\n- }\n- }\n-\n- var options = {};\n-\n- OpenLayers.Util.extend(options, this.formatOptions);\n-\n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject();\n- }\n-\n- var format = new OpenLayers.Format.GeoRSS(options);\n- var features = format.read(doc);\n-\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n-\n- // we don't support features with no geometry in the GeoRSS\n- // layer at this time. \n- if (!feature.geometry) {\n- continue;\n- }\n-\n- var title = feature.attributes.title ?\n- feature.attributes.title : \"Untitled\";\n-\n- var description = feature.attributes.description ?\n- feature.attributes.description : \"No description.\";\n-\n- var link = feature.attributes.link ? feature.attributes.link : \"\";\n-\n- var location = feature.geometry.getBounds().getCenterLonLat();\n-\n-\n- data.icon = this.icon == null ?\n- OpenLayers.Marker.defaultIcon() :\n- this.icon.clone();\n-\n- data.popupSize = this.popupSize ?\n- this.popupSize.clone() :\n- new OpenLayers.Size(250, 120);\n-\n- if (title || description) {\n- // we have supplemental data, store them.\n- data.title = title;\n- data.description = description;\n-\n- var contentHTML = '<div class=\"olLayerGeoRSSClose\">[x]</div>';\n- contentHTML += '<div class=\"olLayerGeoRSSTitle\">';\n- if (link) {\n- contentHTML += '<a class=\"link\" href=\"' + link + '\" target=\"_blank\">';\n- }\n- contentHTML += title;\n- if (link) {\n- contentHTML += '</a>';\n- }\n- contentHTML += '</div>';\n- contentHTML += '<div style=\"\" class=\"olLayerGeoRSSDescription\">';\n- contentHTML += description;\n- contentHTML += '</div>';\n- data['popupContentHTML'] = contentHTML;\n- }\n- var feature = new OpenLayers.Feature(this, location, data);\n- this.features.push(feature);\n- var marker = feature.createMarker();\n- marker.events.register('click', feature, this.markerClick);\n- this.addMarker(marker);\n- }\n- this.events.triggerEvent(\"loadend\");\n- },\n-\n- /**\n- * Method: markerClick\n- *\n- * Parameters:\n- * evt - {Event} \n- */\n- markerClick: function(evt) {\n- var sameMarkerClicked = (this == this.layer.selectedFeature);\n- this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- if (!sameMarkerClicked) {\n- var popup = this.createPopup();\n- OpenLayers.Event.observe(popup.div, \"click\",\n- OpenLayers.Function.bind(function() {\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- }, this)\n- );\n- this.layer.map.addPopup(popup);\n- }\n- OpenLayers.Event.stop(evt);\n- },\n-\n- /**\n- * Method: clearFeatures\n- * Destroy all features in this layer.\n- */\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy();\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.GeoRSS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Google.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/SphericalMercator.js\n- * @requires OpenLayers/Layer/EventPane.js\n- * @requires OpenLayers/Layer/FixedZoomLevels.js\n- * @requires OpenLayers/Lang.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Google\n- * \n- * Provides a wrapper for Google's Maps API\n- * Normally the Terms of Use for this API do not allow wrapping, but Google\n- * have provided written consent to OpenLayers for this - see email in \n- * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.SphericalMercator>\n- * - <OpenLayers.Layer.EventPane>\n- * - <OpenLayers.Layer.FixedZoomLevels>\n- */\n-OpenLayers.Layer.Google = OpenLayers.Class(\n- OpenLayers.Layer.EventPane,\n- OpenLayers.Layer.FixedZoomLevels, {\n-\n- /** \n- * Constant: MIN_ZOOM_LEVEL\n- * {Integer} 0 \n- */\n- MIN_ZOOM_LEVEL: 0,\n-\n- /** \n- * Constant: MAX_ZOOM_LEVEL\n- * {Integer} 21\n- */\n- MAX_ZOOM_LEVEL: 21,\n-\n- /** \n- * Constant: RESOLUTIONS\n- * {Array(Float)} Hardcode these resolutions so that they are more closely\n- * tied with the standard wms projection\n- */\n- RESOLUTIONS: [\n- 1.40625,\n- 0.703125,\n- 0.3515625,\n- 0.17578125,\n- 0.087890625,\n- 0.0439453125,\n- 0.02197265625,\n- 0.010986328125,\n- 0.0054931640625,\n- 0.00274658203125,\n- 0.001373291015625,\n- 0.0006866455078125,\n- 0.00034332275390625,\n- 0.000171661376953125,\n- 0.0000858306884765625,\n- 0.00004291534423828125,\n- 0.00002145767211914062,\n- 0.00001072883605957031,\n- 0.00000536441802978515,\n- 0.00000268220901489257,\n- 0.0000013411045074462891,\n- 0.00000067055225372314453\n- ],\n-\n- /**\n- * APIProperty: type\n- * {GMapType}\n- */\n- type: null,\n-\n- /**\n- * APIProperty: wrapDateLine\n- * {Boolean} Allow user to pan forever east/west. Default is true. \n- * Setting this to false only restricts panning if \n- * <sphericalMercator> is true. \n- */\n- wrapDateLine: true,\n-\n- /**\n- * APIProperty: sphericalMercator\n- * {Boolean} Should the map act as a mercator-projected map? This will\n- * cause all interactions with the map to be in the actual map \n- * projection, which allows support for vector drawing, overlaying \n- * other maps, etc. \n- */\n- sphericalMercator: false,\n-\n- /**\n- * Property: version\n- * {Number} The version of the Google Maps API\n- */\n- version: null,\n-\n- /** \n- * Constructor: OpenLayers.Layer.Google\n- * \n- * Parameters:\n- * name - {String} A name for the layer.\n- * options - {Object} An optional object whose properties will be set\n- * on the layer.\n- */\n- initialize: function(name, options) {\n- options = options || {};\n- if (!options.version) {\n- options.version = typeof GMap2 === \"function\" ? \"2\" : \"3\";\n- }\n- var mixin = OpenLayers.Layer.Google[\"v\" +\n- options.version.replace(/\\./g, \"_\")];\n- if (mixin) {\n- OpenLayers.Util.applyDefaults(options, mixin);\n- } else {\n- throw \"Unsupported Google Maps API version: \" + options.version;\n- }\n-\n- OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);\n- if (options.maxExtent) {\n- options.maxExtent = options.maxExtent.clone();\n- }\n-\n- OpenLayers.Layer.EventPane.prototype.initialize.apply(this,\n- [name, options]);\n- OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,\n- [name, options]);\n-\n- if (this.sphericalMercator) {\n- OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);\n- this.initMercatorParameters();\n- }\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Google>} An exact clone of this layer\n- */\n- clone: function() {\n- /**\n- * This method isn't intended to be called by a subclass and it\n- * doesn't call the same method on the superclass. We don't call\n- * the super's clone because we don't want properties that are set\n- * on this layer after initialize (i.e. this.mapObject etc.).\n- */\n- return new OpenLayers.Layer.Google(\n- this.name, this.getOptions()\n- );\n- },\n-\n- /**\n- * APIMethod: setVisibility\n- * Set the visibility flag for the layer and hide/show & redraw \n- * accordingly. Fire event unless otherwise specified\n- * \n- * Note that visibility is no longer simply whether or not the layer's\n- * style.display is set to \"block\". Now we store a 'visibility' state \n- * property on the layer class, this allows us to remember whether or \n- * not we *desire* for a layer to be visible. In the case where the \n- * map's resolution is out of the layer's range, this desire may be \n- * subverted.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the layer (if in range)\n- */\n- setVisibility: function(visible) {\n- // sharing a map container, opacity has to be set per layer\n- var opacity = this.opacity == null ? 1 : this.opacity;\n- OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);\n- this.setOpacity(opacity);\n- },\n-\n- /** \n- * APIMethod: display\n- * Hide or show the Layer\n- * \n- * Parameters:\n- * visible - {Boolean}\n- */\n- display: function(visible) {\n- if (!this._dragging) {\n- this.setGMapVisibility(visible);\n- }\n- OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);\n- },\n-\n- /**\n- * Method: moveTo\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- this._dragging = dragging;\n- OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);\n- delete this._dragging;\n- },\n-\n- /**\n- * APIMethod: setOpacity\n- * Sets the opacity for the entire layer (all images)\n- * \n- * Parameters:\n- * opacity - {Float}\n- */\n- setOpacity: function(opacity) {\n- if (opacity !== this.opacity) {\n- if (this.map != null) {\n- this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"opacity\"\n- });\n- }\n- this.opacity = opacity;\n- }\n- // Though this layer's opacity may not change, we're sharing a container\n- // and need to update the opacity for the entire container.\n- if (this.getVisibility()) {\n- var container = this.getMapContainer();\n- OpenLayers.Util.modifyDOMElement(\n- container, null, null, null, null, null, null, opacity\n- );\n- }\n- },\n-\n- /**\n- * APIMethod: destroy\n- * Clean up this layer.\n- */\n- destroy: function() {\n- /**\n- * We have to override this method because the event pane destroy\n- * deletes the mapObject reference before removing this layer from\n- * the map.\n- */\n- if (this.map) {\n- this.setGMapVisibility(false);\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache && cache.count <= 1) {\n- this.removeGMapElements();\n- }\n- }\n- OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: removeGMapElements\n- * Remove all elements added to the dom. This should only be called if\n- * this is the last of the Google layers for the given map.\n- */\n- removeGMapElements: function() {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // remove shared elements from dom\n- var container = this.mapObject && this.getMapContainer();\n- if (container && container.parentNode) {\n- container.parentNode.removeChild(container);\n- }\n- var termsOfUse = cache.termsOfUse;\n- if (termsOfUse && termsOfUse.parentNode) {\n- termsOfUse.parentNode.removeChild(termsOfUse);\n- }\n- var poweredBy = cache.poweredBy;\n- if (poweredBy && poweredBy.parentNode) {\n- poweredBy.parentNode.removeChild(poweredBy);\n- }\n- if (this.mapObject && window.google && google.maps &&\n- google.maps.event && google.maps.event.clearListeners) {\n- google.maps.event.clearListeners(this.mapObject, 'tilesloaded');\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: removeMap\n- * On being removed from the map, also remove termsOfUse and poweredBy divs\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- // hide layer before removing\n- if (this.visibility && this.mapObject) {\n- this.setGMapVisibility(false);\n- }\n- // check to see if last Google layer in this map\n- var cache = OpenLayers.Layer.Google.cache[map.id];\n- if (cache) {\n- if (cache.count <= 1) {\n- this.removeGMapElements();\n- delete OpenLayers.Layer.Google.cache[map.id];\n- } else {\n- // decrement the layer count\n- --cache.count;\n- }\n- }\n- // remove references to gmap elements\n- delete this.termsOfUse;\n- delete this.poweredBy;\n- delete this.mapObject;\n- delete this.dragObject;\n- OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n- /**\n- * APIMethod: getOLBoundsFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the \n- * passed-in MapObject Bounds.\n- * Returns null if null value is passed in.\n- */\n- getOLBoundsFromMapObjectBounds: function(moBounds) {\n- var olBounds = null;\n- if (moBounds != null) {\n- var sw = moBounds.getSouthWest();\n- var ne = moBounds.getNorthEast();\n- if (this.sphericalMercator) {\n- sw = this.forwardMercator(sw.lng(), sw.lat());\n- ne = this.forwardMercator(ne.lng(), ne.lat());\n- } else {\n- sw = new OpenLayers.LonLat(sw.lng(), sw.lat());\n- ne = new OpenLayers.LonLat(ne.lng(), ne.lat());\n- }\n- olBounds = new OpenLayers.Bounds(sw.lon,\n- sw.lat,\n- ne.lon,\n- ne.lat);\n- }\n- return olBounds;\n- },\n-\n- /** \n- * APIMethod: getWarningHTML\n- * \n- * Returns: \n- * {String} String with information on why layer is broken, how to get\n- * it working.\n- */\n- getWarningHTML: function() {\n- return OpenLayers.i18n(\"googleWarning\");\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n-\n-\n- // Get&Set Center, Zoom\n-\n- /**\n- * APIMethod: getMapObjectCenter\n- * \n- * Returns: \n- * {Object} The mapObject's current center in Map Object format\n- */\n- getMapObjectCenter: function() {\n- return this.mapObject.getCenter();\n- },\n-\n- /** \n- * APIMethod: getMapObjectZoom\n- * \n- * Returns:\n- * {Integer} The mapObject's current zoom, in Map Object format\n- */\n- getMapObjectZoom: function() {\n- return this.mapObject.getZoom();\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n- /**\n- * APIMethod: getLongitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Longitude of the given MapObject LonLat\n- */\n- getLongitudeFromMapObjectLonLat: function(moLonLat) {\n- return this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :\n- moLonLat.lng();\n- },\n-\n- /**\n- * APIMethod: getLatitudeFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Float} Latitude of the given MapObject LonLat\n- */\n- getLatitudeFromMapObjectLonLat: function(moLonLat) {\n- var lat = this.sphericalMercator ?\n- this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :\n- moLonLat.lat();\n- return lat;\n- },\n-\n- // Pixel\n-\n- /**\n- * APIMethod: getXFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} X value of the MapObject Pixel\n- */\n- getXFromMapObjectPixel: function(moPixel) {\n- return moPixel.x;\n- },\n-\n- /**\n- * APIMethod: getYFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Integer} Y value of the MapObject Pixel\n- */\n- getYFromMapObjectPixel: function(moPixel) {\n- return moPixel.y;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Google\"\n- });\n-\n-/**\n- * Property: OpenLayers.Layer.Google.cache\n- * {Object} Cache for elements that should only be created once per map.\n- */\n-OpenLayers.Layer.Google.cache = {};\n-\n-\n-/**\n- * Constant: OpenLayers.Layer.Google.v2\n- * \n- * Mixin providing functionality specific to the Google Maps API v2.\n- * \n- * This API has been deprecated by Google.\n- * Developers are encouraged to migrate to v3 of the API; support for this\n- * is provided by <OpenLayers.Layer.Google.v3>\n- */\n-OpenLayers.Layer.Google.v2 = {\n-\n- /**\n- * Property: termsOfUse\n- * {DOMElement} Div for Google's copyright and terms of use link\n- */\n- termsOfUse: null,\n-\n- /**\n- * Property: poweredBy\n- * {DOMElement} Div for Google's powered by logo and link\n- */\n- poweredBy: null,\n-\n- /**\n- * Property: dragObject\n- * {GDraggableObject} Since 2.93, Google has exposed the ability to get\n- * the maps GDraggableObject. We can now use this for smooth panning\n- */\n- dragObject: null,\n-\n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners. If we can't \n- * load GMap2, then display a warning message.\n- */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = G_NORMAL_MAP;\n- }\n- var mapObject, termsOfUse, poweredBy;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- termsOfUse = cache.termsOfUse;\n- poweredBy = cache.poweredBy;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n-\n- var container = this.map.viewPortDiv;\n- var div = document.createElement(\"div\");\n- div.id = this.map.id + \"_GMap2Container\";\n- div.style.position = \"absolute\";\n- div.style.width = \"100%\";\n- div.style.height = \"100%\";\n- container.appendChild(div);\n-\n- // create GMap and shuffle elements\n- try {\n- mapObject = new GMap2(div);\n-\n- // move the ToS and branding stuff up to the container div\n- termsOfUse = div.lastChild;\n- container.appendChild(termsOfUse);\n- termsOfUse.style.zIndex = \"1100\";\n- termsOfUse.style.right = \"\";\n- termsOfUse.style.bottom = \"\";\n- termsOfUse.className = \"olLayerGoogleCopyright\";\n-\n- poweredBy = div.lastChild;\n- container.appendChild(poweredBy);\n- poweredBy.style.zIndex = \"1100\";\n- poweredBy.style.right = \"\";\n- poweredBy.style.bottom = \"\";\n- poweredBy.className = \"olLayerGooglePoweredBy gmnoprint\";\n-\n- } catch (e) {\n- throw (e);\n- }\n- // cache elements for use by any other google layers added to\n- // this same map\n- OpenLayers.Layer.Google.cache[this.map.id] = {\n- mapObject: mapObject,\n- termsOfUse: termsOfUse,\n- poweredBy: poweredBy,\n- count: 1\n- };\n- }\n-\n- this.mapObject = mapObject;\n- this.termsOfUse = termsOfUse;\n- this.poweredBy = poweredBy;\n-\n- // ensure this layer type is one of the mapObject types\n- if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),\n- this.type) === -1) {\n- this.mapObject.addMapType(this.type);\n- }\n-\n- //since v 2.93 getDragObject is now available.\n- if (typeof mapObject.getDragObject == \"function\") {\n- this.dragObject = mapObject.getDragObject();\n- } else {\n- this.dragPanMapObject = null;\n- }\n-\n- if (this.isBaseLayer === false) {\n- this.setGMapVisibility(this.div.style.display !== \"none\");\n- }\n-\n- },\n-\n- /**\n- * APIMethod: onMapResize\n- */\n- onMapResize: function() {\n- // workaround for resizing of invisible or not yet fully loaded layers\n- // where GMap2.checkResize() does not work. We need to load the GMap\n- // for the old div size, then checkResize(), and then call\n- // layer.moveTo() to trigger GMap.setCenter() (which will finish\n- // the GMap initialization).\n- if (this.visibility && this.mapObject.isLoaded()) {\n- this.mapObject.checkResize();\n- } else {\n- if (!this._resized) {\n- var layer = this;\n- var handle = GEvent.addListener(this.mapObject, \"load\", function() {\n- GEvent.removeListener(handle);\n- delete layer._resized;\n- layer.mapObject.checkResize();\n- layer.moveTo(layer.map.getCenter(), layer.map.getZoom());\n- });\n- }\n- this._resized = true;\n- }\n- },\n-\n- /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n- */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- var container = this.mapObject.getContainer();\n- if (visible === true) {\n- this.mapObject.setMapType(this.type);\n- container.style.display = \"\";\n- this.termsOfUse.style.left = \"\";\n- this.termsOfUse.style.display = \"\";\n- this.poweredBy.style.display = \"\";\n- cache.displayed = this.id;\n- } else {\n- if (cache.displayed === this.id) {\n- delete cache.displayed;\n- }\n- if (!cache.displayed) {\n- container.style.display = \"none\";\n- this.termsOfUse.style.display = \"none\";\n- // move ToU far to the left in addition to setting display\n- // to \"none\", because at the end of the GMap2 load\n- // sequence, display: none will be unset and ToU would be\n- // visible after loading a map with a google layer that is\n- // initially hidden. \n- this.termsOfUse.style.left = \"-9999px\";\n- this.poweredBy.style.display = \"none\";\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: getMapContainer\n- * \n- * Returns:\n- * {DOMElement} the GMap container's div\n- */\n- getMapContainer: function() {\n- return this.mapObject.getContainer();\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n- /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n- * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n- */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),\n- new GLatLng(ne.lat, ne.lon));\n- }\n- return moBounds;\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n-\n-\n- // Get&Set Center, Zoom\n-\n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n- * \n- * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n- */\n- setMapObjectCenter: function(center, zoom) {\n- this.mapObject.setCenter(center, zoom);\n- },\n-\n- /**\n- * APIMethod: dragPanMapObject\n- * \n- * Parameters:\n- * dX - {Integer}\n- * dY - {Integer}\n- */\n- dragPanMapObject: function(dX, dY) {\n- this.dragObject.moveBy(new GSize(-dX, dY));\n- },\n-\n-\n- // LonLat - Pixel Translation\n-\n- /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n- */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- return this.mapObject.fromContainerPixelToLatLng(moPixel);\n- },\n-\n- /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n- */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- return this.mapObject.fromLatLngToContainerPixel(moLonLat);\n- },\n-\n-\n- // Bounds\n-\n- /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n- */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n- },\n-\n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n- /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n- * \n- * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n- * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n- */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new GLatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new GLatLng(lat, lon);\n- }\n- return gLatLng;\n- },\n-\n- // Pixel\n-\n- /**\n- * APIMethod: getMapObjectPixelFromXY\n- * \n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n- * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n- */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new GPoint(x, y);\n- }\n-\n-};\n-/* ======================================================================\n- OpenLayers/Layer/KaMap.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.KaMap\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /** \n- * APIProperty: isBaseLayer\n- * {Boolean} KaMap Layer is always a base layer \n- */\n- isBaseLayer: true,\n-\n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} parameters set by default. The default parameters set \n- * the format via the 'i' parameter to 'jpeg'. \n- */\n- DEFAULT_PARAMS: {\n- i: 'jpeg',\n- map: ''\n- },\n-\n- /**\n- * Constructor: OpenLayers.Layer.KaMap\n- * \n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object} Parameters to be sent to the HTTP server in the\n- * query string for the tile. The format can be set via the 'i'\n- * parameter (defaults to jpg) , and the map should be set via \n- * the 'map' parameter. It has been reported that ka-Map may behave\n- * inconsistently if your format parameter does not match the format\n- * parameter configured in your config.php. (See ticket #327 for more\n- * information.)\n- * options - {Object} Additional options for the layer. Any of the \n- * APIProperties listed on this layer, and any layer types it\n- * extends, can be overridden through the options parameter. \n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);\n- this.params = OpenLayers.Util.applyDefaults(\n- this.params, this.DEFAULT_PARAMS\n- );\n- },\n-\n- /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n- return this.getFullRequestString({\n- t: pY,\n- l: pX,\n- s: scale\n- });\n- },\n-\n- /** \n- * Method: calculateGridLayout\n- * ka-Map uses the center point of the map as an origin for \n- * its tiles. Override calculateGridLayout to center tiles \n- * correctly for this case.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bound>}\n- * origin - {<OpenLayers.LonLat>}\n- * resolution - {Number}\n- *\n- * Returns:\n- * {Object} Object containing properties tilelon, tilelat, startcol,\n- * startrow\n- */\n- calculateGridLayout: function(bounds, origin, resolution) {\n- var tilelon = resolution * this.tileSize.w;\n- var tilelat = resolution * this.tileSize.h;\n-\n- var offsetlon = bounds.left;\n- var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;\n-\n- var offsetlat = bounds.top;\n- var tilerow = Math.floor(offsetlat / tilelat) + this.buffer;\n-\n- return {\n- tilelon: tilelon,\n- tilelat: tilelat,\n- startcol: tilecol,\n- startrow: tilerow\n- };\n- },\n-\n- /**\n- * Method: getTileBoundsForGridIndex\n- *\n- * Parameters:\n- * row - {Number} The row of the grid\n- * col - {Number} The column of the grid\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)\n- */\n- getTileBoundsForGridIndex: function(row, col) {\n- var origin = this.getTileOrigin();\n- var tileLayout = this.gridLayout;\n- var tilelon = tileLayout.tilelon;\n- var tilelat = tileLayout.tilelat;\n- var minX = (tileLayout.startcol + col) * tilelon;\n- var minY = (tileLayout.startrow - row) * tilelat;\n- return new OpenLayers.Bounds(\n- minX, minY,\n- minX + tilelon, minY + tilelat\n- );\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Parameters: \n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.KaMap(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n- if (this.tileSize != null) {\n- obj.tileSize = this.tileSize.clone();\n- }\n-\n- // we do not want to copy reference to grid, so we make a new array\n- obj.grid = [];\n-\n- return obj;\n- },\n-\n- /**\n- * APIMethod: getTileBounds\n- * Returns The tile bounds for a layer given a pixel location.\n- *\n- * Parameters:\n- * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.\n- *\n- * Returns:\n- * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.\n- */\n- getTileBounds: function(viewPortPx) {\n- var resolution = this.getResolution();\n- var tileMapWidth = resolution * this.tileSize.w;\n- var tileMapHeight = resolution * this.tileSize.h;\n- var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);\n- var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);\n- var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);\n- return new OpenLayers.Bounds(tileLeft, tileBottom,\n- tileLeft + tileMapWidth,\n- tileBottom + tileMapHeight);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.KaMap\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/Text.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.Text\n- * Read Text format. Create a new instance with the <OpenLayers.Format.Text>\n- * constructor. This reads text which is formatted like CSV text, using\n- * tabs as the seperator by default. It provides parsing of data originally\n- * used in the MapViewerService, described on the wiki. This Format is used\n- * by the <OpenLayers.Layer.Text> class.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {\n-\n- /**\n- * APIProperty: defaultStyle\n- * defaultStyle allows one to control the default styling of the features.\n- * It should be a symbolizer hash. By default, this is set to match the\n- * Layer.Text behavior, which is to use the default OpenLayers Icon.\n- */\n- defaultStyle: null,\n-\n- /**\n- * APIProperty: extractStyles\n- * set to true to extract styles from the TSV files, using information\n- * from the image or icon, iconSize and iconOffset fields. This will result\n- * in features with a symbolizer (style) property set, using the\n- * default symbolizer specified in <defaultStyle>. Set to false if you\n- * wish to use a styleMap or OpenLayers.Style options to style your\n- * layer instead.\n- */\n- extractStyles: true,\n-\n- /**\n- * Constructor: OpenLayers.Format.Text\n- * Create a new parser for TSV Text.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- options = options || {};\n-\n- if (options.extractStyles !== false) {\n- options.defaultStyle = {\n- 'externalGraphic': OpenLayers.Util.getImageLocation(\"marker.png\"),\n- 'graphicWidth': 21,\n- 'graphicHeight': 25,\n- 'graphicXOffset': -10.5,\n- 'graphicYOffset': -12.5\n- };\n- }\n-\n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Return a list of features from a Tab Seperated Values text string.\n- * \n- * Parameters:\n- * text - {String} \n- *\n- * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n- */\n- read: function(text) {\n- var lines = text.split('\\n');\n- var columns;\n- var features = [];\n- // length - 1 to allow for trailing new line\n- for (var lcv = 0; lcv < (lines.length - 1); lcv++) {\n- var currLine = lines[lcv].replace(/^\\s*/, '').replace(/\\s*$/, '');\n-\n- if (currLine.charAt(0) != '#') {\n- /* not a comment */\n-\n- if (!columns) {\n- //First line is columns\n- columns = currLine.split('\\t');\n- } else {\n- var vals = currLine.split('\\t');\n- var geometry = new OpenLayers.Geometry.Point(0, 0);\n- var attributes = {};\n- var style = this.defaultStyle ?\n- OpenLayers.Util.applyDefaults({}, this.defaultStyle) :\n- null;\n- var icon, iconSize, iconOffset, overflow;\n- var set = false;\n- for (var valIndex = 0; valIndex < vals.length; valIndex++) {\n- if (vals[valIndex]) {\n- if (columns[valIndex] == 'point') {\n- var coords = vals[valIndex].split(',');\n- geometry.y = parseFloat(coords[0]);\n- geometry.x = parseFloat(coords[1]);\n- set = true;\n- } else if (columns[valIndex] == 'lat') {\n- geometry.y = parseFloat(vals[valIndex]);\n- set = true;\n- } else if (columns[valIndex] == 'lon') {\n- geometry.x = parseFloat(vals[valIndex]);\n- set = true;\n- } else if (columns[valIndex] == 'title')\n- attributes['title'] = vals[valIndex];\n- else if (columns[valIndex] == 'image' ||\n- columns[valIndex] == 'icon' && style) {\n- style['externalGraphic'] = vals[valIndex];\n- } else if (columns[valIndex] == 'iconSize' && style) {\n- var size = vals[valIndex].split(',');\n- style['graphicWidth'] = parseFloat(size[0]);\n- style['graphicHeight'] = parseFloat(size[1]);\n- } else if (columns[valIndex] == 'iconOffset' && style) {\n- var offset = vals[valIndex].split(',');\n- style['graphicXOffset'] = parseFloat(offset[0]);\n- style['graphicYOffset'] = parseFloat(offset[1]);\n- } else if (columns[valIndex] == 'description') {\n- attributes['description'] = vals[valIndex];\n- } else if (columns[valIndex] == 'overflow') {\n- attributes['overflow'] = vals[valIndex];\n- } else {\n- // For StyleMap filtering, allow additional\n- // columns to be stored as attributes.\n- attributes[columns[valIndex]] = vals[valIndex];\n- }\n- }\n- }\n- if (set) {\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);\n- features.push(feature);\n- }\n- }\n- }\n- }\n- return features;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.Text\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Text.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Markers.js\n- * @requires OpenLayers/Format/Text.js\n- * @requires OpenLayers/Request/XMLHttpRequest.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Text\n- * This layer creates markers given data in a text file. The <location>\n- * property of the layer (specified as a property of the options argument\n- * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited\n- * file with data used to create markers.\n- *\n- * The first row of the data file should be a header line with the column names\n- * of the data. Each column should be delimited by a tab space. The\n- * possible columns are:\n- * - *point* lat,lon of the point where a marker is to be placed\n- * - *lat* Latitude of the point where a marker is to be placed\n- * - *lon* Longitude of the point where a marker is to be placed\n- * - *icon* or *image* URL of marker icon to use.\n- * - *iconSize* Size of Icon to use.\n- * - *iconOffset* Where the top-left corner of the icon is to be placed\n- * relative to the latitude and longitude of the point.\n- * - *title* The text of the 'title' is placed inside an 'h2' marker\n- * inside a popup, which opens when the marker is clicked.\n- * - *description* The text of the 'description' is placed below the h2\n- * in the popup. this can be plain text or HTML.\n- *\n- * Example text file:\n- * (code)\n- * lat\tlon\ttitle\tdescription\ticonSize\ticonOffset\ticon\n- * 10\t20\ttitle\tdescription\t21,25\t\t-10,-25\t\thttp://www.openlayers.org/dev/img/marker.png\n- * (end)\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Markers>\n- */\n-OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {\n-\n- /**\n- * APIProperty: location \n- * {String} URL of text file. Must be specified in the \"options\" argument\n- * of the constructor. Can not be changed once passed in. \n- */\n- location: null,\n-\n- /** \n- * Property: features\n- * {Array(<OpenLayers.Feature>)} \n- */\n- features: null,\n-\n- /**\n- * APIProperty: formatOptions\n- * {Object} Hash of options which should be passed to the format when it is\n- * created. Must be passed in the constructor.\n- */\n- formatOptions: null,\n-\n- /** \n- * Property: selectedFeature\n- * {<OpenLayers.Feature>}\n- */\n- selectedFeature: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Text\n- * Create a text layer.\n- * \n- * Parameters:\n- * name - {String} \n- * options - {Object} Object with properties to be set on the layer.\n- * Must include <location> property.\n- */\n- initialize: function(name, options) {\n- OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);\n- this.features = [];\n- },\n-\n- /**\n- * APIMethod: destroy \n- */\n- destroy: function() {\n- // Warning: Layer.Markers.destroy() must be called prior to calling\n- // clearFeatures() here, otherwise we leak memory. Indeed, if\n- // Layer.Markers.destroy() is called after clearFeatures(), it won't be\n- // able to remove the marker image elements from the layer's div since\n- // the markers will have been destroyed by clearFeatures().\n- OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);\n- this.clearFeatures();\n- this.features = null;\n- },\n-\n- /**\n- * Method: loadText\n- * Start the load of the Text data. Don't do this when we first add the layer,\n- * since we may not be visible at any point, and it would therefore be a waste.\n- */\n- loadText: function() {\n- if (!this.loaded) {\n- if (this.location != null) {\n-\n- var onFail = function(e) {\n- this.events.triggerEvent(\"loadend\");\n- };\n-\n- this.events.triggerEvent(\"loadstart\");\n- OpenLayers.Request.GET({\n- url: this.location,\n- success: this.parseData,\n- failure: onFail,\n- scope: this\n- });\n- this.loaded = true;\n- }\n- }\n- },\n-\n- /**\n- * Method: moveTo\n- * If layer is visible and Text has not been loaded, load Text. \n- * \n- * Parameters:\n- * bounds - {Object} \n- * zoomChanged - {Object} \n- * minor - {Object} \n- */\n- moveTo: function(bounds, zoomChanged, minor) {\n- OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);\n- if (this.visibility && !this.loaded) {\n- this.loadText();\n- }\n- },\n-\n- /**\n- * Method: parseData\n- *\n- * Parameters:\n- * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} \n- */\n- parseData: function(ajaxRequest) {\n- var text = ajaxRequest.responseText;\n-\n- var options = {};\n-\n- OpenLayers.Util.extend(options, this.formatOptions);\n-\n- if (this.map && !this.projection.equals(this.map.getProjectionObject())) {\n- options.externalProjection = this.projection;\n- options.internalProjection = this.map.getProjectionObject();\n- }\n-\n- var parser = new OpenLayers.Format.Text(options);\n- var features = parser.read(text);\n- for (var i = 0, len = features.length; i < len; i++) {\n- var data = {};\n- var feature = features[i];\n- var location;\n- var iconSize, iconOffset;\n-\n- location = new OpenLayers.LonLat(feature.geometry.x,\n- feature.geometry.y);\n-\n- if (feature.style.graphicWidth &&\n- feature.style.graphicHeight) {\n- iconSize = new OpenLayers.Size(\n- feature.style.graphicWidth,\n- feature.style.graphicHeight);\n- }\n-\n- // FIXME: At the moment, we only use this if we have an \n- // externalGraphic, because icon has no setOffset API Method.\n- /**\n- * FIXME FIRST!!\n- * The Text format does all sorts of parseFloating\n- * The result of a parseFloat for a bogus string is NaN. That\n- * means the three possible values here are undefined, NaN, or a\n- * number. The previous check was an identity check for null. This\n- * means it was failing for all undefined or NaN. A slightly better\n- * check is for undefined. An even better check is to see if the\n- * value is a number (see #1441).\n- */\n- if (feature.style.graphicXOffset !== undefined &&\n- feature.style.graphicYOffset !== undefined) {\n- iconOffset = new OpenLayers.Pixel(\n- feature.style.graphicXOffset,\n- feature.style.graphicYOffset);\n- }\n-\n- if (feature.style.externalGraphic != null) {\n- data.icon = new OpenLayers.Icon(feature.style.externalGraphic,\n- iconSize,\n- iconOffset);\n- } else {\n- data.icon = OpenLayers.Marker.defaultIcon();\n-\n- //allows for the case where the image url is not \n- // specified but the size is. use a default icon\n- // but change the size\n- if (iconSize != null) {\n- data.icon.setSize(iconSize);\n- }\n- }\n-\n- if ((feature.attributes.title != null) &&\n- (feature.attributes.description != null)) {\n- data['popupContentHTML'] =\n- '<h2>' + feature.attributes.title + '</h2>' +\n- '<p>' + feature.attributes.description + '</p>';\n- }\n-\n- data['overflow'] = feature.attributes.overflow || \"auto\";\n-\n- var markerFeature = new OpenLayers.Feature(this, location, data);\n- this.features.push(markerFeature);\n- var marker = markerFeature.createMarker();\n- if ((feature.attributes.title != null) &&\n- (feature.attributes.description != null)) {\n- marker.events.register('click', markerFeature, this.markerClick);\n- }\n- this.addMarker(marker);\n- }\n- this.events.triggerEvent(\"loadend\");\n- },\n-\n- /**\n- * Property: markerClick\n- * \n- * Parameters:\n- * evt - {Event} \n- *\n- * Context:\n- * - {<OpenLayers.Feature>}\n- */\n- markerClick: function(evt) {\n- var sameMarkerClicked = (this == this.layer.selectedFeature);\n- this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;\n- for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {\n- this.layer.map.removePopup(this.layer.map.popups[i]);\n- }\n- if (!sameMarkerClicked) {\n- this.layer.map.addPopup(this.createPopup());\n- }\n- OpenLayers.Event.stop(evt);\n- },\n-\n- /**\n- * Method: clearFeatures\n- */\n- clearFeatures: function() {\n- if (this.features != null) {\n- while (this.features.length > 0) {\n- var feature = this.features[0];\n- OpenLayers.Util.removeItem(this.features, feature);\n- feature.destroy();\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Text\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Bing.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/XYZ.js\n- */\n-\n-/** \n- * Class: OpenLayers.Layer.Bing\n- * Bing layer using direct tile access as provided by Bing Maps REST Services.\n- * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more\n- * information. Note: Terms of Service compliant use requires the map to be\n- * configured with an <OpenLayers.Control.Attribution> control and the\n- * attribution placed on or near the map.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n- */\n-OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * Property: key\n- * {String} API key for Bing maps, get your own key \n- * at http://bingmapsportal.com/ .\n- */\n- key: null,\n-\n- /**\n- * Property: serverResolutions\n- * {Array} the resolutions provided by the Bing servers.\n- */\n- serverResolutions: [\n- 156543.03390625, 78271.516953125, 39135.7584765625,\n- 19567.87923828125, 9783.939619140625, 4891.9698095703125,\n- 2445.9849047851562, 1222.9924523925781, 611.4962261962891,\n- 305.74811309814453, 152.87405654907226, 76.43702827453613,\n- 38.218514137268066, 19.109257068634033, 9.554628534317017,\n- 4.777314267158508, 2.388657133579254, 1.194328566789627,\n- 0.5971642833948135, 0.29858214169740677, 0.14929107084870338,\n- 0.07464553542435169\n- ],\n-\n- /**\n- * Property: attributionTemplate\n- * {String}\n- */\n- attributionTemplate: '<span class=\"olBingAttribution ${type}\">' +\n- '<div><a target=\"_blank\" href=\"http://www.bing.com/maps/\">' +\n- '<img src=\"${logo}\" /></a></div>${copyrights}' +\n- '<a style=\"white-space: nowrap\" target=\"_blank\" ' +\n- 'href=\"http://www.microsoft.com/maps/product/terms.html\">' +\n- 'Terms of Use</a></span>',\n-\n- /**\n- * Property: metadata\n- * {Object} Metadata for this layer, as returned by the callback script\n- */\n- metadata: null,\n-\n- /**\n- * Property: protocolRegex\n- * {RegExp} Regular expression to match and replace http: in bing urls\n- */\n- protocolRegex: /^http:/i,\n-\n- /**\n- * APIProperty: type\n- * {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used. Default is \"Road\".\n- */\n- type: \"Road\",\n-\n- /**\n- * APIProperty: culture\n- * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx\n- * for the definition and the possible values. Default is \"en-US\".\n- */\n- culture: \"en-US\",\n-\n- /**\n- * APIProperty: metadataParams\n- * {Object} Optional url parameters for the Get Imagery Metadata request\n- * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx\n- */\n- metadataParams: null,\n-\n- /** APIProperty: tileOptions\n- * {Object} optional configuration options for <OpenLayers.Tile> instances\n- * created by this Layer. Default is\n- *\n- * (code)\n- * {crossOriginKeyword: 'anonymous'}\n- * (end)\n- */\n- tileOptions: null,\n-\n- /** APIProperty: protocol\n- * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo\n- * Can be 'http:' 'https:' or ''\n- *\n- * Warning: tiles may not be available under both HTTP and HTTPS protocols.\n- * Microsoft approved use of both HTTP and HTTPS urls for tiles. However\n- * this is undocumented and the Imagery Metadata API always returns HTTP\n- * urls.\n- *\n- * Default is '', unless when executed from a file:/// uri, in which case\n- * it is 'http:'.\n- */\n- protocol: ~window.location.href.indexOf('http') ? '' : 'http:',\n-\n- /**\n- * Constructor: OpenLayers.Layer.Bing\n- * Create a new Bing layer.\n- *\n- * Example:\n- * (code)\n- * var road = new OpenLayers.Layer.Bing({\n- * name: \"My Bing Aerial Layer\",\n- * type: \"Aerial\",\n- * key: \"my-api-key-here\",\n- * });\n- * (end)\n- *\n- * Parameters:\n- * options - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * key - {String} Bing Maps API key for your application. Get one at\n- * http://bingmapsportal.com/.\n- * type - {String} The layer identifier. Any non-birdseye imageryType\n- * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be\n- * used.\n- *\n- * Any other documented layer properties can be provided in the config object.\n- */\n- initialize: function(options) {\n- options = OpenLayers.Util.applyDefaults({\n- sphericalMercator: true\n- }, options);\n- var name = options.name || \"Bing \" + (options.type || this.type);\n-\n- var newArgs = [name, null, options];\n- OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);\n- this.tileOptions = OpenLayers.Util.extend({\n- crossOriginKeyword: 'anonymous'\n- }, this.options.tileOptions);\n- this.loadMetadata();\n- },\n-\n- /**\n- * Method: loadMetadata\n- */\n- loadMetadata: function() {\n- this._callbackId = \"_callback_\" + this.id.replace(/\\./g, \"_\");\n- // link the processMetadata method to the global scope and bind it\n- // to this instance\n- window[this._callbackId] = OpenLayers.Function.bind(\n- OpenLayers.Layer.Bing.processMetadata, this\n- );\n- var params = OpenLayers.Util.applyDefaults({\n- key: this.key,\n- jsonp: this._callbackId,\n- include: \"ImageryProviders\"\n- }, this.metadataParams);\n- var url = this.protocol + \"//dev.virtualearth.net/REST/v1/Imagery/Metadata/\" +\n- this.type + \"?\" + OpenLayers.Util.getParameterString(params);\n- var script = document.createElement(\"script\");\n- script.type = \"text/javascript\";\n- script.src = url;\n- script.id = this._callbackId;\n- document.getElementsByTagName(\"head\")[0].appendChild(script);\n- },\n-\n- /**\n- * Method: initLayer\n- *\n- * Sets layer properties according to the metadata provided by the API\n- */\n- initLayer: function() {\n- var res = this.metadata.resourceSets[0].resources[0];\n- var url = res.imageUrl.replace(\"{quadkey}\", \"${quadkey}\");\n- url = url.replace(\"{culture}\", this.culture);\n- url = url.replace(this.protocolRegex, this.protocol);\n- this.url = [];\n- for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {\n- this.url.push(url.replace(\"{subdomain}\", res.imageUrlSubdomains[i]));\n- }\n- this.addOptions({\n- maxResolution: Math.min(\n- this.serverResolutions[res.zoomMin],\n- this.maxResolution || Number.POSITIVE_INFINITY\n- ),\n- numZoomLevels: Math.min(\n- res.zoomMax + 1 - res.zoomMin, this.numZoomLevels\n- )\n- }, true);\n- if (!this.isBaseLayer) {\n- this.redraw();\n- }\n- this.updateAttribution();\n- },\n-\n- /**\n- * Method: getURL\n- *\n- * Paramters:\n- * bounds - {<OpenLayers.Bounds>}\n- */\n- getURL: function(bounds) {\n- if (!this.url) {\n- return;\n- }\n- var xyz = this.getXYZ(bounds),\n- x = xyz.x,\n- y = xyz.y,\n- z = xyz.z;\n- var quadDigits = [];\n- for (var i = z; i > 0; --i) {\n- var digit = '0';\n- var mask = 1 << (i - 1);\n- if ((x & mask) != 0) {\n- digit++;\n- }\n- if ((y & mask) != 0) {\n- digit++;\n- digit++;\n- }\n- quadDigits.push(digit);\n- }\n- var quadKey = quadDigits.join(\"\");\n- var url = this.selectUrl('' + x + y + z, this.url);\n-\n- return OpenLayers.String.format(url, {\n- 'quadkey': quadKey\n- });\n- },\n-\n- /**\n- * Method: updateAttribution\n- * Updates the attribution according to the requirements outlined in\n- * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html\n- */\n- updateAttribution: function() {\n- var metadata = this.metadata;\n- if (!metadata.resourceSets || !this.map || !this.map.center) {\n- return;\n- }\n- var res = metadata.resourceSets[0].resources[0];\n- var extent = this.map.getExtent().transform(\n- this.map.getProjectionObject(),\n- new OpenLayers.Projection(\"EPSG:4326\")\n- );\n- var providers = res.imageryProviders || [],\n- zoom = OpenLayers.Util.indexOf(this.serverResolutions,\n- this.getServerResolution()),\n- copyrights = \"\",\n- provider, i, ii, j, jj, bbox, coverage;\n- for (i = 0, ii = providers.length; i < ii; ++i) {\n- provider = providers[i];\n- for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {\n- coverage = provider.coverageAreas[j];\n- // axis order provided is Y,X\n- bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);\n- if (extent.intersectsBounds(bbox) &&\n- zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {\n- copyrights += provider.attribution + \" \";\n- }\n- }\n- }\n- var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);\n- this.attribution = OpenLayers.String.format(this.attributionTemplate, {\n- type: this.type.toLowerCase(),\n- logo: logo,\n- copyrights: copyrights\n- });\n- this.map && this.map.events.triggerEvent(\"changelayer\", {\n- layer: this,\n- property: \"attribution\"\n- });\n- },\n-\n- /**\n- * Method: setMap\n- */\n- setMap: function() {\n- OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);\n- this.map.events.register(\"moveend\", this, this.updateAttribution);\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Bing(this.options);\n- }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n- },\n-\n- /**\n- * Method: destroy\n- */\n- destroy: function() {\n- this.map &&\n- this.map.events.unregister(\"moveend\", this, this.updateAttribution);\n- OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Bing\"\n-});\n-\n-/**\n- * Function: OpenLayers.Layer.Bing.processMetadata\n- * This function will be bound to an instance, linked to the global scope with\n- * an id, and called by the JSONP script returned by the API.\n- *\n- * Parameters:\n- * metadata - {Object} metadata as returned by the API\n- */\n-OpenLayers.Layer.Bing.processMetadata = function(metadata) {\n- this.metadata = metadata;\n- this.initLayer();\n- var script = document.getElementById(this._callbackId);\n- script.parentNode.removeChild(script);\n- window[this._callbackId] = undefined; // cannot delete from window in IE\n- delete this._callbackId;\n-};\n-/* ======================================================================\n- OpenLayers/Tile/UTFGrid.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Tile.js\n- * @requires OpenLayers/Format/JSON.js\n- * @requires OpenLayers/Request.js\n- */\n-\n-/**\n- * Class: OpenLayers.Tile.UTFGrid\n- * Instances of OpenLayers.Tile.UTFGrid are used to manage \n- * UTFGrids. This is an unusual tile type in that it doesn't have a\n- * rendered image; only a 'hit grid' that can be used to \n- * look up feature attributes.\n- *\n- * See the <OpenLayers.Tile.UTFGrid> constructor for details on constructing a\n- * new instance.\n- *\n- * Inherits from:\n- * - <OpenLayers.Tile>\n- */\n-OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, {\n-\n- /** \n- * Property: url\n- * {String}\n- * The URL of the UTFGrid file being requested. Provided by the <getURL>\n- * method. \n- */\n- url: null,\n-\n- /**\n- * Property: utfgridResolution\n- * {Number}\n- * Ratio of the pixel width to the width of a UTFGrid data point. If an \n- * entry in the grid represents a 4x4 block of pixels, the \n- * utfgridResolution would be 4. Default is 2.\n- */\n- utfgridResolution: 2,\n-\n- /** \n- * Property: json\n- * {Object}\n- * Stores the parsed JSON tile data structure. \n- */\n- json: null,\n-\n- /** \n- * Property: format\n- * {OpenLayers.Format.JSON}\n- * Parser instance used to parse JSON for cross browser support. The native\n- * JSON.parse method will be used where available (all except IE<8).\n- */\n- format: null,\n-\n- /** \n- * Constructor: OpenLayers.Tile.UTFGrid\n- * Constructor for a new <OpenLayers.Tile.UTFGrid> instance.\n- * \n- * Parameters:\n- * layer - {<OpenLayers.Layer>} layer that the tile will go in.\n- * position - {<OpenLayers.Pixel>}\n- * bounds - {<OpenLayers.Bounds>}\n- * url - {<String>} Deprecated. Remove me in 3.0.\n- * size - {<OpenLayers.Size>}\n- * options - {Object}\n- */\n-\n- /** \n- * APIMethod: destroy\n- * Clean up.\n- */\n- destroy: function() {\n- this.clear();\n- OpenLayers.Tile.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * Method: draw\n- * Check that a tile should be drawn, and draw it.\n- * In the case of UTFGrids, \"drawing\" it means fetching and\n- * parsing the json. \n- * \n- * Returns:\n- * {Boolean} Was a tile drawn?\n- */\n- draw: function() {\n- var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);\n- if (drawn) {\n- if (this.isLoading) {\n- this.abortLoading();\n- //if we're already loading, send 'reload' instead of 'loadstart'.\n- this.events.triggerEvent(\"reload\");\n- } else {\n- this.isLoading = true;\n- this.events.triggerEvent(\"loadstart\");\n- }\n- this.url = this.layer.getURL(this.bounds);\n-\n- if (this.layer.useJSONP) {\n- // Use JSONP method to avoid xbrowser policy\n- var ols = new OpenLayers.Protocol.Script({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- this.json = response.data;\n- },\n- scope: this\n- });\n- ols.read();\n- this.request = ols;\n- } else {\n- // Use standard XHR\n- this.request = OpenLayers.Request.GET({\n- url: this.url,\n- callback: function(response) {\n- this.isLoading = false;\n- this.events.triggerEvent(\"loadend\");\n- if (response.status === 200) {\n- this.parseData(response.responseText);\n- }\n- },\n- scope: this\n- });\n- }\n- } else {\n- this.unload();\n- }\n- return drawn;\n- },\n-\n- /**\n- * Method: abortLoading\n- * Cancel a pending request.\n- */\n- abortLoading: function() {\n- if (this.request) {\n- this.request.abort();\n- delete this.request;\n- }\n- this.isLoading = false;\n- },\n-\n- /**\n- * Method: getFeatureInfo\n- * Get feature information associated with a pixel offset. If the pixel\n- * offset corresponds to a feature, the returned object will have id\n- * and data properties. Otherwise, null will be returned.\n- * \n- *\n- * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n- *\n- * Returns:\n- * {Object} Object with feature id and data properties corresponding to the \n- * given pixel offset.\n- */\n- getFeatureInfo: function(i, j) {\n- var info = null;\n- if (this.json) {\n- var id = this.getFeatureId(i, j);\n- if (id !== null) {\n- info = {\n- id: id,\n- data: this.json.data[id]\n- };\n- }\n- }\n- return info;\n- },\n-\n- /**\n- * Method: getFeatureId\n- * Get the identifier for the feature associated with a pixel offset.\n- *\n- * Parameters:\n- * i - {Number} X-axis pixel offset (from top left of tile)\n- * j - {Number} Y-axis pixel offset (from top left of tile)\n- *\n- * Returns:\n- * {Object} The feature identifier corresponding to the given pixel offset.\n- * Returns null if pixel doesn't correspond to a feature.\n- */\n- getFeatureId: function(i, j) {\n- var id = null;\n- if (this.json) {\n- var resolution = this.utfgridResolution;\n- var row = Math.floor(j / resolution);\n- var col = Math.floor(i / resolution);\n- var charCode = this.json.grid[row].charCodeAt(col);\n- var index = this.indexFromCharCode(charCode);\n- var keys = this.json.keys;\n- if (!isNaN(index) && (index in keys)) {\n- id = keys[index];\n- }\n- }\n- return id;\n- },\n-\n- /**\n- * Method: indexFromCharCode\n- * Given a character code for one of the UTFGrid \"grid\" characters, \n- * resolve the integer index for the feature id in the UTFGrid \"keys\"\n- * array.\n- *\n- * Parameters:\n- * charCode - {Integer}\n- *\n- * Returns:\n- * {Integer} Index for the feature id from the keys array.\n- */\n- indexFromCharCode: function(charCode) {\n- if (charCode >= 93) {\n- charCode--;\n- }\n- if (charCode >= 35) {\n- charCode--;\n- }\n- return charCode - 32;\n- },\n-\n- /**\n- * Method: parseData\n- * Parse the JSON from a request\n- *\n- * Parameters:\n- * str - {String} UTFGrid as a JSON string. \n- * \n- * Returns:\n- * {Object} parsed javascript data\n- */\n- parseData: function(str) {\n- if (!this.format) {\n- this.format = new OpenLayers.Format.JSON();\n- }\n- this.json = this.format.read(str);\n- },\n-\n- /** \n- * Method: clear\n- * Delete data stored with this tile.\n- */\n- clear: function() {\n- this.json = null;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Tile.UTFGrid\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Layer/UTFGrid.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/XYZ.js\n- * @requires OpenLayers/Tile/UTFGrid.js\n- */\n-\n-/** \n- * Class: OpenLayers.Layer.UTFGrid\n- * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are \n- * essentially JSON-based ASCII art with attached attributes, they are not \n- * visibly rendered. In order to use them in the map, you must add a \n- * <OpenLayers.Control.UTFGrid> control as well.\n- *\n- * Example:\n- *\n- * (start code)\n- * var world_utfgrid = new OpenLayers.Layer.UTFGrid({\n- * url: \"/tiles/world_utfgrid/${z}/${x}/${y}.json\",\n- * utfgridResolution: 4,\n- * displayInLayerSwitcher: false\n- * );\n- * map.addLayer(world_utfgrid);\n- * \n- * var control = new OpenLayers.Control.UTFGrid({\n- * layers: [world_utfgrid],\n- * handlerMode: 'move',\n- * callback: function(dataLookup) {\n- * // do something with returned data\n- * }\n- * })\n- * (end code)\n- *\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.XYZ>\n- */\n-OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, {\n-\n- /**\n- * APIProperty: isBaseLayer\n- * Default is false, as UTFGrids are designed to be a transparent overlay layer. \n- */\n- isBaseLayer: false,\n-\n- /**\n- * APIProperty: projection\n- * {<OpenLayers.Projection>}\n- * Source projection for the UTFGrids. Default is \"EPSG:900913\".\n- */\n- projection: new OpenLayers.Projection(\"EPSG:900913\"),\n-\n- /**\n- * Property: useJSONP\n- * {Boolean}\n- * Should we use a JSONP script approach instead of a standard AJAX call?\n- *\n- * Set to true for using utfgrids from another server. \n- * Avoids same-domain policy restrictions. \n- * Note that this only works if the server accepts \n- * the callback GET parameter and dynamically \n- * wraps the returned json in a function call.\n- * \n- * Default is false\n- */\n- useJSONP: false,\n-\n- /**\n- * APIProperty: url\n- * {String}\n- * URL tempate for UTFGrid tiles. Include x, y, and z parameters.\n- * E.g. \"/tiles/${z}/${x}/${y}.json\"\n- */\n-\n- /**\n- * APIProperty: utfgridResolution\n- * {Number}\n- * Ratio of the pixel width to the width of a UTFGrid data point. If an \n- * entry in the grid represents a 4x4 block of pixels, the \n- * utfgridResolution would be 4. Default is 2 (specified in \n- * <OpenLayers.Tile.UTFGrid>).\n- */\n-\n- /**\n- * Property: tileClass\n- * {<OpenLayers.Tile>} The tile class to use for this layer.\n- * Defaults is <OpenLayers.Tile.UTFGrid>.\n- */\n- tileClass: OpenLayers.Tile.UTFGrid,\n-\n- /**\n- * Constructor: OpenLayers.Layer.UTFGrid\n- * Create a new UTFGrid layer.\n- *\n- * Parameters:\n- * config - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * url - {String} The url template for UTFGrid tiles. See the <url> property.\n- */\n- initialize: function(options) {\n- OpenLayers.Layer.Grid.prototype.initialize.apply(\n- this, [options.name, options.url, {}, options]\n- );\n- this.tileOptions = OpenLayers.Util.extend({\n- utfgridResolution: this.utfgridResolution\n- }, this.tileOptions);\n- },\n-\n- /**\n- * Method: createBackBuffer\n- * The UTFGrid cannot create a back buffer, so this method is overriden.\n- */\n- createBackBuffer: function() {},\n-\n- /**\n- * APIMethod: clone\n- * Create a clone of this layer\n- *\n- * Parameters:\n- * obj - {Object} Only used by a subclass of this layer.\n- * \n- * Returns:\n- * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.UTFGrid(this.getOptions());\n- }\n-\n- // get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- return obj;\n- },\n-\n- /**\n- * APIProperty: getFeatureInfo\n- * Get details about a feature associated with a map location. The object\n- * returned will have id and data properties. If the given location\n- * doesn't correspond to a feature, null will be returned.\n- *\n- * Parameters:\n- * location - {<OpenLayers.LonLat>} map location\n- *\n- * Returns:\n- * {Object} Object representing the feature id and UTFGrid data \n- * corresponding to the given map location. Returns null if the given\n- * location doesn't hit a feature.\n- */\n- getFeatureInfo: function(location) {\n- var info = null;\n- var tileInfo = this.getTileData(location);\n- if (tileInfo && tileInfo.tile) {\n- info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j);\n- }\n- return info;\n- },\n-\n- /**\n- * APIMethod: getFeatureId\n- * Get the identifier for the feature associated with a map location.\n- *\n- * Parameters:\n- * location - {<OpenLayers.LonLat>} map location\n- *\n- * Returns:\n- * {String} The feature identifier corresponding to the given map location.\n- * Returns null if the location doesn't hit a feature.\n- */\n- getFeatureId: function(location) {\n- var id = null;\n- var info = this.getTileData(location);\n- if (info.tile) {\n- id = info.tile.getFeatureId(info.i, info.j);\n- }\n- return id;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.UTFGrid\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/KaMapCache.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- * @requires OpenLayers/Layer/KaMap.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.KaMapCache\n- * \n- * This class is designed to talk directly to a web-accessible ka-Map\n- * cache generated by the precache2.php script.\n- * \n- * To create a a new KaMapCache layer, you must indicate also the \"i\" parameter\n- * (that will be used to calculate the file extension), and another special\n- * parameter, object names \"metaTileSize\", with \"h\" (height) and \"w\" (width)\n- * properties.\n- * \n- * // Create a new kaMapCache layer. \n- * var kamap_base = new OpenLayers.Layer.KaMapCache(\n- * \"Satellite\",\n- * \"http://www.example.org/web/acessible/cache\",\n- * {g: \"satellite\", map: \"world\", i: 'png24', metaTileSize: {w: 5, h: 5} }\n- * );\n- * \n- * // Create an kaMapCache overlay layer (using \"isBaseLayer: false\"). \n- * // Forces the output to be a \"gif\", using the \"i\" parameter.\n- * var kamap_overlay = new OpenLayers.Layer.KaMapCache(\n- * \"Streets\",\n- * \"http://www.example.org/web/acessible/cache\",\n- * {g: \"streets\", map: \"world\", i: \"gif\", metaTileSize: {w: 5, h: 5} },\n- * {isBaseLayer: false}\n- * );\n- *\n- * The cache URLs must look like: \n- * var/cache/World/50000/Group_Name/def/t-440320/l20480\n- * \n- * This means that the cache generated via tile.php will *not* work with\n- * this class, and should instead use the KaMap layer.\n- *\n- * More information is available in Ticket #1518.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.KaMap>\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {\n-\n- /**\n- * Constant: IMAGE_EXTENSIONS\n- * {Object} Simple hash map to convert format to extension.\n- */\n- IMAGE_EXTENSIONS: {\n- 'jpeg': 'jpg',\n- 'gif': 'gif',\n- 'png': 'png',\n- 'png8': 'png',\n- 'png24': 'png',\n- 'dithered': 'png'\n- },\n-\n- /**\n- * Constant: DEFAULT_FORMAT\n- * {Object} Simple hash map to convert format to extension.\n- */\n- DEFAULT_FORMAT: 'jpeg',\n-\n- /**\n- * Constructor: OpenLayers.Layer.KaMapCache\n- * \n- * Parameters:\n- * name - {String}\n- * url - {String}\n- * params - {Object} Parameters to be sent to the HTTP server in the\n- * query string for the tile. The format can be set via the 'i'\n- * parameter (defaults to jpg) , and the map should be set via \n- * the 'map' parameter. It has been reported that ka-Map may behave\n- * inconsistently if your format parameter does not match the format\n- * parameter configured in your config.php. (See ticket #327 for more\n- * information.)\n- * options - {Object} Additional options for the layer. Any of the \n- * APIProperties listed on this layer, and any layer types it\n- * extends, can be overridden through the options parameter. \n- */\n- initialize: function(name, url, params, options) {\n- OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);\n- this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT];\n- },\n-\n- /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} \n- * \n- * Returns:\n- * {String} A string with the layer's url and parameters and also the \n- * passed-in bounds and appropriate tile size specified as \n- * parameters\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var mapRes = this.map.getResolution();\n- var scale = Math.round((this.map.getScale() * 10000)) / 10000;\n- var pX = Math.round(bounds.left / mapRes);\n- var pY = -Math.round(bounds.top / mapRes);\n-\n- var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;\n- var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;\n-\n- var components = [\n- \"/\",\n- this.params.map,\n- \"/\",\n- scale,\n- \"/\",\n- this.params.g.replace(/\\s/g, '_'),\n- \"/def/t\",\n- metaY,\n- \"/l\",\n- metaX,\n- \"/t\",\n- pY,\n- \"l\",\n- pX,\n- \".\",\n- this.extension\n- ];\n-\n- var url = this.url;\n-\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(components.join(''), url);\n- }\n- return url + components.join(\"\");\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.KaMapCache\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/WMTS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.WMTS\n- * Instances of the WMTS class allow viewing of tiles from a service that \n- * implements the OGC WMTS specification version 1.0.0.\n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} The layer will be considered a base layer. Default is true.\n- */\n- isBaseLayer: true,\n-\n- /**\n- * Property: version\n- * {String} WMTS version. Default is \"1.0.0\".\n- */\n- version: \"1.0.0\",\n-\n- /**\n- * APIProperty: requestEncoding\n- * {String} Request encoding. Can be \"REST\" or \"KVP\". Default is \"KVP\".\n- */\n- requestEncoding: \"KVP\",\n-\n- /**\n- * APIProperty: url\n- * {String|Array(String)} The base URL or request URL template for the WMTS\n- * service. Must be provided. Array is only supported for base URLs, not\n- * for request URL templates. URL templates are only supported for\n- * REST <requestEncoding>.\n- */\n- url: null,\n-\n- /**\n- * APIProperty: layer\n- * {String} The layer identifier advertised by the WMTS service. Must be \n- * provided.\n- */\n- layer: null,\n-\n- /** \n- * APIProperty: matrixSet\n- * {String} One of the advertised matrix set identifiers. Must be provided.\n- */\n- matrixSet: null,\n-\n- /** \n- * APIProperty: style\n- * {String} One of the advertised layer styles. Must be provided.\n- */\n- style: null,\n-\n- /** \n- * APIProperty: format\n- * {String} The image MIME type. Default is \"image/jpeg\".\n- */\n- format: \"image/jpeg\",\n-\n- /**\n- * APIProperty: tileOrigin\n- * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map \n- * units. If the tile origin for each matrix in a set is different,\n- * the <matrixIds> should include a topLeftCorner property. If\n- * not provided, the tile origin will default to the top left corner\n- * of the layer <maxExtent>.\n- */\n- tileOrigin: null,\n-\n- /**\n- * APIProperty: tileFullExtent\n- * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied,\n- * the layer's <maxExtent> property will be used.\n- */\n- tileFullExtent: null,\n-\n- /**\n- * APIProperty: formatSuffix\n- * {String} For REST request encoding, an image format suffix must be \n- * included in the request. If not provided, the suffix will be derived\n- * from the <format> property.\n- */\n- formatSuffix: null,\n-\n- /**\n- * APIProperty: matrixIds\n- * {Array} A list of tile matrix identifiers. If not provided, the matrix\n- * identifiers will be assumed to be integers corresponding to the \n- * map zoom level. If a list of strings is provided, each item should\n- * be the matrix identifier that corresponds to the map zoom level.\n- * Additionally, a list of objects can be provided. Each object should\n- * describe the matrix as presented in the WMTS capabilities. These\n- * objects should have the propertes shown below.\n- * \n- * Matrix properties:\n- * identifier - {String} The matrix identifier (required).\n- * scaleDenominator - {Number} The matrix scale denominator.\n- * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the \n- * matrix. Must be provided if different than the layer <tileOrigin>.\n- * tileWidth - {Number} The tile width for the matrix. Must be provided \n- * if different than the width given in the layer <tileSize>.\n- * tileHeight - {Number} The tile height for the matrix. Must be provided \n- * if different than the height given in the layer <tileSize>.\n- */\n- matrixIds: null,\n-\n- /**\n- * APIProperty: dimensions\n- * {Array} For RESTful request encoding, extra dimensions may be specified.\n- * Items in this list should be property names in the <params> object.\n- * Values of extra dimensions will be determined from the corresponding\n- * values in the <params> object.\n- */\n- dimensions: null,\n-\n- /**\n- * APIProperty: params\n- * {Object} Extra parameters to include in tile requests. For KVP \n- * <requestEncoding>, these properties will be encoded in the request \n- * query string. For REST <requestEncoding>, these properties will\n- * become part of the request path, with order determined by the \n- * <dimensions> list.\n- */\n- params: null,\n-\n- /**\n- * APIProperty: zoomOffset\n- * {Number} If your cache has more levels than you want to provide\n- * access to with this layer, supply a zoomOffset. This zoom offset\n- * is added to the current map zoom level to determine the level\n- * for a requested tile. For example, if you supply a zoomOffset\n- * of 3, when the map is at the zoom 0, tiles will be requested from\n- * level 3 of your cache. Default is 0 (assumes cache level and map\n- * zoom are equivalent). Additionally, if this layer is to be used\n- * as an overlay and the cache has fewer zoom levels than the base\n- * layer, you can supply a negative zoomOffset. For example, if a\n- * map zoom level of 1 corresponds to your cache level zero, you would\n- * supply a -1 zoomOffset (and set the maxResolution of the layer\n- * appropriately). The zoomOffset value has no effect if complete\n- * matrix definitions (including scaleDenominator) are supplied in\n- * the <matrixIds> property. Defaults to 0 (no zoom offset).\n- */\n- zoomOffset: 0,\n-\n- /**\n- * APIProperty: serverResolutions\n- * {Array} A list of all resolutions available on the server. Only set this\n- * property if the map resolutions differ from the server. This\n- * property serves two purposes. (a) <serverResolutions> can include\n- * resolutions that the server supports and that you don't want to\n- * provide with this layer; you can also look at <zoomOffset>, which is\n- * an alternative to <serverResolutions> for that specific purpose.\n- * (b) The map can work with resolutions that aren't supported by\n- * the server, i.e. that aren't in <serverResolutions>. When the\n- * map is displayed in such a resolution data for the closest\n- * server-supported resolution is loaded and the layer div is\n- * stretched as necessary.\n- */\n- serverResolutions: null,\n-\n- /**\n- * Property: formatSuffixMap\n- * {Object} a map between WMTS 'format' request parameter and tile image file suffix\n- */\n- formatSuffixMap: {\n- \"image/png\": \"png\",\n- \"image/png8\": \"png\",\n- \"image/png24\": \"png\",\n- \"image/png32\": \"png\",\n- \"png\": \"png\",\n- \"image/jpeg\": \"jpg\",\n- \"image/jpg\": \"jpg\",\n- \"jpeg\": \"jpg\",\n- \"jpg\": \"jpg\"\n- },\n-\n- /**\n- * Property: matrix\n- * {Object} Matrix definition for the current map resolution. Updated by\n- * the <updateMatrixProperties> method.\n- */\n- matrix: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.WMTS\n- * Create a new WMTS layer.\n- *\n- * Example:\n- * (code)\n- * var wmts = new OpenLayers.Layer.WMTS({\n- * name: \"My WMTS Layer\",\n- * url: \"http://example.com/wmts\", \n- * layer: \"layer_id\",\n- * style: \"default\",\n- * matrixSet: \"matrix_id\"\n- * });\n- * (end)\n- *\n- * Parameters:\n- * config - {Object} Configuration properties for the layer.\n- *\n- * Required configuration properties:\n- * url - {String} The base url for the service. See the <url> property.\n- * layer - {String} The layer identifier. See the <layer> property.\n- * style - {String} The layer style identifier. See the <style> property.\n- * matrixSet - {String} The tile matrix set identifier. See the <matrixSet>\n- * property.\n- *\n- * Any other documented layer properties can be provided in the config object.\n- */\n- initialize: function(config) {\n-\n- // confirm required properties are supplied\n- var required = {\n- url: true,\n- layer: true,\n- style: true,\n- matrixSet: true\n- };\n- for (var prop in required) {\n- if (!(prop in config)) {\n- throw new Error(\"Missing property '\" + prop + \"' in layer configuration.\");\n- }\n- }\n-\n- config.params = OpenLayers.Util.upperCaseObject(config.params);\n- var args = [config.name, config.url, config.params, config];\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);\n-\n-\n- // determine format suffix (for REST)\n- if (!this.formatSuffix) {\n- this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split(\"/\").pop();\n- }\n-\n- // expand matrixIds (may be array of string or array of object)\n- if (this.matrixIds) {\n- var len = this.matrixIds.length;\n- if (len && typeof this.matrixIds[0] === \"string\") {\n- var ids = this.matrixIds;\n- this.matrixIds = new Array(len);\n- for (var i = 0; i < len; ++i) {\n- this.matrixIds[i] = {\n- identifier: ids[i]\n- };\n- }\n- }\n- }\n-\n- },\n-\n- /**\n- * Method: setMap\n- */\n- setMap: function() {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- },\n-\n- /**\n- * Method: updateMatrixProperties\n- * Called when map resolution changes to update matrix related properties.\n- */\n- updateMatrixProperties: function() {\n- this.matrix = this.getMatrix();\n- if (this.matrix) {\n- if (this.matrix.topLeftCorner) {\n- this.tileOrigin = this.matrix.topLeftCorner;\n- }\n- if (this.matrix.tileWidth && this.matrix.tileHeight) {\n- this.tileSize = new OpenLayers.Size(\n- this.matrix.tileWidth, this.matrix.tileHeight\n- );\n- }\n- if (!this.tileOrigin) {\n- this.tileOrigin = new OpenLayers.LonLat(\n- this.maxExtent.left, this.maxExtent.top\n- );\n- }\n- if (!this.tileFullExtent) {\n- this.tileFullExtent = this.maxExtent;\n- }\n- }\n- },\n-\n- /**\n- * Method: moveTo\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to\n- * do some init work in that case.\n- * dragging - {Boolean}\n- */\n- moveTo: function(bounds, zoomChanged, dragging) {\n- if (zoomChanged || !this.matrix) {\n- this.updateMatrixProperties();\n- }\n- return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: clone\n- * \n- * Parameters:\n- * obj - {Object}\n- * \n- * Returns:\n- * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS>\n- */\n- clone: function(obj) {\n- if (obj == null) {\n- obj = new OpenLayers.Layer.WMTS(this.options);\n- }\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n- // copy/set any non-init, non-simple values here\n- return obj;\n- },\n-\n- /**\n- * Method: getIdentifier\n- * Get the current index in the matrixIds array.\n- */\n- getIdentifier: function() {\n- return this.getServerZoom();\n- },\n-\n- /**\n- * Method: getMatrix\n- * Get the appropriate matrix definition for the current map resolution.\n- */\n- getMatrix: function() {\n- var matrix;\n- if (!this.matrixIds || this.matrixIds.length === 0) {\n- matrix = {\n- identifier: this.getIdentifier()\n- };\n- } else {\n- // get appropriate matrix given the map scale if possible\n- if (\"scaleDenominator\" in this.matrixIds[0]) {\n- // scale denominator calculation based on WMTS spec\n- var denom =\n- OpenLayers.METERS_PER_INCH *\n- OpenLayers.INCHES_PER_UNIT[this.units] *\n- this.getServerResolution() / 0.28E-3;\n- var diff = Number.POSITIVE_INFINITY;\n- var delta;\n- for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {\n- delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));\n- if (delta < diff) {\n- diff = delta;\n- matrix = this.matrixIds[i];\n- }\n- }\n- } else {\n- // fall back on zoom as index\n- matrix = this.matrixIds[this.getIdentifier()];\n- }\n- }\n- return matrix;\n- },\n-\n- /** \n- * Method: getTileInfo\n- * Get tile information for a given location at the current map resolution.\n- *\n- * Parameters:\n- * loc - {<OpenLayers.LonLat} A location in map coordinates.\n- *\n- * Returns:\n- * {Object} An object with \"col\", \"row\", \"i\", and \"j\" properties. The col\n- * and row values are zero based tile indexes from the top left. The\n- * i and j values are the number of pixels to the left and top \n- * (respectively) of the given location within the target tile.\n- */\n- getTileInfo: function(loc) {\n- var res = this.getServerResolution();\n-\n- var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);\n- var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);\n-\n- var col = Math.floor(fx);\n- var row = Math.floor(fy);\n-\n- return {\n- col: col,\n- row: row,\n- i: Math.floor((fx - col) * this.tileSize.w),\n- j: Math.floor((fy - row) * this.tileSize.h)\n- };\n- },\n-\n- /**\n- * Method: getURL\n- * \n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {String} A URL for the tile corresponding to the given bounds.\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var url = \"\";\n- if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {\n-\n- var center = bounds.getCenterLonLat();\n- var info = this.getTileInfo(center);\n- var matrixId = this.matrix.identifier;\n- var dimensions = this.dimensions,\n- params;\n-\n- if (OpenLayers.Util.isArray(this.url)) {\n- url = this.selectUrl([\n- this.version, this.style, this.matrixSet,\n- this.matrix.identifier, info.row, info.col\n- ].join(\",\"), this.url);\n- } else {\n- url = this.url;\n- }\n-\n- if (this.requestEncoding.toUpperCase() === \"REST\") {\n- params = this.params;\n- if (url.indexOf(\"{\") !== -1) {\n- var template = url.replace(/\\{/g, \"${\");\n- var context = {\n- // spec does not make clear if capital S or not\n- style: this.style,\n- Style: this.style,\n- TileMatrixSet: this.matrixSet,\n- TileMatrix: this.matrix.identifier,\n- TileRow: info.row,\n- TileCol: info.col\n- };\n- if (dimensions) {\n- var dimension, i;\n- for (i = dimensions.length - 1; i >= 0; --i) {\n- dimension = dimensions[i];\n- context[dimension] = params[dimension.toUpperCase()];\n- }\n- }\n- url = OpenLayers.String.format(template, context);\n- } else {\n- // include 'version', 'layer' and 'style' in tile resource url\n- var path = this.version + \"/\" + this.layer + \"/\" + this.style + \"/\";\n-\n- // append optional dimension path elements\n- if (dimensions) {\n- for (var i = 0; i < dimensions.length; i++) {\n- if (params[dimensions[i]]) {\n- path = path + params[dimensions[i]] + \"/\";\n- }\n- }\n- }\n-\n- // append other required path elements\n- path = path + this.matrixSet + \"/\" + this.matrix.identifier +\n- \"/\" + info.row + \"/\" + info.col + \".\" + this.formatSuffix;\n-\n- if (!url.match(/\\/$/)) {\n- url = url + \"/\";\n- }\n- url = url + path;\n- }\n- } else if (this.requestEncoding.toUpperCase() === \"KVP\") {\n-\n- // assemble all required parameters\n- params = {\n- SERVICE: \"WMTS\",\n- REQUEST: \"GetTile\",\n- VERSION: this.version,\n- LAYER: this.layer,\n- STYLE: this.style,\n- TILEMATRIXSET: this.matrixSet,\n- TILEMATRIX: this.matrix.identifier,\n- TILEROW: info.row,\n- TILECOL: info.col,\n- FORMAT: this.format\n- };\n- url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);\n-\n- }\n- }\n- return url;\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * Extend the existing layer <params> with new properties. Tiles will be\n- * reloaded with updated params in the request.\n- * \n- * Parameters:\n- * newParams - {Object} Properties to extend to existing <params>.\n- */\n- mergeNewParams: function(newParams) {\n- if (this.requestEncoding.toUpperCase() === \"KVP\") {\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(\n- this, [OpenLayers.Util.upperCaseObject(newParams)]\n- );\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.WMTS\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/ArcGIS93Rest.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.ArcGIS93Rest\n- * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from\n- * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API.\n- * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest>\n- * constructor. More detail on the REST API is available at\n- * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ;\n- * specifically, the URL provided to this layer should be an export service\n- * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html \n- * \n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * Constant: DEFAULT_PARAMS\n- * {Object} Hashtable of default parameter key/value pairs \n- */\n- DEFAULT_PARAMS: {\n- format: \"png\"\n- },\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean} Default is true for ArcGIS93Rest layer\n- */\n- isBaseLayer: true,\n-\n-\n- /**\n- * Constructor: OpenLayers.Layer.ArcGIS93Rest\n- * Create a new ArcGIS93Rest layer object.\n- *\n- * Example:\n- * (code)\n- * var arcims = new OpenLayers.Layer.ArcGIS93Rest(\"MyName\",\n- * \"http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export\", \n- * {\n- * layers: \"0,1,2\"\n- * });\n- * (end)\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * url - {String} Base url for the ArcGIS server REST service\n- * options - {Object} An object with key/value pairs representing the\n- * options and option values.\n- *\n- * Valid Options:\n- * format - {String} MIME type of desired image type.\n- * layers - {String} Comma-separated list of layers to display.\n- * srs - {String} Projection ID.\n- */\n- initialize: function(name, url, params, options) {\n- var newArguments = [];\n- //uppercase params\n- params = OpenLayers.Util.upperCaseObject(params);\n- newArguments.push(name, url, params, options);\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);\n- OpenLayers.Util.applyDefaults(\n- this.params,\n- OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)\n- );\n-\n- //layer is transparent \n- if (this.params.TRANSPARENT &&\n- this.params.TRANSPARENT.toString().toLowerCase() == \"true\") {\n-\n- // unless explicitly set in options, make layer an overlay\n- if ((options == null) || (!options.isBaseLayer)) {\n- this.isBaseLayer = false;\n- }\n-\n- // jpegs can never be transparent, so intelligently switch the \n- // format, depending on the browser's capabilities\n- if (this.params.FORMAT == \"jpg\") {\n- this.params.FORMAT = OpenLayers.Util.alphaHack() ? \"gif\" :\n- \"png\";\n- }\n- }\n- },\n-\n- /**\n- * Method: clone\n- * Create a clone of this layer\n- *\n- * Returns:\n- * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.ArcGIS93Rest(this.name,\n- this.url,\n- this.params,\n- this.getOptions());\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n-\n- /**\n- * Method: getURL\n- * Return an image url this layer.\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the\n- * request.\n- *\n- * Returns:\n- * {String} A string with the map image's url.\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n-\n- // ArcGIS Server only wants the numeric portion of the projection ID.\n- var projWords = this.projection.getCode().split(\":\");\n- var srid = projWords[projWords.length - 1];\n-\n- var imageSize = this.getImageSize();\n- var newParams = {\n- 'BBOX': bounds.toBBOX(),\n- 'SIZE': imageSize.w + \",\" + imageSize.h,\n- // We always want image, the other options were json, image with a whole lotta html around it, etc.\n- 'F': \"image\",\n- 'BBOXSR': srid,\n- 'IMAGESR': srid\n- };\n-\n- // Now add the filter parameters.\n- if (this.layerDefs) {\n- var layerDefStrList = [];\n- var layerID;\n- for (layerID in this.layerDefs) {\n- if (this.layerDefs.hasOwnProperty(layerID)) {\n- if (this.layerDefs[layerID]) {\n- layerDefStrList.push(layerID);\n- layerDefStrList.push(\":\");\n- layerDefStrList.push(this.layerDefs[layerID]);\n- layerDefStrList.push(\";\");\n- }\n- }\n- }\n- if (layerDefStrList.length > 0) {\n- newParams['LAYERDEFS'] = layerDefStrList.join(\"\");\n- }\n- }\n- var requestString = this.getFullRequestString(newParams);\n- return requestString;\n- },\n-\n- /**\n- * Method: setLayerFilter\n- * addTile creates a tile, initializes it, and adds it to the layer div. \n- *\n- * Parameters:\n- * id - {String} The id of the layer to which the filter applies.\n- * queryDef - {String} A sql-ish query filter, for more detail see the ESRI\n- * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html\n- */\n- setLayerFilter: function(id, queryDef) {\n- if (!this.layerDefs) {\n- this.layerDefs = {};\n- }\n- if (queryDef) {\n- this.layerDefs[id] = queryDef;\n- } else {\n- delete this.layerDefs[id];\n- }\n- },\n-\n- /**\n- * Method: clearLayerFilter\n- * Clears layer filters, either from a specific layer,\n- * or all of them.\n- *\n- * Parameters:\n- * id - {String} The id of the layer from which to remove any\n- * filter. If unspecified/blank, all filters\n- * will be removed.\n- */\n- clearLayerFilter: function(id) {\n- if (id) {\n- delete this.layerDefs[id];\n- } else {\n- delete this.layerDefs;\n- }\n- },\n-\n- /**\n- * APIMethod: mergeNewParams\n- * Catch changeParams and uppercase the new params to be merged in\n- * before calling changeParams on the super class.\n- * \n- * Once params have been changed, the tiles will be reloaded with\n- * the new parameters.\n- * \n- * Parameters:\n- * newParams - {Object} Hashtable of new params to use\n- */\n- mergeNewParams: function(newParams) {\n- var upperParams = OpenLayers.Util.upperCaseObject(newParams);\n- var newArguments = [upperParams];\n- return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this,\n- newArguments);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.ArcGIS93Rest\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Zoomify.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/*\n- * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online\n- * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic.\n- */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Grid.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Zoomify\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Grid>\n- */\n-OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {\n-\n- /**\n- * Property: size\n- * {<OpenLayers.Size>} The Zoomify image size in pixels.\n- */\n- size: null,\n-\n- /**\n- * APIProperty: isBaseLayer\n- * {Boolean}\n- */\n- isBaseLayer: true,\n-\n- /**\n- * Property: standardTileSize\n- * {Integer} The size of a standard (non-border) square tile in pixels.\n- */\n- standardTileSize: 256,\n-\n- /** \n- * Property: tileOriginCorner\n- * {String} This layer uses top-left as tile origin\n- **/\n- tileOriginCorner: \"tl\",\n-\n- /**\n- * Property: numberOfTiers\n- * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels)\n- * - filled during Zoomify pyramid initialization.\n- */\n- numberOfTiers: 0,\n-\n- /**\n- * Property: tileCountUpToTier\n- * {Array(Integer)} Number of tiles up to the given tier of pyramid.\n- * - filled during Zoomify pyramid initialization.\n- */\n- tileCountUpToTier: null,\n-\n- /**\n- * Property: tierSizeInTiles\n- * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid.\n- * - filled during Zoomify pyramid initialization.\n- */\n- tierSizeInTiles: null,\n-\n- /**\n- * Property: tierImageSize\n- * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier.\n- * - filled during Zoomify pyramid initialization.\n- */\n- tierImageSize: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Zoomify\n- *\n- * Parameters:\n- * name - {String} A name for the layer.\n- * url - {String} - Relative or absolute path to the image or more\n- * precisly to the TileGroup[X] directories root.\n- * Flash plugin use the variable name \"zoomifyImagePath\" for this.\n- * size - {<OpenLayers.Size>} The size (in pixels) of the image.\n- * options - {Object} Hashtable of extra options to tag onto the layer\n- */\n- initialize: function(name, url, size, options) {\n-\n- // initilize the Zoomify pyramid for given size\n- this.initializeZoomify(size);\n-\n- OpenLayers.Layer.Grid.prototype.initialize.apply(this, [\n- name, url, size, {},\n- options\n- ]);\n- },\n-\n- /**\n- * Method: initializeZoomify\n- * It generates constants for all tiers of the Zoomify pyramid\n- *\n- * Parameters:\n- * size - {<OpenLayers.Size>} The size of the image in pixels\n- *\n- */\n- initializeZoomify: function(size) {\n-\n- var imageSize = size.clone();\n- this.size = size.clone();\n- var tiles = new OpenLayers.Size(\n- Math.ceil(imageSize.w / this.standardTileSize),\n- Math.ceil(imageSize.h / this.standardTileSize)\n- );\n-\n- this.tierSizeInTiles = [tiles];\n- this.tierImageSize = [imageSize];\n-\n- while (imageSize.w > this.standardTileSize ||\n- imageSize.h > this.standardTileSize) {\n-\n- imageSize = new OpenLayers.Size(\n- Math.floor(imageSize.w / 2),\n- Math.floor(imageSize.h / 2)\n- );\n- tiles = new OpenLayers.Size(\n- Math.ceil(imageSize.w / this.standardTileSize),\n- Math.ceil(imageSize.h / this.standardTileSize)\n- );\n- this.tierSizeInTiles.push(tiles);\n- this.tierImageSize.push(imageSize);\n- }\n-\n- this.tierSizeInTiles.reverse();\n- this.tierImageSize.reverse();\n-\n- this.numberOfTiers = this.tierSizeInTiles.length;\n- var resolutions = [1];\n- this.tileCountUpToTier = [0];\n- for (var i = 1; i < this.numberOfTiers; i++) {\n- resolutions.unshift(Math.pow(2, i));\n- this.tileCountUpToTier.push(\n- this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h +\n- this.tileCountUpToTier[i - 1]\n- );\n- }\n- if (!this.serverResolutions) {\n- this.serverResolutions = resolutions;\n- }\n- },\n-\n- /**\n- * APIMethod:destroy\n- */\n- destroy: function() {\n- // for now, nothing special to do here.\n- OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);\n-\n- // Remove from memory the Zoomify pyramid - is that enough?\n- this.tileCountUpToTier.length = 0;\n- this.tierSizeInTiles.length = 0;\n- this.tierImageSize.length = 0;\n-\n- },\n-\n- /**\n- * APIMethod: clone\n- *\n- * Parameters:\n- * obj - {Object}\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify>\n- */\n- clone: function(obj) {\n-\n- if (obj == null) {\n- obj = new OpenLayers.Layer.Zoomify(this.name,\n- this.url,\n- this.size,\n- this.options);\n- }\n-\n- //get all additions from superclasses\n- obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);\n-\n- // copy/set any non-init, non-simple values here\n-\n- return obj;\n- },\n-\n- /**\n- * Method: getURL\n- *\n- * Parameters:\n- * bounds - {<OpenLayers.Bounds>}\n- *\n- * Returns:\n- * {String} A string with the layer's url and parameters and also the\n- * passed-in bounds and appropriate tile size specified as\n- * parameters\n- */\n- getURL: function(bounds) {\n- bounds = this.adjustBounds(bounds);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n-\n- var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];\n- var path = \"TileGroup\" + Math.floor((tileIndex) / 256) +\n- \"/\" + z + \"-\" + x + \"-\" + y + \".jpg\";\n- var url = this.url;\n- if (OpenLayers.Util.isArray(url)) {\n- url = this.selectUrl(path, url);\n- }\n- return url + path;\n- },\n-\n- /**\n- * Method: getImageSize\n- * getImageSize returns size for a particular tile. If bounds are given as\n- * first argument, size is calculated (bottom-right tiles are non square).\n- *\n- */\n- getImageSize: function() {\n- if (arguments.length > 0) {\n- var bounds = this.adjustBounds(arguments[0]);\n- var res = this.getServerResolution();\n- var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));\n- var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));\n- var z = this.getZoomForResolution(res);\n- var w = this.standardTileSize;\n- var h = this.standardTileSize;\n- if (x == this.tierSizeInTiles[z].w - 1) {\n- var w = this.tierImageSize[z].w % this.standardTileSize;\n- }\n- if (y == this.tierSizeInTiles[z].h - 1) {\n- var h = this.tierImageSize[z].h % this.standardTileSize;\n- }\n- return (new OpenLayers.Size(w, h));\n- } else {\n- return this.tileSize;\n- }\n- },\n-\n- /**\n- * APIMethod: setMap\n- * When the layer is added to a map, then we can fetch our origin\n- * (if we don't have one.)\n- *\n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- setMap: function(map) {\n- OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);\n- this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left,\n- this.map.maxExtent.top);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Zoomify\"\n-});\n-/* ======================================================================\n- OpenLayers/Layer/Google/v3.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-\n-/**\n- * @requires OpenLayers/Layer/Google.js\n- */\n-\n-/**\n- * Constant: OpenLayers.Layer.Google.v3\n- * \n- * Mixin providing functionality specific to the Google Maps API v3.\n- * \n- * To use this layer, you must include the GMaps v3 API in your html.\n- * \n- * Note that this layer configures the google.maps.map object with the\n- * \"disableDefaultUI\" option set to true. Using UI controls that the Google\n- * Maps API provides is not supported by the OpenLayers API.\n- */\n-OpenLayers.Layer.Google.v3 = {\n-\n- /**\n- * Constant: DEFAULTS\n- * {Object} It is not recommended to change the properties set here. Note\n- * that Google.v3 layers only work when sphericalMercator is set to true.\n- * \n- * (code)\n- * {\n- * sphericalMercator: true,\n- * projection: \"EPSG:900913\"\n- * }\n- * (end)\n- */\n- DEFAULTS: {\n- sphericalMercator: true,\n- projection: \"EPSG:900913\"\n- },\n-\n- /**\n- * APIProperty: animationEnabled\n- * {Boolean} If set to true, the transition between zoom levels will be\n- * animated (if supported by the GMaps API for the device used). Set to\n- * false to match the zooming experience of other layer types. Default\n- * is true. Note that the GMaps API does not give us control over zoom\n- * animation, so if set to false, when zooming, this will make the\n- * layer temporarily invisible, wait until GMaps reports the map being\n- * idle, and make it visible again. The result will be a blank layer\n- * for a few moments while zooming.\n- */\n- animationEnabled: true,\n-\n- /** \n- * Method: loadMapObject\n- * Load the GMap and register appropriate event listeners.\n- */\n- loadMapObject: function() {\n- if (!this.type) {\n- this.type = google.maps.MapTypeId.ROADMAP;\n- }\n- var mapObject;\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- if (cache) {\n- // there are already Google layers added to this map\n- mapObject = cache.mapObject;\n- // increment the layer count\n- ++cache.count;\n- } else {\n- // this is the first Google layer for this map\n- // create GMap\n- var center = this.map.getCenter();\n- var container = document.createElement('div');\n- container.className = \"olForeignContainer\";\n- container.style.width = '100%';\n- container.style.height = '100%';\n- mapObject = new google.maps.Map(container, {\n- center: center ?\n- new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),\n- zoom: this.map.getZoom() || 0,\n- mapTypeId: this.type,\n- disableDefaultUI: true,\n- keyboardShortcuts: false,\n- draggable: false,\n- disableDoubleClickZoom: true,\n- scrollwheel: false,\n- streetViewControl: false\n- });\n- var googleControl = document.createElement('div');\n- googleControl.style.width = '100%';\n- googleControl.style.height = '100%';\n- mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);\n-\n- // cache elements for use by any other google layers added to\n- // this same map\n- cache = {\n- googleControl: googleControl,\n- mapObject: mapObject,\n- count: 1\n- };\n- OpenLayers.Layer.Google.cache[this.map.id] = cache;\n- }\n- this.mapObject = mapObject;\n- this.setGMapVisibility(this.visibility);\n- },\n-\n- /**\n- * APIMethod: onMapResize\n- */\n- onMapResize: function() {\n- if (this.visibility) {\n- google.maps.event.trigger(this.mapObject, \"resize\");\n- }\n- },\n-\n- /**\n- * Method: setGMapVisibility\n- * Display the GMap container and associated elements.\n- * \n- * Parameters:\n- * visible - {Boolean} Display the GMap elements.\n- */\n- setGMapVisibility: function(visible) {\n- var cache = OpenLayers.Layer.Google.cache[this.map.id];\n- var map = this.map;\n- if (cache) {\n- var type = this.type;\n- var layers = map.layers;\n- var layer;\n- for (var i = layers.length - 1; i >= 0; --i) {\n- layer = layers[i];\n- if (layer instanceof OpenLayers.Layer.Google &&\n- layer.visibility === true && layer.inRange === true) {\n- type = layer.type;\n- visible = true;\n- break;\n- }\n- }\n- var container = this.mapObject.getDiv();\n- if (visible === true) {\n- if (container.parentNode !== map.div) {\n- if (!cache.rendered) {\n- var me = this;\n- google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {\n- cache.rendered = true;\n- me.setGMapVisibility(me.getVisibility());\n- me.moveTo(me.map.getCenter());\n- });\n- } else {\n- map.div.appendChild(container);\n- cache.googleControl.appendChild(map.viewPortDiv);\n- google.maps.event.trigger(this.mapObject, 'resize');\n- }\n- }\n- this.mapObject.setMapTypeId(type);\n- } else if (cache.googleControl.hasChildNodes()) {\n- map.div.appendChild(map.viewPortDiv);\n- map.div.removeChild(container);\n- }\n- }\n- },\n-\n- /**\n- * Method: getMapContainer\n- * \n- * Returns:\n- * {DOMElement} the GMap container's div\n- */\n- getMapContainer: function() {\n- return this.mapObject.getDiv();\n- },\n-\n- //\n- // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds\n- //\n-\n- /**\n- * APIMethod: getMapObjectBoundsFromOLBounds\n- * \n- * Parameters:\n- * olBounds - {<OpenLayers.Bounds>}\n- * \n- * Returns:\n- * {Object} A MapObject Bounds, translated from olBounds\n- * Returns null if null value is passed in\n- */\n- getMapObjectBoundsFromOLBounds: function(olBounds) {\n- var moBounds = null;\n- if (olBounds != null) {\n- var sw = this.sphericalMercator ?\n- this.inverseMercator(olBounds.bottom, olBounds.left) :\n- new OpenLayers.LonLat(olBounds.bottom, olBounds.left);\n- var ne = this.sphericalMercator ?\n- this.inverseMercator(olBounds.top, olBounds.right) :\n- new OpenLayers.LonLat(olBounds.top, olBounds.right);\n- moBounds = new google.maps.LatLngBounds(\n- new google.maps.LatLng(sw.lat, sw.lon),\n- new google.maps.LatLng(ne.lat, ne.lon)\n- );\n- }\n- return moBounds;\n- },\n-\n-\n- /************************************\n- * *\n- * MapObject Interface Controls *\n- * *\n- ************************************/\n-\n-\n- // LonLat - Pixel Translation\n-\n- /**\n- * APIMethod: getMapObjectLonLatFromMapObjectPixel\n- * \n- * Parameters:\n- * moPixel - {Object} MapObject Pixel format\n- * \n- * Returns:\n- * {Object} MapObject LonLat translated from MapObject Pixel\n- */\n- getMapObjectLonLatFromMapObjectPixel: function(moPixel) {\n- var size = this.map.getSize();\n- var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);\n- var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);\n- var res = this.map.getResolution();\n-\n- var delta_x = moPixel.x - (size.w / 2);\n- var delta_y = moPixel.y - (size.h / 2);\n-\n- var lonlat = new OpenLayers.LonLat(\n- lon + delta_x * res,\n- lat - delta_y * res\n- );\n-\n- if (this.wrapDateLine) {\n- lonlat = lonlat.wrapDateLine(this.maxExtent);\n- }\n- return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);\n- },\n-\n- /**\n- * APIMethod: getMapObjectPixelFromMapObjectLonLat\n- * \n- * Parameters:\n- * moLonLat - {Object} MapObject LonLat format\n- * \n- * Returns:\n- * {Object} MapObject Pixel transtlated from MapObject LonLat\n- */\n- getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {\n- var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);\n- var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);\n- var res = this.map.getResolution();\n- var extent = this.map.getExtent();\n- return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)),\n- (1 / res * (extent.top - lat)));\n- },\n-\n-\n- /** \n- * APIMethod: setMapObjectCenter\n- * Set the mapObject to the specified center and zoom\n- * \n- * Parameters:\n- * center - {Object} MapObject LonLat format\n- * zoom - {int} MapObject zoom format\n- */\n- setMapObjectCenter: function(center, zoom) {\n- if (this.animationEnabled === false && zoom != this.mapObject.zoom) {\n- var mapContainer = this.getMapContainer();\n- google.maps.event.addListenerOnce(\n- this.mapObject,\n- \"idle\",\n- function() {\n- mapContainer.style.visibility = \"\";\n- }\n- );\n- mapContainer.style.visibility = \"hidden\";\n- }\n- this.mapObject.setOptions({\n- center: center,\n- zoom: zoom\n- });\n- },\n-\n-\n- // Bounds\n-\n- /** \n- * APIMethod: getMapObjectZoomFromMapObjectBounds\n- * \n- * Parameters:\n- * moBounds - {Object} MapObject Bounds format\n- * \n- * Returns:\n- * {Object} MapObject Zoom for specified MapObject Bounds\n- */\n- getMapObjectZoomFromMapObjectBounds: function(moBounds) {\n- return this.mapObject.getBoundsZoomLevel(moBounds);\n- },\n-\n- /************************************\n- * *\n- * MapObject Primitives *\n- * *\n- ************************************/\n-\n-\n- // LonLat\n-\n- /**\n- * APIMethod: getMapObjectLonLatFromLonLat\n- * \n- * Parameters:\n- * lon - {Float}\n- * lat - {Float}\n- * \n- * Returns:\n- * {Object} MapObject LonLat built from lon and lat params\n- */\n- getMapObjectLonLatFromLonLat: function(lon, lat) {\n- var gLatLng;\n- if (this.sphericalMercator) {\n- var lonlat = this.inverseMercator(lon, lat);\n- gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);\n- } else {\n- gLatLng = new google.maps.LatLng(lat, lon);\n- }\n- return gLatLng;\n- },\n-\n- // Pixel\n-\n- /**\n- * APIMethod: getMapObjectPixelFromXY\n- * \n- * Parameters:\n- * x - {Integer}\n- * y - {Integer}\n- * \n- * Returns:\n- * {Object} MapObject Pixel from x and y parameters\n- */\n- getMapObjectPixelFromXY: function(x, y) {\n- return new google.maps.Point(x, y);\n- }\n-\n-};\n-/* ======================================================================\n- OpenLayers/Layer/Vector/RootContainer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Layer/Vector.js\n- */\n-\n-/**\n- * Class: OpenLayers.Layer.Vector.RootContainer\n- * A special layer type to combine multiple vector layers inside a single\n- * renderer root container. This class is not supposed to be instantiated\n- * from user space, it is a helper class for controls that require event\n- * processing for multiple vector layers.\n- *\n- * Inherits from:\n- * - <OpenLayers.Layer.Vector>\n- */\n-OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {\n-\n- /**\n- * Property: displayInLayerSwitcher\n- * Set to false for this layer type\n- */\n- displayInLayerSwitcher: false,\n-\n- /**\n- * APIProperty: layers\n- * Layers that are attached to this container. Required config option.\n- */\n- layers: null,\n-\n- /**\n- * Constructor: OpenLayers.Layer.Vector.RootContainer\n- * Create a new root container for multiple vector layer. This constructor\n- * is not supposed to be used from user space, it is only to be used by\n- * controls that need feature selection across multiple vector layers.\n- *\n- * Parameters:\n- * name - {String} A name for the layer\n- * options - {Object} Optional object with non-default properties to set on\n- * the layer.\n- * \n- * Required options properties:\n- * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this\n- * container\n- *\n- * Returns:\n- * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root\n- * container\n- */\n-\n- /**\n- * Method: display\n- */\n- display: function() {},\n-\n- /**\n- * Method: getFeatureFromEvent\n- * walk through the layers to find the feature returned by the event\n- * \n- * Parameters:\n- * evt - {Object} event object with a feature property\n- * \n- * Returns:\n- * {<OpenLayers.Feature.Vector>}\n- */\n- getFeatureFromEvent: function(evt) {\n- var layers = this.layers;\n- var feature;\n- for (var i = 0; i < layers.length; i++) {\n- feature = layers[i].getFeatureFromEvent(evt);\n- if (feature) {\n- return feature;\n- }\n- }\n- },\n-\n- /**\n- * Method: setMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- setMap: function(map) {\n- OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);\n- this.collectRoots();\n- map.events.register(\"changelayer\", this, this.handleChangeLayer);\n- },\n-\n- /**\n- * Method: removeMap\n- * \n- * Parameters:\n- * map - {<OpenLayers.Map>}\n- */\n- removeMap: function(map) {\n- map.events.unregister(\"changelayer\", this, this.handleChangeLayer);\n- this.resetRoots();\n- OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);\n- },\n-\n- /**\n- * Method: collectRoots\n- * Collects the root nodes of all layers this control is configured with\n- * and moveswien the nodes to this control's layer\n- */\n- collectRoots: function() {\n- var layer;\n- // walk through all map layers, because we want to keep the order\n- for (var i = 0; i < this.map.layers.length; ++i) {\n- layer = this.map.layers[i];\n- if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- layer.renderer.moveRoot(this.renderer);\n- }\n- }\n- },\n-\n- /**\n- * Method: resetRoots\n- * Resets the root nodes back into the layers they belong to.\n- */\n- resetRoots: function() {\n- var layer;\n- for (var i = 0; i < this.layers.length; ++i) {\n- layer = this.layers[i];\n- if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {\n- this.renderer.moveRoot(layer.renderer);\n- }\n- }\n- },\n-\n- /**\n- * Method: handleChangeLayer\n- * Event handler for the map's changelayer event. We need to rebuild\n- * this container's layer dom if order of one of its layers changes.\n- * This handler is added with the setMap method, and removed with the\n- * removeMap method.\n- * \n- * Parameters:\n- * evt - {Object}\n- */\n- handleChangeLayer: function(evt) {\n- var layer = evt.layer;\n- if (evt.property == \"order\" &&\n- OpenLayers.Util.indexOf(this.layers, layer) != -1) {\n- this.resetRoots();\n- this.collectRoots();\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Layer.Vector.RootContainer\"\n-});\n-/* ======================================================================\n- OpenLayers/Popup/Framed.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Popup/Anchored.js\n- */\n-\n-/**\n- * Class: OpenLayers.Popup.Framed\n- * \n- * Inherits from:\n- * - <OpenLayers.Popup.Anchored>\n- */\n-OpenLayers.Popup.Framed =\n- OpenLayers.Class(OpenLayers.Popup.Anchored, {\n-\n- /**\n- * Property: imageSrc\n- * {String} location of the image to be used as the popup frame\n- */\n- imageSrc: null,\n-\n- /**\n- * Property: imageSize\n- * {<OpenLayers.Size>} Size (measured in pixels) of the image located\n- * by the 'imageSrc' property.\n- */\n- imageSize: null,\n-\n- /**\n- * APIProperty: isAlphaImage\n- * {Boolean} The image has some alpha and thus needs to use the alpha \n- * image hack. Note that setting this to true will have no noticeable\n- * effect in FF or IE7 browsers, but will all but crush the ie6 \n- * browser. \n- * Default is false.\n- */\n- isAlphaImage: false,\n-\n- /**\n- * Property: positionBlocks\n- * {Object} Hash of different position blocks (Object/Hashs). Each block \n- * will be keyed by a two-character 'relativePosition' \n- * code string (ie \"tl\", \"tr\", \"bl\", \"br\"). Block properties are \n- * 'offset', 'padding' (self-explanatory), and finally the 'blocks'\n- * parameter, which is an array of the block objects. \n- * \n- * Each block object must have 'size', 'anchor', and 'position' \n- * properties.\n- * \n- * Note that positionBlocks should never be modified at runtime.\n- */\n- positionBlocks: null,\n-\n- /**\n- * Property: blocks\n- * {Array[Object]} Array of objects, each of which is one \"block\" of the \n- * popup. Each block has a 'div' and an 'image' property, both of \n- * which are DOMElements, and the latter of which is appended to the \n- * former. These are reused as the popup goes changing positions for\n- * great economy and elegance.\n- */\n- blocks: null,\n-\n- /** \n- * APIProperty: fixedRelativePosition\n- * {Boolean} We want the framed popup to work dynamically placed relative\n- * to its anchor but also in just one fixed position. A well designed\n- * framed popup will have the pixels and logic to display itself in \n- * any of the four relative positions, but (understandably), this will\n- * not be the case for all of them. By setting this property to 'true', \n- * framed popup will not recalculate for the best placement each time\n- * it's open, but will always open the same way. \n- * Note that if this is set to true, it is generally advisable to also\n- * set the 'panIntoView' property to true so that the popup can be \n- * scrolled into view (since it will often be offscreen on open)\n- * Default is false.\n- */\n- fixedRelativePosition: false,\n-\n- /** \n- * Constructor: OpenLayers.Popup.Framed\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object to which we'll anchor the popup. Must expose \n- * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n- * (Note that this is generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n-\n- OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);\n-\n- if (this.fixedRelativePosition) {\n- //based on our decided relativePostion, set the current padding\n- // this keeps us from getting into trouble \n- this.updateRelativePosition();\n-\n- //make calculateRelativePosition always return the specified\n- // fixed position.\n- this.calculateRelativePosition = function(px) {\n- return this.relativePosition;\n- };\n- }\n-\n- this.contentDiv.style.position = \"absolute\";\n- this.contentDiv.style.zIndex = 1;\n-\n- if (closeBox) {\n- this.closeDiv.style.zIndex = 1;\n- }\n-\n- this.groupDiv.style.position = \"absolute\";\n- this.groupDiv.style.top = \"0px\";\n- this.groupDiv.style.left = \"0px\";\n- this.groupDiv.style.height = \"100%\";\n- this.groupDiv.style.width = \"100%\";\n- },\n-\n- /** \n- * APIMethod: destroy\n- */\n- destroy: function() {\n- this.imageSrc = null;\n- this.imageSize = null;\n- this.isAlphaImage = null;\n-\n- this.fixedRelativePosition = false;\n- this.positionBlocks = null;\n-\n- //remove our blocks\n- for (var i = 0; i < this.blocks.length; i++) {\n- var block = this.blocks[i];\n-\n- if (block.image) {\n- block.div.removeChild(block.image);\n- }\n- block.image = null;\n-\n- if (block.div) {\n- this.groupDiv.removeChild(block.div);\n- }\n- block.div = null;\n- }\n- this.blocks = null;\n-\n- OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);\n- },\n-\n- /**\n- * APIMethod: setBackgroundColor\n- */\n- setBackgroundColor: function(color) {\n- //does nothing since the framed popup's entire scheme is based on a \n- // an image -- changing the background color makes no sense. \n- },\n-\n- /**\n- * APIMethod: setBorder\n- */\n- setBorder: function() {\n- //does nothing since the framed popup's entire scheme is based on a \n- // an image -- changing the popup's border makes no sense. \n- },\n-\n- /**\n- * Method: setOpacity\n- * Sets the opacity of the popup.\n- * \n- * Parameters:\n- * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). \n- */\n- setOpacity: function(opacity) {\n- //does nothing since we suppose that we'll never apply an opacity\n- // to a framed popup\n- },\n-\n- /**\n- * APIMethod: setSize\n- * Overridden here, because we need to update the blocks whenever the size\n- * of the popup has changed.\n- * \n- * Parameters:\n- * contentSize - {<OpenLayers.Size>} the new size for the popup's \n- * contents div (in pixels).\n- */\n- setSize: function(contentSize) {\n- OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);\n-\n- this.updateBlocks();\n- },\n-\n- /**\n- * Method: updateRelativePosition\n- * When the relative position changes, we need to set the new padding \n- * BBOX on the popup, reposition the close div, and update the blocks.\n- */\n- updateRelativePosition: function() {\n-\n- //update the padding\n- this.padding = this.positionBlocks[this.relativePosition].padding;\n-\n- //update the position of our close box to new padding\n- if (this.closeDiv) {\n- // use the content div's css padding to determine if we should\n- // padd the close div\n- var contentDivPadding = this.getContentDivPadding();\n-\n- this.closeDiv.style.right = contentDivPadding.right +\n- this.padding.right + \"px\";\n- this.closeDiv.style.top = contentDivPadding.top +\n- this.padding.top + \"px\";\n- }\n-\n- this.updateBlocks();\n- },\n-\n- /** \n- * Method: calculateNewPx\n- * Besides the standard offset as determined by the Anchored class, our \n- * Framed popups have a special 'offset' property for each of their \n- * positions, which is used to offset the popup relative to its anchor.\n- * \n- * Parameters:\n- * px - {<OpenLayers.Pixel>}\n- * \n- * Returns:\n- * {<OpenLayers.Pixel>} The the new px position of the popup on the screen\n- * relative to the passed-in px.\n- */\n- calculateNewPx: function(px) {\n- var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(\n- this, arguments\n- );\n-\n- newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);\n-\n- return newPx;\n- },\n-\n- /**\n- * Method: createBlocks\n- */\n- createBlocks: function() {\n- this.blocks = [];\n-\n- //since all positions contain the same number of blocks, we can \n- // just pick the first position and use its blocks array to create\n- // our blocks array\n- var firstPosition = null;\n- for (var key in this.positionBlocks) {\n- firstPosition = key;\n- break;\n- }\n-\n- var position = this.positionBlocks[firstPosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n-\n- var block = {};\n- this.blocks.push(block);\n-\n- var divId = this.id + '_FrameDecorationDiv_' + i;\n- block.div = OpenLayers.Util.createDiv(divId,\n- null, null, null, \"absolute\", null, \"hidden\", null\n- );\n-\n- var imgId = this.id + '_FrameDecorationImg_' + i;\n- var imageCreator =\n- (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv :\n- OpenLayers.Util.createImage;\n-\n- block.image = imageCreator(imgId,\n- null, this.imageSize, this.imageSrc,\n- \"absolute\", null, null, null\n- );\n-\n- block.div.appendChild(block.image);\n- this.groupDiv.appendChild(block.div);\n- }\n- },\n-\n- /**\n- * Method: updateBlocks\n- * Internal method, called on initialize and when the popup's relative\n- * position has changed. This function takes care of re-positioning\n- * the popup's blocks in their appropropriate places.\n- */\n- updateBlocks: function() {\n- if (!this.blocks) {\n- this.createBlocks();\n- }\n-\n- if (this.size && this.relativePosition) {\n- var position = this.positionBlocks[this.relativePosition];\n- for (var i = 0; i < position.blocks.length; i++) {\n-\n- var positionBlock = position.blocks[i];\n- var block = this.blocks[i];\n-\n- // adjust sizes\n- var l = positionBlock.anchor.left;\n- var b = positionBlock.anchor.bottom;\n- var r = positionBlock.anchor.right;\n- var t = positionBlock.anchor.top;\n-\n- //note that we use the isNaN() test here because if the \n- // size object is initialized with a \"auto\" parameter, the \n- // size constructor calls parseFloat() on the string, \n- // which will turn it into NaN\n- //\n- var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) :\n- positionBlock.size.w;\n-\n- var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) :\n- positionBlock.size.h;\n-\n- block.div.style.width = (w < 0 ? 0 : w) + 'px';\n- block.div.style.height = (h < 0 ? 0 : h) + 'px';\n-\n- block.div.style.left = (l != null) ? l + 'px' : '';\n- block.div.style.bottom = (b != null) ? b + 'px' : '';\n- block.div.style.right = (r != null) ? r + 'px' : '';\n- block.div.style.top = (t != null) ? t + 'px' : '';\n-\n- block.image.style.left = positionBlock.position.x + 'px';\n- block.image.style.top = positionBlock.position.y + 'px';\n- }\n-\n- this.contentDiv.style.left = this.padding.left + \"px\";\n- this.contentDiv.style.top = this.padding.top + \"px\";\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Popup.Framed\"\n- });\n-/* ======================================================================\n- OpenLayers/Popup/FramedCloud.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Popup/Framed.js\n- * @requires OpenLayers/Util.js\n- * @requires OpenLayers/BaseTypes/Bounds.js\n- * @requires OpenLayers/BaseTypes/Pixel.js\n- * @requires OpenLayers/BaseTypes/Size.js\n- */\n-\n-/**\n- * Class: OpenLayers.Popup.FramedCloud\n- * \n- * Inherits from: \n- * - <OpenLayers.Popup.Framed>\n- */\n-OpenLayers.Popup.FramedCloud =\n- OpenLayers.Class(OpenLayers.Popup.Framed, {\n-\n- /** \n- * Property: contentDisplayClass\n- * {String} The CSS class of the popup content div.\n- */\n- contentDisplayClass: \"olFramedCloudPopupContent\",\n-\n- /**\n- * APIProperty: autoSize\n- * {Boolean} Framed Cloud is autosizing by default.\n- */\n- autoSize: true,\n-\n- /**\n- * APIProperty: panMapIfOutOfView\n- * {Boolean} Framed Cloud does pan into view by default.\n- */\n- panMapIfOutOfView: true,\n-\n- /**\n- * APIProperty: imageSize\n- * {<OpenLayers.Size>}\n- */\n- imageSize: new OpenLayers.Size(1276, 736),\n-\n- /**\n- * APIProperty: isAlphaImage\n- * {Boolean} The FramedCloud does not use an alpha image (in honor of the \n- * good ie6 folk out there)\n- */\n- isAlphaImage: false,\n-\n- /** \n- * APIProperty: fixedRelativePosition\n- * {Boolean} The Framed Cloud popup works in just one fixed position.\n- */\n- fixedRelativePosition: false,\n-\n- /**\n- * Property: positionBlocks\n- * {Object} Hash of differen position blocks, keyed by relativePosition\n- * two-character code string (ie \"tl\", \"tr\", \"bl\", \"br\")\n- */\n- positionBlocks: {\n- \"tl\": {\n- 'offset': new OpenLayers.Pixel(44, 0),\n- 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 18),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -632)\n- }, { // stem\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(0, -688)\n- }]\n- },\n- \"tr\": {\n- 'offset': new OpenLayers.Pixel(-45, 0),\n- 'padding': new OpenLayers.Bounds(8, 40, 8, 9),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 51, 22, 0),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 50, 0, 0),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 19),\n- anchor: new OpenLayers.Bounds(0, 32, 22, null),\n- position: new OpenLayers.Pixel(0, -631)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 19),\n- anchor: new OpenLayers.Bounds(null, 32, 0, null),\n- position: new OpenLayers.Pixel(-1238, -631)\n- }, { // stem\n- size: new OpenLayers.Size(81, 35),\n- anchor: new OpenLayers.Bounds(0, 0, null, null),\n- position: new OpenLayers.Pixel(-215, -687)\n- }]\n- },\n- \"bl\": {\n- 'offset': new OpenLayers.Pixel(45, 0),\n- 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, { // stem\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(null, null, 0, 0),\n- position: new OpenLayers.Pixel(-101, -674)\n- }]\n- },\n- \"br\": {\n- 'offset': new OpenLayers.Pixel(-44, 0),\n- 'padding': new OpenLayers.Bounds(8, 9, 8, 40),\n- 'blocks': [{ // top-left\n- size: new OpenLayers.Size('auto', 'auto'),\n- anchor: new OpenLayers.Bounds(0, 21, 22, 32),\n- position: new OpenLayers.Pixel(0, 0)\n- }, { //top-right\n- size: new OpenLayers.Size(22, 'auto'),\n- anchor: new OpenLayers.Bounds(null, 21, 0, 32),\n- position: new OpenLayers.Pixel(-1238, 0)\n- }, { //bottom-left\n- size: new OpenLayers.Size('auto', 21),\n- anchor: new OpenLayers.Bounds(0, 0, 22, null),\n- position: new OpenLayers.Pixel(0, -629)\n- }, { //bottom-right\n- size: new OpenLayers.Size(22, 21),\n- anchor: new OpenLayers.Bounds(null, 0, 0, null),\n- position: new OpenLayers.Pixel(-1238, -629)\n- }, { // stem\n- size: new OpenLayers.Size(81, 33),\n- anchor: new OpenLayers.Bounds(0, null, null, 0),\n- position: new OpenLayers.Pixel(-311, -674)\n- }]\n- }\n- },\n-\n- /**\n- * APIProperty: minSize\n- * {<OpenLayers.Size>}\n- */\n- minSize: new OpenLayers.Size(105, 10),\n-\n- /**\n- * APIProperty: maxSize\n- * {<OpenLayers.Size>}\n- */\n- maxSize: new OpenLayers.Size(1200, 660),\n-\n- /** \n- * Constructor: OpenLayers.Popup.FramedCloud\n- * \n- * Parameters:\n- * id - {String}\n- * lonlat - {<OpenLayers.LonLat>}\n- * contentSize - {<OpenLayers.Size>}\n- * contentHTML - {String}\n- * anchor - {Object} Object to which we'll anchor the popup. Must expose \n- * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) \n- * (Note that this is generally an <OpenLayers.Icon>).\n- * closeBox - {Boolean}\n- * closeBoxCallback - {Function} Function to be called on closeBox click.\n- */\n- initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox,\n- closeBoxCallback) {\n-\n- this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');\n- OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);\n- this.contentDiv.className = this.contentDisplayClass;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Popup.FramedCloud\"\n- });\n-/* ======================================================================\n- OpenLayers/Format/WFSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WFSCapabilities\n- * Read WFS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n- */\n- defaultVersion: \"1.1.0\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WFSCapabilities\n- * Create a new parser for WFS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of layers. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named layers.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WFSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/WPSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WPSCapabilities\n- * Read WPS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WPSCapabilities\n- * Create a new parser for WPS Capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return information about\n- * the service.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Info about the WPS\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/Atom.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/GML/v3.js\n- * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/GML/v3.js\n+ * @requires OpenLayers/Feature/Vector.js\n */\n \n /**\n * Class: OpenLayers.Format.Atom\n * Read/write Atom feeds. Create a new instance with the\n * <OpenLayers.Format.AtomFeed> constructor.\n *\n@@ -52883,990 +48025,14 @@\n data[name + \"s\"] = persons;\n }\n },\n \n CLASS_NAME: \"OpenLayers.Format.Atom\"\n });\n /* ======================================================================\n- OpenLayers/Format/SOSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SOSCapabilities\n- * Read SOS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n-\n- /**\n- * Constructor: OpenLayers.Format.SOSCapabilities\n- * Create a new parser for SOS Capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return information about\n- * the service (offering and observedProperty mostly).\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Info about the SOS\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.SOSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/SOSGetFeatureOfInterest.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/GML/v3.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SOSGetFeatureOfInterest\n- * Read and write SOS GetFeatureOfInterest. This is used to get to\n- * the location of the features (stations). The stations can have 1 or more\n- * sensors.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n-\n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- sos: \"http://www.opengis.net/sos/1.0\",\n- gml: \"http://www.opengis.net/gml\",\n- sa: \"http://www.opengis.net/sampling/1.0\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd\",\n-\n- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"sos\",\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.SOSGetFeatureOfInterest\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Parse a GetFeatureOfInterest response and return an array of features\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} An array of features. \n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n-\n- var info = {\n- features: []\n- };\n- this.readNode(data, info);\n-\n- var features = [];\n- for (var i = 0, len = info.features.length; i < len; i++) {\n- var container = info.features[i];\n- // reproject features if needed\n- if (this.internalProjection && this.externalProjection &&\n- container.components[0]) {\n- container.components[0].transform(\n- this.externalProjection, this.internalProjection\n- );\n- }\n- var feature = new OpenLayers.Feature.Vector(\n- container.components[0], container.attributes);\n- features.push(feature);\n- }\n- return features;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"sa\": {\n- \"SamplingPoint\": function(node, obj) {\n- // sampling point can also be without a featureMember if \n- // there is only 1\n- if (!obj.attributes) {\n- var feature = {\n- attributes: {}\n- };\n- obj.features.push(feature);\n- obj = feature;\n- }\n- obj.attributes.id = this.getAttributeNS(node,\n- this.namespaces.gml, \"id\");\n- this.readChildNodes(node, obj);\n- },\n- \"position\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- }\n- },\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"FeatureCollection\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"featureMember\": function(node, obj) {\n- var feature = {\n- attributes: {}\n- };\n- obj.features.push(feature);\n- this.readChildNodes(node, feature);\n- },\n- \"name\": function(node, obj) {\n- obj.attributes.name = this.getChildValue(node);\n- },\n- \"pos\": function(node, obj) {\n- // we need to parse the srsName to get to the \n- // externalProjection, that's why we cannot use\n- // GML v3 for this\n- if (!this.externalProjection) {\n- this.externalProjection = new OpenLayers.Projection(\n- node.getAttribute(\"srsName\"));\n- }\n- OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(\n- this, [node, obj]);\n- }\n- }, OpenLayers.Format.GML.v3.prototype.readers.gml)\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"sos\": {\n- \"GetFeatureOfInterest\": function(options) {\n- var node = this.createElementNSPlus(\"GetFeatureOfInterest\", {\n- attributes: {\n- version: this.VERSION,\n- service: 'SOS',\n- \"xsi:schemaLocation\": this.schemaLocation\n- }\n- });\n- for (var i = 0, len = options.fois.length; i < len; i++) {\n- this.writeNode(\"FeatureOfInterestId\", {\n- foi: options.fois[i]\n- }, node);\n- }\n- return node;\n- },\n- \"FeatureOfInterestId\": function(options) {\n- var node = this.createElementNSPlus(\"FeatureOfInterestId\", {\n- value: options.foi\n- });\n- return node;\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.SOSGetFeatureOfInterest\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/WMSDescribeLayer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSDescribeLayer\n- * Read SLD WMS DescribeLayer response\n- * DescribeLayer is meant to couple WMS to WFS and WCS\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.1\".\n- */\n- defaultVersion: \"1.1.1\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSDescribeLayer\n- * Create a new parser for WMS DescribeLayer responses.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read DescribeLayer data from a string, and return the response. \n- * The OGC currently defines 2 formats which are allowed for output,\n- * so we need to parse these 2 types\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} Array of {<LayerDescription>} objects which have:\n- * - {String} owsType: WFS/WCS\n- * - {String} owsURL: the online resource\n- * - {String} typeName: the name of the typename on the service\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/SOSGetObservation.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SOSGetObservation\n- * Read and write SOS GetObersation (to get the actual values from a sensor) \n- * version 1.0.0\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows\",\n- gml: \"http://www.opengis.net/gml\",\n- sos: \"http://www.opengis.net/sos/1.0\",\n- ogc: \"http://www.opengis.net/ogc\",\n- om: \"http://www.opengis.net/om/1.0\",\n- sa: \"http://www.opengis.net/sampling/1.0\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constant: VERSION\n- * {String} 1.0.0\n- */\n- VERSION: \"1.0.0\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location\n- */\n- schemaLocation: \"http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd\",\n-\n- /**\n- * Property: defaultPrefix\n- */\n- defaultPrefix: \"sos\",\n-\n- /**\n- * Constructor: OpenLayers.Format.SOSGetObservation\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Method: read\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} An object containing the measurements\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var info = {\n- measurements: [],\n- observations: []\n- };\n- this.readNode(data, info);\n- return info;\n- },\n-\n- /**\n- * Method: write\n- *\n- * Parameters:\n- * options - {Object} Optional object.\n- *\n- * Returns:\n- * {String} An SOS GetObservation request XML string.\n- */\n- write: function(options) {\n- var node = this.writeNode(\"sos:GetObservation\", options);\n- node.setAttribute(\"xmlns:om\", this.namespaces.om);\n- node.setAttribute(\"xmlns:ogc\", this.namespaces.ogc);\n- this.setAttributeNS(\n- node, this.namespaces.xsi,\n- \"xsi:schemaLocation\", this.schemaLocation\n- );\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"om\": {\n- \"ObservationCollection\": function(node, obj) {\n- obj.id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n- this.readChildNodes(node, obj);\n- },\n- \"member\": function(node, observationCollection) {\n- this.readChildNodes(node, observationCollection);\n- },\n- \"Measurement\": function(node, observationCollection) {\n- var measurement = {};\n- observationCollection.measurements.push(measurement);\n- this.readChildNodes(node, measurement);\n- },\n- \"Observation\": function(node, observationCollection) {\n- var observation = {};\n- observationCollection.observations.push(observation);\n- this.readChildNodes(node, observation);\n- },\n- \"samplingTime\": function(node, measurement) {\n- var samplingTime = {};\n- measurement.samplingTime = samplingTime;\n- this.readChildNodes(node, samplingTime);\n- },\n- \"observedProperty\": function(node, measurement) {\n- measurement.observedProperty =\n- this.getAttributeNS(node, this.namespaces.xlink, \"href\");\n- this.readChildNodes(node, measurement);\n- },\n- \"procedure\": function(node, measurement) {\n- measurement.procedure =\n- this.getAttributeNS(node, this.namespaces.xlink, \"href\");\n- this.readChildNodes(node, measurement);\n- },\n- \"featureOfInterest\": function(node, observation) {\n- var foi = {\n- features: []\n- };\n- observation.fois = [];\n- observation.fois.push(foi);\n- this.readChildNodes(node, foi);\n- // postprocessing to get actual features\n- var features = [];\n- for (var i = 0, len = foi.features.length; i < len; i++) {\n- var feature = foi.features[i];\n- features.push(new OpenLayers.Feature.Vector(\n- feature.components[0], feature.attributes));\n- }\n- foi.features = features;\n- },\n- \"result\": function(node, measurement) {\n- var result = {};\n- measurement.result = result;\n- if (this.getChildValue(node) !== '') {\n- result.value = this.getChildValue(node);\n- result.uom = node.getAttribute(\"uom\");\n- } else {\n- this.readChildNodes(node, result);\n- }\n- }\n- },\n- \"sa\": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa,\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"TimeInstant\": function(node, samplingTime) {\n- var timeInstant = {};\n- samplingTime.timeInstant = timeInstant;\n- this.readChildNodes(node, timeInstant);\n- },\n- \"timePosition\": function(node, timeInstant) {\n- timeInstant.timePosition = this.getChildValue(node);\n- }\n- }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml)\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"sos\": {\n- \"GetObservation\": function(options) {\n- var node = this.createElementNSPlus(\"GetObservation\", {\n- attributes: {\n- version: this.VERSION,\n- service: 'SOS'\n- }\n- });\n- this.writeNode(\"offering\", options, node);\n- if (options.eventTime) {\n- this.writeNode(\"eventTime\", options, node);\n- }\n- for (var procedure in options.procedures) {\n- this.writeNode(\"procedure\", options.procedures[procedure], node);\n- }\n- for (var observedProperty in options.observedProperties) {\n- this.writeNode(\"observedProperty\", options.observedProperties[observedProperty], node);\n- }\n- if (options.foi) {\n- this.writeNode(\"featureOfInterest\", options.foi, node);\n- }\n- this.writeNode(\"responseFormat\", options, node);\n- if (options.resultModel) {\n- this.writeNode(\"resultModel\", options, node);\n- }\n- if (options.responseMode) {\n- this.writeNode(\"responseMode\", options, node);\n- }\n- return node;\n- },\n- \"featureOfInterest\": function(foi) {\n- var node = this.createElementNSPlus(\"featureOfInterest\");\n- this.writeNode(\"ObjectID\", foi.objectId, node);\n- return node;\n- },\n- \"ObjectID\": function(options) {\n- return this.createElementNSPlus(\"ObjectID\", {\n- value: options\n- });\n- },\n- \"responseFormat\": function(options) {\n- return this.createElementNSPlus(\"responseFormat\", {\n- value: options.responseFormat\n- });\n- },\n- \"procedure\": function(procedure) {\n- return this.createElementNSPlus(\"procedure\", {\n- value: procedure\n- });\n- },\n- \"offering\": function(options) {\n- return this.createElementNSPlus(\"offering\", {\n- value: options.offering\n- });\n- },\n- \"observedProperty\": function(observedProperty) {\n- return this.createElementNSPlus(\"observedProperty\", {\n- value: observedProperty\n- });\n- },\n- \"eventTime\": function(options) {\n- var node = this.createElementNSPlus(\"eventTime\");\n- if (options.eventTime === 'latest') {\n- this.writeNode(\"ogc:TM_Equals\", options, node);\n- }\n- return node;\n- },\n- \"resultModel\": function(options) {\n- return this.createElementNSPlus(\"resultModel\", {\n- value: options.resultModel\n- });\n- },\n- \"responseMode\": function(options) {\n- return this.createElementNSPlus(\"responseMode\", {\n- value: options.responseMode\n- });\n- }\n- },\n- \"ogc\": {\n- \"TM_Equals\": function(options) {\n- var node = this.createElementNSPlus(\"ogc:TM_Equals\");\n- this.writeNode(\"ogc:PropertyName\", {\n- property: \"urn:ogc:data:time:iso8601\"\n- }, node);\n- if (options.eventTime === 'latest') {\n- this.writeNode(\"gml:TimeInstant\", {\n- value: 'latest'\n- }, node);\n- }\n- return node;\n- },\n- \"PropertyName\": function(options) {\n- return this.createElementNSPlus(\"ogc:PropertyName\", {\n- value: options.property\n- });\n- }\n- },\n- \"gml\": {\n- \"TimeInstant\": function(options) {\n- var node = this.createElementNSPlus(\"gml:TimeInstant\");\n- this.writeNode(\"gml:timePosition\", options, node);\n- return node;\n- },\n- \"timePosition\": function(options) {\n- var node = this.createElementNSPlus(\"gml:timePosition\", {\n- value: options.value\n- });\n- return node;\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.SOSGetObservation\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/Context.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.Context\n- * Base class for both Format.WMC and Format.OWSContext\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * Property: layerOptions\n- * {Object} Default options for layers created by the parser. These\n- * options are overridden by the options which are read from the\n- * capabilities document.\n- */\n- layerOptions: null,\n-\n- /**\n- * Property: layerParams\n- * {Object} Default parameters for layers created by the parser. This\n- * can be used e.g. to override DEFAULT_PARAMS for \n- * OpenLayers.Layer.WMS.\n- */\n- layerParams: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.Context\n- * Create a new parser for Context documents.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read Context data from a string, and return an object with map\n- * properties and a list of layers.\n- *\n- * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- * options - {Object} The options object must contain a map property. If\n- * the map property is a string, it must be the id of a dom element\n- * where the new map will be placed. If the map property is an\n- * <OpenLayers.Map>, the layers from the context document will be added\n- * to the map.\n- *\n- * Returns:\n- * {<OpenLayers.Map>} A map based on the context.\n- */\n- read: function(data, options) {\n- var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this,\n- arguments);\n- var map;\n- if (options && options.map) {\n- this.context = context;\n- if (options.map instanceof OpenLayers.Map) {\n- map = this.mergeContextToMap(context, options.map);\n- } else {\n- var mapOptions = options.map;\n- if (OpenLayers.Util.isElement(mapOptions) ||\n- typeof mapOptions == \"string\") {\n- // we assume mapOptions references a div\n- // element\n- mapOptions = {\n- div: mapOptions\n- };\n- }\n- map = this.contextToMap(context, mapOptions);\n- }\n- } else {\n- // not documented as part of the API, provided as a non-API option\n- map = context;\n- }\n- return map;\n- },\n-\n- /**\n- * Method: getLayerFromContext\n- * Create a WMS layer from a layerContext object.\n- *\n- * Parameters:\n- * layerContext - {Object} An object representing a WMS layer.\n- *\n- * Returns:\n- * {<OpenLayers.Layer.WMS>} A WMS layer.\n- */\n- getLayerFromContext: function(layerContext) {\n- var i, len;\n- // fill initial options object from layerContext\n- var options = {\n- queryable: layerContext.queryable, //keep queryable for api compatibility\n- visibility: layerContext.visibility,\n- maxExtent: layerContext.maxExtent,\n- metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {\n- styles: layerContext.styles,\n- formats: layerContext.formats,\n- \"abstract\": layerContext[\"abstract\"],\n- dataURL: layerContext.dataURL\n- }),\n- numZoomLevels: layerContext.numZoomLevels,\n- units: layerContext.units,\n- isBaseLayer: layerContext.isBaseLayer,\n- opacity: layerContext.opacity,\n- displayInLayerSwitcher: layerContext.displayInLayerSwitcher,\n- singleTile: layerContext.singleTile,\n- tileSize: (layerContext.tileSize) ?\n- new OpenLayers.Size(\n- layerContext.tileSize.width,\n- layerContext.tileSize.height\n- ) : undefined,\n- minScale: layerContext.minScale || layerContext.maxScaleDenominator,\n- maxScale: layerContext.maxScale || layerContext.minScaleDenominator,\n- srs: layerContext.srs,\n- dimensions: layerContext.dimensions,\n- metadataURL: layerContext.metadataURL\n- };\n- if (this.layerOptions) {\n- OpenLayers.Util.applyDefaults(options, this.layerOptions);\n- }\n-\n- var params = {\n- layers: layerContext.name,\n- transparent: layerContext.transparent,\n- version: layerContext.version\n- };\n- if (layerContext.formats && layerContext.formats.length > 0) {\n- // set default value for params if current attribute is not positionned\n- params.format = layerContext.formats[0].value;\n- for (i = 0, len = layerContext.formats.length; i < len; i++) {\n- var format = layerContext.formats[i];\n- if (format.current == true) {\n- params.format = format.value;\n- break;\n- }\n- }\n- }\n- if (layerContext.styles && layerContext.styles.length > 0) {\n- for (i = 0, len = layerContext.styles.length; i < len; i++) {\n- var style = layerContext.styles[i];\n- if (style.current == true) {\n- // three style types to consider\n- // 1) linked SLD\n- // 2) inline SLD\n- // 3) named style\n- if (style.href) {\n- params.sld = style.href;\n- } else if (style.body) {\n- params.sld_body = style.body;\n- } else {\n- params.styles = style.name;\n- }\n- break;\n- }\n- }\n- }\n- if (this.layerParams) {\n- OpenLayers.Util.applyDefaults(params, this.layerParams);\n- }\n-\n- var layer = null;\n- var service = layerContext.service;\n- if (service == OpenLayers.Format.Context.serviceTypes.WFS) {\n- options.strategies = [new OpenLayers.Strategy.BBOX()];\n- options.protocol = new OpenLayers.Protocol.WFS({\n- url: layerContext.url,\n- // since we do not know featureNS, let the protocol\n- // determine it automagically using featurePrefix\n- featurePrefix: layerContext.name.split(\":\")[0],\n- featureType: layerContext.name.split(\":\").pop()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {\n- // use a vector layer with an HTTP Protcol and a Fixed strategy\n- options.strategies = [new OpenLayers.Strategy.Fixed()];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.KML()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {\n- // use a vector layer with a HTTP Protocol and a Fixed strategy\n- options.strategies = [new OpenLayers.Strategy.Fixed()];\n- options.protocol = new OpenLayers.Protocol.HTTP({\n- url: layerContext.url,\n- format: new OpenLayers.Format.GML()\n- });\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- } else if (layerContext.features) {\n- // inline GML or KML features\n- layer = new OpenLayers.Layer.Vector(\n- layerContext.title || layerContext.name,\n- options\n- );\n- layer.addFeatures(layerContext.features);\n- } else if (layerContext.categoryLayer !== true) {\n- layer = new OpenLayers.Layer.WMS(\n- layerContext.title || layerContext.name,\n- layerContext.url,\n- params,\n- options\n- );\n- }\n- return layer;\n- },\n-\n- /**\n- * Method: getLayersFromContext\n- * Create an array of layers from an array of layerContext objects.\n- *\n- * Parameters:\n- * layersContext - {Array(Object)} An array of objects representing layers.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Layer>)} An array of layers.\n- */\n- getLayersFromContext: function(layersContext) {\n- var layers = [];\n- for (var i = 0, len = layersContext.length; i < len; i++) {\n- var layer = this.getLayerFromContext(layersContext[i]);\n- if (layer !== null) {\n- layers.push(layer);\n- }\n- }\n- return layers;\n- },\n-\n- /**\n- * Method: contextToMap\n- * Create a map given a context object.\n- *\n- * Parameters:\n- * context - {Object} The context object.\n- * options - {Object} Default map options.\n- *\n- * Returns:\n- * {<OpenLayers.Map>} A map based on the context object.\n- */\n- contextToMap: function(context, options) {\n- options = OpenLayers.Util.applyDefaults({\n- maxExtent: context.maxExtent,\n- projection: context.projection,\n- units: context.units\n- }, options);\n-\n- if (options.maxExtent) {\n- options.maxResolution =\n- options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;\n- }\n-\n- var metadata = {\n- contactInformation: context.contactInformation,\n- \"abstract\": context[\"abstract\"],\n- keywords: context.keywords,\n- logo: context.logo,\n- descriptionURL: context.descriptionURL\n- };\n-\n- options.metadata = metadata;\n-\n- var map = new OpenLayers.Map(options);\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- map.setCenter(\n- context.bounds.getCenterLonLat(),\n- map.getZoomForExtent(context.bounds, true)\n- );\n- return map;\n- },\n-\n- /**\n- * Method: mergeContextToMap\n- * Add layers from a context object to a map.\n- *\n- * Parameters:\n- * context - {Object} The context object.\n- * map - {<OpenLayers.Map>} The map.\n- *\n- * Returns:\n- * {<OpenLayers.Map>} The same map with layers added.\n- */\n- mergeContextToMap: function(context, map) {\n- map.addLayers(this.getLayersFromContext(context.layersContext));\n- return map;\n- },\n-\n- /**\n- * APIMethod: write\n- * Write a context document given a map.\n- *\n- * Parameters:\n- * obj - {<OpenLayers.Map> | Object} A map or context object.\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {String} A context document string.\n- */\n- write: function(obj, options) {\n- obj = this.toContext(obj);\n- return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this,\n- arguments);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.Context\"\n-});\n-\n-/**\n- * Constant: OpenLayers.Format.Context.serviceTypes\n- * Enumeration for service types\n- */\n-OpenLayers.Format.Context.serviceTypes = {\n- \"WMS\": \"urn:ogc:serviceType:WMS\",\n- \"WFS\": \"urn:ogc:serviceType:WFS\",\n- \"WCS\": \"urn:ogc:serviceType:WCS\",\n- \"GML\": \"urn:ogc:serviceType:GML\",\n- \"SLD\": \"urn:ogc:serviceType:SLD\",\n- \"FES\": \"urn:ogc:serviceType:FES\",\n- \"KML\": \"urn:ogc:serviceType:KML\"\n-};\n-/* ======================================================================\n OpenLayers/Format/WMC.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -54521,1193 +48687,1585 @@\n }\n }\n },\n \n CLASS_NAME: \"OpenLayers.Format.OSM\"\n });\n /* ======================================================================\n- OpenLayers/Format/OWSContext.js\n+ OpenLayers/Format/WMTSCapabilities.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/Context.js\n+ * @requires OpenLayers/Format/XML/VersionedOGC.js\n */\n \n /**\n- * Class: OpenLayers.Format.OWSContext\n- * Read and write OWS Context documents. OWS Context documents are a \n- * preliminary OGC (Open Geospatial Consortium) standard for storing the \n- * state of a web mapping application. In a way it is the successor to\n- * Web Map Context (WMC), since it is more generic and more types of layers\n- * can be stored. Also, nesting of layers is supported since version 0.3.1.\n- * For more information see: http://www.ogcnetwork.net/context\n+ * Class: OpenLayers.Format.WMTSCapabilities\n+ * Read WMTS Capabilities.\n *\n * Inherits from:\n- * - <OpenLayers.Format.Context>\n+ * - <OpenLayers.Format.XML.VersionedOGC>\n */\n-OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {\n+OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n \n /**\n * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"0.3.1\".\n+ * {String} Version number to assume if none found. Default is \"1.0.0\".\n */\n- defaultVersion: \"0.3.1\",\n+ defaultVersion: \"1.0.0\",\n \n /**\n- * Constructor: OpenLayers.Format.OWSContext\n- * Create a new parser for OWS Context documents.\n+ * APIProperty: yx\n+ * {Object} Members in the yx object are used to determine if a CRS URN\n+ * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n+ * and values are boolean. By default, the following CRS URN are\n+ * assumed to correspond to a CRS with y,x axis order:\n+ *\n+ * * urn:ogc:def:crs:EPSG::4326\n+ */\n+ yx: {\n+ \"urn:ogc:def:crs:EPSG::4326\": true\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMTSCapabilities\n+ * Create a new parser for WMTS capabilities.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n \n /**\n- * Method: getVersion\n- * Returns the version to use. Subclasses can override this function\n- * if a different version detection is needed.\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return information about\n+ * the service (offering and observedProperty mostly).\n *\n * Parameters:\n- * root - {DOMElement}\n- * options - {Object} Optional configuration object.\n+ * data - {String} or {DOMElement} data to read/parse.\n *\n * Returns:\n- * {String} The version to use.\n+ * {Object} Info about the WMTS Capabilities\n */\n- getVersion: function(root, options) {\n- var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(\n- this, arguments);\n- // 0.3.1 is backwards compatible with 0.3.0\n- if (version === \"0.3.0\") {\n- version = this.defaultVersion;\n- }\n- return version;\n- },\n \n /**\n- * Method: toContext\n- * Create a context object free from layer given a map or a\n- * context object.\n+ * APIMethod: createLayer\n+ * Create a WMTS layer given a capabilities object.\n *\n * Parameters:\n- * obj - {<OpenLayers.Map> | Object} The map or context.\n+ * capabilities - {Object} The object returned from a <read> call to this\n+ * format.\n+ * config - {Object} Configuration properties for the layer. Defaults for\n+ * the layer will apply if not provided.\n+ *\n+ * Required config properties:\n+ * layer - {String} The layer identifier.\n+ *\n+ * Optional config properties:\n+ * matrixSet - {String} The matrix set identifier, required if there is \n+ * more than one matrix set in the layer capabilities.\n+ * style - {String} The name of the style\n+ * format - {String} Image format for the layer. Default is the first\n+ * format returned in the GetCapabilities response.\n+ * param - {Object} The dimensions values eg: {\"Year\": \"2012\"}\n *\n * Returns:\n- * {Object} A context object.\n+ * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an\n+ * error if an incomplete config is provided. Returns undefined if no\n+ * layer could be created with the provided config.\n */\n- toContext: function(obj) {\n- var context = {};\n- if (obj.CLASS_NAME == \"OpenLayers.Map\") {\n- context.bounds = obj.getExtent();\n- context.maxExtent = obj.maxExtent;\n- context.projection = obj.projection;\n- context.size = obj.getSize();\n- context.layers = obj.layers;\n+ createLayer: function(capabilities, config) {\n+ var layer;\n+\n+ // confirm required properties are supplied in config\n+ if (!('layer' in config)) {\n+ throw new Error(\"Missing property 'layer' in configuration.\");\n }\n- return context;\n+\n+ var contents = capabilities.contents;\n+\n+ // find the layer definition with the given identifier\n+ var layers = contents.layers;\n+ var layerDef;\n+ for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n+ if (contents.layers[i].identifier === config.layer) {\n+ layerDef = contents.layers[i];\n+ break;\n+ }\n+ }\n+ if (!layerDef) {\n+ throw new Error(\"Layer not found\");\n+ }\n+\n+ var format = config.format;\n+ if (!format && layerDef.formats && layerDef.formats.length) {\n+ format = layerDef.formats[0];\n+ }\n+\n+ // find the matrixSet definition\n+ var matrixSet;\n+ if (config.matrixSet) {\n+ matrixSet = contents.tileMatrixSets[config.matrixSet];\n+ } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n+ matrixSet = contents.tileMatrixSets[\n+ layerDef.tileMatrixSetLinks[0].tileMatrixSet];\n+ }\n+ if (!matrixSet) {\n+ throw new Error(\"matrixSet not found\");\n+ }\n+\n+ // get the default style for the layer\n+ var style;\n+ for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n+ style = layerDef.styles[i];\n+ if (style.isDefault) {\n+ break;\n+ }\n+ }\n+\n+ var requestEncoding = config.requestEncoding;\n+ if (!requestEncoding) {\n+ requestEncoding = \"KVP\";\n+ if (capabilities.operationsMetadata.GetTile.dcp.http) {\n+ var http = capabilities.operationsMetadata.GetTile.dcp.http;\n+ // Get first get method\n+ if (http.get[0].constraints) {\n+ var constraints = http.get[0].constraints;\n+ var allowedValues = constraints.GetEncoding.allowedValues;\n+\n+ // The OGC documentation is not clear if we should use\n+ // REST or RESTful, ArcGis use RESTful,\n+ // and OpenLayers use REST.\n+ if (!allowedValues.KVP &&\n+ (allowedValues.REST || allowedValues.RESTful)) {\n+ requestEncoding = \"REST\";\n+ }\n+ }\n+ }\n+ }\n+\n+ var dimensions = [];\n+ var params = config.params || {};\n+ // to don't overwrite the changes in the applyDefaults\n+ delete config.params;\n+ for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n+ var dimension = layerDef.dimensions[id];\n+ dimensions.push(dimension.identifier);\n+ if (!params.hasOwnProperty(dimension.identifier)) {\n+ params[dimension.identifier] = dimension['default'];\n+ }\n+ }\n+\n+ var projection = config.projection || matrixSet.supportedCRS.replace(\n+ /urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n+ var units = config.units ||\n+ (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n+\n+ var resolutions = [];\n+ for (var mid in matrixSet.matrixIds) {\n+ if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n+ resolutions.push(\n+ matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 /\n+ OpenLayers.METERS_PER_INCH /\n+ OpenLayers.INCHES_PER_UNIT[units]);\n+ }\n+ }\n+\n+ var url;\n+ if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n+ url = [];\n+ var resourceUrls = layerDef.resourceUrls,\n+ resourceUrl;\n+ for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n+ resourceUrl = layerDef.resourceUrls[t];\n+ if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n+ url.push(resourceUrl.template);\n+ }\n+ }\n+ } else {\n+ var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n+ url = [];\n+ var constraint;\n+ for (var i = 0, ii = httpGet.length; i < ii; i++) {\n+ constraint = httpGet[i].constraints;\n+ if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) {\n+ url.push(httpGet[i].url);\n+ }\n+ }\n+ }\n+\n+ return new OpenLayers.Layer.WMTS(\n+ OpenLayers.Util.applyDefaults(config, {\n+ url: url,\n+ requestEncoding: requestEncoding,\n+ name: layerDef.title,\n+ style: style.identifier,\n+ format: format,\n+ matrixIds: matrixSet.matrixIds,\n+ matrixSet: matrixSet.identifier,\n+ projection: projection,\n+ units: units,\n+ resolutions: config.isBaseLayer === false ? undefined : resolutions,\n+ serverResolutions: resolutions,\n+ tileFullExtent: matrixSet.bounds,\n+ dimensions: dimensions,\n+ params: params\n+ })\n+ );\n },\n \n- CLASS_NAME: \"OpenLayers.Format.OWSContext\"\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/EncodedPolyline.js\n+ OpenLayers/Format/GeoRSS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Format/XML.js\n * @requires OpenLayers/Feature/Vector.js\n+ * @requires OpenLayers/Geometry/Point.js\n+ * @requires OpenLayers/Geometry/LineString.js\n+ * @requires OpenLayers/Geometry/Polygon.js\n */\n \n /**\n- * Class: OpenLayers.Format.EncodedPolyline\n- * Class for reading and writing encoded polylines. Create a new instance\n- * with the <OpenLayers.Format.EncodedPolyline> constructor.\n+ * Class: OpenLayers.Format.GeoRSS\n+ * Read/write GeoRSS parser. Create a new instance with the \n+ * <OpenLayers.Format.GeoRSS> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Format>\n+ * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, {\n+OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {\n \n /**\n- * APIProperty: geometryType\n- * {String} Geometry type to output. One of: linestring (default),\n- * linearring, point, multipoint or polygon. If the geometryType is\n- * point, only the first point of the string is returned.\n+ * APIProperty: rssns\n+ * {String} RSS namespace to use. Defaults to\n+ * \"http://backend.userland.com/rss2\"\n */\n- geometryType: \"linestring\",\n+ rssns: \"http://backend.userland.com/rss2\",\n \n /**\n- * Constructor: OpenLayers.Format.EncodedPolyline\n- * Create a new parser for encoded polylines\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance\n- *\n- * Returns:\n- * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser.\n+ * APIProperty: featurens\n+ * {String} Feature Attributes namespace. Defaults to\n+ * \"http://mapserver.gis.umn.edu/mapserver\"\n */\n- initialize: function(options) {\n- OpenLayers.Format.prototype.initialize.apply(this, [options]);\n- },\n+ featureNS: \"http://mapserver.gis.umn.edu/mapserver\",\n \n /**\n- * APIMethod: read\n- * Deserialize an encoded polyline string and return a vector feature.\n- *\n- * Parameters:\n- * encoded - {String} An encoded polyline string\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A vector feature with a linestring.\n+ * APIProperty: georssns\n+ * {String} GeoRSS namespace to use. Defaults to\n+ * \"http://www.georss.org/georss\"\n */\n- read: function(encoded) {\n- var geomType;\n- if (this.geometryType == \"linestring\")\n- geomType = OpenLayers.Geometry.LineString;\n- else if (this.geometryType == \"linearring\")\n- geomType = OpenLayers.Geometry.LinearRing;\n- else if (this.geometryType == \"multipoint\")\n- geomType = OpenLayers.Geometry.MultiPoint;\n- else if (this.geometryType != \"point\" && this.geometryType != \"polygon\")\n- return null;\n-\n- var flatPoints = this.decodeDeltas(encoded, 2);\n- var flatPointsLength = flatPoints.length;\n-\n- var pointGeometries = [];\n- for (var i = 0; i + 1 < flatPointsLength;) {\n- var y = flatPoints[i++],\n- x = flatPoints[i++];\n- pointGeometries.push(new OpenLayers.Geometry.Point(x, y));\n- }\n-\n-\n- if (this.geometryType == \"point\")\n- return new OpenLayers.Feature.Vector(\n- pointGeometries[0]\n- );\n-\n- if (this.geometryType == \"polygon\")\n- return new OpenLayers.Feature.Vector(\n- new OpenLayers.Geometry.Polygon([\n- new OpenLayers.Geometry.LinearRing(pointGeometries)\n- ])\n- );\n-\n- return new OpenLayers.Feature.Vector(\n- new geomType(pointGeometries)\n- );\n- },\n+ georssns: \"http://www.georss.org/georss\",\n \n /**\n- * APIMethod: decode\n- * Deserialize an encoded string and return an array of n-dimensional\n- * points.\n- *\n- * Parameters:\n- * encoded - {String} An encoded string\n- * dims - {int} The dimension of the points that are returned\n- *\n- * Returns:\n- * {Array(Array(int))} An array containing n-dimensional arrays of\n- * coordinates.\n+ * APIProperty: geons\n+ * {String} W3C Geo namespace to use. Defaults to\n+ * \"http://www.w3.org/2003/01/geo/wgs84_pos#\"\n */\n- decode: function(encoded, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = this.decodeDeltas(encoded, dims, factor);\n- var flatPointsLength = flatPoints.length;\n-\n- var points = [];\n- for (var i = 0; i + (dims - 1) < flatPointsLength;) {\n- var point = [];\n-\n- for (var dim = 0; dim < dims; ++dim) {\n- point.push(flatPoints[i++])\n- }\n-\n- points.push(point);\n- }\n-\n- return points;\n- },\n+ geons: \"http://www.w3.org/2003/01/geo/wgs84_pos#\",\n \n /**\n- * APIMethod: write\n- * Serialize a feature or array of features into a WKT string.\n- *\n- * Parameters:\n- * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of\n- * features\n- *\n- * Returns:\n- * {String} The WKT string representation of the input geometries\n+ * APIProperty: featureTitle\n+ * {String} Default title for features. Defaults to \"Untitled\"\n */\n- write: function(features) {\n- var feature;\n- if (features.constructor == Array)\n- feature = features[0];\n- else\n- feature = features;\n-\n- var geometry = feature.geometry;\n- var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();\n-\n- var pointGeometries;\n- if (type == \"point\")\n- pointGeometries = new Array(geometry);\n- else if (type == \"linestring\" ||\n- type == \"linearring\" ||\n- type == \"multipoint\")\n- pointGeometries = geometry.components;\n- else if (type == \"polygon\")\n- pointGeometries = geometry.components[0].components;\n- else\n- return null;\n-\n- var flatPoints = [];\n-\n- var pointGeometriesLength = pointGeometries.length;\n- for (var i = 0; i < pointGeometriesLength; ++i) {\n- var pointGeometry = pointGeometries[i];\n- flatPoints.push(pointGeometry.y);\n- flatPoints.push(pointGeometry.x);\n- }\n-\n- return this.encodeDeltas(flatPoints, 2);\n- },\n+ featureTitle: \"Untitled\",\n \n /**\n- * APIMethod: encode\n- * Serialize an array of n-dimensional points and return an encoded string\n- *\n- * Parameters:\n- * points - {Array(Array(int))} An array containing n-dimensional\n- * arrays of coordinates\n- * dims - {int} The dimension of the points that should be read\n- *\n- * Returns:\n- * {String} An encoded string\n+ * APIProperty: featureDescription\n+ * {String} Default description for features. Defaults to \"No Description\"\n */\n- encode: function(points, dims, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var flatPoints = [];\n-\n- var pointsLength = points.length;\n- for (var i = 0; i < pointsLength; ++i) {\n- var point = points[i];\n+ featureDescription: \"No Description\",\n \n- for (var dim = 0; dim < dims; ++dim) {\n- flatPoints.push(point[dim]);\n- }\n- }\n+ /**\n+ * Property: gmlParse\n+ * {Object} GML Format object for parsing features\n+ * Non-API and only created if necessary\n+ */\n+ gmlParser: null,\n \n- return this.encodeDeltas(flatPoints, dims, factor);\n- },\n+ /**\n+ * APIProperty: xy\n+ * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)\n+ * For GeoRSS the default is (y,x), therefore: false\n+ */\n+ xy: false,\n \n /**\n- * APIMethod: encodeDeltas\n- * Encode a list of n-dimensional points and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n+ * Constructor: OpenLayers.Format.GeoRSS\n+ * Create a new parser for GeoRSS.\n *\n * Parameters:\n- * numbers - {Array.<number>} A list of n-dimensional points.\n- * dimension - {number} The dimension of the points in the list.\n- * opt_factor - {number=} The factor by which the numbers will be\n- * multiplied. The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n */\n- encodeDeltas: function(numbers, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n-\n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0;\n- }\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- var num = numbers[i];\n- var delta = num - lastNumbers[d];\n- lastNumbers[d] = num;\n-\n- numbers[i] = delta;\n- }\n- }\n-\n- return this.encodeFloats(numbers, factor);\n- },\n-\n \n /**\n- * APIMethod: decodeDeltas\n- * Decode a list of n-dimensional points from an encoded string\n+ * Method: createGeometryFromItem\n+ * Return a geometry from a GeoRSS Item.\n *\n * Parameters:\n- * encoded - {string} An encoded string.\n- * dimension - {number} The dimension of the points in the encoded string.\n- * opt_factor - {number=} The factor by which the resulting numbers will\n- * be divided.\n+ * item - {DOMElement} A GeoRSS item node.\n *\n * Returns:\n- * {Array.<number>} A list of n-dimensional points.\n+ * {<OpenLayers.Geometry>} A geometry representing the node.\n */\n- decodeDeltas: function(encoded, dimension, opt_factor) {\n- var factor = opt_factor || 1e5;\n- var d;\n+ createGeometryFromItem: function(item) {\n+ var point = this.getElementsByTagNameNS(item, this.georssns, \"point\");\n+ var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');\n+ var lon = this.getElementsByTagNameNS(item, this.geons, 'long');\n \n- var lastNumbers = new Array(dimension);\n- for (d = 0; d < dimension; ++d) {\n- lastNumbers[d] = 0;\n- }\n+ var line = this.getElementsByTagNameNS(item,\n+ this.georssns,\n+ \"line\");\n+ var polygon = this.getElementsByTagNameNS(item,\n+ this.georssns,\n+ \"polygon\");\n+ var where = this.getElementsByTagNameNS(item,\n+ this.georssns,\n+ \"where\");\n+ var box = this.getElementsByTagNameNS(item,\n+ this.georssns,\n+ \"box\");\n \n- var numbers = this.decodeFloats(encoded, factor);\n+ if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {\n+ var location;\n+ if (point.length > 0) {\n+ location = OpenLayers.String.trim(\n+ point[0].firstChild.nodeValue).split(/\\s+/);\n+ if (location.length != 2) {\n+ location = OpenLayers.String.trim(\n+ point[0].firstChild.nodeValue).split(/\\s*,\\s*/);\n+ }\n+ } else {\n+ location = [parseFloat(lat[0].firstChild.nodeValue),\n+ parseFloat(lon[0].firstChild.nodeValue)\n+ ];\n+ }\n \n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength;) {\n- for (d = 0; d < dimension; ++d, ++i) {\n- lastNumbers[d] += numbers[i];\n+ var geometry = new OpenLayers.Geometry.Point(location[1], location[0]);\n \n- numbers[i] = lastNumbers[d];\n+ } else if (line.length > 0) {\n+ var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\\s+/);\n+ var components = [];\n+ var point;\n+ for (var i = 0, len = coords.length; i < len; i += 2) {\n+ point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]);\n+ components.push(point);\n+ }\n+ geometry = new OpenLayers.Geometry.LineString(components);\n+ } else if (polygon.length > 0) {\n+ var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\\s+/);\n+ var components = [];\n+ var point;\n+ for (var i = 0, len = coords.length; i < len; i += 2) {\n+ point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]);\n+ components.push(point);\n+ }\n+ geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);\n+ } else if (where.length > 0) {\n+ if (!this.gmlParser) {\n+ this.gmlParser = new OpenLayers.Format.GML({\n+ 'xy': this.xy\n+ });\n+ }\n+ var feature = this.gmlParser.parseFeature(where[0]);\n+ geometry = feature.geometry;\n+ } else if (box.length > 0) {\n+ var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\\s+/);\n+ var components = [];\n+ var point;\n+ if (coords.length > 3) {\n+ point = new OpenLayers.Geometry.Point(coords[1], coords[0]);\n+ components.push(point);\n+ point = new OpenLayers.Geometry.Point(coords[1], coords[2]);\n+ components.push(point);\n+ point = new OpenLayers.Geometry.Point(coords[3], coords[2]);\n+ components.push(point);\n+ point = new OpenLayers.Geometry.Point(coords[3], coords[0]);\n+ components.push(point);\n+ point = new OpenLayers.Geometry.Point(coords[1], coords[0]);\n+ components.push(point);\n }\n+ geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);\n }\n \n- return numbers;\n- },\n-\n-\n- /**\n- * APIMethod: encodeFloats\n- * Encode a list of floating point numbers and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of floating point numbers.\n- * opt_factor - {number=} The factor by which the numbers will be\n- * multiplied. The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeFloats: function(numbers, opt_factor) {\n- var factor = opt_factor || 1e5;\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] = Math.round(numbers[i] * factor);\n+ if (geometry && this.internalProjection && this.externalProjection) {\n+ geometry.transform(this.externalProjection,\n+ this.internalProjection);\n }\n \n- return this.encodeSignedIntegers(numbers);\n+ return geometry;\n },\n \n-\n /**\n- * APIMethod: decodeFloats\n- * Decode a list of floating point numbers from an encoded string\n+ * Method: createFeatureFromItem\n+ * Return a feature from a GeoRSS Item.\n *\n * Parameters:\n- * encoded - {string} An encoded string.\n- * opt_factor - {number=} The factor by which the result will be divided.\n+ * item - {DOMElement} A GeoRSS item node.\n *\n * Returns:\n- * {Array.<number>} A list of floating point numbers.\n+ * {<OpenLayers.Feature.Vector>} A feature representing the item.\n */\n- decodeFloats: function(encoded, opt_factor) {\n- var factor = opt_factor || 1e5;\n-\n- var numbers = this.decodeSignedIntegers(encoded);\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- numbers[i] /= factor;\n- }\n-\n- return numbers;\n- },\n+ createFeatureFromItem: function(item) {\n+ var geometry = this.createGeometryFromItem(item);\n \n+ /* Provide defaults for title and description */\n+ var title = this._getChildValue(item, \"*\", \"title\", this.featureTitle);\n \n- /**\n- * APIMethod: encodeSignedIntegers\n- * Encode a list of signed integers and return an encoded string\n- *\n- * Attention: This function will modify the passed array!\n- *\n- * Parameters:\n- * numbers - {Array.<number>} A list of signed integers.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeSignedIntegers: function(numbers) {\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n+ /* First try RSS descriptions, then Atom summaries */\n+ var description = this._getChildValue(\n+ item, \"*\", \"description\",\n+ this._getChildValue(item, \"*\", \"content\",\n+ this._getChildValue(item, \"*\", \"summary\", this.featureDescription)));\n \n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~(signedNum);\n+ /* If no link URL is found in the first child node, try the\n+ href attribute */\n+ var link = this._getChildValue(item, \"*\", \"link\");\n+ if (!link) {\n+ try {\n+ link = this.getElementsByTagNameNS(item, \"*\", \"link\")[0].getAttribute(\"href\");\n+ } catch (e) {\n+ link = null;\n }\n-\n- numbers[i] = signedNum;\n }\n \n- return this.encodeUnsignedIntegers(numbers);\n- },\n+ var id = this._getChildValue(item, \"*\", \"id\", null);\n \n+ var data = {\n+ \"title\": title,\n+ \"description\": description,\n+ \"link\": link\n+ };\n+ var feature = new OpenLayers.Feature.Vector(geometry, data);\n+ feature.fid = id;\n+ return feature;\n+ },\n \n /**\n- * APIMethod: decodeSignedIntegers\n- * Decode a list of signed integers from an encoded string\n+ * Method: _getChildValue\n *\n * Parameters:\n- * encoded - {string} An encoded string.\n+ * node - {DOMElement}\n+ * nsuri - {String} Child node namespace uri (\"*\" for any).\n+ * name - {String} Child node name.\n+ * def - {String} Optional string default to return if no child found.\n *\n * Returns:\n- * {Array.<number>} A list of signed integers.\n+ * {String} The value of the first child with the given tag name. Returns\n+ * default value or empty string if none found.\n */\n- decodeSignedIntegers: function(encoded) {\n- var numbers = this.decodeUnsignedIntegers(encoded);\n-\n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- var num = numbers[i];\n- numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1);\n+ _getChildValue: function(node, nsuri, name, def) {\n+ var value;\n+ var eles = this.getElementsByTagNameNS(node, nsuri, name);\n+ if (eles && eles[0] && eles[0].firstChild &&\n+ eles[0].firstChild.nodeValue) {\n+ value = this.getChildValue(eles[0]);\n+ } else {\n+ value = (def == undefined) ? \"\" : def;\n }\n-\n- return numbers;\n+ return value;\n },\n \n-\n /**\n- * APIMethod: encodeUnsignedIntegers\n- * Encode a list of unsigned integers and return an encoded string\n+ * APIMethod: read\n+ * Return a list of features from a GeoRSS doc\n *\n * Parameters:\n- * numbers - {Array.<number>} A list of unsigned integers.\n+ * doc - {Element} \n *\n * Returns:\n- * {string} The encoded string.\n+ * {Array(<OpenLayers.Feature.Vector>)}\n */\n- encodeUnsignedIntegers: function(numbers) {\n- var encoded = '';\n+ read: function(doc) {\n+ if (typeof doc == \"string\") {\n+ doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n+ }\n \n- var numbersLength = numbers.length;\n- for (var i = 0; i < numbersLength; ++i) {\n- encoded += this.encodeUnsignedInteger(numbers[i]);\n+ /* Try RSS items first, then Atom entries */\n+ var itemlist = null;\n+ itemlist = this.getElementsByTagNameNS(doc, '*', 'item');\n+ if (itemlist.length == 0) {\n+ itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');\n }\n \n- return encoded;\n+ var numItems = itemlist.length;\n+ var features = new Array(numItems);\n+ for (var i = 0; i < numItems; i++) {\n+ features[i] = this.createFeatureFromItem(itemlist[i]);\n+ }\n+ return features;\n },\n \n \n /**\n- * APIMethod: decodeUnsignedIntegers\n- * Decode a list of unsigned integers from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {Array.<number>} A list of unsigned integers.\n+ * APIMethod: write\n+ * Accept Feature Collection, and return a string. \n+ * \n+ * Parameters: \n+ * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n */\n- decodeUnsignedIntegers: function(encoded) {\n- var numbers = [];\n-\n- var current = 0;\n- var shift = 0;\n-\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n-\n- current |= (b & 0x1f) << shift;\n-\n- if (b < 0x20) {\n- numbers.push(current);\n- current = 0;\n- shift = 0;\n- } else {\n- shift += 5;\n+ write: function(features) {\n+ var georss;\n+ if (OpenLayers.Util.isArray(features)) {\n+ georss = this.createElementNS(this.rssns, \"rss\");\n+ for (var i = 0, len = features.length; i < len; i++) {\n+ georss.appendChild(this.createFeatureXML(features[i]));\n }\n+ } else {\n+ georss = this.createFeatureXML(features);\n }\n-\n- return numbers;\n- },\n-\n-\n- /**\n- * Method: encodeFloat\n- * Encode one single floating point number and return an encoded string\n- *\n- * Parameters:\n- * num - {number} Floating point number that should be encoded.\n- * opt_factor - {number=} The factor by which num will be multiplied.\n- * The remaining decimal places will get rounded away.\n- *\n- * Returns:\n- * {string} The encoded string.\n- */\n- encodeFloat: function(num, opt_factor) {\n- num = Math.round(num * (opt_factor || 1e5));\n- return this.encodeSignedInteger(num);\n- },\n-\n-\n- /**\n- * Method: decodeFloat\n- * Decode one single floating point number from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- * opt_factor - {number=} The factor by which the result will be divided.\n- *\n- * Returns:\n- * {number} The decoded floating point number.\n- */\n- decodeFloat: function(encoded, opt_factor) {\n- var result = this.decodeSignedInteger(encoded);\n- return result / (opt_factor || 1e5);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);\n },\n \n-\n /**\n- * Method: encodeSignedInteger\n- * Encode one single signed integer and return an encoded string\n- *\n+ * Method: createFeatureXML\n+ * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.\n+ * \n * Parameters:\n- * num - {number} Signed integer that should be encoded.\n+ * feature - {<OpenLayers.Feature.Vector>} \n *\n * Returns:\n- * {string} The encoded string.\n+ * {DOMElement}\n */\n- encodeSignedInteger: function(num) {\n- var signedNum = num << 1;\n- if (num < 0) {\n- signedNum = ~(signedNum);\n+ createFeatureXML: function(feature) {\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n+ var featureNode = this.createElementNS(this.rssns, \"item\");\n+ var titleNode = this.createElementNS(this.rssns, \"title\");\n+ titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : \"\"));\n+ var descNode = this.createElementNS(this.rssns, \"description\");\n+ descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : \"\"));\n+ featureNode.appendChild(titleNode);\n+ featureNode.appendChild(descNode);\n+ if (feature.attributes.link) {\n+ var linkNode = this.createElementNS(this.rssns, \"link\");\n+ linkNode.appendChild(this.createTextNode(feature.attributes.link));\n+ featureNode.appendChild(linkNode);\n }\n-\n- return this.encodeUnsignedInteger(signedNum);\n- },\n-\n-\n- /**\n- * Method: decodeSignedInteger\n- * Decode one single signed integer from an encoded string\n- *\n- * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {number} The decoded signed integer.\n- */\n- decodeSignedInteger: function(encoded) {\n- var result = this.decodeUnsignedInteger(encoded);\n- return ((result & 1) ? ~(result >> 1) : (result >> 1));\n+ for (var attr in feature.attributes) {\n+ if (attr == \"link\" || attr == \"title\" || attr == \"description\") {\n+ continue;\n+ }\n+ var attrText = this.createTextNode(feature.attributes[attr]);\n+ var nodename = attr;\n+ if (attr.search(\":\") != -1) {\n+ nodename = attr.split(\":\")[1];\n+ }\n+ var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n+ attrContainer.appendChild(attrText);\n+ featureNode.appendChild(attrContainer);\n+ }\n+ featureNode.appendChild(geometryNode);\n+ return featureNode;\n },\n \n-\n- /**\n- * Method: encodeUnsignedInteger\n- * Encode one single unsigned integer and return an encoded string\n- *\n+ /** \n+ * Method: buildGeometryNode\n+ * builds a GeoRSS node with a given geometry\n+ * \n * Parameters:\n- * num - {number} Unsigned integer that should be encoded.\n+ * geometry - {<OpenLayers.Geometry>}\n *\n * Returns:\n- * {string} The encoded string.\n+ * {DOMElement} A gml node.\n */\n- encodeUnsignedInteger: function(num) {\n- var value, encoded = '';\n- while (num >= 0x20) {\n- value = (0x20 | (num & 0x1f)) + 63;\n- encoded += (String.fromCharCode(value));\n- num >>= 5;\n+ buildGeometryNode: function(geometry) {\n+ if (this.internalProjection && this.externalProjection) {\n+ geometry = geometry.clone();\n+ geometry.transform(this.internalProjection,\n+ this.externalProjection);\n }\n- value = num + 63;\n- encoded += (String.fromCharCode(value));\n- return encoded;\n- },\n+ var node;\n+ // match Polygon\n+ if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n+ node = this.createElementNS(this.georssns, 'georss:polygon');\n \n+ node.appendChild(this.buildCoordinatesNode(geometry.components[0]));\n+ }\n+ // match LineString\n+ else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n+ node = this.createElementNS(this.georssns, 'georss:line');\n \n- /**\n- * Method: decodeUnsignedInteger\n- * Decode one single unsigned integer from an encoded string\n- *\n+ node.appendChild(this.buildCoordinatesNode(geometry));\n+ }\n+ // match Point\n+ else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n+ node = this.createElementNS(this.georssns, 'georss:point');\n+ node.appendChild(this.buildCoordinatesNode(geometry));\n+ } else {\n+ throw \"Couldn't parse \" + geometry.CLASS_NAME;\n+ }\n+ return node;\n+ },\n+\n+ /** \n+ * Method: buildCoordinatesNode\n+ * \n * Parameters:\n- * encoded - {string} An encoded string.\n- *\n- * Returns:\n- * {number} The decoded unsigned integer.\n+ * geometry - {<OpenLayers.Geometry>}\n */\n- decodeUnsignedInteger: function(encoded) {\n- var result = 0;\n- var shift = 0;\n-\n- var encodedLength = encoded.length;\n- for (var i = 0; i < encodedLength; ++i) {\n- var b = encoded.charCodeAt(i) - 63;\n-\n- result |= (b & 0x1f) << shift;\n-\n- if (b < 0x20)\n- break;\n+ buildCoordinatesNode: function(geometry) {\n+ var points = null;\n \n- shift += 5;\n+ if (geometry.components) {\n+ points = geometry.components;\n }\n \n- return result;\n+ var path;\n+ if (points) {\n+ var numPoints = points.length;\n+ var parts = new Array(numPoints);\n+ for (var i = 0; i < numPoints; i++) {\n+ parts[i] = points[i].y + \" \" + points[i].x;\n+ }\n+ path = parts.join(\" \");\n+ } else {\n+ path = geometry.y + \" \" + geometry.x;\n+ }\n+ return this.createTextNode(path);\n },\n \n- CLASS_NAME: \"OpenLayers.Format.EncodedPolyline\"\n+ CLASS_NAME: \"OpenLayers.Format.GeoRSS\"\n });\n /* ======================================================================\n- OpenLayers/Format/WMSGetFeatureInfo.js\n+ OpenLayers/Format/CQL.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/WKT.js\n+ * @requires OpenLayers/Filter/Comparison.js\n+ * @requires OpenLayers/Filter/Logical.js\n+ * @requires OpenLayers/Filter/Spatial.js\n */\n \n /**\n- * Class: OpenLayers.Format.WMSGetFeatureInfo\n- * Class to read GetFeatureInfo responses from Web Mapping Services\n+ * Class: OpenLayers.Format.CQL\n+ * Read CQL strings to get <OpenLayers.Filter> objects. Write \n+ * <OpenLayers.Filter> objects to get CQL strings. Create a new parser with \n+ * the <OpenLayers.Format.CQL> constructor.\n *\n * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * - <OpenLayers.Format>\n */\n-OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {\n+OpenLayers.Format.CQL = (function() {\n \n- /**\n- * APIProperty: layerIdentifier\n- * {String} All xml nodes containing this search criteria will populate an\n- * internal array of layer nodes.\n- */\n- layerIdentifier: '_layer',\n+ var tokens = [\n+ \"PROPERTY\", \"COMPARISON\", \"VALUE\", \"LOGICAL\"\n+ ],\n \n- /**\n- * APIProperty: featureIdentifier\n- * {String} All xml nodes containing this search criteria will populate an \n- * internal array of feature nodes for each layer node found.\n- */\n- featureIdentifier: '_feature',\n+ patterns = {\n+ PROPERTY: /^[_a-zA-Z]\\w*/,\n+ COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i,\n+ IS_NULL: /^IS NULL/i,\n+ COMMA: /^,/,\n+ LOGICAL: /^(AND|OR)/i,\n+ VALUE: /^('([^']|'')*'|\\d+(\\.\\d*)?|\\.\\d+)/,\n+ LPAREN: /^\\(/,\n+ RPAREN: /^\\)/,\n+ SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i,\n+ NOT: /^NOT/i,\n+ BETWEEN: /^BETWEEN/i,\n+ GEOMETRY: function(text) {\n+ var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text);\n+ if (type) {\n+ var len = text.length;\n+ var idx = text.indexOf(\"(\", type[0].length);\n+ if (idx > -1) {\n+ var depth = 1;\n+ while (idx < len && depth > 0) {\n+ idx++;\n+ switch (text.charAt(idx)) {\n+ case '(':\n+ depth++;\n+ break;\n+ case ')':\n+ depth--;\n+ break;\n+ default:\n+ // in default case, do nothing\n+ }\n+ }\n+ }\n+ return [text.substr(0, idx + 1)];\n+ }\n+ },\n+ END: /^$/\n+ },\n \n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+ follows = {\n+ LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'],\n+ RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'],\n+ PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'],\n+ BETWEEN: ['VALUE'],\n+ IS_NULL: ['END'],\n+ COMPARISON: ['VALUE'],\n+ COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'],\n+ VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'],\n+ SPATIAL: ['LPAREN'],\n+ LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'],\n+ NOT: ['PROPERTY', 'LPAREN'],\n+ GEOMETRY: ['COMMA', 'RPAREN']\n+ },\n \n- /**\n- * Property: gmlFormat\n- * {<OpenLayers.Format.GML>} internal GML format for parsing geometries\n- * in msGMLOutput\n- */\n- gmlFormat: null,\n+ operators = {\n+ '=': OpenLayers.Filter.Comparison.EQUAL_TO,\n+ '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO,\n+ '<': OpenLayers.Filter.Comparison.LESS_THAN,\n+ '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,\n+ '>': OpenLayers.Filter.Comparison.GREATER_THAN,\n+ '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,\n+ 'LIKE': OpenLayers.Filter.Comparison.LIKE,\n+ 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN,\n+ 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL\n+ },\n \n- /**\n- * Constructor: OpenLayers.Format.WMSGetFeatureInfo\n- * Create a new parser for WMS GetFeatureInfo responses\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n+ operatorReverse = {},\n \n- /**\n- * APIMethod: read\n- * Read WMS GetFeatureInfo data from a string, and return an array of features\n- *\n- * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)} An array of features.\n- */\n- read: function(data) {\n- var result;\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ logicals = {\n+ 'AND': OpenLayers.Filter.Logical.AND,\n+ 'OR': OpenLayers.Filter.Logical.OR\n+ },\n+\n+ logicalReverse = {},\n+\n+ precedence = {\n+ 'RPAREN': 3,\n+ 'LOGICAL': 2,\n+ 'COMPARISON': 1\n+ };\n+\n+ var i;\n+ for (i in operators) {\n+ if (operators.hasOwnProperty(i)) {\n+ operatorReverse[operators[i]] = i;\n }\n- var root = data.documentElement;\n- if (root) {\n- var scope = this;\n- var read = this[\"read_\" + root.nodeName];\n- if (read) {\n- result = read.call(this, root);\n- } else {\n- // fall-back to GML since this is a common output format for WMS\n- // GetFeatureInfo responses\n- result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);\n- }\n- } else {\n- result = data;\n+ }\n+\n+ for (i in logicals) {\n+ if (logicals.hasOwnProperty(i)) {\n+ logicalReverse[logicals[i]] = i;\n }\n- return result;\n- },\n+ }\n \n+ function tryToken(text, pattern) {\n+ if (pattern instanceof RegExp) {\n+ return pattern.exec(text);\n+ } else {\n+ return pattern(text);\n+ }\n+ }\n \n- /**\n- * Method: read_msGMLOutput\n- * Parse msGMLOutput nodes.\n- *\n- * Parameters:\n- * data - {DOMElement}\n- *\n- * Returns:\n- * {Array}\n- */\n- read_msGMLOutput: function(data) {\n- var response = [];\n- var layerNodes = this.getSiblingNodesByTagCriteria(data,\n- this.layerIdentifier);\n- if (layerNodes) {\n- for (var i = 0, len = layerNodes.length; i < len; ++i) {\n- var node = layerNodes[i];\n- var layerName = node.nodeName;\n- if (node.prefix) {\n- layerName = layerName.split(':')[1];\n- }\n- var layerName = layerName.replace(this.layerIdentifier, '');\n- var featureNodes = this.getSiblingNodesByTagCriteria(node,\n- this.featureIdentifier);\n- if (featureNodes) {\n- for (var j = 0; j < featureNodes.length; j++) {\n- var featureNode = featureNodes[j];\n- var geomInfo = this.parseGeometry(featureNode);\n- var attributes = this.parseAttributes(featureNode);\n- var feature = new OpenLayers.Feature.Vector(geomInfo.geometry,\n- attributes, null);\n- feature.bounds = geomInfo.bounds;\n- feature.type = layerName;\n- response.push(feature);\n- }\n- }\n+ function nextToken(text, tokens) {\n+ var i, token, len = tokens.length;\n+ for (i = 0; i < len; i++) {\n+ token = tokens[i];\n+ var pat = patterns[token];\n+ var matches = tryToken(text, pat);\n+ if (matches) {\n+ var match = matches[0];\n+ var remainder = text.substr(match.length).replace(/^\\s*/, \"\");\n+ return {\n+ type: token,\n+ text: match,\n+ remainder: remainder\n+ };\n }\n }\n- return response;\n- },\n \n- /**\n- * Method: read_FeatureInfoResponse\n- * Parse FeatureInfoResponse nodes.\n- *\n- * Parameters:\n- * data - {DOMElement}\n- *\n- * Returns:\n- * {Array}\n- */\n- read_FeatureInfoResponse: function(data) {\n- var response = [];\n- var featureNodes = this.getElementsByTagNameNS(data, '*',\n- 'FIELDS');\n+ var msg = \"ERROR: In parsing: [\" + text + \"], expected one of: \";\n+ for (i = 0; i < len; i++) {\n+ token = tokens[i];\n+ msg += \"\\n \" + token + \": \" + patterns[token];\n+ }\n \n- for (var i = 0, len = featureNodes.length; i < len; i++) {\n- var featureNode = featureNodes[i];\n- var geom = null;\n+ throw new Error(msg);\n+ }\n \n- // attributes can be actual attributes on the FIELDS tag, \n- // or FIELD children\n- var attributes = {};\n- var j;\n- var jlen = featureNode.attributes.length;\n- if (jlen > 0) {\n- for (j = 0; j < jlen; j++) {\n- var attribute = featureNode.attributes[j];\n- attributes[attribute.nodeName] = attribute.nodeValue;\n- }\n- } else {\n- var nodes = featureNode.childNodes;\n- for (j = 0, jlen = nodes.length; j < jlen; ++j) {\n- var node = nodes[j];\n- if (node.nodeType != 3) {\n- attributes[node.getAttribute(\"name\")] =\n- node.getAttribute(\"value\");\n- }\n- }\n+ function tokenize(text) {\n+ var results = [];\n+ var token, expect = [\"NOT\", \"GEOMETRY\", \"SPATIAL\", \"PROPERTY\", \"LPAREN\"];\n+\n+ do {\n+ token = nextToken(text, expect);\n+ text = token.remainder;\n+ expect = follows[token.type];\n+ if (token.type != \"END\" && !expect) {\n+ throw new Error(\"No follows list for \" + token.type);\n }\n+ results.push(token);\n+ } while (token.type != \"END\");\n \n- response.push(\n- new OpenLayers.Feature.Vector(geom, attributes, null)\n- );\n- }\n- return response;\n- },\n+ return results;\n+ }\n \n- /**\n- * Method: getSiblingNodesByTagCriteria\n- * Recursively searches passed xml node and all it's descendant levels for \n- * nodes whose tagName contains the passed search string. This returns an \n- * array of all sibling nodes which match the criteria from the highest \n- * hierarchial level from which a match is found.\n- * \n- * Parameters:\n- * node - {DOMElement} An xml node\n- * criteria - {String} Search string which will match some part of a tagName \n- * \n- * Returns:\n- * Array({DOMElement}) An array of sibling xml nodes\n- */\n- getSiblingNodesByTagCriteria: function(node, criteria) {\n- var nodes = [];\n- var children, tagName, n, matchNodes, child;\n- if (node && node.hasChildNodes()) {\n- children = node.childNodes;\n- n = children.length;\n+ function buildAst(tokens) {\n+ var operatorStack = [],\n+ postfix = [];\n \n- for (var k = 0; k < n; k++) {\n- child = children[k];\n- while (child && child.nodeType != 1) {\n- child = child.nextSibling;\n- k++;\n- }\n- tagName = (child ? child.nodeName : '');\n- if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {\n- nodes.push(child);\n- } else {\n- matchNodes = this.getSiblingNodesByTagCriteria(\n- child, criteria);\n+ while (tokens.length) {\n+ var tok = tokens.shift();\n+ switch (tok.type) {\n+ case \"PROPERTY\":\n+ case \"GEOMETRY\":\n+ case \"VALUE\":\n+ postfix.push(tok);\n+ break;\n+ case \"COMPARISON\":\n+ case \"BETWEEN\":\n+ case \"IS_NULL\":\n+ case \"LOGICAL\":\n+ var p = precedence[tok.type];\n \n- if (matchNodes.length > 0) {\n- (nodes.length == 0) ?\n- nodes = matchNodes: nodes.push(matchNodes);\n+ while (operatorStack.length > 0 &&\n+ (precedence[operatorStack[operatorStack.length - 1].type] <= p)\n+ ) {\n+ postfix.push(operatorStack.pop());\n }\n- }\n+\n+ operatorStack.push(tok);\n+ break;\n+ case \"SPATIAL\":\n+ case \"NOT\":\n+ case \"LPAREN\":\n+ operatorStack.push(tok);\n+ break;\n+ case \"RPAREN\":\n+ while (operatorStack.length > 0 &&\n+ (operatorStack[operatorStack.length - 1].type != \"LPAREN\")\n+ ) {\n+ postfix.push(operatorStack.pop());\n+ }\n+ operatorStack.pop(); // toss out the LPAREN\n+\n+ if (operatorStack.length > 0 &&\n+ operatorStack[operatorStack.length - 1].type == \"SPATIAL\") {\n+ postfix.push(operatorStack.pop());\n+ }\n+ case \"COMMA\":\n+ case \"END\":\n+ break;\n+ default:\n+ throw new Error(\"Unknown token type \" + tok.type);\n }\n+ }\n \n+ while (operatorStack.length > 0) {\n+ postfix.push(operatorStack.pop());\n }\n- return nodes;\n- },\n \n- /**\n- * Method: parseAttributes\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An attributes object.\n- * \n- * Notes:\n- * Assumes that attributes are direct child xml nodes of the passed node\n- * and contain only a single text node. \n- */\n- parseAttributes: function(node) {\n- var attributes = {};\n- if (node.nodeType == 1) {\n- var children = node.childNodes;\n- var n = children.length;\n- for (var i = 0; i < n; ++i) {\n- var child = children[i];\n- if (child.nodeType == 1) {\n- var grandchildren = child.childNodes;\n- var name = (child.prefix) ?\n- child.nodeName.split(\":\")[1] : child.nodeName;\n- if (grandchildren.length == 0) {\n- attributes[name] = null;\n- } else if (grandchildren.length == 1) {\n- var grandchild = grandchildren[0];\n- if (grandchild.nodeType == 3 ||\n- grandchild.nodeType == 4) {\n- var value = grandchild.nodeValue.replace(\n- this.regExes.trimSpace, \"\");\n- attributes[name] = value;\n- }\n+ function buildTree() {\n+ var tok = postfix.pop();\n+ switch (tok.type) {\n+ case \"LOGICAL\":\n+ var rhs = buildTree(),\n+ lhs = buildTree();\n+ return new OpenLayers.Filter.Logical({\n+ filters: [lhs, rhs],\n+ type: logicals[tok.text.toUpperCase()]\n+ });\n+ case \"NOT\":\n+ var operand = buildTree();\n+ return new OpenLayers.Filter.Logical({\n+ filters: [operand],\n+ type: OpenLayers.Filter.Logical.NOT\n+ });\n+ case \"BETWEEN\":\n+ var min, max, property;\n+ postfix.pop(); // unneeded AND token here\n+ max = buildTree();\n+ min = buildTree();\n+ property = buildTree();\n+ return new OpenLayers.Filter.Comparison({\n+ property: property,\n+ lowerBoundary: min,\n+ upperBoundary: max,\n+ type: OpenLayers.Filter.Comparison.BETWEEN\n+ });\n+ case \"COMPARISON\":\n+ var value = buildTree(),\n+ property = buildTree();\n+ return new OpenLayers.Filter.Comparison({\n+ property: property,\n+ value: value,\n+ type: operators[tok.text.toUpperCase()]\n+ });\n+ case \"IS_NULL\":\n+ var property = buildTree();\n+ return new OpenLayers.Filter.Comparison({\n+ property: property,\n+ type: operators[tok.text.toUpperCase()]\n+ });\n+ case \"VALUE\":\n+ var match = tok.text.match(/^'(.*)'$/);\n+ if (match) {\n+ return match[1].replace(/''/g, \"'\");\n+ } else {\n+ return Number(tok.text);\n }\n- }\n+ case \"SPATIAL\":\n+ switch (tok.text.toUpperCase()) {\n+ case \"BBOX\":\n+ var maxy = buildTree(),\n+ maxx = buildTree(),\n+ miny = buildTree(),\n+ minx = buildTree(),\n+ prop = buildTree();\n+\n+ return new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.BBOX,\n+ property: prop,\n+ value: OpenLayers.Bounds.fromArray(\n+ [minx, miny, maxx, maxy]\n+ )\n+ });\n+ case \"INTERSECTS\":\n+ var value = buildTree(),\n+ property = buildTree();\n+ return new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.INTERSECTS,\n+ property: property,\n+ value: value\n+ });\n+ case \"WITHIN\":\n+ var value = buildTree(),\n+ property = buildTree();\n+ return new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.WITHIN,\n+ property: property,\n+ value: value\n+ });\n+ case \"CONTAINS\":\n+ var value = buildTree(),\n+ property = buildTree();\n+ return new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.CONTAINS,\n+ property: property,\n+ value: value\n+ });\n+ case \"DWITHIN\":\n+ var distance = buildTree(),\n+ value = buildTree(),\n+ property = buildTree();\n+ return new OpenLayers.Filter.Spatial({\n+ type: OpenLayers.Filter.Spatial.DWITHIN,\n+ value: value,\n+ property: property,\n+ distance: Number(distance)\n+ });\n+ }\n+ case \"GEOMETRY\":\n+ return OpenLayers.Geometry.fromWKT(tok.text);\n+ default:\n+ return tok.text;\n }\n }\n- return attributes;\n- },\n \n- /**\n- * Method: parseGeometry\n- * Parse the geometry and the feature bounds out of the node using \n- * Format.GML\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An object containing the geometry and the feature bounds\n- */\n- parseGeometry: function(node) {\n- // we need to use the old Format.GML parser since we do not know the \n- // geometry name\n- if (!this.gmlFormat) {\n- this.gmlFormat = new OpenLayers.Format.GML();\n- }\n- var feature = this.gmlFormat.parseFeature(node);\n- var geometry, bounds = null;\n- if (feature) {\n- geometry = feature.geometry && feature.geometry.clone();\n- bounds = feature.bounds && feature.bounds.clone();\n- feature.destroy();\n+ var result = buildTree();\n+ if (postfix.length > 0) {\n+ var msg = \"Remaining tokens after building AST: \\n\";\n+ for (var i = postfix.length - 1; i >= 0; i--) {\n+ msg += postfix[i].type + \": \" + postfix[i].text + \"\\n\";\n+ }\n+ throw new Error(msg);\n }\n- return {\n- geometry: geometry,\n- bounds: bounds\n- };\n- },\n \n- CLASS_NAME: \"OpenLayers.Format.WMSGetFeatureInfo\"\n+ return result;\n+ }\n+\n+ return OpenLayers.Class(OpenLayers.Format, {\n+ /**\n+ * APIMethod: read\n+ * Generate a filter from a CQL string.\n+ \n+ * Parameters:\n+ * text - {String} The CQL text.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Filter>} A filter based on the CQL text.\n+ */\n+ read: function(text) {\n+ var result = buildAst(tokenize(text));\n+ if (this.keepData) {\n+ this.data = result;\n+ }\n+ return result;\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Convert a filter into a CQL string.\n+ \n+ * Parameters:\n+ * filter - {<OpenLayers.Filter>} The filter.\n+ *\n+ * Returns:\n+ * {String} A CQL string based on the filter.\n+ */\n+ write: function(filter) {\n+ if (filter instanceof OpenLayers.Geometry) {\n+ return filter.toString();\n+ }\n+ switch (filter.CLASS_NAME) {\n+ case \"OpenLayers.Filter.Spatial\":\n+ switch (filter.type) {\n+ case OpenLayers.Filter.Spatial.BBOX:\n+ return \"BBOX(\" +\n+ filter.property + \",\" +\n+ filter.value.toBBOX() +\n+ \")\";\n+ case OpenLayers.Filter.Spatial.DWITHIN:\n+ return \"DWITHIN(\" +\n+ filter.property + \", \" +\n+ this.write(filter.value) + \", \" +\n+ filter.distance + \")\";\n+ case OpenLayers.Filter.Spatial.WITHIN:\n+ return \"WITHIN(\" +\n+ filter.property + \", \" +\n+ this.write(filter.value) + \")\";\n+ case OpenLayers.Filter.Spatial.INTERSECTS:\n+ return \"INTERSECTS(\" +\n+ filter.property + \", \" +\n+ this.write(filter.value) + \")\";\n+ case OpenLayers.Filter.Spatial.CONTAINS:\n+ return \"CONTAINS(\" +\n+ filter.property + \", \" +\n+ this.write(filter.value) + \")\";\n+ default:\n+ throw new Error(\"Unknown spatial filter type: \" + filter.type);\n+ }\n+ case \"OpenLayers.Filter.Logical\":\n+ if (filter.type == OpenLayers.Filter.Logical.NOT) {\n+ // TODO: deal with precedence of logical operators to \n+ // avoid extra parentheses (not urgent)\n+ return \"NOT (\" + this.write(filter.filters[0]) + \")\";\n+ } else {\n+ var res = \"(\";\n+ var first = true;\n+ for (var i = 0; i < filter.filters.length; i++) {\n+ if (first) {\n+ first = false;\n+ } else {\n+ res += \") \" + logicalReverse[filter.type] + \" (\";\n+ }\n+ res += this.write(filter.filters[i]);\n+ }\n+ return res + \")\";\n+ }\n+ case \"OpenLayers.Filter.Comparison\":\n+ if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) {\n+ return filter.property + \" BETWEEN \" +\n+ this.write(filter.lowerBoundary) + \" AND \" +\n+ this.write(filter.upperBoundary);\n+ } else {\n+ return (filter.value !== null) ? filter.property +\n+ \" \" + operatorReverse[filter.type] + \" \" +\n+ this.write(filter.value) : filter.property +\n+ \" \" + operatorReverse[filter.type];\n+ }\n+ case undefined:\n+ if (typeof filter === \"string\") {\n+ return \"'\" + filter.replace(/'/g, \"''\") + \"'\";\n+ } else if (typeof filter === \"number\") {\n+ return String(filter);\n+ }\n+ default:\n+ throw new Error(\"Can't encode: \" + filter.CLASS_NAME + \" \" + filter);\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.CQL\"\n+\n+ });\n+})();\n \n-});\n /* ======================================================================\n- OpenLayers/Format/CSWGetDomain.js\n+ OpenLayers/Format/WFSDescribeFeatureType.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format.js\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/OGCExceptionReport.js\n */\n \n /**\n- * Class: OpenLayers.Format.CSWGetDomain\n- * Default version is 2.0.2.\n- *\n- * Returns:\n- * {<OpenLayers.Format>} A CSWGetDomain format of the given version.\n+ * Class: OpenLayers.Format.WFSDescribeFeatureType\n+ * Read WFS DescribeFeatureType response\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.CSWGetDomain = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Format.CSWGetDomain.DEFAULTS\n- );\n- var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetDomain version: \" + options.version;\n- }\n- return new cls(options);\n-};\n+OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n \n-/**\n- * Constant: DEFAULTS\n- * {Object} Default properties for the CSWGetDomain format.\n- */\n-OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n- \"version\": \"2.0.2\"\n-};\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g)\n+ },\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ xsd: \"http://www.w3.org/2001/XMLSchema\"\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFSDescribeFeatureType\n+ * Create a new parser for WFS DescribeFeatureType responses.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"xsd\": {\n+ \"schema\": function(node, obj) {\n+ var complexTypes = [];\n+ var customTypes = {};\n+ var schema = {\n+ complexTypes: complexTypes,\n+ customTypes: customTypes\n+ };\n+ var i, len;\n+\n+ this.readChildNodes(node, schema);\n+\n+ var attributes = node.attributes;\n+ var attr, name;\n+ for (i = 0, len = attributes.length; i < len; ++i) {\n+ attr = attributes[i];\n+ name = attr.name;\n+ if (name.indexOf(\"xmlns\") === 0) {\n+ this.setNamespace(name.split(\":\")[1] || \"\", attr.value);\n+ } else {\n+ obj[name] = attr.value;\n+ }\n+ }\n+ obj.featureTypes = complexTypes;\n+ obj.targetPrefix = this.namespaceAlias[obj.targetNamespace];\n+\n+ // map complexTypes to names of customTypes\n+ var complexType, customType;\n+ for (i = 0, len = complexTypes.length; i < len; ++i) {\n+ complexType = complexTypes[i];\n+ customType = customTypes[complexType.typeName];\n+ if (customTypes[complexType.typeName]) {\n+ complexType.typeName = customType.name;\n+ }\n+ }\n+ },\n+ \"complexType\": function(node, obj) {\n+ var complexType = {\n+ // this is a temporary typeName, it will be overwritten by\n+ // the schema reader with the metadata found in the\n+ // customTypes hash\n+ \"typeName\": node.getAttribute(\"name\")\n+ };\n+ this.readChildNodes(node, complexType);\n+ obj.complexTypes.push(complexType);\n+ },\n+ \"complexContent\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"extension\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"sequence\": function(node, obj) {\n+ var sequence = {\n+ elements: []\n+ };\n+ this.readChildNodes(node, sequence);\n+ obj.properties = sequence.elements;\n+ },\n+ \"element\": function(node, obj) {\n+ var type;\n+ if (obj.elements) {\n+ var element = {};\n+ var attributes = node.attributes;\n+ var attr;\n+ for (var i = 0, len = attributes.length; i < len; ++i) {\n+ attr = attributes[i];\n+ element[attr.name] = attr.value;\n+ }\n+\n+ type = element.type;\n+ if (!type) {\n+ type = {};\n+ this.readChildNodes(node, type);\n+ element.restriction = type;\n+ element.type = type.base;\n+ }\n+ var fullType = type.base || type;\n+ element.localType = fullType.split(\":\").pop();\n+ obj.elements.push(element);\n+ this.readChildNodes(node, element);\n+ }\n+\n+ if (obj.complexTypes) {\n+ type = node.getAttribute(\"type\");\n+ var localType = type.split(\":\").pop();\n+ obj.customTypes[localType] = {\n+ \"name\": node.getAttribute(\"name\"),\n+ \"type\": type\n+ };\n+ }\n+ },\n+ \"annotation\": function(node, obj) {\n+ obj.annotation = {};\n+ this.readChildNodes(node, obj.annotation);\n+ },\n+ \"appinfo\": function(node, obj) {\n+ if (!obj.appinfo) {\n+ obj.appinfo = [];\n+ }\n+ obj.appinfo.push(this.getChildValue(node));\n+ },\n+ \"documentation\": function(node, obj) {\n+ if (!obj.documentation) {\n+ obj.documentation = [];\n+ }\n+ var value = this.getChildValue(node);\n+ obj.documentation.push({\n+ lang: node.getAttribute(\"xml:lang\"),\n+ textContent: value.replace(this.regExes.trimSpace, \"\")\n+ });\n+ },\n+ \"simpleType\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"restriction\": function(node, obj) {\n+ obj.base = node.getAttribute(\"base\");\n+ this.readRestriction(node, obj);\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: readRestriction\n+ * Reads restriction defined in the child nodes of a restriction element\n+ * \n+ * Parameters:\n+ * node - {DOMElement} the node to parse\n+ * obj - {Object} the object that receives the read result\n+ */\n+ readRestriction: function(node, obj) {\n+ var children = node.childNodes;\n+ var child, nodeName, value;\n+ for (var i = 0, len = children.length; i < len; ++i) {\n+ child = children[i];\n+ if (child.nodeType == 1) {\n+ nodeName = child.nodeName.split(\":\").pop();\n+ value = child.getAttribute(\"value\");\n+ if (!obj[nodeName]) {\n+ obj[nodeName] = value;\n+ } else {\n+ if (typeof obj[nodeName] == \"string\") {\n+ obj[nodeName] = [obj[nodeName]];\n+ }\n+ obj[nodeName].push(value);\n+ }\n+ }\n+ }\n+ },\n+\n+ /**\n+ * Method: read\n+ *\n+ * Parameters:\n+ * data - {DOMElement|String} A WFS DescribeFeatureType document.\n+ *\n+ * Returns:\n+ * {Object} An object representing the WFS DescribeFeatureType response.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var schema = {};\n+ if (data.nodeName.split(\":\").pop() === 'ExceptionReport') {\n+ // an exception must have occurred, so parse it\n+ var parser = new OpenLayers.Format.OGCExceptionReport();\n+ schema.error = parser.read(data);\n+ } else {\n+ this.readNode(data, schema);\n+ }\n+ return schema;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFSDescribeFeatureType\"\n+\n+ });\n /* ======================================================================\n- OpenLayers/Format/QueryStringFilter.js\n+ OpenLayers/Format/WFS.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n+ * @requires OpenLayers/Format/GML.js\n * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Format.js\n- * @requires OpenLayers/Filter/Spatial.js\n- * @requires OpenLayers/Filter/Comparison.js\n- * @requires OpenLayers/Filter/Logical.js\n+ * @requires OpenLayers/Lang.js\n */\n \n /**\n- * Class: OpenLayers.Format.QueryStringFilter\n- * Parser for reading a query string and creating a simple filter.\n+ * Class: OpenLayers.Format.WFS\n+ * Read/Write WFS. \n *\n * Inherits from:\n- * - <OpenLayers.Format>\n+ * - <OpenLayers.Format.GML>\n */\n-OpenLayers.Format.QueryStringFilter = (function() {\n+OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {\n \n /** \n- * Map the OpenLayers.Filter.Comparison types to the operation strings of \n- * the protocol.\n+ * Property: layer\n+ * {<OpenLayers.Layer>}\n */\n- var cmpToStr = {};\n- cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = \"eq\";\n- cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = \"ne\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = \"lt\";\n- cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = \"lte\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = \"gt\";\n- cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = \"gte\";\n- cmpToStr[OpenLayers.Filter.Comparison.LIKE] = \"ilike\";\n+ layer: null,\n \n /**\n- * Function: regex2value\n- * Convert the value from a regular expression string to a LIKE/ILIKE\n- * string known to the web service.\n+ * APIProperty: wfsns\n+ * {String}\n+ */\n+ wfsns: \"http://www.opengis.net/wfs\",\n+\n+ /**\n+ * Property: ogcns\n+ * {String}\n+ */\n+ ogcns: \"http://www.opengis.net/ogc\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFS\n+ * Create a WFS-T formatter. This requires a layer: that layer should\n+ * have two properties: geometry_column and typename. The parser\n+ * for this format is subclassed entirely from GML: There is a writer \n+ * only, which uses most of the code from the GML layer, and wraps\n+ * it in transactional elements.\n+ * \n+ * Parameters: \n+ * options - {Object} \n+ * layer - {<OpenLayers.Layer>} \n+ */\n+ initialize: function(options, layer) {\n+ OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);\n+ this.layer = layer;\n+ if (this.layer.featureNS) {\n+ this.featureNS = this.layer.featureNS;\n+ }\n+ if (this.layer.options.geometry_column) {\n+ this.geometryName = this.layer.options.geometry_column;\n+ }\n+ if (this.layer.options.typename) {\n+ this.featureName = this.layer.options.typename;\n+ }\n+ },\n+\n+ /**\n+ * Method: write \n+ * Takes a feature list, and generates a WFS-T Transaction \n *\n * Parameters:\n- * value - {String} The regex string.\n+ * features - {Array(<OpenLayers.Feature.Vector>)} \n+ */\n+ write: function(features) {\n+\n+ var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');\n+ transaction.setAttribute(\"version\", \"1.0.0\");\n+ transaction.setAttribute(\"service\", \"WFS\");\n+ for (var i = 0; i < features.length; i++) {\n+ switch (features[i].state) {\n+ case OpenLayers.State.INSERT:\n+ transaction.appendChild(this.insert(features[i]));\n+ break;\n+ case OpenLayers.State.UPDATE:\n+ transaction.appendChild(this.update(features[i]));\n+ break;\n+ case OpenLayers.State.DELETE:\n+ transaction.appendChild(this.remove(features[i]));\n+ break;\n+ }\n+ }\n+\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]);\n+ },\n+\n+ /**\n+ * Method: createFeatureXML\n *\n- * Returns:\n- * {String} The converted string.\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>}\n */\n- function regex2value(value) {\n+ createFeatureXML: function(feature) {\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n+ var geomContainer = this.createElementNS(this.featureNS, \"feature:\" + this.geometryName);\n+ geomContainer.appendChild(geometryNode);\n+ var featureContainer = this.createElementNS(this.featureNS, \"feature:\" + this.featureName);\n+ featureContainer.appendChild(geomContainer);\n+ for (var attr in feature.attributes) {\n+ var attrText = this.createTextNode(feature.attributes[attr]);\n+ var nodename = attr;\n+ if (attr.search(\":\") != -1) {\n+ nodename = attr.split(\":\")[1];\n+ }\n+ var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n+ attrContainer.appendChild(attrText);\n+ featureContainer.appendChild(attrContainer);\n+ }\n+ return featureContainer;\n+ },\n \n- // highly sensitive!! Do not change this without running the\n- // Protocol/HTTP.html unit tests\n+ /**\n+ * Method: insert \n+ * Takes a feature, and generates a WFS-T Transaction \"Insert\" \n+ *\n+ * Parameters: \n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ insert: function(feature) {\n+ var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');\n+ insertNode.appendChild(this.createFeatureXML(feature));\n+ return insertNode;\n+ },\n \n- // convert % to \\%\n- value = value.replace(/%/g, \"\\\\%\");\n+ /**\n+ * Method: update\n+ * Takes a feature, and generates a WFS-T Transaction \"Update\" \n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ update: function(feature) {\n+ if (!feature.fid) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"noFID\"));\n+ }\n+ var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');\n+ updateNode.setAttribute(\"typeName\", this.featurePrefix + ':' + this.featureName);\n+ updateNode.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n \n- // convert \\\\. to \\\\_ (\\\\.* occurences converted later)\n- value = value.replace(/\\\\\\\\\\.(\\*)?/g, function($0, $1) {\n- return $1 ? $0 : \"\\\\\\\\_\";\n- });\n+ var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');\n+ var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');\n \n- // convert \\\\.* to \\\\%\n- value = value.replace(/\\\\\\\\\\.\\*/g, \"\\\\\\\\%\");\n+ var txtNode = this.createTextNode(this.geometryName);\n+ nameNode.appendChild(txtNode);\n+ propertyNode.appendChild(nameNode);\n \n- // convert . to _ (\\. and .* occurences converted later)\n- value = value.replace(/(\\\\)?\\.(\\*)?/g, function($0, $1, $2) {\n- return $1 || $2 ? $0 : \"_\";\n- });\n+ var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');\n \n- // convert .* to % (\\.* occurnces converted later)\n- value = value.replace(/(\\\\)?\\.\\*/g, function($0, $1) {\n- return $1 ? $0 : \"%\";\n- });\n+ var geometryNode = this.buildGeometryNode(feature.geometry);\n \n- // convert \\. to .\n- value = value.replace(/\\\\\\./g, \".\");\n+ if (feature.layer) {\n+ geometryNode.setAttribute(\n+ \"srsName\", feature.layer.projection.getCode()\n+ );\n+ }\n \n- // replace \\* with * (watching out for \\\\*)\n- value = value.replace(/(\\\\)?\\\\\\*/g, function($0, $1) {\n- return $1 ? $0 : \"*\";\n- });\n+ valueNode.appendChild(geometryNode);\n \n- return value;\n- }\n+ propertyNode.appendChild(valueNode);\n+ updateNode.appendChild(propertyNode);\n \n- return OpenLayers.Class(OpenLayers.Format, {\n+ // add in attributes\n+ for (var propName in feature.attributes) {\n+ propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');\n+ nameNode = this.createElementNS(this.wfsns, 'wfs:Name');\n+ nameNode.appendChild(this.createTextNode(propName));\n+ propertyNode.appendChild(nameNode);\n+ valueNode = this.createElementNS(this.wfsns, 'wfs:Value');\n+ valueNode.appendChild(this.createTextNode(feature.attributes[propName]));\n+ propertyNode.appendChild(valueNode);\n+ updateNode.appendChild(propertyNode);\n+ }\n \n- /**\n- * Property: wildcarded.\n- * {Boolean} If true percent signs are added around values\n- * read from LIKE filters, for example if the protocol\n- * read method is passed a LIKE filter whose property\n- * is \"foo\" and whose value is \"bar\" the string\n- * \"foo__ilike=%bar%\" will be sent in the query string;\n- * defaults to false.\n- */\n- wildcarded: false,\n \n- /**\n- * APIProperty: srsInBBOX\n- * {Boolean} Include the SRS identifier in BBOX query string parameter. \n- * Default is false. If true and the layer has a projection object set,\n- * any BBOX filter will be serialized with a fifth item identifying the\n- * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913\n- */\n- srsInBBOX: false,\n+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');\n+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');\n+ filterIdNode.setAttribute(\"fid\", feature.fid);\n+ filterNode.appendChild(filterIdNode);\n+ updateNode.appendChild(filterNode);\n \n- /**\n- * APIMethod: write\n- * Serialize an <OpenLayers.Filter> objects using the \"simple\" filter syntax for \n- * query string parameters. This function must be called as a method of\n- * a protocol instance.\n- *\n- * Parameters:\n- * filter - {<OpenLayers.Filter>} filter to convert.\n- * params - {Object} The parameters object.\n- *\n- * Returns:\n- * {Object} The resulting parameters object.\n- */\n- write: function(filter, params) {\n- params = params || {};\n- var className = filter.CLASS_NAME;\n- var filterType = className.substring(className.lastIndexOf(\".\") + 1);\n- switch (filterType) {\n- case \"Spatial\":\n- switch (filter.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- params.bbox = filter.value.toArray();\n- if (this.srsInBBOX && filter.projection) {\n- params.bbox.push(filter.projection.getCode());\n- }\n- break;\n- case OpenLayers.Filter.Spatial.DWITHIN:\n- params.tolerance = filter.distance;\n- // no break here\n- case OpenLayers.Filter.Spatial.WITHIN:\n- params.lon = filter.value.x;\n- params.lat = filter.value.y;\n- break;\n- default:\n- OpenLayers.Console.warn(\n- \"Unknown spatial filter type \" + filter.type);\n- }\n- break;\n- case \"Comparison\":\n- var op = cmpToStr[filter.type];\n- if (op !== undefined) {\n- var value = filter.value;\n- if (filter.type == OpenLayers.Filter.Comparison.LIKE) {\n- value = regex2value(value);\n- if (this.wildcarded) {\n- value = \"%\" + value + \"%\";\n- }\n- }\n- params[filter.property + \"__\" + op] = value;\n- params.queryable = params.queryable || [];\n- params.queryable.push(filter.property);\n- } else {\n- OpenLayers.Console.warn(\n- \"Unknown comparison filter type \" + filter.type);\n- }\n- break;\n- case \"Logical\":\n- if (filter.type === OpenLayers.Filter.Logical.AND) {\n- for (var i = 0, len = filter.filters.length; i < len; i++) {\n- params = this.write(filter.filters[i], params);\n- }\n- } else {\n- OpenLayers.Console.warn(\n- \"Unsupported logical filter type \" + filter.type);\n- }\n- break;\n- default:\n- OpenLayers.Console.warn(\"Unknown filter type \" + filterType);\n- }\n- return params;\n- },\n+ return updateNode;\n+ },\n \n- CLASS_NAME: \"OpenLayers.Format.QueryStringFilter\"\n+ /**\n+ * Method: remove \n+ * Takes a feature, and generates a WFS-T Transaction \"Delete\" \n+ *\n+ * Parameters:\n+ * feature - {<OpenLayers.Feature.Vector>} \n+ */\n+ remove: function(feature) {\n+ if (!feature.fid) {\n+ OpenLayers.Console.userError(OpenLayers.i18n(\"noFID\"));\n+ return false;\n+ }\n+ var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');\n+ deleteNode.setAttribute(\"typeName\", this.featurePrefix + ':' + this.featureName);\n+ deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n \n- });\n+ var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');\n+ var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');\n+ filterIdNode.setAttribute(\"fid\", feature.fid);\n+ filterNode.appendChild(filterIdNode);\n+ deleteNode.appendChild(filterNode);\n \n+ return deleteNode;\n+ },\n \n-})();\n+ /**\n+ * APIMethod: destroy\n+ * Remove ciruclar ref to layer \n+ */\n+ destroy: function() {\n+ this.layer = null;\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFS\"\n+});\n /* ======================================================================\n OpenLayers/Format/KML.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -57222,1896 +51780,14 @@\n return extendedData;\n }\n },\n \n CLASS_NAME: \"OpenLayers.Format.KML\"\n });\n /* ======================================================================\n- OpenLayers/Format/WCSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WCSCapabilities\n- * Read WCS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n- */\n- defaultVersion: \"1.1.0\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WCSCapabilities\n- * Create a new parser for WCS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of coverages. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named coverages.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WCSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/GPX.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Projection.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.GPX\n- * Read/write GPX parser. Create a new instance with the \n- * <OpenLayers.Format.GPX> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n-\n- /** \n- * APIProperty: defaultDesc\n- * {String} Default description for the waypoints/tracks in the case\n- * where the feature has no \"description\" attribute.\n- * Default is \"No description available\".\n- */\n- defaultDesc: \"No description available\",\n-\n- /**\n- * APIProperty: extractWaypoints\n- * {Boolean} Extract waypoints from GPX. (default: true)\n- */\n- extractWaypoints: true,\n-\n- /**\n- * APIProperty: extractTracks\n- * {Boolean} Extract tracks from GPX. (default: true)\n- */\n- extractTracks: true,\n-\n- /**\n- * APIProperty: extractRoutes\n- * {Boolean} Extract routes from GPX. (default: true)\n- */\n- extractRoutes: true,\n-\n- /**\n- * APIProperty: extractAttributes\n- * {Boolean} Extract feature attributes from GPX. (default: true)\n- * NOTE: Attributes as part of extensions to the GPX standard may not\n- * be extracted.\n- */\n- extractAttributes: true,\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- gpx: \"http://www.topografix.com/GPX/1/1\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: schemaLocation\n- * {String} Schema location. Defaults to\n- * \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\"\n- */\n- schemaLocation: \"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\",\n-\n- /**\n- * APIProperty: creator\n- * {String} The creator attribute to be added to the written GPX files.\n- * Defaults to \"OpenLayers\"\n- */\n- creator: \"OpenLayers\",\n-\n- /**\n- * Constructor: OpenLayers.Format.GPX\n- * Create a new parser for GPX.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- // GPX coordinates are always in longlat WGS84\n- this.externalProjection = new OpenLayers.Projection(\"EPSG:4326\");\n-\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Return a list of features from a GPX doc\n- *\n- * Parameters:\n- * doc - {Element} \n- *\n- * Returns:\n- * Array({<OpenLayers.Feature.Vector>})\n- */\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n- }\n- var features = [];\n-\n- if (this.extractTracks) {\n- var tracks = doc.getElementsByTagName(\"trk\");\n- for (var i = 0, len = tracks.length; i < len; i++) {\n- // Attributes are only in trk nodes, not trkseg nodes\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(tracks[i]);\n- }\n-\n- var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, \"trkseg\");\n- for (var j = 0, seglen = segs.length; j < seglen; j++) {\n- // We don't yet support extraction of trkpt attributes\n- // All trksegs of a trk get that trk's attributes\n- var track = this.extractSegment(segs[j], \"trkpt\");\n- features.push(new OpenLayers.Feature.Vector(track, attrs));\n- }\n- }\n- }\n-\n- if (this.extractRoutes) {\n- var routes = doc.getElementsByTagName(\"rte\");\n- for (var k = 0, klen = routes.length; k < klen; k++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(routes[k]);\n- }\n- var route = this.extractSegment(routes[k], \"rtept\");\n- features.push(new OpenLayers.Feature.Vector(route, attrs));\n- }\n- }\n-\n- if (this.extractWaypoints) {\n- var waypoints = doc.getElementsByTagName(\"wpt\");\n- for (var l = 0, len = waypoints.length; l < len; l++) {\n- var attrs = {};\n- if (this.extractAttributes) {\n- attrs = this.parseAttributes(waypoints[l]);\n- }\n- var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute(\"lon\"), waypoints[l].getAttribute(\"lat\"));\n- features.push(new OpenLayers.Feature.Vector(wpt, attrs));\n- }\n- }\n-\n- if (this.internalProjection && this.externalProjection) {\n- for (var g = 0, featLength = features.length; g < featLength; g++) {\n- features[g].geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n- }\n-\n- return features;\n- },\n-\n- /**\n- * Method: extractSegment\n- *\n- * Parameters:\n- * segment - {DOMElement} a trkseg or rte node to parse\n- * segmentType - {String} nodeName of waypoints that form the line\n- *\n- * Returns:\n- * {<OpenLayers.Geometry.LineString>} A linestring geometry\n- */\n- extractSegment: function(segment, segmentType) {\n- var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);\n- var point_features = [];\n- for (var i = 0, len = points.length; i < len; i++) {\n- point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute(\"lon\"), points[i].getAttribute(\"lat\")));\n- }\n- return new OpenLayers.Geometry.LineString(point_features);\n- },\n-\n- /**\n- * Method: parseAttributes\n- *\n- * Parameters:\n- * node - {<DOMElement>}\n- *\n- * Returns:\n- * {Object} An attributes object.\n- */\n- parseAttributes: function(node) {\n- // node is either a wpt, trk or rte\n- // attributes are children of the form <attr>value</attr>\n- var attributes = {};\n- var attrNode = node.firstChild,\n- value, name;\n- while (attrNode) {\n- if (attrNode.nodeType == 1 && attrNode.firstChild) {\n- value = attrNode.firstChild;\n- if (value.nodeType == 3 || value.nodeType == 4) {\n- name = (attrNode.prefix) ?\n- attrNode.nodeName.split(\":\")[1] :\n- attrNode.nodeName;\n- if (name != \"trkseg\" && name != \"rtept\") {\n- attributes[name] = value.nodeValue;\n- }\n- }\n- }\n- attrNode = attrNode.nextSibling;\n- }\n- return attributes;\n- },\n-\n- /**\n- * APIMethod: write\n- * Accepts Feature Collection, and returns a string. \n- * \n- * Parameters: \n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n- * metadata - {Object} A key/value pairs object to build a metadata node to\n- * add to the gpx. Supported keys are 'name', 'desc', 'author'.\n- */\n- write: function(features, metadata) {\n- features = OpenLayers.Util.isArray(features) ?\n- features : [features];\n- var gpx = this.createElementNS(this.namespaces.gpx, \"gpx\");\n- gpx.setAttribute(\"version\", \"1.1\");\n- gpx.setAttribute(\"creator\", this.creator);\n- this.setAttributes(gpx, {\n- \"xsi:schemaLocation\": this.schemaLocation\n- });\n-\n- if (metadata && typeof metadata == 'object') {\n- gpx.appendChild(this.buildMetadataNode(metadata));\n- }\n- for (var i = 0, len = features.length; i < len; i++) {\n- gpx.appendChild(this.buildFeatureNode(features[i]));\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]);\n- },\n-\n- /**\n- * Method: buildMetadataNode\n- * Creates a \"metadata\" node.\n- *\n- * Returns:\n- * {DOMElement}\n- */\n- buildMetadataNode: function(metadata) {\n- var types = ['name', 'desc', 'author'],\n- node = this.createElementNS(this.namespaces.gpx, 'metadata');\n- for (var i = 0; i < types.length; i++) {\n- var type = types[i];\n- if (metadata[type]) {\n- var n = this.createElementNS(this.namespaces.gpx, type);\n- n.appendChild(this.createTextNode(metadata[type]));\n- node.appendChild(n);\n- }\n- }\n- return node;\n- },\n-\n- /**\n- * Method: buildFeatureNode\n- * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>}\n- *\n- * Returns:\n- * {DOMElement} - The created node, either a 'wpt' or a 'trk'.\n- */\n- buildFeatureNode: function(feature) {\n- var geometry = feature.geometry;\n- geometry = geometry.clone();\n- if (this.internalProjection && this.externalProjection) {\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- var wpt = this.buildWptNode(geometry);\n- this.appendAttributesNode(wpt, feature);\n- return wpt;\n- } else {\n- var trkNode = this.createElementNS(this.namespaces.gpx, \"trk\");\n- this.appendAttributesNode(trkNode, feature);\n- var trkSegNodes = this.buildTrkSegNode(geometry);\n- trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ?\n- trkSegNodes : [trkSegNodes];\n- for (var i = 0, len = trkSegNodes.length; i < len; i++) {\n- trkNode.appendChild(trkSegNodes[i]);\n- }\n- return trkNode;\n- }\n- },\n-\n- /**\n- * Method: buildTrkSegNode\n- * Builds trkseg node(s) given a geometry\n- *\n- * Parameters:\n- * trknode\n- * geometry - {<OpenLayers.Geometry>}\n- */\n- buildTrkSegNode: function(geometry) {\n- var node,\n- i,\n- len,\n- point,\n- nodes;\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\" ||\n- geometry.CLASS_NAME == \"OpenLayers.Geometry.LinearRing\") {\n- node = this.createElementNS(this.namespaces.gpx, \"trkseg\");\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- point = geometry.components[i];\n- node.appendChild(this.buildTrkPtNode(point));\n- }\n- return node;\n- } else {\n- nodes = [];\n- for (i = 0, len = geometry.components.length; i < len; i++) {\n- nodes.push(this.buildTrkSegNode(geometry.components[i]));\n- }\n- return nodes;\n- }\n- },\n-\n- /**\n- * Method: buildTrkPtNode\n- * Builds a trkpt node given a point \n- *\n- * Parameters:\n- * point - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {DOMElement} A trkpt node\n- */\n- buildTrkPtNode: function(point) {\n- var node = this.createElementNS(this.namespaces.gpx, \"trkpt\");\n- node.setAttribute(\"lon\", point.x);\n- node.setAttribute(\"lat\", point.y);\n- return node;\n- },\n-\n- /**\n- * Method: buildWptNode\n- * Builds a wpt node given a point\n- *\n- * Parameters:\n- * geometry - {<OpenLayers.Geometry.Point>}\n- *\n- * Returns:\n- * {DOMElement} A wpt node\n- */\n- buildWptNode: function(geometry) {\n- var node = this.createElementNS(this.namespaces.gpx, \"wpt\");\n- node.setAttribute(\"lon\", geometry.x);\n- node.setAttribute(\"lat\", geometry.y);\n- return node;\n- },\n-\n- /**\n- * Method: appendAttributesNode\n- * Adds some attributes node.\n- *\n- * Parameters:\n- * node - {DOMElement} the node to append the attribute nodes to.\n- * feature - {<OpenLayers.Feature.Vector>}\n- */\n- appendAttributesNode: function(node, feature) {\n- var name = this.createElementNS(this.namespaces.gpx, 'name');\n- name.appendChild(this.createTextNode(\n- feature.attributes.name || feature.id));\n- node.appendChild(name);\n- var desc = this.createElementNS(this.namespaces.gpx, 'desc');\n- desc.appendChild(this.createTextNode(\n- feature.attributes.description || this.defaultDesc));\n- node.appendChild(desc);\n- // TBD - deal with remaining (non name/description) attributes.\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.GPX\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/GeoRSS.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Feature/Vector.js\n- * @requires OpenLayers/Geometry/Point.js\n- * @requires OpenLayers/Geometry/LineString.js\n- * @requires OpenLayers/Geometry/Polygon.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.GeoRSS\n- * Read/write GeoRSS parser. Create a new instance with the \n- * <OpenLayers.Format.GeoRSS> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * APIProperty: rssns\n- * {String} RSS namespace to use. Defaults to\n- * \"http://backend.userland.com/rss2\"\n- */\n- rssns: \"http://backend.userland.com/rss2\",\n-\n- /**\n- * APIProperty: featurens\n- * {String} Feature Attributes namespace. Defaults to\n- * \"http://mapserver.gis.umn.edu/mapserver\"\n- */\n- featureNS: \"http://mapserver.gis.umn.edu/mapserver\",\n-\n- /**\n- * APIProperty: georssns\n- * {String} GeoRSS namespace to use. Defaults to\n- * \"http://www.georss.org/georss\"\n- */\n- georssns: \"http://www.georss.org/georss\",\n-\n- /**\n- * APIProperty: geons\n- * {String} W3C Geo namespace to use. Defaults to\n- * \"http://www.w3.org/2003/01/geo/wgs84_pos#\"\n- */\n- geons: \"http://www.w3.org/2003/01/geo/wgs84_pos#\",\n-\n- /**\n- * APIProperty: featureTitle\n- * {String} Default title for features. Defaults to \"Untitled\"\n- */\n- featureTitle: \"Untitled\",\n-\n- /**\n- * APIProperty: featureDescription\n- * {String} Default description for features. Defaults to \"No Description\"\n- */\n- featureDescription: \"No Description\",\n-\n- /**\n- * Property: gmlParse\n- * {Object} GML Format object for parsing features\n- * Non-API and only created if necessary\n- */\n- gmlParser: null,\n-\n- /**\n- * APIProperty: xy\n- * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x)\n- * For GeoRSS the default is (y,x), therefore: false\n- */\n- xy: false,\n-\n- /**\n- * Constructor: OpenLayers.Format.GeoRSS\n- * Create a new parser for GeoRSS.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Method: createGeometryFromItem\n- * Return a geometry from a GeoRSS Item.\n- *\n- * Parameters:\n- * item - {DOMElement} A GeoRSS item node.\n- *\n- * Returns:\n- * {<OpenLayers.Geometry>} A geometry representing the node.\n- */\n- createGeometryFromItem: function(item) {\n- var point = this.getElementsByTagNameNS(item, this.georssns, \"point\");\n- var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');\n- var lon = this.getElementsByTagNameNS(item, this.geons, 'long');\n-\n- var line = this.getElementsByTagNameNS(item,\n- this.georssns,\n- \"line\");\n- var polygon = this.getElementsByTagNameNS(item,\n- this.georssns,\n- \"polygon\");\n- var where = this.getElementsByTagNameNS(item,\n- this.georssns,\n- \"where\");\n- var box = this.getElementsByTagNameNS(item,\n- this.georssns,\n- \"box\");\n-\n- if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {\n- var location;\n- if (point.length > 0) {\n- location = OpenLayers.String.trim(\n- point[0].firstChild.nodeValue).split(/\\s+/);\n- if (location.length != 2) {\n- location = OpenLayers.String.trim(\n- point[0].firstChild.nodeValue).split(/\\s*,\\s*/);\n- }\n- } else {\n- location = [parseFloat(lat[0].firstChild.nodeValue),\n- parseFloat(lon[0].firstChild.nodeValue)\n- ];\n- }\n-\n- var geometry = new OpenLayers.Geometry.Point(location[1], location[0]);\n-\n- } else if (line.length > 0) {\n- var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\\s+/);\n- var components = [];\n- var point;\n- for (var i = 0, len = coords.length; i < len; i += 2) {\n- point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]);\n- components.push(point);\n- }\n- geometry = new OpenLayers.Geometry.LineString(components);\n- } else if (polygon.length > 0) {\n- var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\\s+/);\n- var components = [];\n- var point;\n- for (var i = 0, len = coords.length; i < len; i += 2) {\n- point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]);\n- components.push(point);\n- }\n- geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);\n- } else if (where.length > 0) {\n- if (!this.gmlParser) {\n- this.gmlParser = new OpenLayers.Format.GML({\n- 'xy': this.xy\n- });\n- }\n- var feature = this.gmlParser.parseFeature(where[0]);\n- geometry = feature.geometry;\n- } else if (box.length > 0) {\n- var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\\s+/);\n- var components = [];\n- var point;\n- if (coords.length > 3) {\n- point = new OpenLayers.Geometry.Point(coords[1], coords[0]);\n- components.push(point);\n- point = new OpenLayers.Geometry.Point(coords[1], coords[2]);\n- components.push(point);\n- point = new OpenLayers.Geometry.Point(coords[3], coords[2]);\n- components.push(point);\n- point = new OpenLayers.Geometry.Point(coords[3], coords[0]);\n- components.push(point);\n- point = new OpenLayers.Geometry.Point(coords[1], coords[0]);\n- components.push(point);\n- }\n- geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);\n- }\n-\n- if (geometry && this.internalProjection && this.externalProjection) {\n- geometry.transform(this.externalProjection,\n- this.internalProjection);\n- }\n-\n- return geometry;\n- },\n-\n- /**\n- * Method: createFeatureFromItem\n- * Return a feature from a GeoRSS Item.\n- *\n- * Parameters:\n- * item - {DOMElement} A GeoRSS item node.\n- *\n- * Returns:\n- * {<OpenLayers.Feature.Vector>} A feature representing the item.\n- */\n- createFeatureFromItem: function(item) {\n- var geometry = this.createGeometryFromItem(item);\n-\n- /* Provide defaults for title and description */\n- var title = this._getChildValue(item, \"*\", \"title\", this.featureTitle);\n-\n- /* First try RSS descriptions, then Atom summaries */\n- var description = this._getChildValue(\n- item, \"*\", \"description\",\n- this._getChildValue(item, \"*\", \"content\",\n- this._getChildValue(item, \"*\", \"summary\", this.featureDescription)));\n-\n- /* If no link URL is found in the first child node, try the\n- href attribute */\n- var link = this._getChildValue(item, \"*\", \"link\");\n- if (!link) {\n- try {\n- link = this.getElementsByTagNameNS(item, \"*\", \"link\")[0].getAttribute(\"href\");\n- } catch (e) {\n- link = null;\n- }\n- }\n-\n- var id = this._getChildValue(item, \"*\", \"id\", null);\n-\n- var data = {\n- \"title\": title,\n- \"description\": description,\n- \"link\": link\n- };\n- var feature = new OpenLayers.Feature.Vector(geometry, data);\n- feature.fid = id;\n- return feature;\n- },\n-\n- /**\n- * Method: _getChildValue\n- *\n- * Parameters:\n- * node - {DOMElement}\n- * nsuri - {String} Child node namespace uri (\"*\" for any).\n- * name - {String} Child node name.\n- * def - {String} Optional string default to return if no child found.\n- *\n- * Returns:\n- * {String} The value of the first child with the given tag name. Returns\n- * default value or empty string if none found.\n- */\n- _getChildValue: function(node, nsuri, name, def) {\n- var value;\n- var eles = this.getElementsByTagNameNS(node, nsuri, name);\n- if (eles && eles[0] && eles[0].firstChild &&\n- eles[0].firstChild.nodeValue) {\n- value = this.getChildValue(eles[0]);\n- } else {\n- value = (def == undefined) ? \"\" : def;\n- }\n- return value;\n- },\n-\n- /**\n- * APIMethod: read\n- * Return a list of features from a GeoRSS doc\n- *\n- * Parameters:\n- * doc - {Element} \n- *\n- * Returns:\n- * {Array(<OpenLayers.Feature.Vector>)}\n- */\n- read: function(doc) {\n- if (typeof doc == \"string\") {\n- doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);\n- }\n-\n- /* Try RSS items first, then Atom entries */\n- var itemlist = null;\n- itemlist = this.getElementsByTagNameNS(doc, '*', 'item');\n- if (itemlist.length == 0) {\n- itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');\n- }\n-\n- var numItems = itemlist.length;\n- var features = new Array(numItems);\n- for (var i = 0; i < numItems; i++) {\n- features[i] = this.createFeatureFromItem(itemlist[i]);\n- }\n- return features;\n- },\n-\n-\n- /**\n- * APIMethod: write\n- * Accept Feature Collection, and return a string. \n- * \n- * Parameters: \n- * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string.\n- */\n- write: function(features) {\n- var georss;\n- if (OpenLayers.Util.isArray(features)) {\n- georss = this.createElementNS(this.rssns, \"rss\");\n- for (var i = 0, len = features.length; i < len; i++) {\n- georss.appendChild(this.createFeatureXML(features[i]));\n- }\n- } else {\n- georss = this.createFeatureXML(features);\n- }\n- return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);\n- },\n-\n- /**\n- * Method: createFeatureXML\n- * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it.\n- * \n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- *\n- * Returns:\n- * {DOMElement}\n- */\n- createFeatureXML: function(feature) {\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n- var featureNode = this.createElementNS(this.rssns, \"item\");\n- var titleNode = this.createElementNS(this.rssns, \"title\");\n- titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : \"\"));\n- var descNode = this.createElementNS(this.rssns, \"description\");\n- descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : \"\"));\n- featureNode.appendChild(titleNode);\n- featureNode.appendChild(descNode);\n- if (feature.attributes.link) {\n- var linkNode = this.createElementNS(this.rssns, \"link\");\n- linkNode.appendChild(this.createTextNode(feature.attributes.link));\n- featureNode.appendChild(linkNode);\n- }\n- for (var attr in feature.attributes) {\n- if (attr == \"link\" || attr == \"title\" || attr == \"description\") {\n- continue;\n- }\n- var attrText = this.createTextNode(feature.attributes[attr]);\n- var nodename = attr;\n- if (attr.search(\":\") != -1) {\n- nodename = attr.split(\":\")[1];\n- }\n- var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n- attrContainer.appendChild(attrText);\n- featureNode.appendChild(attrContainer);\n- }\n- featureNode.appendChild(geometryNode);\n- return featureNode;\n- },\n-\n- /** \n- * Method: buildGeometryNode\n- * builds a GeoRSS node with a given geometry\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- *\n- * Returns:\n- * {DOMElement} A gml node.\n- */\n- buildGeometryNode: function(geometry) {\n- if (this.internalProjection && this.externalProjection) {\n- geometry = geometry.clone();\n- geometry.transform(this.internalProjection,\n- this.externalProjection);\n- }\n- var node;\n- // match Polygon\n- if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Polygon\") {\n- node = this.createElementNS(this.georssns, 'georss:polygon');\n-\n- node.appendChild(this.buildCoordinatesNode(geometry.components[0]));\n- }\n- // match LineString\n- else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.LineString\") {\n- node = this.createElementNS(this.georssns, 'georss:line');\n-\n- node.appendChild(this.buildCoordinatesNode(geometry));\n- }\n- // match Point\n- else if (geometry.CLASS_NAME == \"OpenLayers.Geometry.Point\") {\n- node = this.createElementNS(this.georssns, 'georss:point');\n- node.appendChild(this.buildCoordinatesNode(geometry));\n- } else {\n- throw \"Couldn't parse \" + geometry.CLASS_NAME;\n- }\n- return node;\n- },\n-\n- /** \n- * Method: buildCoordinatesNode\n- * \n- * Parameters:\n- * geometry - {<OpenLayers.Geometry>}\n- */\n- buildCoordinatesNode: function(geometry) {\n- var points = null;\n-\n- if (geometry.components) {\n- points = geometry.components;\n- }\n-\n- var path;\n- if (points) {\n- var numPoints = points.length;\n- var parts = new Array(numPoints);\n- for (var i = 0; i < numPoints; i++) {\n- parts[i] = points[i].y + \" \" + points[i].x;\n- }\n- path = parts.join(\" \");\n- } else {\n- path = geometry.y + \" \" + geometry.x;\n- }\n- return this.createTextNode(path);\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.GeoRSS\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSCapabilities\n- * Read WMS Capabilities.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.1\".\n- */\n- defaultVersion: \"1.1.1\",\n-\n- /**\n- * APIProperty: profile\n- * {String} If provided, use a custom profile.\n- *\n- * Currently supported profiles:\n- * - WMSC - parses vendor specific capabilities for WMS-C.\n- */\n- profile: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSCapabilities\n- * Create a new parser for WMS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return a list of layers. \n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Array} List of named layers.\n- */\n-\n- CLASS_NAME: \"OpenLayers.Format.WMSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMTSCapabilities.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML/VersionedOGC.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMTSCapabilities\n- * Read WMTS Capabilities.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML.VersionedOGC>\n- */\n-OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n-\n- /**\n- * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.0.0\".\n- */\n- defaultVersion: \"1.0.0\",\n-\n- /**\n- * APIProperty: yx\n- * {Object} Members in the yx object are used to determine if a CRS URN\n- * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n- * and values are boolean. By default, the following CRS URN are\n- * assumed to correspond to a CRS with y,x axis order:\n- *\n- * * urn:ogc:def:crs:EPSG::4326\n- */\n- yx: {\n- \"urn:ogc:def:crs:EPSG::4326\": true\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WMTSCapabilities\n- * Create a new parser for WMTS capabilities.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return information about\n- * the service (offering and observedProperty mostly).\n- *\n- * Parameters:\n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Info about the WMTS Capabilities\n- */\n-\n- /**\n- * APIMethod: createLayer\n- * Create a WMTS layer given a capabilities object.\n- *\n- * Parameters:\n- * capabilities - {Object} The object returned from a <read> call to this\n- * format.\n- * config - {Object} Configuration properties for the layer. Defaults for\n- * the layer will apply if not provided.\n- *\n- * Required config properties:\n- * layer - {String} The layer identifier.\n- *\n- * Optional config properties:\n- * matrixSet - {String} The matrix set identifier, required if there is \n- * more than one matrix set in the layer capabilities.\n- * style - {String} The name of the style\n- * format - {String} Image format for the layer. Default is the first\n- * format returned in the GetCapabilities response.\n- * param - {Object} The dimensions values eg: {\"Year\": \"2012\"}\n- *\n- * Returns:\n- * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an\n- * error if an incomplete config is provided. Returns undefined if no\n- * layer could be created with the provided config.\n- */\n- createLayer: function(capabilities, config) {\n- var layer;\n-\n- // confirm required properties are supplied in config\n- if (!('layer' in config)) {\n- throw new Error(\"Missing property 'layer' in configuration.\");\n- }\n-\n- var contents = capabilities.contents;\n-\n- // find the layer definition with the given identifier\n- var layers = contents.layers;\n- var layerDef;\n- for (var i = 0, ii = contents.layers.length; i < ii; ++i) {\n- if (contents.layers[i].identifier === config.layer) {\n- layerDef = contents.layers[i];\n- break;\n- }\n- }\n- if (!layerDef) {\n- throw new Error(\"Layer not found\");\n- }\n-\n- var format = config.format;\n- if (!format && layerDef.formats && layerDef.formats.length) {\n- format = layerDef.formats[0];\n- }\n-\n- // find the matrixSet definition\n- var matrixSet;\n- if (config.matrixSet) {\n- matrixSet = contents.tileMatrixSets[config.matrixSet];\n- } else if (layerDef.tileMatrixSetLinks.length >= 1) {\n- matrixSet = contents.tileMatrixSets[\n- layerDef.tileMatrixSetLinks[0].tileMatrixSet];\n- }\n- if (!matrixSet) {\n- throw new Error(\"matrixSet not found\");\n- }\n-\n- // get the default style for the layer\n- var style;\n- for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {\n- style = layerDef.styles[i];\n- if (style.isDefault) {\n- break;\n- }\n- }\n-\n- var requestEncoding = config.requestEncoding;\n- if (!requestEncoding) {\n- requestEncoding = \"KVP\";\n- if (capabilities.operationsMetadata.GetTile.dcp.http) {\n- var http = capabilities.operationsMetadata.GetTile.dcp.http;\n- // Get first get method\n- if (http.get[0].constraints) {\n- var constraints = http.get[0].constraints;\n- var allowedValues = constraints.GetEncoding.allowedValues;\n-\n- // The OGC documentation is not clear if we should use\n- // REST or RESTful, ArcGis use RESTful,\n- // and OpenLayers use REST.\n- if (!allowedValues.KVP &&\n- (allowedValues.REST || allowedValues.RESTful)) {\n- requestEncoding = \"REST\";\n- }\n- }\n- }\n- }\n-\n- var dimensions = [];\n- var params = config.params || {};\n- // to don't overwrite the changes in the applyDefaults\n- delete config.params;\n- for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) {\n- var dimension = layerDef.dimensions[id];\n- dimensions.push(dimension.identifier);\n- if (!params.hasOwnProperty(dimension.identifier)) {\n- params[dimension.identifier] = dimension['default'];\n- }\n- }\n-\n- var projection = config.projection || matrixSet.supportedCRS.replace(\n- /urn:ogc:def:crs:(\\w+):(.*:)?(\\w+)$/, \"$1:$3\");\n- var units = config.units ||\n- (projection === \"EPSG:4326\" ? \"degrees\" : \"m\");\n-\n- var resolutions = [];\n- for (var mid in matrixSet.matrixIds) {\n- if (matrixSet.matrixIds.hasOwnProperty(mid)) {\n- resolutions.push(\n- matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 /\n- OpenLayers.METERS_PER_INCH /\n- OpenLayers.INCHES_PER_UNIT[units]);\n- }\n- }\n-\n- var url;\n- if (requestEncoding === \"REST\" && layerDef.resourceUrls) {\n- url = [];\n- var resourceUrls = layerDef.resourceUrls,\n- resourceUrl;\n- for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) {\n- resourceUrl = layerDef.resourceUrls[t];\n- if (resourceUrl.format === format && resourceUrl.resourceType === \"tile\") {\n- url.push(resourceUrl.template);\n- }\n- }\n- } else {\n- var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get;\n- url = [];\n- var constraint;\n- for (var i = 0, ii = httpGet.length; i < ii; i++) {\n- constraint = httpGet[i].constraints;\n- if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) {\n- url.push(httpGet[i].url);\n- }\n- }\n- }\n-\n- return new OpenLayers.Layer.WMTS(\n- OpenLayers.Util.applyDefaults(config, {\n- url: url,\n- requestEncoding: requestEncoding,\n- name: layerDef.title,\n- style: style.identifier,\n- format: format,\n- matrixIds: matrixSet.matrixIds,\n- matrixSet: matrixSet.identifier,\n- projection: projection,\n- units: units,\n- resolutions: config.isBaseLayer === false ? undefined : resolutions,\n- serverResolutions: resolutions,\n- tileFullExtent: matrixSet.bounds,\n- dimensions: dimensions,\n- params: params\n- })\n- );\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities\"\n-\n-});\n-/* ======================================================================\n- OpenLayers/Format/CSWGetRecords.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CSWGetRecords\n- * Default version is 2.0.2.\n- *\n- * Returns:\n- * {<OpenLayers.Format>} A CSWGetRecords format of the given version.\n- */\n-OpenLayers.Format.CSWGetRecords = function(options) {\n- options = OpenLayers.Util.applyDefaults(\n- options, OpenLayers.Format.CSWGetRecords.DEFAULTS\n- );\n- var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n- if (!cls) {\n- throw \"Unsupported CSWGetRecords version: \" + options.version;\n- }\n- return new cls(options);\n-};\n-\n-/**\n- * Constant: DEFAULTS\n- * {Object} Default properties for the CSWGetRecords format.\n- */\n-OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n- \"version\": \"2.0.2\"\n-};\n-/* ======================================================================\n- OpenLayers/Format/CQL.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WKT.js\n- * @requires OpenLayers/Filter/Comparison.js\n- * @requires OpenLayers/Filter/Logical.js\n- * @requires OpenLayers/Filter/Spatial.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CQL\n- * Read CQL strings to get <OpenLayers.Filter> objects. Write \n- * <OpenLayers.Filter> objects to get CQL strings. Create a new parser with \n- * the <OpenLayers.Format.CQL> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format>\n- */\n-OpenLayers.Format.CQL = (function() {\n-\n- var tokens = [\n- \"PROPERTY\", \"COMPARISON\", \"VALUE\", \"LOGICAL\"\n- ],\n-\n- patterns = {\n- PROPERTY: /^[_a-zA-Z]\\w*/,\n- COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i,\n- IS_NULL: /^IS NULL/i,\n- COMMA: /^,/,\n- LOGICAL: /^(AND|OR)/i,\n- VALUE: /^('([^']|'')*'|\\d+(\\.\\d*)?|\\.\\d+)/,\n- LPAREN: /^\\(/,\n- RPAREN: /^\\)/,\n- SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i,\n- NOT: /^NOT/i,\n- BETWEEN: /^BETWEEN/i,\n- GEOMETRY: function(text) {\n- var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text);\n- if (type) {\n- var len = text.length;\n- var idx = text.indexOf(\"(\", type[0].length);\n- if (idx > -1) {\n- var depth = 1;\n- while (idx < len && depth > 0) {\n- idx++;\n- switch (text.charAt(idx)) {\n- case '(':\n- depth++;\n- break;\n- case ')':\n- depth--;\n- break;\n- default:\n- // in default case, do nothing\n- }\n- }\n- }\n- return [text.substr(0, idx + 1)];\n- }\n- },\n- END: /^$/\n- },\n-\n- follows = {\n- LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'],\n- RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'],\n- PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'],\n- BETWEEN: ['VALUE'],\n- IS_NULL: ['END'],\n- COMPARISON: ['VALUE'],\n- COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'],\n- VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'],\n- SPATIAL: ['LPAREN'],\n- LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'],\n- NOT: ['PROPERTY', 'LPAREN'],\n- GEOMETRY: ['COMMA', 'RPAREN']\n- },\n-\n- operators = {\n- '=': OpenLayers.Filter.Comparison.EQUAL_TO,\n- '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO,\n- '<': OpenLayers.Filter.Comparison.LESS_THAN,\n- '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,\n- '>': OpenLayers.Filter.Comparison.GREATER_THAN,\n- '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,\n- 'LIKE': OpenLayers.Filter.Comparison.LIKE,\n- 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN,\n- 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL\n- },\n-\n- operatorReverse = {},\n-\n- logicals = {\n- 'AND': OpenLayers.Filter.Logical.AND,\n- 'OR': OpenLayers.Filter.Logical.OR\n- },\n-\n- logicalReverse = {},\n-\n- precedence = {\n- 'RPAREN': 3,\n- 'LOGICAL': 2,\n- 'COMPARISON': 1\n- };\n-\n- var i;\n- for (i in operators) {\n- if (operators.hasOwnProperty(i)) {\n- operatorReverse[operators[i]] = i;\n- }\n- }\n-\n- for (i in logicals) {\n- if (logicals.hasOwnProperty(i)) {\n- logicalReverse[logicals[i]] = i;\n- }\n- }\n-\n- function tryToken(text, pattern) {\n- if (pattern instanceof RegExp) {\n- return pattern.exec(text);\n- } else {\n- return pattern(text);\n- }\n- }\n-\n- function nextToken(text, tokens) {\n- var i, token, len = tokens.length;\n- for (i = 0; i < len; i++) {\n- token = tokens[i];\n- var pat = patterns[token];\n- var matches = tryToken(text, pat);\n- if (matches) {\n- var match = matches[0];\n- var remainder = text.substr(match.length).replace(/^\\s*/, \"\");\n- return {\n- type: token,\n- text: match,\n- remainder: remainder\n- };\n- }\n- }\n-\n- var msg = \"ERROR: In parsing: [\" + text + \"], expected one of: \";\n- for (i = 0; i < len; i++) {\n- token = tokens[i];\n- msg += \"\\n \" + token + \": \" + patterns[token];\n- }\n-\n- throw new Error(msg);\n- }\n-\n- function tokenize(text) {\n- var results = [];\n- var token, expect = [\"NOT\", \"GEOMETRY\", \"SPATIAL\", \"PROPERTY\", \"LPAREN\"];\n-\n- do {\n- token = nextToken(text, expect);\n- text = token.remainder;\n- expect = follows[token.type];\n- if (token.type != \"END\" && !expect) {\n- throw new Error(\"No follows list for \" + token.type);\n- }\n- results.push(token);\n- } while (token.type != \"END\");\n-\n- return results;\n- }\n-\n- function buildAst(tokens) {\n- var operatorStack = [],\n- postfix = [];\n-\n- while (tokens.length) {\n- var tok = tokens.shift();\n- switch (tok.type) {\n- case \"PROPERTY\":\n- case \"GEOMETRY\":\n- case \"VALUE\":\n- postfix.push(tok);\n- break;\n- case \"COMPARISON\":\n- case \"BETWEEN\":\n- case \"IS_NULL\":\n- case \"LOGICAL\":\n- var p = precedence[tok.type];\n-\n- while (operatorStack.length > 0 &&\n- (precedence[operatorStack[operatorStack.length - 1].type] <= p)\n- ) {\n- postfix.push(operatorStack.pop());\n- }\n-\n- operatorStack.push(tok);\n- break;\n- case \"SPATIAL\":\n- case \"NOT\":\n- case \"LPAREN\":\n- operatorStack.push(tok);\n- break;\n- case \"RPAREN\":\n- while (operatorStack.length > 0 &&\n- (operatorStack[operatorStack.length - 1].type != \"LPAREN\")\n- ) {\n- postfix.push(operatorStack.pop());\n- }\n- operatorStack.pop(); // toss out the LPAREN\n-\n- if (operatorStack.length > 0 &&\n- operatorStack[operatorStack.length - 1].type == \"SPATIAL\") {\n- postfix.push(operatorStack.pop());\n- }\n- case \"COMMA\":\n- case \"END\":\n- break;\n- default:\n- throw new Error(\"Unknown token type \" + tok.type);\n- }\n- }\n-\n- while (operatorStack.length > 0) {\n- postfix.push(operatorStack.pop());\n- }\n-\n- function buildTree() {\n- var tok = postfix.pop();\n- switch (tok.type) {\n- case \"LOGICAL\":\n- var rhs = buildTree(),\n- lhs = buildTree();\n- return new OpenLayers.Filter.Logical({\n- filters: [lhs, rhs],\n- type: logicals[tok.text.toUpperCase()]\n- });\n- case \"NOT\":\n- var operand = buildTree();\n- return new OpenLayers.Filter.Logical({\n- filters: [operand],\n- type: OpenLayers.Filter.Logical.NOT\n- });\n- case \"BETWEEN\":\n- var min, max, property;\n- postfix.pop(); // unneeded AND token here\n- max = buildTree();\n- min = buildTree();\n- property = buildTree();\n- return new OpenLayers.Filter.Comparison({\n- property: property,\n- lowerBoundary: min,\n- upperBoundary: max,\n- type: OpenLayers.Filter.Comparison.BETWEEN\n- });\n- case \"COMPARISON\":\n- var value = buildTree(),\n- property = buildTree();\n- return new OpenLayers.Filter.Comparison({\n- property: property,\n- value: value,\n- type: operators[tok.text.toUpperCase()]\n- });\n- case \"IS_NULL\":\n- var property = buildTree();\n- return new OpenLayers.Filter.Comparison({\n- property: property,\n- type: operators[tok.text.toUpperCase()]\n- });\n- case \"VALUE\":\n- var match = tok.text.match(/^'(.*)'$/);\n- if (match) {\n- return match[1].replace(/''/g, \"'\");\n- } else {\n- return Number(tok.text);\n- }\n- case \"SPATIAL\":\n- switch (tok.text.toUpperCase()) {\n- case \"BBOX\":\n- var maxy = buildTree(),\n- maxx = buildTree(),\n- miny = buildTree(),\n- minx = buildTree(),\n- prop = buildTree();\n-\n- return new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.BBOX,\n- property: prop,\n- value: OpenLayers.Bounds.fromArray(\n- [minx, miny, maxx, maxy]\n- )\n- });\n- case \"INTERSECTS\":\n- var value = buildTree(),\n- property = buildTree();\n- return new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.INTERSECTS,\n- property: property,\n- value: value\n- });\n- case \"WITHIN\":\n- var value = buildTree(),\n- property = buildTree();\n- return new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.WITHIN,\n- property: property,\n- value: value\n- });\n- case \"CONTAINS\":\n- var value = buildTree(),\n- property = buildTree();\n- return new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.CONTAINS,\n- property: property,\n- value: value\n- });\n- case \"DWITHIN\":\n- var distance = buildTree(),\n- value = buildTree(),\n- property = buildTree();\n- return new OpenLayers.Filter.Spatial({\n- type: OpenLayers.Filter.Spatial.DWITHIN,\n- value: value,\n- property: property,\n- distance: Number(distance)\n- });\n- }\n- case \"GEOMETRY\":\n- return OpenLayers.Geometry.fromWKT(tok.text);\n- default:\n- return tok.text;\n- }\n- }\n-\n- var result = buildTree();\n- if (postfix.length > 0) {\n- var msg = \"Remaining tokens after building AST: \\n\";\n- for (var i = postfix.length - 1; i >= 0; i--) {\n- msg += postfix[i].type + \": \" + postfix[i].text + \"\\n\";\n- }\n- throw new Error(msg);\n- }\n-\n- return result;\n- }\n-\n- return OpenLayers.Class(OpenLayers.Format, {\n- /**\n- * APIMethod: read\n- * Generate a filter from a CQL string.\n- \n- * Parameters:\n- * text - {String} The CQL text.\n- *\n- * Returns:\n- * {<OpenLayers.Filter>} A filter based on the CQL text.\n- */\n- read: function(text) {\n- var result = buildAst(tokenize(text));\n- if (this.keepData) {\n- this.data = result;\n- }\n- return result;\n- },\n-\n- /**\n- * APIMethod: write\n- * Convert a filter into a CQL string.\n- \n- * Parameters:\n- * filter - {<OpenLayers.Filter>} The filter.\n- *\n- * Returns:\n- * {String} A CQL string based on the filter.\n- */\n- write: function(filter) {\n- if (filter instanceof OpenLayers.Geometry) {\n- return filter.toString();\n- }\n- switch (filter.CLASS_NAME) {\n- case \"OpenLayers.Filter.Spatial\":\n- switch (filter.type) {\n- case OpenLayers.Filter.Spatial.BBOX:\n- return \"BBOX(\" +\n- filter.property + \",\" +\n- filter.value.toBBOX() +\n- \")\";\n- case OpenLayers.Filter.Spatial.DWITHIN:\n- return \"DWITHIN(\" +\n- filter.property + \", \" +\n- this.write(filter.value) + \", \" +\n- filter.distance + \")\";\n- case OpenLayers.Filter.Spatial.WITHIN:\n- return \"WITHIN(\" +\n- filter.property + \", \" +\n- this.write(filter.value) + \")\";\n- case OpenLayers.Filter.Spatial.INTERSECTS:\n- return \"INTERSECTS(\" +\n- filter.property + \", \" +\n- this.write(filter.value) + \")\";\n- case OpenLayers.Filter.Spatial.CONTAINS:\n- return \"CONTAINS(\" +\n- filter.property + \", \" +\n- this.write(filter.value) + \")\";\n- default:\n- throw new Error(\"Unknown spatial filter type: \" + filter.type);\n- }\n- case \"OpenLayers.Filter.Logical\":\n- if (filter.type == OpenLayers.Filter.Logical.NOT) {\n- // TODO: deal with precedence of logical operators to \n- // avoid extra parentheses (not urgent)\n- return \"NOT (\" + this.write(filter.filters[0]) + \")\";\n- } else {\n- var res = \"(\";\n- var first = true;\n- for (var i = 0; i < filter.filters.length; i++) {\n- if (first) {\n- first = false;\n- } else {\n- res += \") \" + logicalReverse[filter.type] + \" (\";\n- }\n- res += this.write(filter.filters[i]);\n- }\n- return res + \")\";\n- }\n- case \"OpenLayers.Filter.Comparison\":\n- if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) {\n- return filter.property + \" BETWEEN \" +\n- this.write(filter.lowerBoundary) + \" AND \" +\n- this.write(filter.upperBoundary);\n- } else {\n- return (filter.value !== null) ? filter.property +\n- \" \" + operatorReverse[filter.type] + \" \" +\n- this.write(filter.value) : filter.property +\n- \" \" + operatorReverse[filter.type];\n- }\n- case undefined:\n- if (typeof filter === \"string\") {\n- return \"'\" + filter.replace(/'/g, \"''\") + \"'\";\n- } else if (typeof filter === \"number\") {\n- return String(filter);\n- }\n- default:\n- throw new Error(\"Can't encode: \" + filter.CLASS_NAME + \" \" + filter);\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.CQL\"\n-\n- });\n-})();\n-\n-/* ======================================================================\n- OpenLayers/Format/WFSDescribeFeatureType.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/OGCExceptionReport.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WFSDescribeFeatureType\n- * Read WFS DescribeFeatureType response\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g)\n- },\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- xsd: \"http://www.w3.org/2001/XMLSchema\"\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WFSDescribeFeatureType\n- * Create a new parser for WFS DescribeFeatureType responses.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"xsd\": {\n- \"schema\": function(node, obj) {\n- var complexTypes = [];\n- var customTypes = {};\n- var schema = {\n- complexTypes: complexTypes,\n- customTypes: customTypes\n- };\n- var i, len;\n-\n- this.readChildNodes(node, schema);\n-\n- var attributes = node.attributes;\n- var attr, name;\n- for (i = 0, len = attributes.length; i < len; ++i) {\n- attr = attributes[i];\n- name = attr.name;\n- if (name.indexOf(\"xmlns\") === 0) {\n- this.setNamespace(name.split(\":\")[1] || \"\", attr.value);\n- } else {\n- obj[name] = attr.value;\n- }\n- }\n- obj.featureTypes = complexTypes;\n- obj.targetPrefix = this.namespaceAlias[obj.targetNamespace];\n-\n- // map complexTypes to names of customTypes\n- var complexType, customType;\n- for (i = 0, len = complexTypes.length; i < len; ++i) {\n- complexType = complexTypes[i];\n- customType = customTypes[complexType.typeName];\n- if (customTypes[complexType.typeName]) {\n- complexType.typeName = customType.name;\n- }\n- }\n- },\n- \"complexType\": function(node, obj) {\n- var complexType = {\n- // this is a temporary typeName, it will be overwritten by\n- // the schema reader with the metadata found in the\n- // customTypes hash\n- \"typeName\": node.getAttribute(\"name\")\n- };\n- this.readChildNodes(node, complexType);\n- obj.complexTypes.push(complexType);\n- },\n- \"complexContent\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"extension\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"sequence\": function(node, obj) {\n- var sequence = {\n- elements: []\n- };\n- this.readChildNodes(node, sequence);\n- obj.properties = sequence.elements;\n- },\n- \"element\": function(node, obj) {\n- var type;\n- if (obj.elements) {\n- var element = {};\n- var attributes = node.attributes;\n- var attr;\n- for (var i = 0, len = attributes.length; i < len; ++i) {\n- attr = attributes[i];\n- element[attr.name] = attr.value;\n- }\n-\n- type = element.type;\n- if (!type) {\n- type = {};\n- this.readChildNodes(node, type);\n- element.restriction = type;\n- element.type = type.base;\n- }\n- var fullType = type.base || type;\n- element.localType = fullType.split(\":\").pop();\n- obj.elements.push(element);\n- this.readChildNodes(node, element);\n- }\n-\n- if (obj.complexTypes) {\n- type = node.getAttribute(\"type\");\n- var localType = type.split(\":\").pop();\n- obj.customTypes[localType] = {\n- \"name\": node.getAttribute(\"name\"),\n- \"type\": type\n- };\n- }\n- },\n- \"annotation\": function(node, obj) {\n- obj.annotation = {};\n- this.readChildNodes(node, obj.annotation);\n- },\n- \"appinfo\": function(node, obj) {\n- if (!obj.appinfo) {\n- obj.appinfo = [];\n- }\n- obj.appinfo.push(this.getChildValue(node));\n- },\n- \"documentation\": function(node, obj) {\n- if (!obj.documentation) {\n- obj.documentation = [];\n- }\n- var value = this.getChildValue(node);\n- obj.documentation.push({\n- lang: node.getAttribute(\"xml:lang\"),\n- textContent: value.replace(this.regExes.trimSpace, \"\")\n- });\n- },\n- \"simpleType\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"restriction\": function(node, obj) {\n- obj.base = node.getAttribute(\"base\");\n- this.readRestriction(node, obj);\n- }\n- }\n- },\n-\n- /**\n- * Method: readRestriction\n- * Reads restriction defined in the child nodes of a restriction element\n- * \n- * Parameters:\n- * node - {DOMElement} the node to parse\n- * obj - {Object} the object that receives the read result\n- */\n- readRestriction: function(node, obj) {\n- var children = node.childNodes;\n- var child, nodeName, value;\n- for (var i = 0, len = children.length; i < len; ++i) {\n- child = children[i];\n- if (child.nodeType == 1) {\n- nodeName = child.nodeName.split(\":\").pop();\n- value = child.getAttribute(\"value\");\n- if (!obj[nodeName]) {\n- obj[nodeName] = value;\n- } else {\n- if (typeof obj[nodeName] == \"string\") {\n- obj[nodeName] = [obj[nodeName]];\n- }\n- obj[nodeName].push(value);\n- }\n- }\n- }\n- },\n-\n- /**\n- * Method: read\n- *\n- * Parameters:\n- * data - {DOMElement|String} A WFS DescribeFeatureType document.\n- *\n- * Returns:\n- * {Object} An object representing the WFS DescribeFeatureType response.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var schema = {};\n- if (data.nodeName.split(\":\").pop() === 'ExceptionReport') {\n- // an exception must have occurred, so parse it\n- var parser = new OpenLayers.Format.OGCExceptionReport();\n- schema.error = parser.read(data);\n- } else {\n- this.readNode(data, schema);\n- }\n- return schema;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WFSDescribeFeatureType\"\n-\n- });\n-/* ======================================================================\n OpenLayers/Format/SLD.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -59189,313 +51865,145 @@\n * Returns:\n * {Object} An object representing the SLD.\n */\n \n CLASS_NAME: \"OpenLayers.Format.SLD\"\n });\n /* ======================================================================\n- OpenLayers/Format/WFS.js\n+ OpenLayers/Format/CSWGetDomain.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/GML.js\n- * @requires OpenLayers/Console.js\n- * @requires OpenLayers/Lang.js\n+ * @requires OpenLayers/Format.js\n */\n \n /**\n- * Class: OpenLayers.Format.WFS\n- * Read/Write WFS. \n+ * Class: OpenLayers.Format.CSWGetDomain\n+ * Default version is 2.0.2.\n *\n- * Inherits from:\n- * - <OpenLayers.Format.GML>\n+ * Returns:\n+ * {<OpenLayers.Format>} A CSWGetDomain format of the given version.\n */\n-OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {\n-\n- /** \n- * Property: layer\n- * {<OpenLayers.Layer>}\n- */\n- layer: null,\n-\n- /**\n- * APIProperty: wfsns\n- * {String}\n- */\n- wfsns: \"http://www.opengis.net/wfs\",\n-\n- /**\n- * Property: ogcns\n- * {String}\n- */\n- ogcns: \"http://www.opengis.net/ogc\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WFS\n- * Create a WFS-T formatter. This requires a layer: that layer should\n- * have two properties: geometry_column and typename. The parser\n- * for this format is subclassed entirely from GML: There is a writer \n- * only, which uses most of the code from the GML layer, and wraps\n- * it in transactional elements.\n- * \n- * Parameters: \n- * options - {Object} \n- * layer - {<OpenLayers.Layer>} \n- */\n- initialize: function(options, layer) {\n- OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);\n- this.layer = layer;\n- if (this.layer.featureNS) {\n- this.featureNS = this.layer.featureNS;\n- }\n- if (this.layer.options.geometry_column) {\n- this.geometryName = this.layer.options.geometry_column;\n- }\n- if (this.layer.options.typename) {\n- this.featureName = this.layer.options.typename;\n- }\n- },\n-\n- /**\n- * Method: write \n- * Takes a feature list, and generates a WFS-T Transaction \n- *\n- * Parameters:\n- * features - {Array(<OpenLayers.Feature.Vector>)} \n- */\n- write: function(features) {\n-\n- var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');\n- transaction.setAttribute(\"version\", \"1.0.0\");\n- transaction.setAttribute(\"service\", \"WFS\");\n- for (var i = 0; i < features.length; i++) {\n- switch (features[i].state) {\n- case OpenLayers.State.INSERT:\n- transaction.appendChild(this.insert(features[i]));\n- break;\n- case OpenLayers.State.UPDATE:\n- transaction.appendChild(this.update(features[i]));\n- break;\n- case OpenLayers.State.DELETE:\n- transaction.appendChild(this.remove(features[i]));\n- break;\n- }\n- }\n-\n- return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]);\n- },\n-\n- /**\n- * Method: createFeatureXML\n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>}\n- */\n- createFeatureXML: function(feature) {\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n- var geomContainer = this.createElementNS(this.featureNS, \"feature:\" + this.geometryName);\n- geomContainer.appendChild(geometryNode);\n- var featureContainer = this.createElementNS(this.featureNS, \"feature:\" + this.featureName);\n- featureContainer.appendChild(geomContainer);\n- for (var attr in feature.attributes) {\n- var attrText = this.createTextNode(feature.attributes[attr]);\n- var nodename = attr;\n- if (attr.search(\":\") != -1) {\n- nodename = attr.split(\":\")[1];\n- }\n- var attrContainer = this.createElementNS(this.featureNS, \"feature:\" + nodename);\n- attrContainer.appendChild(attrText);\n- featureContainer.appendChild(attrContainer);\n- }\n- return featureContainer;\n- },\n-\n- /**\n- * Method: insert \n- * Takes a feature, and generates a WFS-T Transaction \"Insert\" \n- *\n- * Parameters: \n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- insert: function(feature) {\n- var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');\n- insertNode.appendChild(this.createFeatureXML(feature));\n- return insertNode;\n- },\n-\n- /**\n- * Method: update\n- * Takes a feature, and generates a WFS-T Transaction \"Update\" \n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- update: function(feature) {\n- if (!feature.fid) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"noFID\"));\n- }\n- var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');\n- updateNode.setAttribute(\"typeName\", this.featurePrefix + ':' + this.featureName);\n- updateNode.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n-\n- var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');\n- var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');\n-\n- var txtNode = this.createTextNode(this.geometryName);\n- nameNode.appendChild(txtNode);\n- propertyNode.appendChild(nameNode);\n-\n- var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');\n-\n- var geometryNode = this.buildGeometryNode(feature.geometry);\n-\n- if (feature.layer) {\n- geometryNode.setAttribute(\n- \"srsName\", feature.layer.projection.getCode()\n- );\n- }\n-\n- valueNode.appendChild(geometryNode);\n-\n- propertyNode.appendChild(valueNode);\n- updateNode.appendChild(propertyNode);\n-\n- // add in attributes\n- for (var propName in feature.attributes) {\n- propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');\n- nameNode = this.createElementNS(this.wfsns, 'wfs:Name');\n- nameNode.appendChild(this.createTextNode(propName));\n- propertyNode.appendChild(nameNode);\n- valueNode = this.createElementNS(this.wfsns, 'wfs:Value');\n- valueNode.appendChild(this.createTextNode(feature.attributes[propName]));\n- propertyNode.appendChild(valueNode);\n- updateNode.appendChild(propertyNode);\n- }\n-\n-\n- var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');\n- var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');\n- filterIdNode.setAttribute(\"fid\", feature.fid);\n- filterNode.appendChild(filterIdNode);\n- updateNode.appendChild(filterNode);\n-\n- return updateNode;\n- },\n+OpenLayers.Format.CSWGetDomain = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Format.CSWGetDomain.DEFAULTS\n+ );\n+ var cls = OpenLayers.Format.CSWGetDomain[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetDomain version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n \n- /**\n- * Method: remove \n- * Takes a feature, and generates a WFS-T Transaction \"Delete\" \n- *\n- * Parameters:\n- * feature - {<OpenLayers.Feature.Vector>} \n- */\n- remove: function(feature) {\n- if (!feature.fid) {\n- OpenLayers.Console.userError(OpenLayers.i18n(\"noFID\"));\n- return false;\n- }\n- var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');\n- deleteNode.setAttribute(\"typeName\", this.featurePrefix + ':' + this.featureName);\n- deleteNode.setAttribute(\"xmlns:\" + this.featurePrefix, this.featureNS);\n+/**\n+ * Constant: DEFAULTS\n+ * {Object} Default properties for the CSWGetDomain format.\n+ */\n+OpenLayers.Format.CSWGetDomain.DEFAULTS = {\n+ \"version\": \"2.0.2\"\n+};\n+/* ======================================================================\n+ OpenLayers/Format/CSWGetRecords.js\n+ ====================================================================== */\n \n- var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');\n- var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');\n- filterIdNode.setAttribute(\"fid\", feature.fid);\n- filterNode.appendChild(filterIdNode);\n- deleteNode.appendChild(filterNode);\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- return deleteNode;\n- },\n+/**\n+ * @requires OpenLayers/Format.js\n+ */\n \n- /**\n- * APIMethod: destroy\n- * Remove ciruclar ref to layer \n- */\n- destroy: function() {\n- this.layer = null;\n- },\n+/**\n+ * Class: OpenLayers.Format.CSWGetRecords\n+ * Default version is 2.0.2.\n+ *\n+ * Returns:\n+ * {<OpenLayers.Format>} A CSWGetRecords format of the given version.\n+ */\n+OpenLayers.Format.CSWGetRecords = function(options) {\n+ options = OpenLayers.Util.applyDefaults(\n+ options, OpenLayers.Format.CSWGetRecords.DEFAULTS\n+ );\n+ var cls = OpenLayers.Format.CSWGetRecords[\"v\" + options.version.replace(/\\./g, \"_\")];\n+ if (!cls) {\n+ throw \"Unsupported CSWGetRecords version: \" + options.version;\n+ }\n+ return new cls(options);\n+};\n \n- CLASS_NAME: \"OpenLayers.Format.WFS\"\n-});\n+/**\n+ * Constant: DEFAULTS\n+ * {Object} Default properties for the CSWGetRecords format.\n+ */\n+OpenLayers.Format.CSWGetRecords.DEFAULTS = {\n+ \"version\": \"2.0.2\"\n+};\n /* ======================================================================\n- OpenLayers/Format/XLS.js\n+ OpenLayers/Format/WMSDescribeLayer.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n * @requires OpenLayers/Format/XML/VersionedOGC.js\n */\n \n /**\n- * Class: OpenLayers.Format.XLS\n- * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS>\n- * constructor. Currently only implemented for Location Utility Services, more\n- * specifically only for Geocoding. No support for Reverse Geocoding as yet.\n+ * Class: OpenLayers.Format.WMSDescribeLayer\n+ * Read SLD WMS DescribeLayer response\n+ * DescribeLayer is meant to couple WMS to WFS and WCS\n * \n * Inherits from:\n * - <OpenLayers.Format.XML.VersionedOGC>\n */\n-OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n+OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {\n \n /**\n * APIProperty: defaultVersion\n- * {String} Version number to assume if none found. Default is \"1.1.0\".\n- */\n- defaultVersion: \"1.1.0\",\n-\n- /**\n- * APIProperty: stringifyOutput\n- * {Boolean} If true, write will return a string otherwise a DOMElement.\n- * Default is true.\n+ * {String} Version number to assume if none found. Default is \"1.1.1\".\n */\n- stringifyOutput: true,\n+ defaultVersion: \"1.1.1\",\n \n /**\n- * Constructor: OpenLayers.Format.XLS\n- * Create a new parser for XLS.\n+ * Constructor: OpenLayers.Format.WMSDescribeLayer\n+ * Create a new parser for WMS DescribeLayer responses.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n \n /**\n- * APIMethod: write\n- * Write out an XLS request.\n- *\n- * Parameters:\n- * request - {Object} An object representing the LUS request.\n- * options - {Object} Optional configuration object.\n- *\n- * Returns:\n- * {String} An XLS document string.\n- */\n-\n- /**\n * APIMethod: read\n- * Read an XLS doc and return an object representing the result.\n- *\n- * Parameters:\n- * data - {String | DOMElement} Data to read.\n- * options - {Object} Options for the reader.\n+ * Read DescribeLayer data from a string, and return the response. \n+ * The OGC currently defines 2 formats which are allowed for output,\n+ * so we need to parse these 2 types\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n *\n * Returns:\n- * {Object} An object representing the GeocodeResponse.\n+ * {Array} Array of {<LayerDescription>} objects which have:\n+ * - {String} owsType: WFS/WCS\n+ * - {String} owsURL: the online resource\n+ * - {String} typeName: the name of the typename on the service\n */\n \n- CLASS_NAME: \"OpenLayers.Format.XLS\"\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer\"\n+\n });\n /* ======================================================================\n OpenLayers/Format/GML/v2.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n@@ -59882,485 +52390,305 @@\n },\n \n \n CLASS_NAME: \"OpenLayers.Format.Filter.v1_0_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/CSWGetRecords/v2_0_2.js\n+ OpenLayers/Format/WMSDescribeLayer/v1_1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/CSWGetRecords.js\n- * @requires OpenLayers/Format/Filter/v1_0_0.js\n- * @requires OpenLayers/Format/Filter/v1_1_0.js\n- * @requires OpenLayers/Format/OWSCommon/v1_0_0.js\n+ * @requires OpenLayers/Format/WMSDescribeLayer.js\n+ * @requires OpenLayers/Format/OGCExceptionReport.js\n */\n \n /**\n- * Class: OpenLayers.Format.CSWGetRecords.v2_0_2\n- * A format for creating CSWGetRecords v2.0.2 transactions. \n- * Create a new instance with the\n- * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor.\n+ * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1\n+ * Read SLD WMS DescribeLayer response for WMS 1.1.X\n+ * WMS 1.1.X is tightly coupled to SLD 1.0.0\n+ *\n+ * Example DescribeLayer request: \n+ * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states\n *\n * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * - <OpenLayers.Format.WMSDescribeLayer>\n */\n-OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\",\n- dc: \"http://purl.org/dc/elements/1.1/\",\n- dct: \"http://purl.org/dc/terms/\",\n- gmd: \"http://www.isotc211.org/2005/gmd\",\n- geonet: \"http://www.fao.org/geonetwork\",\n- ogc: \"http://www.opengis.net/ogc\",\n- ows: \"http://www.opengis.net/ows\",\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\"\n- },\n-\n- /**\n- * Property: defaultPrefix\n- * {String} The default prefix (used by Format.XML).\n- */\n- defaultPrefix: \"csw\",\n-\n- /**\n- * Property: version\n- * {String} CSW version number.\n- */\n- version: \"2.0.2\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} http://www.opengis.net/cat/csw/2.0.2\n- * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n- */\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n-\n- /**\n- * APIProperty: requestId\n- * {String} Value of the requestId attribute of the GetRecords element.\n- */\n- requestId: null,\n-\n- /**\n- * APIProperty: resultType\n- * {String} Value of the resultType attribute of the GetRecords element,\n- * specifies the result type in the GetRecords response, \"hits\" is\n- * the default.\n- */\n- resultType: null,\n+OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(\n+ OpenLayers.Format.WMSDescribeLayer, {\n \n- /**\n- * APIProperty: outputFormat\n- * {String} Value of the outputFormat attribute of the GetRecords element,\n- * specifies the format of the GetRecords response,\n- * \"application/xml\" is the default.\n- */\n- outputFormat: null,\n+ /**\n+ * Constructor: OpenLayers.Format.WMSDescribeLayer\n+ * Create a new parser for WMS DescribeLayer responses.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,\n+ [options]);\n+ },\n \n- /**\n- * APIProperty: outputSchema\n- * {String} Value of the outputSchema attribute of the GetRecords element,\n- * specifies the schema of the GetRecords response.\n- */\n- outputSchema: null,\n+ /**\n+ * APIMethod: read\n+ * Read DescribeLayer data from a string, and return the response. \n+ * The OGC defines 2 formats which are allowed for output,\n+ * so we need to parse these 2 types for version 1.1.X\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Object with a layerDescriptions property, which holds an Array\n+ * of {<LayerDescription>} objects which have:\n+ * - {String} owsType: WFS/WCS\n+ * - {String} owsURL: the online resource\n+ * - {String} typeName: the name of the typename on the owsType service\n+ * - {String} layerName: the name of the WMS layer we did a lookup for\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ var root = data.documentElement;\n+ var children = root.childNodes;\n+ var describelayer = {\n+ layerDescriptions: []\n+ };\n+ var childNode, nodeName;\n+ for (var i = 0; i < children.length; ++i) {\n+ childNode = children[i];\n+ nodeName = childNode.nodeName;\n+ if (nodeName == 'LayerDescription') {\n+ var layerName = childNode.getAttribute('name');\n+ var owsType = '';\n+ var owsURL = '';\n+ var typeName = '';\n+ // check for owsType and owsURL attributes\n+ if (childNode.getAttribute('owsType')) {\n+ owsType = childNode.getAttribute('owsType');\n+ owsURL = childNode.getAttribute('owsURL');\n+ } else {\n+ // look for wfs or wcs attribute\n+ if (childNode.getAttribute('wfs') != '') {\n+ owsType = 'WFS';\n+ owsURL = childNode.getAttribute('wfs');\n+ } else if (childNode.getAttribute('wcs') != '') {\n+ owsType = 'WCS';\n+ owsURL = childNode.getAttribute('wcs');\n+ }\n+ }\n+ // look for Query child\n+ var query = childNode.getElementsByTagName('Query');\n+ if (query.length > 0) {\n+ typeName = query[0].getAttribute('typeName');\n+ if (!typeName) {\n+ // because of Ionic bug\n+ typeName = query[0].getAttribute('typename');\n+ }\n+ }\n+ var layerDescription = {\n+ layerName: layerName,\n+ owsType: owsType,\n+ owsURL: owsURL,\n+ typeName: typeName\n+ };\n+ describelayer.layerDescriptions.push(layerDescription);\n \n- /**\n- * APIProperty: startPosition\n- * {String} Value of the startPosition attribute of the GetRecords element,\n- * specifies the start position (offset+1) for the GetRecords response,\n- * 1 is the default.\n- */\n- startPosition: null,\n+ //TODO do this in deprecated.js instead:\n+ // array style index for backwards compatibility\n+ describelayer.length = describelayer.layerDescriptions.length;\n+ describelayer[describelayer.length - 1] = layerDescription;\n \n- /**\n- * APIProperty: maxRecords\n- * {String} Value of the maxRecords attribute of the GetRecords element,\n- * specifies the maximum number of records in the GetRecords response,\n- * 10 is the default.\n- */\n- maxRecords: null,\n+ } else if (nodeName == 'ServiceException') {\n+ // an exception must have occurred, so parse it\n+ var parser = new OpenLayers.Format.OGCExceptionReport();\n+ return {\n+ error: parser.read(data)\n+ };\n+ }\n+ }\n+ return describelayer;\n+ },\n \n- /**\n- * APIProperty: DistributedSearch\n- * {String} Value of the csw:DistributedSearch element, used when writing\n- * a csw:GetRecords document.\n- */\n- DistributedSearch: null,\n+ CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n \n- /**\n- * APIProperty: ResponseHandler\n- * {Array({String})} Values of the csw:ResponseHandler elements, used when\n- * writting a csw:GetRecords document.\n- */\n- ResponseHandler: null,\n+ });\n \n- /**\n- * APIProperty: Query\n- * {String} Value of the csw:Query element, used when writing a csw:GetRecords\n- * document.\n- */\n- Query: null,\n+// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257\n+OpenLayers.Format.WMSDescribeLayer.v1_1_0 =\n+ OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n+/* ======================================================================\n+ OpenLayers/Format/SOSCapabilities/v1_0_0.js\n+ ====================================================================== */\n \n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n \n- /**\n- * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2\n- * A class for parsing and generating CSWGetRecords v2.0.2 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties (documented as class properties):\n- * - requestId\n- * - resultType\n- * - outputFormat\n- * - outputSchema\n- * - startPosition\n- * - maxRecords\n- * - DistributedSearch\n- * - ResponseHandler\n- * - Query\n- */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n+/**\n+ * @requires OpenLayers/Format/SOSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/GML/v3.js\n+ */\n \n- /**\n- * APIMethod: read\n- * Parse the response from a GetRecords request.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj;\n- },\n+/**\n+ * Class: OpenLayers.Format.SOSCapabilities.v1_0_0\n+ * Read SOS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.SOSCapabilities>\n+ */\n+OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.SOSCapabilities, {\n \n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"csw\": {\n- \"GetRecordsResponse\": function(node, obj) {\n- obj.records = [];\n- this.readChildNodes(node, obj);\n- var version = this.getAttributeNS(node, \"\", 'version');\n- if (version != \"\") {\n- obj.version = version;\n- }\n- },\n- \"RequestId\": function(node, obj) {\n- obj.RequestId = this.getChildValue(node);\n- },\n- \"SearchStatus\": function(node, obj) {\n- obj.SearchStatus = {};\n- var timestamp = this.getAttributeNS(node, \"\", 'timestamp');\n- if (timestamp != \"\") {\n- obj.SearchStatus.timestamp = timestamp;\n- }\n- },\n- \"SearchResults\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- var attrs = node.attributes;\n- var SearchResults = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- if ((attrs[i].name == \"numberOfRecordsMatched\") ||\n- (attrs[i].name == \"numberOfRecordsReturned\") ||\n- (attrs[i].name == \"nextRecord\")) {\n- SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue);\n- } else {\n- SearchResults[attrs[i].name] = attrs[i].nodeValue;\n- }\n- }\n- obj.SearchResults = SearchResults;\n- },\n- \"SummaryRecord\": function(node, obj) {\n- var record = {\n- type: \"SummaryRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"BriefRecord\": function(node, obj) {\n- var record = {\n- type: \"BriefRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"DCMIRecord\": function(node, obj) {\n- var record = {\n- type: \"DCMIRecord\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"Record\": function(node, obj) {\n- var record = {\n- type: \"Record\"\n- };\n- this.readChildNodes(node, record);\n- obj.records.push(record);\n- },\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- obj[name] = this.getChildValue(node);\n- }\n- },\n- \"geonet\": {\n- \"info\": function(node, obj) {\n- var gninfo = {};\n- this.readChildNodes(node, gninfo);\n- obj.gninfo = gninfo;\n- }\n- },\n- \"dc\": {\n- // audience, contributor, coverage, creator, date, description, format,\n- // identifier, language, provenance, publisher, relation, rights,\n- // rightsHolder, source, subject, title, type, URI\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!(OpenLayers.Util.isArray(obj[name]))) {\n- obj[name] = [];\n- }\n- var dc_element = {};\n- var attrs = node.attributes;\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- dc_element[attrs[i].name] = attrs[i].nodeValue;\n- }\n- dc_element.value = this.getChildValue(node);\n- if (dc_element.value != \"\") {\n- obj[name].push(dc_element);\n- }\n- }\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ sos: \"http://www.opengis.net/sos/1.0\",\n+ gml: \"http://www.opengis.net/gml\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n },\n- \"dct\": {\n- // abstract, modified, spatial\n- \"*\": function(node, obj) {\n- var name = node.localName || node.nodeName.split(\":\").pop();\n- if (!(OpenLayers.Util.isArray(obj[name]))) {\n- obj[name] = [];\n- }\n- obj[name].push(this.getChildValue(node));\n- }\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n },\n- \"ows\": OpenLayers.Util.applyDefaults({\n- \"BoundingBox\": function(node, obj) {\n- if (obj.bounds) {\n- obj.BoundingBox = [{\n- crs: obj.projection,\n- value: [\n- obj.bounds.left,\n- obj.bounds.bottom,\n- obj.bounds.right,\n- obj.bounds.top\n- ]\n- }];\n- delete obj.projection;\n- delete obj.bounds;\n- }\n- OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"][\"BoundingBox\"].apply(\n- this, arguments);\n- }\n- }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers[\"ows\"])\n- },\n \n- /**\n- * Method: write\n- * Given an configuration js object, write a CSWGetRecords request. \n- *\n- * Parameters:\n- * options - {Object} A object mapping the request.\n- *\n- * Returns:\n- * {String} A serialized CSWGetRecords request.\n- */\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetRecords\", options);\n- node.setAttribute(\"xmlns:gmd\", this.namespaces.gmd);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n+ /**\n+ * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0\n+ * Create a new parser for SOS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options;\n+ },\n \n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"csw\": {\n- \"GetRecords\": function(options) {\n- if (!options) {\n- options = {};\n- }\n- var node = this.createElementNSPlus(\"csw:GetRecords\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version,\n- requestId: options.requestId || this.requestId,\n- resultType: options.resultType || this.resultType,\n- outputFormat: options.outputFormat || this.outputFormat,\n- outputSchema: options.outputSchema || this.outputSchema,\n- startPosition: options.startPosition || this.startPosition,\n- maxRecords: options.maxRecords || this.maxRecords\n- }\n- });\n- if (options.DistributedSearch || this.DistributedSearch) {\n- this.writeNode(\n- \"csw:DistributedSearch\",\n- options.DistributedSearch || this.DistributedSearch,\n- node\n- );\n- }\n- var ResponseHandler = options.ResponseHandler || this.ResponseHandler;\n- if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {\n- // ResponseHandler must be a non-empty array\n- for (var i = 0, len = ResponseHandler.length; i < len; i++) {\n- this.writeNode(\n- \"csw:ResponseHandler\",\n- ResponseHandler[i],\n- node\n- );\n- }\n- }\n- this.writeNode(\"Query\", options.Query || this.Query, node);\n- return node;\n- },\n- \"DistributedSearch\": function(options) {\n- var node = this.createElementNSPlus(\"csw:DistributedSearch\", {\n- attributes: {\n- hopCount: options.hopCount\n- }\n- });\n- return node;\n- },\n- \"ResponseHandler\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ResponseHandler\", {\n- value: options.value\n- });\n- return node;\n- },\n- \"Query\": function(options) {\n- if (!options) {\n- options = {};\n- }\n- var node = this.createElementNSPlus(\"csw:Query\", {\n- attributes: {\n- typeNames: options.typeNames || \"csw:Record\"\n- }\n- });\n- var ElementName = options.ElementName;\n- if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {\n- // ElementName must be a non-empty array\n- for (var i = 0, len = ElementName.length; i < len; i++) {\n- this.writeNode(\n- \"csw:ElementName\",\n- ElementName[i],\n- node\n- );\n- }\n- } else {\n- this.writeNode(\n- \"csw:ElementSetName\",\n- options.ElementSetName || {\n- value: 'summary'\n- },\n- node\n- );\n- }\n- if (options.Constraint) {\n- this.writeNode(\n- \"csw:Constraint\",\n- options.Constraint,\n- node\n- );\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the SOS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the SOS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"gml\": OpenLayers.Util.applyDefaults({\n+ \"name\": function(node, obj) {\n+ obj.name = this.getChildValue(node);\n+ },\n+ \"TimePeriod\": function(node, obj) {\n+ obj.timePeriod = {};\n+ this.readChildNodes(node, obj.timePeriod);\n+ },\n+ \"beginPosition\": function(node, timePeriod) {\n+ timePeriod.beginPosition = this.getChildValue(node);\n+ },\n+ \"endPosition\": function(node, timePeriod) {\n+ timePeriod.endPosition = this.getChildValue(node);\n }\n- if (options.SortBy) {\n- this.writeNode(\n- \"ogc:SortBy\",\n- options.SortBy,\n- node\n- );\n+ }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n+ \"sos\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Contents\": function(node, obj) {\n+ obj.contents = {};\n+ this.readChildNodes(node, obj.contents);\n+ },\n+ \"ObservationOfferingList\": function(node, contents) {\n+ contents.offeringList = {};\n+ this.readChildNodes(node, contents.offeringList);\n+ },\n+ \"ObservationOffering\": function(node, offeringList) {\n+ var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n+ offeringList[id] = {\n+ procedures: [],\n+ observedProperties: [],\n+ featureOfInterestIds: [],\n+ responseFormats: [],\n+ resultModels: [],\n+ responseModes: []\n+ };\n+ this.readChildNodes(node, offeringList[id]);\n+ },\n+ \"time\": function(node, offering) {\n+ offering.time = {};\n+ this.readChildNodes(node, offering.time);\n+ },\n+ \"procedure\": function(node, offering) {\n+ offering.procedures.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"observedProperty\": function(node, offering) {\n+ offering.observedProperties.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"featureOfInterest\": function(node, offering) {\n+ offering.featureOfInterestIds.push(this.getAttributeNS(node,\n+ this.namespaces.xlink, \"href\"));\n+ },\n+ \"responseFormat\": function(node, offering) {\n+ offering.responseFormats.push(this.getChildValue(node));\n+ },\n+ \"resultModel\": function(node, offering) {\n+ offering.resultModels.push(this.getChildValue(node));\n+ },\n+ \"responseMode\": function(node, offering) {\n+ offering.responseModes.push(this.getChildValue(node));\n }\n- return node;\n- },\n- \"ElementName\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementName\", {\n- value: options.value\n- });\n- return node;\n- },\n- \"ElementSetName\": function(options) {\n- var node = this.createElementNSPlus(\"csw:ElementSetName\", {\n- attributes: {\n- typeNames: options.typeNames\n- },\n- value: options.value\n- });\n- return node;\n },\n- \"Constraint\": function(options) {\n- var node = this.createElementNSPlus(\"csw:Constraint\", {\n- attributes: {\n- version: options.version\n- }\n- });\n- if (options.Filter) {\n- var format = new OpenLayers.Format.Filter({\n- version: options.version\n- });\n- node.appendChild(format.write(options.Filter));\n- } else if (options.CqlText) {\n- var child = this.createElementNSPlus(\"CqlText\", {\n- value: options.CqlText.value\n- });\n- node.appendChild(child);\n- }\n- return node;\n- }\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n- \"ogc\": OpenLayers.Format.Filter.v1_1_0.prototype.writers[\"ogc\"]\n- },\n \n- CLASS_NAME: \"OpenLayers.Format.CSWGetRecords.v2_0_2\"\n-});\n+ CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n+\n+ });\n /* ======================================================================\n OpenLayers/Format/SLD/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -62381,14 +54709,823 @@\n \"feature\": OpenLayers.Format.GML.v2.prototype.writers.feature\n },\n \n CLASS_NAME: \"OpenLayers.Format.OWSContext.v0_3_1\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/WPSCapabilities/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WPSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WPSCapabilities.v1_0_0\n+ * Read WPS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wps: \"http://www.opengis.net/wps/1.0.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+\n+ /**\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0\n+ * Create a new parser for WPS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the WPS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the WPS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ return capabilities;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wps\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"ProcessOfferings\": function(node, obj) {\n+ obj.processOfferings = {};\n+ this.readChildNodes(node, obj.processOfferings);\n+ },\n+ \"Process\": function(node, processOfferings) {\n+ var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n+ var process = {\n+ processVersion: processVersion\n+ };\n+ this.readChildNodes(node, process);\n+ processOfferings[process.identifier] = process;\n+ },\n+ \"Languages\": function(node, obj) {\n+ obj.languages = [];\n+ this.readChildNodes(node, obj.languages);\n+ },\n+ \"Default\": function(node, languages) {\n+ var language = {\n+ isDefault: true\n+ };\n+ this.readChildNodes(node, language);\n+ languages.push(language);\n+ },\n+ \"Supported\": function(node, languages) {\n+ var language = {};\n+ this.readChildNodes(node, language);\n+ languages.push(language);\n+ }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WFST/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WFST/v1.js\n+ * @requires OpenLayers/Format/Filter/v1_0_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WFST.v1_0_0\n+ * A format for creating WFS v1.0.0 transactions. Create a new instance with the\n+ * <OpenLayers.Format.WFST.v1_0_0> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.Filter.v1_0_0>\n+ * - <OpenLayers.Format.WFST.v1>\n+ */\n+OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n+\n+ /**\n+ * Property: version\n+ * {String} WFS version number.\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * APIProperty: srsNameInQuery\n+ * {Boolean} If true the reference system is passed in Query requests\n+ * via the \"srsName\" attribute to the \"wfs:Query\" element, this\n+ * property defaults to false as it isn't WFS 1.0.0 compliant.\n+ */\n+ srsNameInQuery: false,\n+\n+ /**\n+ * Property: schemaLocations\n+ * {Object} Properties are namespace aliases, values are schema locations.\n+ */\n+ schemaLocations: {\n+ \"wfs\": \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFST.v1_0_0\n+ * A class for parsing and generating WFS v1.0.0 transactions.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * featureType - {String} Local (without prefix) feature typeName (required).\n+ * featureNS - {String} Feature namespace (optional).\n+ * featurePrefix - {String} Feature namespace alias (optional - only used\n+ * if featureNS is provided). Default is 'feature'.\n+ * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n+ OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n+ },\n+\n+ /**\n+ * Method: readNode\n+ * Shorthand for applying one of the named readers given the node\n+ * namespace and local name. Readers take two args (node, obj) and\n+ * generally extend or modify the second.\n+ *\n+ * Parameters:\n+ * node - {DOMElement} The node to be read (required).\n+ * obj - {Object} The object to be modified (optional).\n+ * first - {Boolean} Should be set to true for the first node read. This\n+ * is usually the readNode call in the read method. Without this being\n+ * set, auto-configured properties will stick on subsequent reads.\n+ *\n+ * Returns:\n+ * {Object} The input object, modified (or a new one if none was provided).\n+ */\n+ readNode: function(node, obj, first) {\n+ // Not the superclass, only the mixin classes inherit from\n+ // Format.GML.v2. We need this because we don't want to get readNode\n+ // from the superclass's superclass, which is OpenLayers.Format.XML.\n+ return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"WFS_TransactionResponse\": function(node, obj) {\n+ obj.insertIds = [];\n+ obj.success = false;\n+ this.readChildNodes(node, obj);\n+ },\n+ \"InsertResult\": function(node, container) {\n+ var obj = {\n+ fids: []\n+ };\n+ this.readChildNodes(node, obj);\n+ container.insertIds = container.insertIds.concat(obj.fids);\n+ },\n+ \"TransactionResult\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Status\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"SUCCESS\": function(node, obj) {\n+ obj.success = true;\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"Query\": function(options) {\n+ options = OpenLayers.Util.extend({\n+ featureNS: this.featureNS,\n+ featurePrefix: this.featurePrefix,\n+ featureType: this.featureType,\n+ srsName: this.srsName,\n+ srsNameInQuery: this.srsNameInQuery\n+ }, options);\n+ var prefix = options.featurePrefix;\n+ var node = this.createElementNSPlus(\"wfs:Query\", {\n+ attributes: {\n+ typeName: (prefix ? prefix + \":\" : \"\") +\n+ options.featureType\n+ }\n+ });\n+ if (options.srsNameInQuery && options.srsName) {\n+ node.setAttribute(\"srsName\", options.srsName);\n+ }\n+ if (options.featureNS) {\n+ node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n+ }\n+ if (options.propertyNames) {\n+ for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n+ this.writeNode(\n+ \"ogc:PropertyName\", {\n+ property: options.propertyNames[i]\n+ },\n+ node\n+ );\n+ }\n+ }\n+ if (options.filter) {\n+ this.setFilterProperty(options.filter);\n+ this.writeNode(\"ogc:Filter\", options.filter, node);\n+ }\n+ return node;\n+ }\n+ }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n+ \"gml\": OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n+ \"feature\": OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n+ \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/WMTSCapabilities/v1_0_0.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WMTSCapabilities.js\n+ * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0\n+ * Read WMTS Capabilities version 1.0.0.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.WMTSCapabilities>\n+ */\n+OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.OWSCommon.v1_1_0, {\n+\n+ /**\n+ * Property: version\n+ * {String} The parser version (\"1.0.0\").\n+ */\n+ version: \"1.0.0\",\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ ows: \"http://www.opengis.net/ows/1.1\",\n+ wmts: \"http://www.opengis.net/wmts/1.0\",\n+ xlink: \"http://www.w3.org/1999/xlink\"\n+ },\n+\n+ /**\n+ * Property: yx\n+ * {Object} Members in the yx object are used to determine if a CRS URN\n+ * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n+ * and values are boolean. Defaults come from the \n+ * <OpenLayers.Format.WMTSCapabilities> prototype.\n+ */\n+ yx: null,\n+\n+ /**\n+ * Property: defaultPrefix\n+ * {String} The default namespace alias for creating element nodes.\n+ */\n+ defaultPrefix: \"wmts\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0\n+ * Create a new parser for WMTS capabilities version 1.0.0. \n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+ initialize: function(options) {\n+ OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n+ this.options = options;\n+ var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n+ this.yx = OpenLayers.Util.extend(yx, this.yx);\n+ },\n+\n+ /**\n+ * APIMethod: read\n+ * Read capabilities data from a string, and return info about the WMTS.\n+ * \n+ * Parameters: \n+ * data - {String} or {DOMElement} data to read/parse.\n+ *\n+ * Returns:\n+ * {Object} Information about the SOS service.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var capabilities = {};\n+ this.readNode(data, capabilities);\n+ capabilities.version = this.version;\n+ return capabilities;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wmts\": {\n+ \"Capabilities\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"Contents\": function(node, obj) {\n+ obj.contents = {};\n+ obj.contents.layers = [];\n+ obj.contents.tileMatrixSets = {};\n+ this.readChildNodes(node, obj.contents);\n+ },\n+ \"Layer\": function(node, obj) {\n+ var layer = {\n+ styles: [],\n+ formats: [],\n+ dimensions: [],\n+ tileMatrixSetLinks: []\n+ };\n+ layer.layers = [];\n+ this.readChildNodes(node, layer);\n+ obj.layers.push(layer);\n+ },\n+ \"Style\": function(node, obj) {\n+ var style = {};\n+ style.isDefault = (node.getAttribute(\"isDefault\") === \"true\");\n+ this.readChildNodes(node, style);\n+ obj.styles.push(style);\n+ },\n+ \"Format\": function(node, obj) {\n+ obj.formats.push(this.getChildValue(node));\n+ },\n+ \"TileMatrixSetLink\": function(node, obj) {\n+ var tileMatrixSetLink = {};\n+ this.readChildNodes(node, tileMatrixSetLink);\n+ obj.tileMatrixSetLinks.push(tileMatrixSetLink);\n+ },\n+ \"TileMatrixSet\": function(node, obj) {\n+ // node could be child of wmts:Contents or wmts:TileMatrixSetLink\n+ // duck type wmts:Contents by looking for layers\n+ if (obj.layers) {\n+ // TileMatrixSet as object type in schema\n+ var tileMatrixSet = {\n+ matrixIds: []\n+ };\n+ this.readChildNodes(node, tileMatrixSet);\n+ obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;\n+ } else {\n+ // TileMatrixSet as string type in schema\n+ obj.tileMatrixSet = this.getChildValue(node);\n+ }\n+ },\n+ \"TileMatrix\": function(node, obj) {\n+ var tileMatrix = {\n+ supportedCRS: obj.supportedCRS\n+ };\n+ this.readChildNodes(node, tileMatrix);\n+ obj.matrixIds.push(tileMatrix);\n+ },\n+ \"ScaleDenominator\": function(node, obj) {\n+ obj.scaleDenominator = parseFloat(this.getChildValue(node));\n+ },\n+ \"TopLeftCorner\": function(node, obj) {\n+ var topLeftCorner = this.getChildValue(node);\n+ var coords = topLeftCorner.split(\" \");\n+ // decide on axis order for the given CRS\n+ var yx;\n+ if (obj.supportedCRS) {\n+ // extract out version from URN\n+ var crs = obj.supportedCRS.replace(\n+ /urn:ogc:def:crs:(\\w+):.+:(\\w+)$/,\n+ \"urn:ogc:def:crs:$1::$2\"\n+ );\n+ yx = !!this.yx[crs];\n+ }\n+ if (yx) {\n+ obj.topLeftCorner = new OpenLayers.LonLat(\n+ coords[1], coords[0]\n+ );\n+ } else {\n+ obj.topLeftCorner = new OpenLayers.LonLat(\n+ coords[0], coords[1]\n+ );\n+ }\n+ },\n+ \"TileWidth\": function(node, obj) {\n+ obj.tileWidth = parseInt(this.getChildValue(node));\n+ },\n+ \"TileHeight\": function(node, obj) {\n+ obj.tileHeight = parseInt(this.getChildValue(node));\n+ },\n+ \"MatrixWidth\": function(node, obj) {\n+ obj.matrixWidth = parseInt(this.getChildValue(node));\n+ },\n+ \"MatrixHeight\": function(node, obj) {\n+ obj.matrixHeight = parseInt(this.getChildValue(node));\n+ },\n+ \"ResourceURL\": function(node, obj) {\n+ obj.resourceUrl = obj.resourceUrl || {};\n+ var resourceType = node.getAttribute(\"resourceType\");\n+ if (!obj.resourceUrls) {\n+ obj.resourceUrls = [];\n+ }\n+ var resourceUrl = obj.resourceUrl[resourceType] = {\n+ format: node.getAttribute(\"format\"),\n+ template: node.getAttribute(\"template\"),\n+ resourceType: resourceType\n+ };\n+ obj.resourceUrls.push(resourceUrl);\n+ },\n+ // not used for now, can be added in the future though\n+ /*\"Themes\": function(node, obj) {\n+ obj.themes = [];\n+ this.readChildNodes(node, obj.themes);\n+ },\n+ \"Theme\": function(node, obj) {\n+ var theme = {}; \n+ this.readChildNodes(node, theme);\n+ obj.push(theme);\n+ },*/\n+ \"WSDL\": function(node, obj) {\n+ obj.wsdl = {};\n+ obj.wsdl.href = node.getAttribute(\"xlink:href\");\n+ // TODO: other attributes of <WSDL> element \n+ },\n+ \"ServiceMetadataURL\": function(node, obj) {\n+ obj.serviceMetadataUrl = {};\n+ obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\");\n+ // TODO: other attributes of <ServiceMetadataURL> element \n+ },\n+ \"LegendURL\": function(node, obj) {\n+ obj.legend = {};\n+ obj.legend.href = node.getAttribute(\"xlink:href\");\n+ obj.legend.format = node.getAttribute(\"format\");\n+ },\n+ \"Dimension\": function(node, obj) {\n+ var dimension = {\n+ values: []\n+ };\n+ this.readChildNodes(node, dimension);\n+ obj.dimensions.push(dimension);\n+ },\n+ \"Default\": function(node, obj) {\n+ obj[\"default\"] = this.getChildValue(node);\n+ },\n+ \"Value\": function(node, obj) {\n+ obj.values.push(this.getChildValue(node));\n+ }\n+ },\n+ \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+\n+ });\n+/* ======================================================================\n+ OpenLayers/Format/CSWGetDomain/v2_0_2.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/XML.js\n+ * @requires OpenLayers/Format/CSWGetDomain.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.CSWGetDomain.v2_0_2\n+ * A format for creating CSWGetDomain v2.0.2 transactions. \n+ * Create a new instance with the\n+ * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor.\n+ *\n+ * Inherits from:\n+ * - <OpenLayers.Format.XML>\n+ */\n+OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n+\n+ /**\n+ * Property: namespaces\n+ * {Object} Mapping of namespace aliases to namespace URIs.\n+ */\n+ namespaces: {\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n+ },\n+\n+ /**\n+ * Property: defaultPrefix\n+ * {String} The default prefix (used by Format.XML).\n+ */\n+ defaultPrefix: \"csw\",\n+\n+ /**\n+ * Property: version\n+ * {String} CSW version number.\n+ */\n+ version: \"2.0.2\",\n+\n+ /**\n+ * Property: schemaLocation\n+ * {String} http://www.opengis.net/cat/csw/2.0.2\n+ * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n+ */\n+ schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n+\n+ /**\n+ * APIProperty: PropertyName\n+ * {String} Value of the csw:PropertyName element, used when\n+ * writing a GetDomain document.\n+ */\n+ PropertyName: null,\n+\n+ /**\n+ * APIProperty: ParameterName\n+ * {String} Value of the csw:ParameterName element, used when\n+ * writing a GetDomain document.\n+ */\n+ ParameterName: null,\n+\n+ /**\n+ * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2\n+ * A class for parsing and generating CSWGetDomain v2.0.2 transactions.\n+ *\n+ * Parameters:\n+ * options - {Object} Optional object whose properties will be set on the\n+ * instance.\n+ *\n+ * Valid options properties:\n+ * - PropertyName\n+ * - ParameterName\n+ */\n+\n+ /**\n+ * APIMethod: read\n+ * Parse the response from a GetDomain request.\n+ */\n+ read: function(data) {\n+ if (typeof data == \"string\") {\n+ data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n+ }\n+ if (data && data.nodeType == 9) {\n+ data = data.documentElement;\n+ }\n+ var obj = {};\n+ this.readNode(data, obj);\n+ return obj;\n+ },\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"csw\": {\n+ \"GetDomainResponse\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"DomainValues\": function(node, obj) {\n+ if (!(OpenLayers.Util.isArray(obj.DomainValues))) {\n+ obj.DomainValues = [];\n+ }\n+ var attrs = node.attributes;\n+ var domainValue = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ domainValue[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ this.readChildNodes(node, domainValue);\n+ obj.DomainValues.push(domainValue);\n+ },\n+ \"PropertyName\": function(node, obj) {\n+ obj.PropertyName = this.getChildValue(node);\n+ },\n+ \"ParameterName\": function(node, obj) {\n+ obj.ParameterName = this.getChildValue(node);\n+ },\n+ \"ListOfValues\": function(node, obj) {\n+ if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {\n+ obj.ListOfValues = [];\n+ }\n+ this.readChildNodes(node, obj.ListOfValues);\n+ },\n+ \"Value\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.push({\n+ Value: value\n+ });\n+ },\n+ \"ConceptualScheme\": function(node, obj) {\n+ obj.ConceptualScheme = {};\n+ this.readChildNodes(node, obj.ConceptualScheme);\n+ },\n+ \"Name\": function(node, obj) {\n+ obj.Name = this.getChildValue(node);\n+ },\n+ \"Document\": function(node, obj) {\n+ obj.Document = this.getChildValue(node);\n+ },\n+ \"Authority\": function(node, obj) {\n+ obj.Authority = this.getChildValue(node);\n+ },\n+ \"RangeOfValues\": function(node, obj) {\n+ obj.RangeOfValues = {};\n+ this.readChildNodes(node, obj.RangeOfValues);\n+ },\n+ \"MinValue\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MinValue = value;\n+ },\n+ \"MaxValue\": function(node, obj) {\n+ var attrs = node.attributes;\n+ var value = {};\n+ for (var i = 0, len = attrs.length; i < len; ++i) {\n+ value[attrs[i].name] = attrs[i].nodeValue;\n+ }\n+ value.value = this.getChildValue(node);\n+ obj.MaxValue = value;\n+ }\n+ }\n+ },\n+\n+ /**\n+ * APIMethod: write\n+ * Given an configuration js object, write a CSWGetDomain request. \n+ *\n+ * Parameters:\n+ * options - {Object} A object mapping the request.\n+ *\n+ * Returns:\n+ * {String} A serialized CSWGetDomain request.\n+ */\n+ write: function(options) {\n+ var node = this.writeNode(\"csw:GetDomain\", options);\n+ return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n+ },\n+\n+ /**\n+ * Property: writers\n+ * As a compliment to the readers property, this structure contains public\n+ * writing functions grouped by namespace alias and named like the\n+ * node names they produce.\n+ */\n+ writers: {\n+ \"csw\": {\n+ \"GetDomain\": function(options) {\n+ var node = this.createElementNSPlus(\"csw:GetDomain\", {\n+ attributes: {\n+ service: \"CSW\",\n+ version: this.version\n+ }\n+ });\n+ if (options.PropertyName || this.PropertyName) {\n+ this.writeNode(\n+ \"csw:PropertyName\",\n+ options.PropertyName || this.PropertyName,\n+ node\n+ );\n+ } else if (options.ParameterName || this.ParameterName) {\n+ this.writeNode(\n+ \"csw:ParameterName\",\n+ options.ParameterName || this.ParameterName,\n+ node\n+ );\n+ }\n+ this.readChildNodes(node, options);\n+ return node;\n+ },\n+ \"PropertyName\": function(value) {\n+ var node = this.createElementNSPlus(\"csw:PropertyName\", {\n+ value: value\n+ });\n+ return node;\n+ },\n+ \"ParameterName\": function(value) {\n+ var node = this.createElementNSPlus(\"csw:ParameterName\", {\n+ value: value\n+ });\n+ return node;\n+ }\n+ }\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n+});\n+/* ======================================================================\n OpenLayers/Format/XLS/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -63090,926 +56227,332 @@\n \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n },\n \n CLASS_NAME: \"OpenLayers.Format.WCSCapabilities.v1_1_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/SLD/v1_0_0_GeoServer.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/SLD/v1_0_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer\n- * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options.\n- * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd\n- * for more information.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.SLD.v1_0_0>\n- */\n-OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(\n- OpenLayers.Format.SLD.v1_0_0, {\n-\n- /**\n- * Property: version\n- * {String} The specific parser version.\n- */\n- version: \"1.0.0\",\n-\n- /**\n- * Property: profile\n- * {String} The specific profile\n- */\n- profile: \"GeoServer\",\n-\n- /**\n- * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer\n- * Create a new parser for GeoServer-enhanced SLD version 1.0.0.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: OpenLayers.Util.applyDefaults({\n- \"sld\": OpenLayers.Util.applyDefaults({\n- \"Priority\": function(node, obj) {\n- var value = this.readers.ogc._expression.call(this, node);\n- if (value) {\n- obj.priority = value;\n- }\n- },\n- \"VendorOption\": function(node, obj) {\n- if (!obj.vendorOptions) {\n- obj.vendorOptions = {};\n- }\n- obj.vendorOptions[node.getAttribute(\"name\")] = this.getChildValue(node);\n- },\n- \"TextSymbolizer\": function(node, rule) {\n- OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments);\n- var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer[\"Text\"];\n- if (symbolizer.graphic === undefined) {\n- symbolizer.graphic = false;\n- }\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.readers),\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: OpenLayers.Util.applyDefaults({\n- \"sld\": OpenLayers.Util.applyDefaults({\n- \"Priority\": function(priority) {\n- return this.writers.sld._OGCExpression.call(\n- this, \"sld:Priority\", priority\n- );\n- },\n- \"VendorOption\": function(option) {\n- return this.createElementNSPlus(\"sld:VendorOption\", {\n- attributes: {\n- name: option.name\n- },\n- value: option.value\n- });\n- },\n- \"TextSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"TextSymbolizer\"].apply(this, arguments);\n- if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) {\n- this.writeNode(\"Graphic\", symbolizer, node);\n- }\n- if (\"priority\" in symbolizer) {\n- this.writeNode(\"Priority\", symbolizer.priority, node);\n- }\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"PointSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PointSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"LineSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"LineSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- },\n- \"PolygonSymbolizer\": function(symbolizer) {\n- var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers;\n- var node = writers[\"sld\"][\"PolygonSymbolizer\"].apply(this, arguments);\n- return this.addVendorOptions(node, symbolizer);\n- }\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers[\"sld\"])\n- }, OpenLayers.Format.SLD.v1_0_0.prototype.writers),\n-\n- /**\n- * Method: addVendorOptions\n- * Add in the VendorOption tags and return the node again.\n- *\n- * Parameters:\n- * node - {DOMElement} A DOM node.\n- * symbolizer - {Object}\n- *\n- * Returns:\n- * {DOMElement} A DOM node.\n- */\n- addVendorOptions: function(node, symbolizer) {\n- var options = symbolizer.vendorOptions;\n- if (options) {\n- for (var key in symbolizer.vendorOptions) {\n- this.writeNode(\"VendorOption\", {\n- name: key,\n- value: symbolizer.vendorOptions[key]\n- }, node);\n- }\n- }\n- return node;\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.SLD.v1_0_0_GeoServer\"\n-\n- });\n-/* ======================================================================\n- OpenLayers/Format/CSWGetDomain/v2_0_2.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/XML.js\n- * @requires OpenLayers/Format/CSWGetDomain.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.CSWGetDomain.v2_0_2\n- * A format for creating CSWGetDomain v2.0.2 transactions. \n- * Create a new instance with the\n- * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.XML>\n- */\n-OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- xlink: \"http://www.w3.org/1999/xlink\",\n- xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n- csw: \"http://www.opengis.net/cat/csw/2.0.2\"\n- },\n-\n- /**\n- * Property: defaultPrefix\n- * {String} The default prefix (used by Format.XML).\n- */\n- defaultPrefix: \"csw\",\n-\n- /**\n- * Property: version\n- * {String} CSW version number.\n- */\n- version: \"2.0.2\",\n-\n- /**\n- * Property: schemaLocation\n- * {String} http://www.opengis.net/cat/csw/2.0.2\n- * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\n- */\n- schemaLocation: \"http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd\",\n-\n- /**\n- * APIProperty: PropertyName\n- * {String} Value of the csw:PropertyName element, used when\n- * writing a GetDomain document.\n- */\n- PropertyName: null,\n-\n- /**\n- * APIProperty: ParameterName\n- * {String} Value of the csw:ParameterName element, used when\n- * writing a GetDomain document.\n- */\n- ParameterName: null,\n-\n- /**\n- * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2\n- * A class for parsing and generating CSWGetDomain v2.0.2 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * - PropertyName\n- * - ParameterName\n- */\n-\n- /**\n- * APIMethod: read\n- * Parse the response from a GetDomain request.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var obj = {};\n- this.readNode(data, obj);\n- return obj;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"csw\": {\n- \"GetDomainResponse\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"DomainValues\": function(node, obj) {\n- if (!(OpenLayers.Util.isArray(obj.DomainValues))) {\n- obj.DomainValues = [];\n- }\n- var attrs = node.attributes;\n- var domainValue = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- domainValue[attrs[i].name] = attrs[i].nodeValue;\n- }\n- this.readChildNodes(node, domainValue);\n- obj.DomainValues.push(domainValue);\n- },\n- \"PropertyName\": function(node, obj) {\n- obj.PropertyName = this.getChildValue(node);\n- },\n- \"ParameterName\": function(node, obj) {\n- obj.ParameterName = this.getChildValue(node);\n- },\n- \"ListOfValues\": function(node, obj) {\n- if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {\n- obj.ListOfValues = [];\n- }\n- this.readChildNodes(node, obj.ListOfValues);\n- },\n- \"Value\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.push({\n- Value: value\n- });\n- },\n- \"ConceptualScheme\": function(node, obj) {\n- obj.ConceptualScheme = {};\n- this.readChildNodes(node, obj.ConceptualScheme);\n- },\n- \"Name\": function(node, obj) {\n- obj.Name = this.getChildValue(node);\n- },\n- \"Document\": function(node, obj) {\n- obj.Document = this.getChildValue(node);\n- },\n- \"Authority\": function(node, obj) {\n- obj.Authority = this.getChildValue(node);\n- },\n- \"RangeOfValues\": function(node, obj) {\n- obj.RangeOfValues = {};\n- this.readChildNodes(node, obj.RangeOfValues);\n- },\n- \"MinValue\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.MinValue = value;\n- },\n- \"MaxValue\": function(node, obj) {\n- var attrs = node.attributes;\n- var value = {};\n- for (var i = 0, len = attrs.length; i < len; ++i) {\n- value[attrs[i].name] = attrs[i].nodeValue;\n- }\n- value.value = this.getChildValue(node);\n- obj.MaxValue = value;\n- }\n- }\n- },\n-\n- /**\n- * APIMethod: write\n- * Given an configuration js object, write a CSWGetDomain request. \n- *\n- * Parameters:\n- * options - {Object} A object mapping the request.\n- *\n- * Returns:\n- * {String} A serialized CSWGetDomain request.\n- */\n- write: function(options) {\n- var node = this.writeNode(\"csw:GetDomain\", options);\n- return OpenLayers.Format.XML.prototype.write.apply(this, [node]);\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"csw\": {\n- \"GetDomain\": function(options) {\n- var node = this.createElementNSPlus(\"csw:GetDomain\", {\n- attributes: {\n- service: \"CSW\",\n- version: this.version\n- }\n- });\n- if (options.PropertyName || this.PropertyName) {\n- this.writeNode(\n- \"csw:PropertyName\",\n- options.PropertyName || this.PropertyName,\n- node\n- );\n- } else if (options.ParameterName || this.ParameterName) {\n- this.writeNode(\n- \"csw:ParameterName\",\n- options.ParameterName || this.ParameterName,\n- node\n- );\n- }\n- this.readChildNodes(node, options);\n- return node;\n- },\n- \"PropertyName\": function(value) {\n- var node = this.createElementNSPlus(\"csw:PropertyName\", {\n- value: value\n- });\n- return node;\n- },\n- \"ParameterName\": function(value) {\n- var node = this.createElementNSPlus(\"csw:ParameterName\", {\n- value: value\n- });\n- return node;\n- }\n- }\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.CSWGetDomain.v2_0_2\"\n-});\n-/* ======================================================================\n- OpenLayers/Format/WMTSCapabilities/v1_0_0.js\n+ OpenLayers/Format/WFSCapabilities/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WMTSCapabilities.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/WFSCapabilities.js\n */\n \n /**\n- * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0\n- * Read WMTS Capabilities version 1.0.0.\n+ * Class: OpenLayers.Format.WFSCapabilities.v1\n+ * Abstract class not to be instantiated directly.\n * \n * Inherits from:\n- * - <OpenLayers.Format.WMTSCapabilities>\n+ * - <OpenLayers.Format.XML>\n */\n-OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.OWSCommon.v1_1_0, {\n-\n- /**\n- * Property: version\n- * {String} The parser version (\"1.0.0\").\n- */\n- version: \"1.0.0\",\n+OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(\n+ OpenLayers.Format.XML, {\n \n /**\n * Property: namespaces\n * {Object} Mapping of namespace aliases to namespace URIs.\n */\n namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wmts: \"http://www.opengis.net/wmts/1.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n+ wfs: \"http://www.opengis.net/wfs\",\n+ xlink: \"http://www.w3.org/1999/xlink\",\n+ xsi: \"http://www.w3.org/2001/XMLSchema-instance\",\n+ ows: \"http://www.opengis.net/ows\"\n },\n \n+\n /**\n- * Property: yx\n- * {Object} Members in the yx object are used to determine if a CRS URN\n- * corresponds to a CRS with y,x axis order. Member names are CRS URNs\n- * and values are boolean. Defaults come from the \n- * <OpenLayers.Format.WMTSCapabilities> prototype.\n+ * APIProperty: errorProperty\n+ * {String} Which property of the returned object to check for in order to\n+ * determine whether or not parsing has failed. In the case that the\n+ * errorProperty is undefined on the returned object, the document will be\n+ * run through an OGCExceptionReport parser.\n */\n- yx: null,\n+ errorProperty: \"featureTypeList\",\n \n /**\n * Property: defaultPrefix\n- * {String} The default namespace alias for creating element nodes.\n */\n- defaultPrefix: \"wmts\",\n+ defaultPrefix: \"wfs\",\n \n /**\n- * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0\n- * Create a new parser for WMTS capabilities version 1.0.0. \n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_1\n+ * Create an instance of one of the subclasses.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options;\n- var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);\n- this.yx = OpenLayers.Util.extend(yx, this.yx);\n- },\n \n /**\n * APIMethod: read\n- * Read capabilities data from a string, and return info about the WMTS.\n+ * Read capabilities data from a string, and return a list of layers. \n * \n * Parameters: \n * data - {String} or {DOMElement} data to read/parse.\n *\n * Returns:\n- * {Object} Information about the SOS service.\n+ * {Array} List of named layers.\n */\n read: function(data) {\n if (typeof data == \"string\") {\n data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n }\n+ var raw = data;\n if (data && data.nodeType == 9) {\n data = data.documentElement;\n }\n var capabilities = {};\n this.readNode(data, capabilities);\n- capabilities.version = this.version;\n return capabilities;\n },\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n- \"wmts\": {\n- \"Capabilities\": function(node, obj) {\n+ \"wfs\": {\n+ \"WFS_Capabilities\": function(node, obj) {\n this.readChildNodes(node, obj);\n },\n- \"Contents\": function(node, obj) {\n- obj.contents = {};\n- obj.contents.layers = [];\n- obj.contents.tileMatrixSets = {};\n- this.readChildNodes(node, obj.contents);\n- },\n- \"Layer\": function(node, obj) {\n- var layer = {\n- styles: [],\n- formats: [],\n- dimensions: [],\n- tileMatrixSetLinks: []\n+ \"FeatureTypeList\": function(node, request) {\n+ request.featureTypeList = {\n+ featureTypes: []\n };\n- layer.layers = [];\n- this.readChildNodes(node, layer);\n- obj.layers.push(layer);\n- },\n- \"Style\": function(node, obj) {\n- var style = {};\n- style.isDefault = (node.getAttribute(\"isDefault\") === \"true\");\n- this.readChildNodes(node, style);\n- obj.styles.push(style);\n- },\n- \"Format\": function(node, obj) {\n- obj.formats.push(this.getChildValue(node));\n+ this.readChildNodes(node, request.featureTypeList);\n },\n- \"TileMatrixSetLink\": function(node, obj) {\n- var tileMatrixSetLink = {};\n- this.readChildNodes(node, tileMatrixSetLink);\n- obj.tileMatrixSetLinks.push(tileMatrixSetLink);\n+ \"FeatureType\": function(node, featureTypeList) {\n+ var featureType = {};\n+ this.readChildNodes(node, featureType);\n+ featureTypeList.featureTypes.push(featureType);\n },\n- \"TileMatrixSet\": function(node, obj) {\n- // node could be child of wmts:Contents or wmts:TileMatrixSetLink\n- // duck type wmts:Contents by looking for layers\n- if (obj.layers) {\n- // TileMatrixSet as object type in schema\n- var tileMatrixSet = {\n- matrixIds: []\n- };\n- this.readChildNodes(node, tileMatrixSet);\n- obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;\n- } else {\n- // TileMatrixSet as string type in schema\n- obj.tileMatrixSet = this.getChildValue(node);\n+ \"Name\": function(node, obj) {\n+ var name = this.getChildValue(node);\n+ if (name) {\n+ var parts = name.split(\":\");\n+ obj.name = parts.pop();\n+ if (parts.length > 0) {\n+ obj.featureNS = this.lookupNamespaceURI(node, parts[0]);\n+ }\n }\n },\n- \"TileMatrix\": function(node, obj) {\n- var tileMatrix = {\n- supportedCRS: obj.supportedCRS\n- };\n- this.readChildNodes(node, tileMatrix);\n- obj.matrixIds.push(tileMatrix);\n- },\n- \"ScaleDenominator\": function(node, obj) {\n- obj.scaleDenominator = parseFloat(this.getChildValue(node));\n- },\n- \"TopLeftCorner\": function(node, obj) {\n- var topLeftCorner = this.getChildValue(node);\n- var coords = topLeftCorner.split(\" \");\n- // decide on axis order for the given CRS\n- var yx;\n- if (obj.supportedCRS) {\n- // extract out version from URN\n- var crs = obj.supportedCRS.replace(\n- /urn:ogc:def:crs:(\\w+):.+:(\\w+)$/,\n- \"urn:ogc:def:crs:$1::$2\"\n- );\n- yx = !!this.yx[crs];\n- }\n- if (yx) {\n- obj.topLeftCorner = new OpenLayers.LonLat(\n- coords[1], coords[0]\n- );\n- } else {\n- obj.topLeftCorner = new OpenLayers.LonLat(\n- coords[0], coords[1]\n- );\n+ \"Title\": function(node, obj) {\n+ var title = this.getChildValue(node);\n+ if (title) {\n+ obj.title = title;\n }\n },\n- \"TileWidth\": function(node, obj) {\n- obj.tileWidth = parseInt(this.getChildValue(node));\n- },\n- \"TileHeight\": function(node, obj) {\n- obj.tileHeight = parseInt(this.getChildValue(node));\n- },\n- \"MatrixWidth\": function(node, obj) {\n- obj.matrixWidth = parseInt(this.getChildValue(node));\n- },\n- \"MatrixHeight\": function(node, obj) {\n- obj.matrixHeight = parseInt(this.getChildValue(node));\n- },\n- \"ResourceURL\": function(node, obj) {\n- obj.resourceUrl = obj.resourceUrl || {};\n- var resourceType = node.getAttribute(\"resourceType\");\n- if (!obj.resourceUrls) {\n- obj.resourceUrls = [];\n+ \"Abstract\": function(node, obj) {\n+ var abst = this.getChildValue(node);\n+ if (abst) {\n+ obj[\"abstract\"] = abst;\n }\n- var resourceUrl = obj.resourceUrl[resourceType] = {\n- format: node.getAttribute(\"format\"),\n- template: node.getAttribute(\"template\"),\n- resourceType: resourceType\n- };\n- obj.resourceUrls.push(resourceUrl);\n- },\n- // not used for now, can be added in the future though\n- /*\"Themes\": function(node, obj) {\n- obj.themes = [];\n- this.readChildNodes(node, obj.themes);\n- },\n- \"Theme\": function(node, obj) {\n- var theme = {}; \n- this.readChildNodes(node, theme);\n- obj.push(theme);\n- },*/\n- \"WSDL\": function(node, obj) {\n- obj.wsdl = {};\n- obj.wsdl.href = node.getAttribute(\"xlink:href\");\n- // TODO: other attributes of <WSDL> element \n- },\n- \"ServiceMetadataURL\": function(node, obj) {\n- obj.serviceMetadataUrl = {};\n- obj.serviceMetadataUrl.href = node.getAttribute(\"xlink:href\");\n- // TODO: other attributes of <ServiceMetadataURL> element \n- },\n- \"LegendURL\": function(node, obj) {\n- obj.legend = {};\n- obj.legend.href = node.getAttribute(\"xlink:href\");\n- obj.legend.format = node.getAttribute(\"format\");\n- },\n- \"Dimension\": function(node, obj) {\n- var dimension = {\n- values: []\n- };\n- this.readChildNodes(node, dimension);\n- obj.dimensions.push(dimension);\n- },\n- \"Default\": function(node, obj) {\n- obj[\"default\"] = this.getChildValue(node);\n- },\n- \"Value\": function(node, obj) {\n- obj.values.push(this.getChildValue(node));\n }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ }\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMTSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WPSCapabilities/v1_0_0.js\n+ OpenLayers/Format/WFSCapabilities/v1_0_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WPSCapabilities.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n+ * @requires OpenLayers/Format/WFSCapabilities/v1.js\n */\n \n /**\n- * Class: OpenLayers.Format.WPSCapabilities.v1_0_0\n- * Read WPS Capabilities version 1.0.0.\n+ * Class: OpenLayers.Format.WFSCapabilities/v1_0_0\n+ * Read WFS Capabilities version 1.0.0.\n * \n * Inherits from:\n- * - <OpenLayers.Format.XML>\n+ * - <OpenLayers.Format.WFSCapabilities.v1>\n */\n-OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.XML, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- wps: \"http://www.opengis.net/wps/1.0.0\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n+OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(\n+ OpenLayers.Format.WFSCapabilities.v1, {\n \n /**\n- * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0\n- * Create a new parser for WPS capabilities version 1.0.0. \n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0\n+ * Create a new parser for WFS capabilities version 1.0.0.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return info about the WPS.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Information about the WPS service.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities;\n- },\n \n /**\n * Property: readers\n * Contains public functions, grouped by namespace prefix, that will\n * be applied when a namespaced node is found matching the function\n * name. The function will be applied in the scope of this parser\n * with two arguments: the node being read and a context object passed\n * from the parent.\n */\n readers: {\n- \"wps\": {\n- \"Capabilities\": function(node, obj) {\n- this.readChildNodes(node, obj);\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"Service\": function(node, capabilities) {\n+ capabilities.service = {};\n+ this.readChildNodes(node, capabilities.service);\n },\n- \"ProcessOfferings\": function(node, obj) {\n- obj.processOfferings = {};\n- this.readChildNodes(node, obj.processOfferings);\n+ \"Fees\": function(node, service) {\n+ var fees = this.getChildValue(node);\n+ if (fees && fees.toLowerCase() != \"none\") {\n+ service.fees = fees;\n+ }\n },\n- \"Process\": function(node, processOfferings) {\n- var processVersion = this.getAttributeNS(node, this.namespaces.wps, \"processVersion\");\n- var process = {\n- processVersion: processVersion\n- };\n- this.readChildNodes(node, process);\n- processOfferings[process.identifier] = process;\n+ \"AccessConstraints\": function(node, service) {\n+ var constraints = this.getChildValue(node);\n+ if (constraints && constraints.toLowerCase() != \"none\") {\n+ service.accessConstraints = constraints;\n+ }\n },\n- \"Languages\": function(node, obj) {\n- obj.languages = [];\n- this.readChildNodes(node, obj.languages);\n+ \"OnlineResource\": function(node, service) {\n+ var onlineResource = this.getChildValue(node);\n+ if (onlineResource && onlineResource.toLowerCase() != \"none\") {\n+ service.onlineResource = onlineResource;\n+ }\n },\n- \"Default\": function(node, languages) {\n- var language = {\n- isDefault: true\n+ \"Keywords\": function(node, service) {\n+ var keywords = this.getChildValue(node);\n+ if (keywords && keywords.toLowerCase() != \"none\") {\n+ service.keywords = keywords.split(', ');\n+ }\n+ },\n+ \"Capability\": function(node, capabilities) {\n+ capabilities.capability = {};\n+ this.readChildNodes(node, capabilities.capability);\n+ },\n+ \"Request\": function(node, obj) {\n+ obj.request = {};\n+ this.readChildNodes(node, obj.request);\n+ },\n+ \"GetFeature\": function(node, request) {\n+ request.getfeature = {\n+ href: {}, // DCPType\n+ formats: [] // ResultFormat\n };\n- this.readChildNodes(node, language);\n- languages.push(language);\n+ this.readChildNodes(node, request.getfeature);\n },\n- \"Supported\": function(node, languages) {\n- var language = {};\n- this.readChildNodes(node, language);\n- languages.push(language);\n+ \"ResultFormat\": function(node, obj) {\n+ var children = node.childNodes;\n+ var childNode;\n+ for (var i = 0; i < children.length; i++) {\n+ childNode = children[i];\n+ if (childNode.nodeType == 1) {\n+ obj.formats.push(childNode.nodeName);\n+ }\n+ }\n+ },\n+ \"DCPType\": function(node, obj) {\n+ this.readChildNodes(node, obj);\n+ },\n+ \"HTTP\": function(node, obj) {\n+ this.readChildNodes(node, obj.href);\n+ },\n+ \"Get\": function(node, obj) {\n+ obj.get = node.getAttribute(\"onlineResource\");\n+ },\n+ \"Post\": function(node, obj) {\n+ obj.post = node.getAttribute(\"onlineResource\");\n+ },\n+ \"SRS\": function(node, obj) {\n+ var srs = this.getChildValue(node);\n+ if (srs) {\n+ obj.srs = srs;\n+ }\n }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"])\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WPSCapabilities.v1_0_0\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_0_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WMSDescribeLayer/v1_1.js\n+ OpenLayers/Format/WFSCapabilities/v1_1_0.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n \n /**\n- * @requires OpenLayers/Format/WMSDescribeLayer.js\n- * @requires OpenLayers/Format/OGCExceptionReport.js\n+ * @requires OpenLayers/Format/WFSCapabilities/v1.js\n+ * @requires OpenLayers/Format/OWSCommon/v1.js\n */\n \n /**\n- * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1\n- * Read SLD WMS DescribeLayer response for WMS 1.1.X\n- * WMS 1.1.X is tightly coupled to SLD 1.0.0\n- *\n- * Example DescribeLayer request: \n- * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states\n- *\n+ * Class: OpenLayers.Format.WFSCapabilities/v1_1_0\n+ * Read WFS Capabilities version 1.1.0.\n+ * \n * Inherits from:\n- * - <OpenLayers.Format.WMSDescribeLayer>\n+ * - <OpenLayers.Format.WFSCapabilities>\n */\n-OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class(\n- OpenLayers.Format.WMSDescribeLayer, {\n+OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(\n+ OpenLayers.Format.WFSCapabilities.v1, {\n \n /**\n- * Constructor: OpenLayers.Format.WMSDescribeLayer\n- * Create a new parser for WMS DescribeLayer responses.\n+ * Property: regExes\n+ * Compiled regular expressions for manipulating strings.\n+ */\n+ regExes: {\n+ trimSpace: (/^\\s*|\\s*$/g),\n+ removeSpace: (/\\s*/g),\n+ splitSpace: (/\\s+/),\n+ trimComma: (/\\s*,\\s*/g)\n+ },\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0\n+ * Create a new parser for WFS capabilities version 1.1.0.\n *\n * Parameters:\n * options - {Object} An optional object whose properties will be set on\n * this instance.\n */\n- initialize: function(options) {\n- OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this,\n- [options]);\n- },\n \n /**\n- * APIMethod: read\n- * Read DescribeLayer data from a string, and return the response. \n- * The OGC defines 2 formats which are allowed for output,\n- * so we need to parse these 2 types for version 1.1.X\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Object with a layerDescriptions property, which holds an Array\n- * of {<LayerDescription>} objects which have:\n- * - {String} owsType: WFS/WCS\n- * - {String} owsURL: the online resource\n- * - {String} typeName: the name of the typename on the owsType service\n- * - {String} layerName: the name of the WMS layer we did a lookup for\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- var root = data.documentElement;\n- var children = root.childNodes;\n- var describelayer = {\n- layerDescriptions: []\n- };\n- var childNode, nodeName;\n- for (var i = 0; i < children.length; ++i) {\n- childNode = children[i];\n- nodeName = childNode.nodeName;\n- if (nodeName == 'LayerDescription') {\n- var layerName = childNode.getAttribute('name');\n- var owsType = '';\n- var owsURL = '';\n- var typeName = '';\n- // check for owsType and owsURL attributes\n- if (childNode.getAttribute('owsType')) {\n- owsType = childNode.getAttribute('owsType');\n- owsURL = childNode.getAttribute('owsURL');\n- } else {\n- // look for wfs or wcs attribute\n- if (childNode.getAttribute('wfs') != '') {\n- owsType = 'WFS';\n- owsURL = childNode.getAttribute('wfs');\n- } else if (childNode.getAttribute('wcs') != '') {\n- owsType = 'WCS';\n- owsURL = childNode.getAttribute('wcs');\n- }\n- }\n- // look for Query child\n- var query = childNode.getElementsByTagName('Query');\n- if (query.length > 0) {\n- typeName = query[0].getAttribute('typeName');\n- if (!typeName) {\n- // because of Ionic bug\n- typeName = query[0].getAttribute('typename');\n- }\n+ readers: {\n+ \"wfs\": OpenLayers.Util.applyDefaults({\n+ \"DefaultSRS\": function(node, obj) {\n+ var defaultSRS = this.getChildValue(node);\n+ if (defaultSRS) {\n+ obj.srs = defaultSRS;\n }\n- var layerDescription = {\n- layerName: layerName,\n- owsType: owsType,\n- owsURL: owsURL,\n- typeName: typeName\n- };\n- describelayer.layerDescriptions.push(layerDescription);\n-\n- //TODO do this in deprecated.js instead:\n- // array style index for backwards compatibility\n- describelayer.length = describelayer.layerDescriptions.length;\n- describelayer[describelayer.length - 1] = layerDescription;\n-\n- } else if (nodeName == 'ServiceException') {\n- // an exception must have occurred, so parse it\n- var parser = new OpenLayers.Format.OGCExceptionReport();\n- return {\n- error: parser.read(data)\n- };\n }\n- }\n- return describelayer;\n+ }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers[\"wfs\"]),\n+ \"ows\": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows\n },\n \n- CLASS_NAME: \"OpenLayers.Format.WMSDescribeLayer.v1_1_1\"\n+ CLASS_NAME: \"OpenLayers.Format.WFSCapabilities.v1_1_0\"\n \n });\n-\n-// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257\n-OpenLayers.Format.WMSDescribeLayer.v1_1_0 =\n- OpenLayers.Format.WMSDescribeLayer.v1_1_1;\n /* ======================================================================\n OpenLayers/Format/ArcXML/Features.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n@@ -64053,357 +56596,14 @@\n var axl = new OpenLayers.Format.ArcXML();\n var parsed = axl.read(data);\n \n return parsed.features.feature;\n }\n });\n /* ======================================================================\n- OpenLayers/Format/WFST/v1_0_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WFST/v1.js\n- * @requires OpenLayers/Format/Filter/v1_0_0.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WFST.v1_0_0\n- * A format for creating WFS v1.0.0 transactions. Create a new instance with the\n- * <OpenLayers.Format.WFST.v1_0_0> constructor.\n- *\n- * Inherits from:\n- * - <OpenLayers.Format.Filter.v1_0_0>\n- * - <OpenLayers.Format.WFST.v1>\n- */\n-OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {\n-\n- /**\n- * Property: version\n- * {String} WFS version number.\n- */\n- version: \"1.0.0\",\n-\n- /**\n- * APIProperty: srsNameInQuery\n- * {Boolean} If true the reference system is passed in Query requests\n- * via the \"srsName\" attribute to the \"wfs:Query\" element, this\n- * property defaults to false as it isn't WFS 1.0.0 compliant.\n- */\n- srsNameInQuery: false,\n-\n- /**\n- * Property: schemaLocations\n- * {Object} Properties are namespace aliases, values are schema locations.\n- */\n- schemaLocations: {\n- \"wfs\": \"http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd\"\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.WFST.v1_0_0\n- * A class for parsing and generating WFS v1.0.0 transactions.\n- *\n- * Parameters:\n- * options - {Object} Optional object whose properties will be set on the\n- * instance.\n- *\n- * Valid options properties:\n- * featureType - {String} Local (without prefix) feature typeName (required).\n- * featureNS - {String} Feature namespace (optional).\n- * featurePrefix - {String} Feature namespace alias (optional - only used\n- * if featureNS is provided). Default is 'feature'.\n- * geometryName - {String} Name of geometry attribute. Default is 'the_geom'.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);\n- OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);\n- },\n-\n- /**\n- * Method: readNode\n- * Shorthand for applying one of the named readers given the node\n- * namespace and local name. Readers take two args (node, obj) and\n- * generally extend or modify the second.\n- *\n- * Parameters:\n- * node - {DOMElement} The node to be read (required).\n- * obj - {Object} The object to be modified (optional).\n- * first - {Boolean} Should be set to true for the first node read. This\n- * is usually the readNode call in the read method. Without this being\n- * set, auto-configured properties will stick on subsequent reads.\n- *\n- * Returns:\n- * {Object} The input object, modified (or a new one if none was provided).\n- */\n- readNode: function(node, obj, first) {\n- // Not the superclass, only the mixin classes inherit from\n- // Format.GML.v2. We need this because we don't want to get readNode\n- // from the superclass's superclass, which is OpenLayers.Format.XML.\n- return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"WFS_TransactionResponse\": function(node, obj) {\n- obj.insertIds = [];\n- obj.success = false;\n- this.readChildNodes(node, obj);\n- },\n- \"InsertResult\": function(node, container) {\n- var obj = {\n- fids: []\n- };\n- this.readChildNodes(node, obj);\n- container.insertIds = container.insertIds.concat(obj.fids);\n- },\n- \"TransactionResult\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Status\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"SUCCESS\": function(node, obj) {\n- obj.success = true;\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.readers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v2.prototype.readers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v2.prototype.readers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.readers[\"ogc\"]\n- },\n-\n- /**\n- * Property: writers\n- * As a compliment to the readers property, this structure contains public\n- * writing functions grouped by namespace alias and named like the\n- * node names they produce.\n- */\n- writers: {\n- \"wfs\": OpenLayers.Util.applyDefaults({\n- \"Query\": function(options) {\n- options = OpenLayers.Util.extend({\n- featureNS: this.featureNS,\n- featurePrefix: this.featurePrefix,\n- featureType: this.featureType,\n- srsName: this.srsName,\n- srsNameInQuery: this.srsNameInQuery\n- }, options);\n- var prefix = options.featurePrefix;\n- var node = this.createElementNSPlus(\"wfs:Query\", {\n- attributes: {\n- typeName: (prefix ? prefix + \":\" : \"\") +\n- options.featureType\n- }\n- });\n- if (options.srsNameInQuery && options.srsName) {\n- node.setAttribute(\"srsName\", options.srsName);\n- }\n- if (options.featureNS) {\n- node.setAttribute(\"xmlns:\" + prefix, options.featureNS);\n- }\n- if (options.propertyNames) {\n- for (var i = 0, len = options.propertyNames.length; i < len; i++) {\n- this.writeNode(\n- \"ogc:PropertyName\", {\n- property: options.propertyNames[i]\n- },\n- node\n- );\n- }\n- }\n- if (options.filter) {\n- this.setFilterProperty(options.filter);\n- this.writeNode(\"ogc:Filter\", options.filter, node);\n- }\n- return node;\n- }\n- }, OpenLayers.Format.WFST.v1.prototype.writers[\"wfs\"]),\n- \"gml\": OpenLayers.Format.GML.v2.prototype.writers[\"gml\"],\n- \"feature\": OpenLayers.Format.GML.v2.prototype.writers[\"feature\"],\n- \"ogc\": OpenLayers.Format.Filter.v1_0_0.prototype.writers[\"ogc\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.WFST.v1_0_0\"\n- });\n-/* ======================================================================\n- OpenLayers/Format/SOSCapabilities/v1_0_0.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/SOSCapabilities.js\n- * @requires OpenLayers/Format/OWSCommon/v1_1_0.js\n- * @requires OpenLayers/Format/GML/v3.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.SOSCapabilities.v1_0_0\n- * Read SOS Capabilities version 1.0.0.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.SOSCapabilities>\n- */\n-OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(\n- OpenLayers.Format.SOSCapabilities, {\n-\n- /**\n- * Property: namespaces\n- * {Object} Mapping of namespace aliases to namespace URIs.\n- */\n- namespaces: {\n- ows: \"http://www.opengis.net/ows/1.1\",\n- sos: \"http://www.opengis.net/sos/1.0\",\n- gml: \"http://www.opengis.net/gml\",\n- xlink: \"http://www.w3.org/1999/xlink\"\n- },\n-\n- /**\n- * Property: regExes\n- * Compiled regular expressions for manipulating strings.\n- */\n- regExes: {\n- trimSpace: (/^\\s*|\\s*$/g),\n- removeSpace: (/\\s*/g),\n- splitSpace: (/\\s+/),\n- trimComma: (/\\s*,\\s*/g)\n- },\n-\n- /**\n- * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0\n- * Create a new parser for SOS capabilities version 1.0.0. \n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n- initialize: function(options) {\n- OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);\n- this.options = options;\n- },\n-\n- /**\n- * APIMethod: read\n- * Read capabilities data from a string, and return info about the SOS.\n- * \n- * Parameters: \n- * data - {String} or {DOMElement} data to read/parse.\n- *\n- * Returns:\n- * {Object} Information about the SOS service.\n- */\n- read: function(data) {\n- if (typeof data == \"string\") {\n- data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);\n- }\n- if (data && data.nodeType == 9) {\n- data = data.documentElement;\n- }\n- var capabilities = {};\n- this.readNode(data, capabilities);\n- return capabilities;\n- },\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope of this parser\n- * with two arguments: the node being read and a context object passed\n- * from the parent.\n- */\n- readers: {\n- \"gml\": OpenLayers.Util.applyDefaults({\n- \"name\": function(node, obj) {\n- obj.name = this.getChildValue(node);\n- },\n- \"TimePeriod\": function(node, obj) {\n- obj.timePeriod = {};\n- this.readChildNodes(node, obj.timePeriod);\n- },\n- \"beginPosition\": function(node, timePeriod) {\n- timePeriod.beginPosition = this.getChildValue(node);\n- },\n- \"endPosition\": function(node, timePeriod) {\n- timePeriod.endPosition = this.getChildValue(node);\n- }\n- }, OpenLayers.Format.GML.v3.prototype.readers[\"gml\"]),\n- \"sos\": {\n- \"Capabilities\": function(node, obj) {\n- this.readChildNodes(node, obj);\n- },\n- \"Contents\": function(node, obj) {\n- obj.contents = {};\n- this.readChildNodes(node, obj.contents);\n- },\n- \"ObservationOfferingList\": function(node, contents) {\n- contents.offeringList = {};\n- this.readChildNodes(node, contents.offeringList);\n- },\n- \"ObservationOffering\": function(node, offeringList) {\n- var id = this.getAttributeNS(node, this.namespaces.gml, \"id\");\n- offeringList[id] = {\n- procedures: [],\n- observedProperties: [],\n- featureOfInterestIds: [],\n- responseFormats: [],\n- resultModels: [],\n- responseModes: []\n- };\n- this.readChildNodes(node, offeringList[id]);\n- },\n- \"time\": function(node, offering) {\n- offering.time = {};\n- this.readChildNodes(node, offering.time);\n- },\n- \"procedure\": function(node, offering) {\n- offering.procedures.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"observedProperty\": function(node, offering) {\n- offering.observedProperties.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"featureOfInterest\": function(node, offering) {\n- offering.featureOfInterestIds.push(this.getAttributeNS(node,\n- this.namespaces.xlink, \"href\"));\n- },\n- \"responseFormat\": function(node, offering) {\n- offering.responseFormats.push(this.getChildValue(node));\n- },\n- \"resultModel\": function(node, offering) {\n- offering.resultModels.push(this.getChildValue(node));\n- },\n- \"responseMode\": function(node, offering) {\n- offering.responseModes.push(this.getChildValue(node));\n- }\n- },\n- \"ows\": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers[\"ows\"]\n- },\n-\n- CLASS_NAME: \"OpenLayers.Format.SOSCapabilities.v1_0_0\"\n-\n- });\n-/* ======================================================================\n OpenLayers/Format/WMC/v1.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -66506,14 +58706,109 @@\n }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers[\"wms\"])\n },\n \n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1\"\n \n });\n /* ======================================================================\n+ OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js\n+ ====================================================================== */\n+\n+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n+ * full list of contributors). Published under the 2-clause BSD license.\n+ * See license.txt in the OpenLayers distribution or repository for the\n+ * full text of the license. */\n+\n+/**\n+ * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js\n+ */\n+\n+/**\n+ * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC\n+ * Read WMS-C Capabilities version 1.1.1.\n+ * \n+ * Inherits from:\n+ * - <OpenLayers.Format.WMSCapabilities.v1_1_1>\n+ */\n+OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(\n+ OpenLayers.Format.WMSCapabilities.v1_1_1, {\n+\n+ /**\n+ * Property: version\n+ * {String} The specific parser version.\n+ */\n+ version: \"1.1.1\",\n+\n+ /**\n+ * Property: profile\n+ * {String} The specific profile\n+ */\n+ profile: \"WMSC\",\n+\n+ /**\n+ * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1\n+ * Create a new parser for WMS-C capabilities version 1.1.1.\n+ *\n+ * Parameters:\n+ * options - {Object} An optional object whose properties will be set on\n+ * this instance.\n+ */\n+\n+ /**\n+ * Property: readers\n+ * Contains public functions, grouped by namespace prefix, that will\n+ * be applied when a namespaced node is found matching the function\n+ * name. The function will be applied in the scope of this parser\n+ * with two arguments: the node being read and a context object passed\n+ * from the parent.\n+ */\n+ readers: {\n+ \"wms\": OpenLayers.Util.applyDefaults({\n+ \"VendorSpecificCapabilities\": function(node, obj) {\n+ obj.vendorSpecific = {\n+ tileSets: []\n+ };\n+ this.readChildNodes(node, obj.vendorSpecific);\n+ },\n+ \"TileSet\": function(node, vendorSpecific) {\n+ var tileset = {\n+ srs: {},\n+ bbox: {},\n+ resolutions: []\n+ };\n+ this.readChildNodes(node, tileset);\n+ vendorSpecific.tileSets.push(tileset);\n+ },\n+ \"Resolutions\": function(node, tileset) {\n+ var res = this.getChildValue(node).split(\" \");\n+ for (var i = 0, len = res.length; i < len; i++) {\n+ if (res[i] != \"\") {\n+ tileset.resolutions.push(parseFloat(res[i]));\n+ }\n+ }\n+ },\n+ \"Width\": function(node, tileset) {\n+ tileset.width = parseInt(this.getChildValue(node));\n+ },\n+ \"Height\": function(node, tileset) {\n+ tileset.height = parseInt(this.getChildValue(node));\n+ },\n+ \"Layers\": function(node, tileset) {\n+ tileset.layers = this.getChildValue(node);\n+ },\n+ \"Styles\": function(node, tileset) {\n+ tileset.styles = this.getChildValue(node);\n+ }\n+ }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers[\"wms\"])\n+ },\n+\n+ CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC\"\n+\n+ });\n+/* ======================================================================\n OpenLayers/Format/WMSCapabilities/v1_3.js\n ====================================================================== */\n \n /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n * full list of contributors). Published under the 2-clause BSD license.\n * See license.txt in the OpenLayers distribution or repository for the\n * full text of the license. */\n@@ -66674,109 +58969,14 @@\n */\n version: \"1.3.0\",\n \n CLASS_NAME: \"OpenLayers.Format.WMSCapabilities.v1_3_0\"\n \n });\n /* ======================================================================\n- OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js\n- ====================================================================== */\n-\n-/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for\n- * full list of contributors). Published under the 2-clause BSD license.\n- * See license.txt in the OpenLayers distribution or repository for the\n- * full text of the license. */\n-\n-/**\n- * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js\n- */\n-\n-/**\n- * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC\n- * Read WMS-C Capabilities version 1.1.1.\n- * \n- * Inherits from:\n- * - <OpenLayers.Format.WMSCapabilities.v1_1_1>\n- */\n-OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(\n- OpenLayers.Format.WMSCapabilities.v1_1_1, {\n-\n- /**\n- * Property: version\n- * {String} The specific parser version.\n- */\n- version: \"1.1.1\",\n-\n- /**\n- * Property: profile\n- * {String} The specific profile\n- */\n- profile: \"WMSC\",\n-\n- /**\n- * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1\n- * Create a new parser for WMS-C capabilities version 1.1.1.\n- *\n- * Parameters:\n- * options - {Object} An optional object whose properties will be set on\n- * this instance.\n- */\n-\n- /**\n- * Property: readers\n- * Contains public functions, grouped by namespace prefix, that will\n- * be applied when a namespaced node is found matching the function\n- * name. The function will be applied in the scope o